#!ruby # -*- mode: ruby; coding: utf-8 -*- # Last updated: <2022/08/19 03:23:56 +0900> # # ScreenSaver sample by using Ruby/SDL # # * Windows10 x64 21H2 # * Ruby 1.8.7 p330 i386-mingw32 + rubysdl 1.3.1.1 + exerb 5.3.0 require "sdl" require "Win32API" require "stringio" 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 # include "ruby_logo.png" IMG_DATA_BASE64 = [ "Qk22EAAAAAAAALYAAAAoAAAAQAAAAEAAAAABAAgAAAAAAAAAAAAAAAAAAAAA", "ACAAAAAAAAAA/wAAAAIJdwAEB4UAAQiPAAAOiAAFCJoAAQakAA4JpQAADp0A", "AAy0AAAQqQAPD7QAGxapAAQWyAAfHMkAJi2gABwpxQAtNMYAMS/fAEZLxQA+", "R9cAWF/cAGtuvABvdeUAen/hAJKVzQCRlucArrHlAL/C8wDT1O8A6On4AP//", "/wAAAAAAAAAAAAACDwIEAwwHDAUFAw8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAgECAQUHBwcHBQUDBQMD", "AgMCAwIDAgMCAwIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AgMEDwIPAgMGBwcHBwcFBQUFAwUCBAQEAgQEBAMEBAQCBAQEBAQEBAQPBAQE", "DwAAAAAAAAAAAAAAAAAAAAAAAwICAwIDAgQDBgYHBwcHBwUFBQUDAwMDAgQC", "BAIEAwQCBAIEAgQCBAIEAgQDBAMPBQwMEREREhQAAAAAAAAAAwwDAwIMAgMC", "BwYLBwwHBwUHBQUFBQMFAwMDAwQEBAQCAwQMBQwMDAcRDhEOEg4SEhISEhIS", "EhQMEwAAAAAAAAUDBQMDAwMCBQcLBgcHBwcHBQUFBQUFAwMDAwIDBQsHCwsO", "Cw4LDg4ODhIOEg4SEhISEhISEhIMBBEAAAAAAAwFDAUMBQUDBQcLCwsHDAcM", "BwcFBQUFAwUDAwMEAgMGCwsLCw4LDg4ODg4OEg4SDhISEhISEhEDBAMUAAAA", "AAAFDAUFBQUFBQULCwsLCwcHBwcFBQUFBQUDAwMDAgICAgULBwsLDgsOCw4L", "Dg4ODg4OEg4SEg4DBAEMEgAAAAAADAUHBQcFDAULCw4LDgsMBwwHBwUFBQUF", "BQMDAwQCBAICAwcLCwsOCw4LDg4ODhIOEg4SDhEEBAQEDBIAAAAADAUMBQcF", "BwUHCw4OEAsOCwwHBwUHBQUFBQMDAwMCAgMCAgICBQcLCwsLCwsOCw4ODg4O", "DhECBAQEAQ8SAAAAAAUMBQcFBwcHBxAQEA4QDhAHDAcHBQcFBQUFAwMDAwMD", "AgQCAgIFBwsLDgsOCw4ODg4ODgwCBAQEBAQOEgAAAAAFBQwFBwUHBRAOEA4Q", "DhAOCwcHBwUFBQUFAwUDAwMDAwMCBAICAgMGCwcLCwsLDgsOBwwCBAIEAgQC", "DhIAAAAABQwFBwcIBxESEhASEhISEA4MBwwHBwUFBQUDBQMFAwMDBAIEAgQC", "AwULBwsLDgsOCwUEBAQEBAQEBA4SAAAAAAcFBQULBwcOFBISEBIQEhAUBwcH", "BwUFBQUFBQUFAwMDAwMDAgQCAgICBQcHCwcLBwQCBAIEAgQCBAMODgAAAAAJ", "BwUHCwsHFBQUFBQSFBIUEBEHBwUHBQUFBQUFBQUDBQMDAwQCBAICAgIFBwcL", "BwMCBAQEBAQEBAIMDhIAAAAACwoMBQsLERQVFBUUFBQUEhQRBwcHBQUFBQUF", "BQUFBQMDAwMDAgMCAgIBAgMHBQQCBAIEAgQCBAIEBQ4OAAAAABAQCAcLDBUX", "FRcVFRUVFRUVFQwMBwcFBwUGBggFBQMFAwMDAwIEAgQCAgIDAwQEBAQEBAQE", "BAQEBAwODgAAAAAQCQsFDBMYFxcXFxUXFRUVFRQTBwcFBQUIBQgFBQUFAwUD", "AwMEAgIDAwUGBgQBBAIEAgQCBAIEAgQHDgsOAAAAEBAQCAwaGhoaGhgYFxgX", "FxcXEwcHBwUGBggFBQUFAwUDAwMDAwUFBgYKBgYCAgIEBAQEBAQEBAQECwsO", "CwAAABAOEAwWHBwbHBobGhoaGhgaGBgMBwcFBQUFBQUFAwUDAwMFBgYGBgYG", "BgYFAgECAgQCBAIEAgQCBAcLCwsAAAAQEhAUHR4eHh0dHB0cHBsbGxsaFgcM", "BQUFBQUFAwUFCAYKBgoGCgYKBgoGBQICAQIEBAQEBAQEBAQLCw4LAAAAFBAU", "GB8fHx8fHh4eHh0dHR0cHBkHBQMDBQMFBQYGBgYKBgYGCgYGBgoKBgMCAgIB", "AgIEAgQCBAIEBwsLCwAAAAAUERwfHx4dHB0dHh4fHx8eHh4dDwUDBQUGBgsG", "CQYLBgsGCQoLCgsKCwYEAgICAgECBAQEBAQEAwsHCwsAAAAAFBUdHhsaFxUT", "FBMWGR0eHx8fHhYFBwYLCQsKCwkJCgsKCwoLCgkHCQcIAgICAgECAQICBAIE", "AgUGBwcHAAAAABgaHhsaFxcTFBERDA8PGR0fHx8dFgkLCwsLCwsLCQsLCwkL", "CwsJCwsLBQMDBAICAgIBAgQEBAQFBwYHBwAAAAAbHRsaFxUTFBARBw8DAwIP", "Fh0eHx0WCQsJCwkLCQsJCwkLCQsJCwkLCQUDAwMCAgIBAgEEAgQCBQUHBgcA", "AAAAHx4aFxcTFBARDA8DAwICAwMPGR4fHRcJCwsLCQsJCwkLCwsJCwsQCQsD", "AwMDAwICAgICAQQEBAUFBQcFAAAAAB8dFxUTEQsRCA8DAwICAwMDAwMWGx8d", "FgsNCw0LEAsNCxALCwsQCw0GAwMDAwMDAgICAQIBBAMFBQUFBQAAAAAfHBcT", "FBEQDw8DAwIEAgQDAwMFCA8ZHx4aDhAODg4ODg4ODg4NCw0NBwMFAwMDAwME", "AgICAgIEBQUFBQUAAAAAHxwTEQsQBw8DAwIEAwMDAwMFAwUFCBYdHhkQERAQ", "EBAQEBAQDhAOEAUDAwMDAwMDAgICAgECAgUFBQUFAAAAAB8cFBAQDA8CAwID", "AwMDBQMFAwUICAgIEx0eGhEUERQRFBERERERERAIAwUDAwMDAwMCAgICAgIF", "BQUFBQUAAAAfHRERBwwCAgIEAwMDBQUFBQgECAgKCAoPGx4bFBQTFBMUExQT", "FBERBQUDAwMDAwMDAgICAgICAwUFBQUFAAAAHx4WDA8DAwIEAwMDBQUICAgI", "CAgICAoKCg8bHhsVFRUVFRUVFRQVEQgFBQMFAwMDAwMEAwQCBAIFBQUFBQAA", "AAAfGQ8DAgICAgMDBQUICAgGCAoKCAoKCgYKBxkeHBUXFRUVFRUVFREFBQUF", "AwMDAwMDAwMDAwICAgUFBQUAAAAAHx0CBAICAgQDBQUICAgICAoKCgoKCgoK", "CgoHGR4dGBgXGBcXFxcMBgUFBQUDBQMFBQUDAwMEAgIDBQUFAAAAAAAeFgIC", "AgIDAwMICAgGCAoKCAoICgkKCgoICgcZHh0aGhoaGBoWBwUGBQUFBQYFBQUD", "AwMDAgICAwUFBQAAAAAAABkEAwMDAwMFCAgICAoKCgoKCgoKCgoKCgoKBxse", "HhwcGxsbGAUGBQgGCAYGBggFBQMDAwMCAgIFBQUAAAAAAAAeAQQDBAMIBQgG", "CAoKCAoKCgkKCQoJCgoKCQoHGx4eHR0dHRYGBQYGBgYGBggGBQUFAwMCAgIC", "AgUFAAAAAAAAABsCBAMDAwgICAoKCgoKCgoKCgoJCgoKCQoKCg8dHx8eHx4W", "BQYFCAYGBgYGCAUFAwMDAwICAgIDBQAAAAAAAAAdFgMDAwUFCAYKCAoJCgkK", "CQoJCgkKCQoJCgkKEx0fHx8dBAUFCAUGBgYGBQUFAwMDAwICAgIBAwUAAAAA", "AAAAABsPCAUFCAgKCgoKCgoKCgoJCgoKCQoJCgkKCgoWHh8fGwUFBQUIBQgG", "BgUFBQUDAwMDAgIBAgIFAAAAAAAAAAAeGQUFCAgKBgoKCgkKCQoJCgkKCQoJ", "CgkKCQoJChkfHxsGCwYGBgoGBgYIBQUFBQMDAwQCAgECAwAAAAAAAAAAAB4W", "CAgICAoKCgoKCgoJCgkKCQoJCgkKCQoJCgkRHB8eEwkLCgkKCwoLCgoGCgYK", "BgYGCAUIAwMAAAAAAAAAAAAeHQ8ICAgGCggKCQoJCgkKCQkJCgkJCQkJCQkK", "CRYdHxYLCQsJCwoLCgkGCwoKBgoGBgYGBQIBAAAAAAAAAAAAAB8bDAoICAoK", "CgoJCgoKCQoJCgkKCQoJCgkKCQoJGx8dCQsJCwsLCQsLCQkLCQkGCwoLBQEB", "AQMAAAAAAAAAAAAAHhkGCggKCgoJCgkKCQoJCgkJCQoJCQkJCQkJChEdHREJ", "EAkQCQsJCwkLCQsJCwoLAwEBAQECAAAAAAAAAAAAAAAeGQgKCgoKCgoKCQoJ", "CgkKCQoJDQkJCQ0JCgkNGR8WEBAQCRALEAkLCQsJCwsLAwEBAQEBAQAAAAAA", "AAAAAAAAAB0WBgoICgkKCQoJCgkJCQkJCQkJCQkJCQkJCRQdGw0QCRAJEAkQ", "CxALCwkLAwEBAQEBAQEAAAAAAAAAAAAAAAAAHRYKCgoKCgoJCgkKCQoJDQkJ", "CQ0JDQkNCQ0NGx0UEBAQEA4OEBAQEBALAwICAgEBAQEBAAAAAAAAAAAAAAAA", "AAAdFgoKCAoJCgkJCQoJCQkJCQkJDQkJCQ0JDRMeFxQRFBAQEBAOEAkRAwMC", "AgECAQEBAQAAAAAAAAAAAAAAAAAAAB0WCgoKCgkKCQoJCgkJCQ0JDQkNCQ0J", "DQ0QHBsVFRQUFBQQERAQAw8CAwQCAgIBAQEAAAAAAAAAAAAAAAAAAAAAHRkK", "CgkKCQoJCQkJCQkJCQkJCQ0JDQkNDRodGBUXFRUUFBERBQUDAwIDAgIBAQEB", "AAAAAAAAAAAAAAAAAAAAAAAeGwwKCgoJCgkKCQ0JDQkNCQ0JDQ0NDQ0YHxwb", "GhoYGBURBQwFBQMPAwMCAgICAQAAAAAAAAAAAAAAAAAAAAAAAB4bDwoJCgkK", "CQkJCQkJCQ0JDQkNCQ0NFR4fHh0cHBkTBQwFDAUFAwMCAwIEAQIAAAAAAAAA", "AAAAAAAAAAAAAAAAHx0WCwoKCQoJDQkNCQ0JDQkNDQ0NDRMfHx8fHxsWBQwF", "DAUMBQUDDwIEAQ8BAAAAAAAAAAAAAAAAAAAAAAAAAAAfHhkRCQkJCQkJCQ0J", "DQkNCQ0NDQ0VHh8fHxsPBQUFDAUFAwwDBAECAQEBAAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAB8fHBgLEAkNCQ0JDQ0NDQ0NDQ0NGB8fHxkPBwcIBwUIBQcCBAIC", "AQ8BAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHx8eGxMJCQ0JDQkNCQ0NDQ0N", "DRwfHhkRCQkJCgoIBQgDAwIEAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AAAAAB8eGxcJDQ0NDQ0NDQ0NDRceHRoYExEJCgkJCgYIBQMDBAIBAQEAAAAA", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8dGxYUDQ0NDQ0NDRccHBoYFRQR", "DgkJCQoGBQMFAgMCAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AAAfHRwaGBcYGhwdHRsaFxUUEA4OCQsGCgUIAw8DBAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHx4fHh8eHRsaGBUTEg0ODQ0LCQYI", "BQUDAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "Hx8fHhwbGhgVFA4ODg4LDQsNBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AAAA" ] # 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 img = get_surface_image_from_base64 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 img = get_surface_image_from_base64 # 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 def get_surface_image_from_base64 # read base64 text base64_text = "" # DATA.each { |l| base64_text += l.chomp } IMG_DATA_BASE64.each {|s| base64_text += s } bin_data_io = StringIO.new(base64_text.unpack("m")[0], "rb") # load image from StringIO img = SDL::Surface.loadFromIO(bin_data_io) img.set_color_key(SDL::SRCCOLORKEY, 0) return img.display_format 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