mieki256's diary



2022/08/19(金) [n年前の日記]

#1 [ruby][windows] Ruby/SDLでスクリーンセーバを作成

Windows10 x64 21H2上で、Ruby 1.8.7, 1.9.3 + Ruby/SDL (rubysdl) 2.1.1.1 を使って、Windows用のスクリーンセーバ(の雛形)を作成してみた。

動作させている様子は以下。解像度が低いけど雰囲気ぐらいは伝わるかと。




Windows用スクリーンセーバに求められる仕様については、pygame でスクリーンセーバを作った際にメモしたソレが多少は参考になるかと。

_Windows用スクリーンセーバについてのおさらい。

動作確認環境。 :

動作確認環境は以下。
  • Windows10 x64 21H2
  • RubyInstaller 1.9.3 p551 i386-mingw32 + rubysdl 2.1.1.1 + ocra 1.3.10
  • ActiveScriptRuby 1.8.7 p330 i386-mswin32 + rubysdl 2.1.1.1 + ocra 1.3.1
  • RyubyInstaller 1.8.7 p330 i386-mingw32 + rubysdl 1.3.1.1 + ocra 1.3.1

セットアップファイル名も一応列挙。
  • rubyinstaller-1.9.3-p551.exe
  • rubysdl-2.1.1.1-mswin32-1.9.1-p243.zip
  • ActiveRuby.msi
  • rubysdl-2.1.1.1-mswin32-1.8.7-p174.zip
  • rubyinstaller-1.8.7-p330.exe
  • rubysdl-1.3.1.1-mswin32-1.8.6-p36.zip

_ActiveScriptRuby and Other packages
_Download Archives - RubyInstaller

Ruby は32bit版を利用。
Ruby は32bit版を利用。
大事なことなので2回言いました。
Ruby/SDLのバイナリが32bitなので、64bit版のRubyに入れて使おうとしても動かないです。たぶん。

ちなみに、Windows用のRuby/SDLは Ruby 1.8 / 1.9用しか存在しないので、Ruby 2.0以降でこういったことはできないと思われます。

利用できるRubyのバージョンや、Ruby/SDL のインストール方法は、以下を参照のこと。

_Ruby/SDLでスクリーンセーバを作れないものか
_Ruby/SDLをインストールしようとしてハマった

ソース。 :

ソースは以下。処理内容は、画面内をRubyのロゴ画像が跳ね回るだけの処理。

_rbsdlsav.rb
require "sdl"
require "Win32API"

APPLI_NAME = "Screensaver sample by using Ruby/SDL"
APPLIT_VER = "0.0.1"

IMG_FILE = "res/ruby_logo.png"

MUTEX_NAME = {
  "None" => "screensaver_rubysdl_sample_fullscreen",
  "fullscreen" => "screensaver_rubysdl_sample_fullscreen",
  "config" => "screensaver_rubysdl_sample_config",
  "preview" => "screensaver_rubysdl_sample_preview",
}

ERROR_ALREADY_EXISTS = 183

# Draw fullscreen
def draw_fullscreen
  SDL.init(SDL::INIT_VIDEO)
  screen = SDL::Screen.open(0, 0, 0, SDL::FULLSCREEN | SDL::HWSURFACE | SDL::DOUBLEBUF)

  img = SDL::Surface.load(get_resource_path(IMG_FILE)).display_format

  x, y = screen.w / 2, screen.h / 2
  dx, dy = screen.w / (60.0 * 3), screen.h / (60.0 * 4)
  count = 0
  looping = true

  SDL::Mouse.hide  # hide mouse cursor

  # main loop
  while looping
    while event = SDL::Event2.poll
      case event
      when SDL::Event2::Quit
        looping = false
      when SDL::Event2::KeyUp
        looping = false
      when SDL::Event2::MouseMotion
        if count >= 120
          rx = event.xrel
          ry = event.yrel
          r = 10
          looping = false if (rx * rx + ry * ry >= r * r)
        end
      end
    end

    # update sprite position
    x += dx
    y += dy
    dx *= -1 if (x <= 0 or x >= screen.w - img.w)
    dy *= -1 if (y <= 0 or y >= screen.h - img.h)

    count += 1

    screen.fill_rect(0, 0, screen.w, screen.h, [0, 0, 0])  # fill bg
    screen.put(img, x, y)  # draw sprite

    screen.flip  # update screen
    SDL.delay(16)  # wait
  end

  SDL::Mouse.show  # show mouse cursor
end

# Draw preview
def draw_preview(hwnd)
  if hwnd > 0
    # /p xxxxxx
    scrw, scrh = get_window_size(hwnd)
    SDL.putenv("SDL_VIDEODRIVER=windib")
    SDL.putenv("SDL_WINDOWID=#{hwnd}")
  else
    # /p 0
    scrw, scrh = 152, 112
  end

  SDL.init(SDL::INIT_VIDEO)
  screen = SDL::Screen.open(scrw, scrh, 0, SDL::SWSURFACE)
  img = SDL::Surface.load(get_resource_path(IMG_FILE)).display_format

  # main loop
  frames = 180 * 5
  frames.times do |t|
    while event = SDL::Event2.poll
      case event
      when SDL::Event2::Quit
        return
      when SDL::Event2::KeyDown
        return if event.sym == SDL::Key::ESCAPE
      end
    end

    x = (screen.w - img.w) / 2
    y = 12 * Math.sin((t * 4) * Math::PI / 180.0) + ((screen.h - img.h) / 2)
    screen.fill_rect(0, 0, screen.w, screen.h, [235, 108, 0])  # fill bg
    screen.fill_rect(8, 8, screen.w - 16, screen.h - 16, [255, 128, 0])  # fill bg
    screen.put(img, x, y)
    screen.flip
    SDL.delay(16)
  end
end

def draw_config
  s = "#{APPLI_NAME}\nVer. #{APPLIT_VER}"
  title = "Information"
  msg_box(s, title)
end

def get_resource_path(filepath)
  # return File.dirname(__FILE__) + "/" + filepath
  return File.join(File.dirname(__FILE__), filepath)
end

# Display messagebox dialog
def msg_box(msg, title)
  msgbox = Win32API.new("user32", "MessageBoxA", %w(p p p i), "i")
  msgbox.call(0, msg, title, 0x40)
end

# Get window size
def get_window_size(hwnd)
  get_window_rect = Win32API.new("user32", "GetWindowRect", "lp", "i")
  buf = "\0" * 256
  get_window_rect.call(hwnd, buf)
  x0, y0, x1, y1 = buf.unpack("l4")
  w, h = (x1 - x0), (y1 - y0)
  return w, h
end

# parse command line option
kind = "None"
hwnd = 0
if ARGV.size >= 1
  s = ARGV[0].downcase.slice(0..1)
  case s
  when "/s"
    kind = "fullscreen"
  when "/c"
    kind = "config"
  when "/p"
    kind = "preview"
    hwnd = ARGV[1].to_i
  end
end

# Mutex
create_mutex = Win32API.new("kernel32", "CreateMutex", "llp", "l")
get_last_error = Win32API.new("kernel32", "GetLastError", "", "l")
release_mutex = Win32API.new("kernel32", "ReleaseMutex", "l", "l")
close_handle = Win32API.new("kernel32", "CloseHandle", "l", "l")

mutex = create_mutex.call(0, 1, MUTEX_NAME[kind])
err = get_last_error.call()
exit if (mutex == 0 or err == ERROR_ALREADY_EXISTS)

# main job
case kind
when "config"
  draw_config
when "preview"
  draw_preview(hwnd)
else
  # fullscreen
  draw_fullscreen
end

if mutex != 0
  release_mutex.call(mutex)
  close_handle.call(mutex)
end

exit
  • draw_fullscreen() が、フルスクリーン表示モードの処理。
  • draw_config() が、設定画面モードの処理。
  • draw_preview() が、プレビューモードの処理。
  • Mutex 云々が、多重起動抑止処理。

使用画像は以下。.rb と同じ階層に res というフォルダを作って、その中に入れておく。

_ruby_logo.png

テストで実行。 :

実行は以下。
ruby rbsdlsav.rb /s
ruby rbsdlsav.rb /c
ruby rbsdlsav.rb /p 0
ruby rbsdlsav.rb
  • /s : フルスクリーン表示で実行。
  • /c : 設定画面を表示。ここではアプリ名をメッセージボックスで表示するだけ。
  • /p 0 : プレビューモード。スクリーンセーバ設定画面を表示した際に、プレビュー窓内に表示されるモード。ウインドウハンドルとして 0 を指定すると、小さいウインドウを表示してテスト描画する。本番では0より大きい数値(10進数)が渡されるはず。
  • コマンドラインオプション無し : フルスクリーン表示で実行するようにしておいた。

exe化する。 :

Windows用スクリーンセーバのファイル拡張子は .scr だけど、実体は .exe ファイル。.exe を .scr にリネームして、スクリーンセーバとして利用する。

今回、Rubyスクリプトのexe化には ocra を利用した。
  • Ruby 1.8.7 の場合、ocra 1.3.1 が利用できる。ocra 1.3.2 以降はエラーが出る。
  • RUby 1.9.3 の場合、ocra 1.3.10 が利用できる。ocra 1.3.11 はエラーが出る。

ocra のインストールは以下。
# Ruby 1.8.7
gem install ocra -v 1.3.1

# Ruby 1.9.3
gem install ocra -v 1.3.10

以下で exe ファイルが生成される。
ocra --windows rbsdlsav.rb res
  • --windows : GUIアプリとして実行する指定。
  • *.rb : 変換したいスクリプトファイル名。
  • res : 画像ファイル等が入ってるフォルダ。*.rb と一緒にフォルダ名を列挙すると exe に同梱される。

スクリーンセーバとして利用する。 :

.exe が生成されたら、.scr にリネームする。以下の場所に .scr をコピーすれば、スクリーンセーバとして利用できるようになる。
  • Windows が64bit版、スクリーンセーバが32bit版のプログラムなら、C:\Windows\SysWOW64\ 以下に .scr をコピーする。
  • Windows が64bit版、スクリーンセーバが64bit版のプログラムなら、C:\Windows\System32\ 以下に .scr をコピーする。
  • Windows が32bit版なら、C:\Windows\System32\ 以下に .scr をコピーする。

今回作ったスクリーンセーバは、32bit版 Ruby で作ったので、C:\Windows\SysWOW64\ 以下にコピーして動作確認した。

exerbではexe化できない。 :

exerb 5.3.0 でもexe化ができないか試していたのだけど、どうやら無理っぽいことが分かってきた。

ちょっと長くなりそうなので _別記事 に分けてメモしておく。

以上です。

過去ログ表示

Prev - 2022/08 - Next
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project