glfw-ruby 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ module API
5
+ extend FFI::Library
6
+
7
+ typedef :pointer, :GLFWwindow
8
+ typedef :pointer, :GLFWmonitor
9
+ typedef :pointer, :GLFWcursor
10
+ typedef :pointer, :GLFWglproc
11
+ typedef :pointer, :GLFWvkproc
12
+
13
+ callback :GLFWallocatefun, [:size_t, :pointer], :pointer
14
+ callback :GLFWreallocatefun, [:pointer, :size_t, :pointer], :pointer
15
+ callback :GLFWdeallocatefun, [:pointer, :pointer], :void
16
+
17
+ class GLFWvidmode < FFI::Struct
18
+ layout :width, :int,
19
+ :height, :int,
20
+ :redBits, :int,
21
+ :greenBits, :int,
22
+ :blueBits, :int,
23
+ :refreshRate, :int
24
+ end
25
+
26
+ class GLFWgammaramp < FFI::Struct
27
+ layout :red, :pointer,
28
+ :green, :pointer,
29
+ :blue, :pointer,
30
+ :size, :uint
31
+ end
32
+
33
+ class GLFWimage < FFI::Struct
34
+ layout :width, :int,
35
+ :height, :int,
36
+ :pixels, :pointer
37
+ end
38
+
39
+ class GLFWgamepadstate < FFI::Struct
40
+ layout :buttons, [:uchar, 15],
41
+ :axes, [:float, 6]
42
+ end
43
+
44
+ class GLFWallocator < FFI::Struct
45
+ layout :allocate, :GLFWallocatefun,
46
+ :reallocate, :GLFWreallocatefun,
47
+ :deallocate, :GLFWdeallocatefun,
48
+ :user, :pointer
49
+
50
+ def self.build(allocate:, reallocate:, deallocate:, user: nil)
51
+ allocator = new
52
+ allocator.allocate = allocate
53
+ allocator.reallocate = reallocate
54
+ allocator.deallocate = deallocate
55
+ allocator[:user] = user
56
+ allocator
57
+ end
58
+
59
+ def allocate=(callback)
60
+ @allocate_callback = callback
61
+ self[:allocate] = callback
62
+ end
63
+
64
+ def reallocate=(callback)
65
+ @reallocate_callback = callback
66
+ self[:reallocate] = callback
67
+ end
68
+
69
+ def deallocate=(callback)
70
+ @deallocate_callback = callback
71
+ self[:deallocate] = callback
72
+ end
73
+ end
74
+
75
+ callback :GLFWerrorfun, [:int, :string], :void
76
+ callback :GLFWwindowposfun, [:GLFWwindow, :int, :int], :void
77
+ callback :GLFWwindowsizefun, [:GLFWwindow, :int, :int], :void
78
+ callback :GLFWwindowclosefun, [:GLFWwindow], :void
79
+ callback :GLFWwindowrefreshfun, [:GLFWwindow], :void
80
+ callback :GLFWwindowfocusfun, [:GLFWwindow, :int], :void
81
+ callback :GLFWwindowiconifyfun, [:GLFWwindow, :int], :void
82
+ callback :GLFWwindowmaximizefun, [:GLFWwindow, :int], :void
83
+ callback :GLFWframebuffersizefun, [:GLFWwindow, :int, :int], :void
84
+ callback :GLFWwindowcontentscalefun, [:GLFWwindow, :float, :float], :void
85
+ callback :GLFWmousebuttonfun, [:GLFWwindow, :int, :int, :int], :void
86
+ callback :GLFWcursorposfun, [:GLFWwindow, :double, :double], :void
87
+ callback :GLFWcursorenterfun, [:GLFWwindow, :int], :void
88
+ callback :GLFWscrollfun, [:GLFWwindow, :double, :double], :void
89
+ callback :GLFWkeyfun, [:GLFWwindow, :int, :int, :int, :int], :void
90
+ callback :GLFWcharfun, [:GLFWwindow, :uint], :void
91
+ callback :GLFWcharmodsfun, [:GLFWwindow, :uint, :int], :void
92
+ callback :GLFWdropfun, [:GLFWwindow, :int, :pointer], :void
93
+ callback :GLFWmonitorfun, [:GLFWmonitor, :int], :void
94
+ callback :GLFWjoystickfun, [:int, :int], :void
95
+ end
96
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ module API
5
+ V34_FUNCTIONS = {
6
+ glfwGetPlatform: [[], :int],
7
+ glfwPlatformSupported: [[:int], :int],
8
+ glfwGetWindowTitle: [[:GLFWwindow], :string],
9
+ glfwInitAllocator: [[:pointer], :void],
10
+ glfwInitVulkanLoader: [[:GLFWvkproc], :void]
11
+ }.freeze
12
+
13
+ def self.bind_v34_functions!
14
+ V34_FUNCTIONS.each do |name, (args, ret)|
15
+ attach_function name, args, ret
16
+ rescue FFI::NotFoundError
17
+ define_singleton_method(name) { |*_| raise GLFW::NotSupportedError, "#{name} requires GLFW 3.4+" }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ module API
5
+ attach_function :glfwVulkanSupported, [], :int
6
+ attach_function :glfwGetRequiredInstanceExtensions, [:pointer], :pointer
7
+ attach_function :glfwGetInstanceProcAddress, [:pointer, :string], :GLFWvkproc
8
+ attach_function :glfwGetPhysicalDevicePresentationSupport, [:pointer, :pointer, :uint32], :int
9
+ attach_function :glfwCreateWindowSurface, [:pointer, :GLFWwindow, :pointer, :pointer], :int
10
+ end
11
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ module API
5
+ attach_function :glfwDefaultWindowHints, [], :void
6
+ attach_function :glfwWindowHint, [:int, :int], :void
7
+ attach_function :glfwWindowHintString, [:int, :string], :void
8
+ attach_function :glfwCreateWindow, [:int, :int, :string, :GLFWmonitor, :GLFWwindow], :GLFWwindow
9
+ attach_function :glfwDestroyWindow, [:GLFWwindow], :void
10
+ attach_function :glfwWindowShouldClose, [:GLFWwindow], :int
11
+ attach_function :glfwSetWindowShouldClose, [:GLFWwindow, :int], :void
12
+ attach_function :glfwSetWindowTitle, [:GLFWwindow, :string], :void
13
+ attach_function :glfwSetWindowIcon, [:GLFWwindow, :int, :pointer], :void
14
+ attach_function :glfwGetWindowPos, [:GLFWwindow, :pointer, :pointer], :void
15
+ attach_function :glfwSetWindowPos, [:GLFWwindow, :int, :int], :void
16
+ attach_function :glfwGetWindowSize, [:GLFWwindow, :pointer, :pointer], :void
17
+ attach_function :glfwSetWindowSize, [:GLFWwindow, :int, :int], :void
18
+ attach_function :glfwSetWindowSizeLimits, [:GLFWwindow, :int, :int, :int, :int], :void
19
+ attach_function :glfwSetWindowAspectRatio, [:GLFWwindow, :int, :int], :void
20
+ attach_function :glfwGetFramebufferSize, [:GLFWwindow, :pointer, :pointer], :void
21
+ attach_function :glfwGetWindowFrameSize, [:GLFWwindow, :pointer, :pointer, :pointer, :pointer], :void
22
+ attach_function :glfwGetWindowContentScale, [:GLFWwindow, :pointer, :pointer], :void
23
+ attach_function :glfwGetWindowOpacity, [:GLFWwindow], :float
24
+ attach_function :glfwSetWindowOpacity, [:GLFWwindow, :float], :void
25
+ attach_function :glfwIconifyWindow, [:GLFWwindow], :void
26
+ attach_function :glfwRestoreWindow, [:GLFWwindow], :void
27
+ attach_function :glfwMaximizeWindow, [:GLFWwindow], :void
28
+ attach_function :glfwShowWindow, [:GLFWwindow], :void
29
+ attach_function :glfwHideWindow, [:GLFWwindow], :void
30
+ attach_function :glfwFocusWindow, [:GLFWwindow], :void
31
+ attach_function :glfwRequestWindowAttention, [:GLFWwindow], :void
32
+ attach_function :glfwGetWindowMonitor, [:GLFWwindow], :GLFWmonitor
33
+ attach_function :glfwSetWindowMonitor, [:GLFWwindow, :GLFWmonitor, :int, :int, :int, :int, :int], :void
34
+ attach_function :glfwGetWindowAttrib, [:GLFWwindow, :int], :int
35
+ attach_function :glfwSetWindowAttrib, [:GLFWwindow, :int, :int], :void
36
+ attach_function :glfwSetWindowUserPointer, [:GLFWwindow, :pointer], :void
37
+ attach_function :glfwGetWindowUserPointer, [:GLFWwindow], :pointer
38
+ attach_function :glfwSetWindowPosCallback, [:GLFWwindow, :GLFWwindowposfun], :pointer
39
+ attach_function :glfwSetWindowSizeCallback, [:GLFWwindow, :GLFWwindowsizefun], :pointer
40
+ attach_function :glfwSetWindowCloseCallback, [:GLFWwindow, :GLFWwindowclosefun], :pointer
41
+ attach_function :glfwSetWindowRefreshCallback, [:GLFWwindow, :GLFWwindowrefreshfun], :pointer
42
+ attach_function :glfwSetWindowFocusCallback, [:GLFWwindow, :GLFWwindowfocusfun], :pointer
43
+ attach_function :glfwSetWindowIconifyCallback, [:GLFWwindow, :GLFWwindowiconifyfun], :pointer
44
+ attach_function :glfwSetWindowMaximizeCallback, [:GLFWwindow, :GLFWwindowmaximizefun], :pointer
45
+ attach_function :glfwSetFramebufferSizeCallback, [:GLFWwindow, :GLFWframebuffersizefun], :pointer
46
+ attach_function :glfwSetWindowContentScaleCallback, [:GLFWwindow, :GLFWwindowcontentscalefun], :pointer
47
+ attach_function :glfwPollEvents, [], :void
48
+ attach_function :glfwWaitEvents, [], :void
49
+ attach_function :glfwWaitEventsTimeout, [:double], :void
50
+ attach_function :glfwPostEmptyEvent, [], :void
51
+ attach_function :glfwSwapBuffers, [:GLFWwindow], :void
52
+ end
53
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ class Error < StandardError; end
5
+ class LibraryNotFoundError < Error; end
6
+ class InitError < Error; end
7
+ class NotSupportedError < Error; end
8
+
9
+ class APIError < Error
10
+ attr_reader :code
11
+
12
+ def initialize(code, message)
13
+ @code = code
14
+ super("[GLFW #{error_name(code)}] #{message}")
15
+ end
16
+
17
+ private
18
+
19
+ def error_name(code)
20
+ ERROR_CODE_NAMES[code] || "0x#{code.to_s(16).rjust(8, '0')}"
21
+ end
22
+ end
23
+
24
+ ERROR_CODE_NAMES = {
25
+ 0x00010001 => "NOT_INITIALIZED",
26
+ 0x00010002 => "NO_CURRENT_CONTEXT",
27
+ 0x00010003 => "INVALID_ENUM",
28
+ 0x00010004 => "INVALID_VALUE",
29
+ 0x00010005 => "OUT_OF_MEMORY",
30
+ 0x00010006 => "API_UNAVAILABLE",
31
+ 0x00010007 => "VERSION_UNAVAILABLE",
32
+ 0x00010008 => "PLATFORM_ERROR",
33
+ 0x00010009 => "FORMAT_UNAVAILABLE",
34
+ 0x0001000A => "NO_WINDOW_CONTEXT",
35
+ 0x0001000B => "CURSOR_UNAVAILABLE",
36
+ 0x0001000C => "FEATURE_UNAVAILABLE",
37
+ 0x0001000D => "FEATURE_UNIMPLEMENTED",
38
+ 0x0001000E => "PLATFORM_UNAVAILABLE"
39
+ }.freeze
40
+
41
+ class NotInitializedError < APIError; end
42
+ class NoCurrentContextError < APIError; end
43
+ class InvalidEnumError < APIError; end
44
+ class InvalidValueError < APIError; end
45
+ class OutOfMemoryError < APIError; end
46
+ class APIUnavailableError < APIError; end
47
+ class VersionUnavailableError < APIError; end
48
+ class PlatformError < APIError; end
49
+ class FormatUnavailableError < APIError; end
50
+ class NoWindowContextError < APIError; end
51
+ class CursorUnavailableError < APIError; end
52
+ class FeatureUnavailableError < APIError; end
53
+ class FeatureUnimplementedError < APIError; end
54
+ class PlatformUnavailableError < APIError; end
55
+
56
+ ERROR_CLASS_MAP = {
57
+ 0x00010001 => NotInitializedError,
58
+ 0x00010002 => NoCurrentContextError,
59
+ 0x00010003 => InvalidEnumError,
60
+ 0x00010004 => InvalidValueError,
61
+ 0x00010005 => OutOfMemoryError,
62
+ 0x00010006 => APIUnavailableError,
63
+ 0x00010007 => VersionUnavailableError,
64
+ 0x00010008 => PlatformError,
65
+ 0x00010009 => FormatUnavailableError,
66
+ 0x0001000A => NoWindowContextError,
67
+ 0x0001000B => CursorUnavailableError,
68
+ 0x0001000C => FeatureUnavailableError,
69
+ 0x0001000D => FeatureUnimplementedError,
70
+ 0x0001000E => PlatformUnavailableError
71
+ }.freeze
72
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ class Cursor
5
+ attr_reader :handle
6
+
7
+ def initialize(handle)
8
+ @handle = handle
9
+ end
10
+
11
+ def self.create(image, xhot, yhot)
12
+ img_struct = image.to_struct
13
+ handle = API.glfwCreateCursor(img_struct, xhot, yhot)
14
+ raise GLFW::Error, "Failed to create cursor" if handle.null?
15
+
16
+ new(handle)
17
+ end
18
+
19
+ def self.standard(shape)
20
+ handle = API.glfwCreateStandardCursor(shape)
21
+ raise GLFW::Error, "Failed to create standard cursor" if handle.null?
22
+
23
+ new(handle)
24
+ end
25
+
26
+ def destroy
27
+ API.glfwDestroyCursor(@handle)
28
+ @handle = nil
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ class GamepadState
5
+ attr_reader :buttons, :axes
6
+
7
+ def initialize(buttons, axes)
8
+ @buttons = buttons
9
+ @axes = axes
10
+ end
11
+
12
+ def button_pressed?(button)
13
+ @buttons[button] == GLFW_PRESS
14
+ end
15
+
16
+ def axis_value(axis)
17
+ @axes[axis]
18
+ end
19
+ end
20
+
21
+ class Gamepad
22
+ def initialize(jid)
23
+ @jid = jid
24
+ end
25
+
26
+ def name
27
+ API.glfwGetGamepadName(@jid)
28
+ end
29
+
30
+ def state
31
+ gs = API::GLFWgamepadstate.new
32
+ return nil unless API.glfwGetGamepadState(@jid, gs) == GLFW_TRUE
33
+
34
+ buttons = gs[:buttons].to_a
35
+ axes = gs[:axes].to_a
36
+ GamepadState.new(buttons, axes)
37
+ end
38
+
39
+ def self.update_mappings(string)
40
+ API.glfwUpdateGamepadMappings(string) == GLFW_TRUE
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ class GammaRamp
5
+ attr_reader :red, :green, :blue, :size
6
+
7
+ def initialize(red, green, blue, size)
8
+ @red = red
9
+ @green = green
10
+ @blue = blue
11
+ @size = size
12
+ end
13
+
14
+ def self.from_struct(ptr)
15
+ struct = API::GLFWgammaramp.new(ptr)
16
+ size = struct[:size]
17
+ red = struct[:red].read_array_of_uint16(size)
18
+ green = struct[:green].read_array_of_uint16(size)
19
+ blue = struct[:blue].read_array_of_uint16(size)
20
+ new(red, green, blue, size)
21
+ end
22
+
23
+ def to_struct
24
+ ramp = API::GLFWgammaramp.new
25
+ ramp[:size] = @size
26
+ ramp[:red] = FFI::MemoryPointer.new(:uint16, @size).tap { |p| p.write_array_of_uint16(@red) }
27
+ ramp[:green] = FFI::MemoryPointer.new(:uint16, @size).tap { |p| p.write_array_of_uint16(@green) }
28
+ ramp[:blue] = FFI::MemoryPointer.new(:uint16, @size).tap { |p| p.write_array_of_uint16(@blue) }
29
+ ramp
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ class Image
5
+ attr_reader :width, :height, :pixels
6
+
7
+ def initialize(width, height, pixels)
8
+ @width = width
9
+ @height = height
10
+ @pixels = pixels
11
+ end
12
+
13
+ def self.from_rgba(width, height, data)
14
+ pixels = FFI::MemoryPointer.new(:uchar, data.bytesize)
15
+ pixels.put_bytes(0, data)
16
+ new(width, height, pixels)
17
+ end
18
+
19
+ def to_struct
20
+ img = API::GLFWimage.new
21
+ img[:width] = @width
22
+ img[:height] = @height
23
+ img[:pixels] = @pixels
24
+ img
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ class Joystick
5
+ attr_reader :jid
6
+
7
+ def initialize(jid)
8
+ @jid = jid
9
+ end
10
+
11
+ def self.[](jid)
12
+ new(jid)
13
+ end
14
+
15
+ def present?
16
+ API.glfwJoystickPresent(@jid) == GLFW_TRUE
17
+ end
18
+
19
+ def name
20
+ API.glfwGetJoystickName(@jid)
21
+ end
22
+
23
+ def guid
24
+ API.glfwGetJoystickGUID(@jid)
25
+ end
26
+
27
+ def axes
28
+ count = FFI::MemoryPointer.new(:int)
29
+ ptr = API.glfwGetJoystickAxes(@jid, count)
30
+ return [] if ptr.null?
31
+
32
+ ptr.read_array_of_float(count.read_int)
33
+ end
34
+
35
+ def buttons
36
+ count = FFI::MemoryPointer.new(:int)
37
+ ptr = API.glfwGetJoystickButtons(@jid, count)
38
+ return [] if ptr.null?
39
+
40
+ ptr.read_array_of_uchar(count.read_int).map { |b| ACTION_MAP[b] || b }
41
+ end
42
+
43
+ def hats
44
+ count = FFI::MemoryPointer.new(:int)
45
+ ptr = API.glfwGetJoystickHats(@jid, count)
46
+ return [] if ptr.null?
47
+
48
+ ptr.read_array_of_uchar(count.read_int)
49
+ end
50
+
51
+ def gamepad?
52
+ API.glfwJoystickIsGamepad(@jid) == GLFW_TRUE
53
+ end
54
+
55
+ def gamepad
56
+ return nil unless gamepad?
57
+
58
+ Gamepad.new(@jid)
59
+ end
60
+
61
+ def self.on_connect(&block)
62
+ cb = proc { |jid, event|
63
+ block.call(new(jid), event == GLFW_CONNECTED ? :connected : :disconnected)
64
+ }
65
+ GLFW.register_callback(:joystick, nil, cb)
66
+ API.glfwSetJoystickCallback(cb)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ class Monitor
5
+ attr_reader :handle
6
+
7
+ def initialize(handle)
8
+ @handle = handle
9
+ end
10
+
11
+ def self.all
12
+ count = FFI::MemoryPointer.new(:int)
13
+ ptr = API.glfwGetMonitors(count)
14
+ return [] if ptr.null?
15
+
16
+ n = count.read_int
17
+ ptr.read_array_of_pointer(n).map { |p| new(p) }
18
+ end
19
+
20
+ def self.primary
21
+ handle = API.glfwGetPrimaryMonitor
22
+ return nil if handle.null?
23
+
24
+ new(handle)
25
+ end
26
+
27
+ def name
28
+ API.glfwGetMonitorName(@handle)
29
+ end
30
+
31
+ def position
32
+ x = FFI::MemoryPointer.new(:int)
33
+ y = FFI::MemoryPointer.new(:int)
34
+ API.glfwGetMonitorPos(@handle, x, y)
35
+ [x.read_int, y.read_int]
36
+ end
37
+
38
+ def workarea
39
+ x = FFI::MemoryPointer.new(:int)
40
+ y = FFI::MemoryPointer.new(:int)
41
+ w = FFI::MemoryPointer.new(:int)
42
+ h = FFI::MemoryPointer.new(:int)
43
+ API.glfwGetMonitorWorkarea(@handle, x, y, w, h)
44
+ { x: x.read_int, y: y.read_int, width: w.read_int, height: h.read_int }
45
+ end
46
+
47
+ def physical_size
48
+ w = FFI::MemoryPointer.new(:int)
49
+ h = FFI::MemoryPointer.new(:int)
50
+ API.glfwGetMonitorPhysicalSize(@handle, w, h)
51
+ [w.read_int, h.read_int]
52
+ end
53
+
54
+ def content_scale
55
+ x = FFI::MemoryPointer.new(:float)
56
+ y = FFI::MemoryPointer.new(:float)
57
+ API.glfwGetMonitorContentScale(@handle, x, y)
58
+ [x.read_float, y.read_float]
59
+ end
60
+
61
+ def video_modes
62
+ count = FFI::MemoryPointer.new(:int)
63
+ ptr = API.glfwGetVideoModes(@handle, count)
64
+ return [] if ptr.null?
65
+
66
+ n = count.read_int
67
+ n.times.map do |i|
68
+ struct = API::GLFWvidmode.new(ptr + i * API::GLFWvidmode.size)
69
+ VideoMode.from_struct(struct)
70
+ end
71
+ end
72
+
73
+ def current_video_mode
74
+ ptr = API.glfwGetVideoMode(@handle)
75
+ return nil if ptr.null?
76
+
77
+ VideoMode.from_struct(API::GLFWvidmode.new(ptr))
78
+ end
79
+
80
+ def gamma=(val)
81
+ API.glfwSetGamma(@handle, val.to_f)
82
+ end
83
+
84
+ def gamma_ramp
85
+ ptr = API.glfwGetGammaRamp(@handle)
86
+ return nil if ptr.null?
87
+
88
+ GammaRamp.from_struct(ptr)
89
+ end
90
+
91
+ def gamma_ramp=(ramp)
92
+ struct = ramp.to_struct
93
+ API.glfwSetGammaRamp(@handle, struct)
94
+ end
95
+
96
+ def self.on_connect(&block)
97
+ cb = proc { |monitor_ptr, event|
98
+ block.call(new(monitor_ptr), event == GLFW_CONNECTED ? :connected : :disconnected)
99
+ }
100
+ GLFW.register_callback(:monitor, nil, cb)
101
+ API.glfwSetMonitorCallback(cb)
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GLFW
4
+ class VideoMode
5
+ attr_reader :width, :height, :red_bits, :green_bits, :blue_bits, :refresh_rate
6
+
7
+ def initialize(width, height, red_bits, green_bits, blue_bits, refresh_rate)
8
+ @width = width
9
+ @height = height
10
+ @red_bits = red_bits
11
+ @green_bits = green_bits
12
+ @blue_bits = blue_bits
13
+ @refresh_rate = refresh_rate
14
+ end
15
+
16
+ def self.from_struct(struct)
17
+ new(struct[:width], struct[:height], struct[:redBits],
18
+ struct[:greenBits], struct[:blueBits], struct[:refreshRate])
19
+ end
20
+
21
+ def ==(other)
22
+ other.is_a?(VideoMode) &&
23
+ width == other.width && height == other.height &&
24
+ red_bits == other.red_bits && green_bits == other.green_bits &&
25
+ blue_bits == other.blue_bits && refresh_rate == other.refresh_rate
26
+ end
27
+
28
+ def to_s
29
+ "#{width}x#{height}@#{refresh_rate}Hz (#{red_bits}/#{green_bits}/#{blue_bits})"
30
+ end
31
+ end
32
+ end