rb_sdl2 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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.txt +17 -0
  4. data/README.md +40 -0
  5. data/lib/rb_sdl2/audio/audio_allowed_changes.rb +17 -0
  6. data/lib/rb_sdl2/audio/audio_buffer.rb +49 -0
  7. data/lib/rb_sdl2/audio/audio_device.rb +44 -0
  8. data/lib/rb_sdl2/audio/audio_format.rb +23 -0
  9. data/lib/rb_sdl2/audio/audio_spec.rb +29 -0
  10. data/lib/rb_sdl2/audio.rb +132 -0
  11. data/lib/rb_sdl2/clipboard.rb +44 -0
  12. data/lib/rb_sdl2/cpu_info.rb +39 -0
  13. data/lib/rb_sdl2/cursor/color_cursor.rb +19 -0
  14. data/lib/rb_sdl2/cursor/cursor_class.rb +24 -0
  15. data/lib/rb_sdl2/cursor/cursor_pointer.rb +12 -0
  16. data/lib/rb_sdl2/cursor/default_cursor.rb +18 -0
  17. data/lib/rb_sdl2/cursor/system_cursor.rb +45 -0
  18. data/lib/rb_sdl2/cursor.rb +39 -0
  19. data/lib/rb_sdl2/display.rb +103 -0
  20. data/lib/rb_sdl2/display_mode.rb +35 -0
  21. data/lib/rb_sdl2/error.rb +9 -0
  22. data/lib/rb_sdl2/event/event_filter.rb +83 -0
  23. data/lib/rb_sdl2/event/event_queue.rb +137 -0
  24. data/lib/rb_sdl2/event/event_type.rb +271 -0
  25. data/lib/rb_sdl2/event.rb +161 -0
  26. data/lib/rb_sdl2/filesystem.rb +40 -0
  27. data/lib/rb_sdl2/hint.rb +24 -0
  28. data/lib/rb_sdl2/keyboard/key_mod.rb +37 -0
  29. data/lib/rb_sdl2/keyboard/keyboard_state.rb +34 -0
  30. data/lib/rb_sdl2/keyboard.rb +50 -0
  31. data/lib/rb_sdl2/message_box.rb +121 -0
  32. data/lib/rb_sdl2/mouse/global_mouse.rb +18 -0
  33. data/lib/rb_sdl2/mouse/mouse_button.rb +24 -0
  34. data/lib/rb_sdl2/mouse/mouse_class.rb +33 -0
  35. data/lib/rb_sdl2/mouse/mouse_wheel.rb +53 -0
  36. data/lib/rb_sdl2/mouse/relative_mouse.rb +12 -0
  37. data/lib/rb_sdl2/mouse/window_mouse.rb +17 -0
  38. data/lib/rb_sdl2/mouse.rb +74 -0
  39. data/lib/rb_sdl2/palette.rb +70 -0
  40. data/lib/rb_sdl2/pixel_format_enum.rb +73 -0
  41. data/lib/rb_sdl2/platform.rb +7 -0
  42. data/lib/rb_sdl2/power_info.rb +36 -0
  43. data/lib/rb_sdl2/rect.rb +14 -0
  44. data/lib/rb_sdl2/ref_count_pointer.rb +20 -0
  45. data/lib/rb_sdl2/rw_ops/rw_operator.rb +102 -0
  46. data/lib/rb_sdl2/rw_ops.rb +124 -0
  47. data/lib/rb_sdl2/screen_saver.rb +11 -0
  48. data/lib/rb_sdl2/sdl.rb +32 -0
  49. data/lib/rb_sdl2/surface/blend_mode.rb +41 -0
  50. data/lib/rb_sdl2/surface/pixel_format.rb +89 -0
  51. data/lib/rb_sdl2/surface.rb +244 -0
  52. data/lib/rb_sdl2/text_input.rb +15 -0
  53. data/lib/rb_sdl2/timer.rb +22 -0
  54. data/lib/rb_sdl2/version.rb +13 -0
  55. data/lib/rb_sdl2/video.rb +25 -0
  56. data/lib/rb_sdl2/window/dialog.rb +19 -0
  57. data/lib/rb_sdl2/window/display.rb +85 -0
  58. data/lib/rb_sdl2/window/grab.rb +23 -0
  59. data/lib/rb_sdl2/window/hit_test.rb +52 -0
  60. data/lib/rb_sdl2/window/position.rb +38 -0
  61. data/lib/rb_sdl2/window/shape.rb +76 -0
  62. data/lib/rb_sdl2/window/size.rb +59 -0
  63. data/lib/rb_sdl2/window/window_flags.rb +78 -0
  64. data/lib/rb_sdl2/window.rb +242 -0
  65. data/lib/rb_sdl2.rb +53 -0
  66. data/rb_sdl2.gemspec +19 -0
  67. metadata +135 -0
@@ -0,0 +1,161 @@
1
+ module RbSDL2
2
+ class Event
3
+ class EventPointer < ::FFI::AutoPointer
4
+ class << self
5
+ def malloc
6
+ ptr = new(::SDL2.SDL_calloc(1, ::SDL2::SDL_Event.size))
7
+ raise NoMemoryError if ptr.null?
8
+ ptr
9
+ end
10
+
11
+ def release(ptr)
12
+ # メンバーのポインターを開放する。
13
+ Event.to_ptr(ptr).clear
14
+ ::SDL2.SDL_free(ptr)
15
+ end
16
+ end
17
+ end
18
+
19
+ require 'forwardable'
20
+ extend SingleForwardable
21
+
22
+ require_relative 'event/event_filter'
23
+ def_single_delegators "Event::EventFilter",
24
+ *%i(add_watch_callback filter_callback_set filter_callback_defined?
25
+ remove_watch_callback)
26
+
27
+ require_relative 'event/event_queue'
28
+ def_single_delegators "Event::EventQueue",
29
+ *%i(clear count deq each empty? enq get length peep poll pump push push!
30
+ quit? size reject! wait)
31
+
32
+ require_relative 'event/event_type'
33
+ def_single_delegators "Event::EventQueue", *%i(define_user_event disable enable ignore?)
34
+
35
+ class << self
36
+ def new(type: 0, **members)
37
+ ptr = EventPointer.malloc
38
+ num = Numeric === type ? type : EventType.to_num(type)
39
+ ptr.write_uint32(num)
40
+ obj = super(ptr)
41
+ if obj.typed?
42
+ members.each_pair { |sym, val| obj[sym] = val }
43
+ end
44
+ obj
45
+ end
46
+
47
+ def to_ptr(ptr)
48
+ obj = allocate
49
+ obj.__send__(:initialize, ptr)
50
+ obj
51
+ end
52
+ end
53
+
54
+ # UserEvent の data1, data2 メンバー、SysWMEvent の msg メンバーはポインターである。
55
+ # ポインターとしての読み書きの対応はしている。しかしポインターが指し示すメモリーの管理は行わない。
56
+ def initialize(ptr)
57
+ @ptr = ptr
58
+ end
59
+
60
+ def [](sym)
61
+ raise ArgumentError unless member?(sym)
62
+
63
+ case sym
64
+ when :data
65
+ member[:data].to_a
66
+ when :file
67
+ member[:file].then { |ptr| ptr.null? ? nil : ptr.read_string }
68
+ when :keysym
69
+ member[:keysym].then { |st| {scancode: st[:scancode], sym: st[:sym], mod: st[:mod]} }
70
+ when :text
71
+ member[:text].to_s
72
+ else
73
+ member[sym]
74
+ end
75
+ end
76
+
77
+ # DropEvent の file メンバーのポインターの管理は行われる。
78
+ # UserEvent の data1, data2 メンバーと SysWMEvent の msg メンバーはポインターである。
79
+ # これらのポインターの管理は行ない。アプリケーション側で実装を行うこと。
80
+ def []=(sym, val)
81
+ raise FrozenError if frozen?
82
+ raise ArgumentError unless member?(sym)
83
+
84
+ case sym
85
+ when :data
86
+ val.each.with_index { |v, i| member[:data][i] = v }
87
+ when :file
88
+ if drop_file? || drop_text?
89
+ _ptr = member[:file]
90
+
91
+ member[:file] = if val.nil?
92
+ nil
93
+ else
94
+ c_str = "#{val}\x00"
95
+ ptr = ::SDL2.SDL_malloc(c_str.size)
96
+ raise NoMemoryError if ptr.null?
97
+ ptr.write_bytes(c_str)
98
+ end
99
+
100
+ ::SDL2.SDL_free(_ptr)
101
+ else
102
+ raise ArgumentError
103
+ end
104
+ when :keysym
105
+ val.each { |k, v| member[:keysym][k] = v }
106
+ when :msg
107
+ if val.nil? || val.respond_to?(:null?) && val.null? || val.respond_to?(:zero?) && val.zero?
108
+ raise ArgumentError
109
+ end
110
+ member[:msg] = val
111
+ when :type
112
+ raise ArgumentError
113
+ else
114
+ member[sym] = val
115
+ end
116
+ end
117
+
118
+ def clear
119
+ raise FrozenError if frozen?
120
+ if drop_file? || drop_text?
121
+ self[:file] = nil
122
+ end
123
+ member.clear
124
+ @st = nil
125
+ self
126
+ end
127
+
128
+ # deep copy を行う。
129
+ # これは @ptr が イベントキューの一部(SDL の管理領域)を指している場合があるためである。
130
+ def initialize_copy(obj)
131
+ super
132
+ @ptr = EventPointer.malloc.write_bytes(obj.to_ptr.read_bytes(::SDL2::SDL_Event.size))
133
+ if drop_file? || drop_text?
134
+ self[:file] = obj[:file]
135
+ end
136
+ end
137
+
138
+ def inspect
139
+ "#<#{self.class.name} ptr=#{@ptr.inspect} name=#{name.inspect} #{to_h}>"
140
+ end
141
+
142
+ protected def member = @st ||= ::SDL2::SDL_Event.new(@ptr)[EventType.to_type(type)]
143
+
144
+ def member?(name) = members.include?(name)
145
+
146
+ def members = member.members.grep_v(/\Apadding/)
147
+
148
+ def name = EventType.to_name(type)
149
+ alias to_s name
150
+
151
+ def to_h = members.map { |sym| [sym, self[sym]] }.to_h
152
+
153
+ def to_ptr = @ptr
154
+
155
+ def type = to_ptr.read_uint32
156
+
157
+ include EventType
158
+
159
+ def typed? = type != 0
160
+ end
161
+ end
@@ -0,0 +1,40 @@
1
+ module RbSDL2
2
+ module Filesystem
3
+ class << self
4
+ # RbSDL2 にロードされた SDL2 の配置パスを戻す。
5
+ # パスの末尾にはパスの区切り文字がかならずある。
6
+ # パスの区切り記号は環境依存である。Windows であれば "\" が使われる。
7
+ # Ruby は環境依存のパスの区切り文字を正しく取り扱うことができる。
8
+ def base_path
9
+ ptr = ::SDL2.SDL_GetBasePath
10
+ raise RbSDL2Error if ptr.null?
11
+ ptr.read_string.force_encoding(Encoding::UTF_8)
12
+ ensure
13
+ ::SDL2.SDL_free(ptr)
14
+ end
15
+
16
+ # アプリケーションが書き込むことのできるパスを戻す。
17
+ # このパスはユーザ毎に存在し、かつアプリケーション固有のものであり OS によって保証されている。
18
+ # org_name, app_name 引数はパスの生成に利用される。引数にパスの区切り記号がある場合はそれを取り除く。
19
+ # ここに SDL2 が提示した守るべきルールの要約を記す。
20
+ # - アプリケーション内でこのメソッドw呼び出す際は org_name は常に同じ文字列を使うこと。
21
+ # - アプリケーションごとに違う app_name を使うこと.
22
+ # - アプリケーションに一度割り当てた app_name は変更しないこと。
23
+ # - 使用する文字はアルファベット, 数字, 空白のみにすること.
24
+ #
25
+ # 注意: このメソッドを呼ぶと SDL はこのメソッドが戻すパスを実際に作成する。
26
+ # 書き込みを行わなかった場合、空のフォルダーが残る。
27
+ # 引数に空文字を渡しすことも、また既存のフォルダーになるように引数を与えることもできる。
28
+ # SDL が知りたいことはアプリケーションがアクセス可能かどうかだけだ。
29
+ # エラーが出るかどうかはユーザが設定するパスのアクセス制限による。
30
+ def pref_path(org_name, app_name)
31
+ ptr = ::SDL2.SDL_GetPrefPath(org_name.encode(Encoding::UTF_8),
32
+ app_name.encode(Encoding::UTF_8))
33
+ raise RbSDL2Error if ptr.null?
34
+ ptr.read_string.force_encoding(Encoding::UTF_8)
35
+ ensure
36
+ ::SDL2.SDL_free(ptr)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ module RbSDL2
2
+ module Hint
3
+ class << self
4
+ def [](name)
5
+ ptr = ::SDL2.SDL_GetHint(name.to_s)
6
+ ptr.null? ? nil : ptr.read_string
7
+ end
8
+
9
+ def []=(name, value)
10
+ bool = ::SDL2.SDL_SetHintWithPriority(name.to_s, value&.to_s, ::SDL2::SDL_HINT_OVERRIDE)
11
+ raise RbSDL2Error, "failed to set hint" if bool == ::SDL2::SDL_FALSE
12
+ end
13
+
14
+ def clear = ::SDL2.SDL_ClearHints
15
+
16
+ def freeze = raise(TypeError, "cannot freeze Hint")
17
+
18
+ def include?(name) = ::SDL2.SDL_GetHintBoolean(name, -1) != -1
19
+ alias has_key? include?
20
+ alias member? include?
21
+ alias key? include?
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ module RbSDL2
2
+ module Keyboard
3
+ module KeyMod
4
+ def mod_key? = ::SDL2::KMOD_NONE != mod
5
+
6
+ def l_shift_key? = ::SDL2::KMOD_LSHIFT & mod != 0
7
+
8
+ def r_shift_key? = ::SDL2::KMOD_RSHIFT & mod != 0
9
+
10
+ def l_ctrl_key? = ::SDL2::KMOD_LCTRL & mod != 0
11
+
12
+ def r_ctrl_key? = ::SDL2::KMOD_RCTRL & mod != 0
13
+
14
+ def l_alt_key? = ::SDL2::KMOD_LALT & mod != 0
15
+
16
+ def r_alt_key? = ::SDL2::KMOD_RALT & mod != 0
17
+
18
+ def l_gui_key? = ::SDL2::KMOD_LGUI & mod != 0
19
+
20
+ def r_gui_key? = ::SDL2::KMOD_RGUI & mod != 0
21
+
22
+ def alt_key? = ::SDL2::KMOD_ALT & mod != 0
23
+
24
+ def caps_key? = ::SDL2::KMOD_CAPS & mod != 0
25
+
26
+ def ctrl_key? = ::SDL2::KMOD_CTRL & mod != 0
27
+
28
+ def gui_key? = ::SDL2::KMOD_GUI & mod != 0
29
+
30
+ def mode_key? = ::SDL2::KMOD_MODE & mod != 0
31
+
32
+ def num_key? = ::SDL2::KMOD_NUM & mod != 0
33
+
34
+ def shift_key? = ::SDL2::KMOD_SHIFT & mod != 0
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ module RbSDL2
2
+ class KeyboardState
3
+ require 'singleton'
4
+ include Singleton
5
+
6
+ def initialize
7
+ num_keys = ::FFI::MemoryPointer.new(:int)
8
+ # SDL_GetKeyboardState が戻すポインターは SDL がロードされた時点でメモリー確保している。
9
+ # 戻されたポインターは不変と考えてよい。
10
+ # SDL_GetKeyboardState は SDL_init より前に呼ぶことができる。
11
+ # SDL_GetKeyboardState は引数に NULL ポインターを与えた場合にエラーを戻す。
12
+ @ptr = ::SDL2.SDL_GetKeyboardState(num_keys)
13
+ raise RbSDL2Error if @ptr.null?
14
+ @size = num_keys.read_int
15
+ end
16
+
17
+ # nth のキーが押されている場合、nth を戻す。
18
+ # nth のキーが押されていない、nth が範囲外の場合は nil を戻す。
19
+ # 真偽値を戻すようにしなかったのは、このメソッドを応用したコードを書く際に index 情報を不要にするためである。
20
+ # 戻り値が nil | obj なのは Numeric#nonzero? を参考にした。(この戻り値は Ruby において真偽値と同等である)
21
+ def [](nth)
22
+ if 0 <= nth && nth < size && @ptr[nth].read_uint8 == ::SDL2::SDL_PRESSED
23
+ nth
24
+ end
25
+ end
26
+
27
+ def each = block_given? ? size.times { |i| yield(self[i]) } : to_enum
28
+
29
+ attr_reader :size
30
+ alias length size
31
+
32
+ def to_str = @ptr.read_bytes(size)
33
+ end
34
+ end
@@ -0,0 +1,50 @@
1
+ module RbSDL2
2
+ module Keyboard
3
+ class << self
4
+ def keycode_to_name(keycode) = ::SDL2.SDL_GetKeyName(keycode).read_string
5
+
6
+ # 対応するコードが存在しない場合 0 を戻す。戻り値 は nonzero? メソッドをチェーンすることができる。
7
+ # これは KeyboardState#[] での利用を考慮して設計した。
8
+ def keycode_to_scancode(keycode) = ::SDL2.SDL_GetScancodeFromKey(keycode)
9
+
10
+ def name_to_keycode(name) = ::SDL2.SDL_GetKeyFromName(name)
11
+
12
+ def name_to_scancode(name) = ::SDL2.SDL_GetScancodeFromName(name)
13
+
14
+ def scancode_to_keycode(scancode) = ::SDL2.SDL_GetKeyFromScancode(scancode)
15
+
16
+ def scancode_to_name(scancode) = ::SDL2.SDL_GetScancodeName(scancode).read_string
17
+ end
18
+
19
+ require 'forwardable'
20
+ extend SingleForwardable
21
+ require_relative 'keyboard/keyboard_state'
22
+ def_single_delegators 'KeyboardState.instance', *%i([] each to_str)
23
+
24
+ class << self
25
+ # いずれかのキーが押されている場合に true を戻す。つまり *any key* が押されたということ。
26
+ def any_key? = each.any?
27
+
28
+ # 引数に与えたキー名のキーが押されている場合に対応するスキャンコードを戻す。
29
+ # 押されていない場合は nil を戻す。
30
+ # 不正な名前の場合でも例外を戻さない、その場合 0 または nil を戻す。
31
+ # キー名は SDL が定義したものである。
32
+ def key?(name) = self[name_to_scancode(name)]
33
+
34
+ # 現在押されているキーの名前を配列で戻す。
35
+ def names = scancodes.map { |num| scancode_to_name(num) }
36
+
37
+ # 現在押されているキーのスキャンコードを配列で戻す。
38
+ def scancodes = each.to_a.compact!
39
+
40
+ def mod = ::SDL2.SDL_GetModState
41
+
42
+ require_relative 'keyboard/key_mod'
43
+ include KeyMod
44
+
45
+ def mod=(state)
46
+ ::SDL2::SDL_SetModState(state)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,121 @@
1
+ module RbSDL2
2
+ module MessageBox
3
+ class MessageBoxButtonDataArray
4
+ def initialize(num)
5
+ @entity_class = ::SDL2::SDL_MessageBoxButtonData
6
+ @ptr = ::FFI::MemoryPointer.new(@entity_class.size, num)
7
+ end
8
+
9
+ def [](nth) = @entity_class.new(@ptr + @entity_class.size * nth)
10
+
11
+ def to_ptr = @ptr
12
+ end
13
+
14
+ class MessageBoxData
15
+ def initialize(buttons: nil, colors: nil, escape_key: nil, level: nil, message: nil,
16
+ return_key: nil, title: nil, window: nil)
17
+ @st = ::SDL2::SDL_MessageBoxData.new.tap do |data|
18
+ button_data = *buttons
19
+ data[:numbuttons] = num_buttons = button_data.length
20
+ data[:buttons] = @buttons = num_buttons.nonzero? &&
21
+ MessageBoxButtonDataArray.new(num_buttons).tap do |data_ary|
22
+ @button_texts = []
23
+ num_buttons.times do |idx|
24
+ st, (text, *) = data_ary[idx], button_data[idx]
25
+ st[:buttonid] = idx
26
+ st[:flags] = case idx
27
+ when escape_key then ::SDL2::SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
28
+ when return_key then ::SDL2::SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
29
+ else 0
30
+ end
31
+ st[:text] = @button_texts[idx] =
32
+ ::FFI::MemoryPointer.from_string(text.to_s.encode(Encoding::UTF_8))
33
+ end
34
+ end
35
+ data[:colorScheme] = @color_scheme = colors &&
36
+ ::SDL2::SDL_MessageBoxColorScheme.new.tap do |st|
37
+ # r, g, b, a 形式だった場合にエラーを出さない。
38
+ st[:colors].each.with_index { |c, i| c[:r], c[:g], c[:b] = colors[i] }
39
+ end
40
+ data[:flags] = MessageBoxFlags.to_num(level)
41
+ data[:message] = @message =
42
+ ::FFI::MemoryPointer.from_string(message.to_s.encode(Encoding::UTF_8))
43
+ data[:title] = @title =
44
+ ::FFI::MemoryPointer.from_string(title.to_s.encode(Encoding::UTF_8))
45
+ data[:window] = window
46
+ end
47
+ end
48
+
49
+ def to_ptr = @st.to_ptr
50
+ end
51
+
52
+ module MessageBoxFlags
53
+ class << self
54
+ def to_num(obj)
55
+ case obj
56
+ when /\Aerror/ then ::SDL2::SDL_MESSAGEBOX_ERROR
57
+ when /\Ainfo/ then ::SDL2::SDL_MESSAGEBOX_INFORMATION
58
+ when /\Awarn/ then ::SDL2::SDL_MESSAGEBOX_WARNING
59
+ when nil then 0
60
+ else
61
+ raise ArgumentError
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ CONFIRMATION_OPTIONS = { buttons: { Cancel: false, OK: true }, default: true }.freeze
68
+
69
+ class << self
70
+ def alert(msg = nil, window = nil, level: nil, message: msg, title: nil)
71
+ err = ::SDL2.SDL_ShowSimpleMessageBox(MessageBoxFlags.to_num(level),
72
+ title&.to_s&.encode(Encoding::UTF_8),
73
+ message&.to_s&.encode(Encoding::UTF_8),
74
+ window)
75
+ raise RbSDL2Error if err < 0
76
+ end
77
+
78
+ def confirm(*args, **opts) = dialog(*args, **opts.merge!(CONFIRMATION_OPTIONS))
79
+
80
+ # buttons: "label" | ["label",...] | [["label", obj],...] | {"label" => obj,...}
81
+ # buttons 1個以上のオブジェクトがあること。0個の場合はエラーになる。
82
+ # colors: [[r,g,b],...] | nil
83
+ # 環境(例えば Win10)によっては colors は反映されない。 nil の場合はシステム設定のカラーが使用される。
84
+ # ユーザがクリックしたボタンに応じたオブジェクトが戻る。
85
+ # ユーザが Escape キーが押した場合(何も選択しなかった場合)ブロックが与えられていればブロックの評価内容が、
86
+ # ブロックがなければ nil が戻る。
87
+ def dialog(msg = nil, window = nil, buttons:, message: msg, **opts)
88
+ button_data = *buttons
89
+ raise ArgumentError if button_data.empty?
90
+ # Escape キーの割り当ては可能だが行わないようにした。
91
+ # Return キーと Escape キーの割り当てが同じ場合に Return キーは機能しなくなる。
92
+ if opts.key?(:default)
93
+ opts.merge!(return_key: button_data.index { |*, obj| obj == opts[:default] })
94
+ opts.delete(:default)
95
+ end
96
+ ptr = ::FFI::MemoryPointer.new(:int)
97
+ data = MessageBoxData.new(buttons: button_data, message: message, window: window, **opts)
98
+ err = ::SDL2.SDL_ShowMessageBox(data, ptr)
99
+ raise RbSDL2Error if err < 0
100
+ # (Escape キーの割り当てがない場合に) Escape キーが押された場合 idx = -1
101
+ if (idx = ptr.read_int) < 0
102
+ block_given? ? yield : nil
103
+ else
104
+ button_data[idx]
105
+ end
106
+ end
107
+
108
+ def error(*args, title: :Error, **opts)
109
+ alert(*args, title: title, **opts.merge!(level: :error))
110
+ end
111
+
112
+ def info(*args, title: :Information, **opts)
113
+ alert(*args, title: title, **opts.merge!(level: :info))
114
+ end
115
+
116
+ def warn(*args, title: :Warning, **opts)
117
+ alert(*args, title: title, **opts.merge!(level: :warn))
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,18 @@
1
+ module RbSDL2
2
+ module Mouse
3
+ require_relative 'mouse_class'
4
+
5
+ class GlobalMouse < MouseClass
6
+ def position=(x_y)
7
+ err = ::SDL2.SDL_WarpMouseGlobal(*x_y)
8
+ raise RbSDL2Error if err < 0
9
+ update
10
+ end
11
+
12
+ def update
13
+ self.button = ::SDL2.SDL_GetGlobalMouseState(x_ptr, y_ptr)
14
+ self
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ module RbSDL2
2
+ module Mouse
3
+ module MouseButton
4
+ SDL_BUTTON = -> (x) { 1 << x - 1 }
5
+ SDL_BUTTON_LMASK = SDL_BUTTON.(::SDL2::SDL_BUTTON_LEFT)
6
+ SDL_BUTTON_MMASK = SDL_BUTTON.(::SDL2::SDL_BUTTON_MIDDLE)
7
+ SDL_BUTTON_RMASK = SDL_BUTTON.(::SDL2::SDL_BUTTON_RIGHT)
8
+ SDL_BUTTON_X1MASK = SDL_BUTTON.(::SDL2::SDL_BUTTON_X1)
9
+ SDL_BUTTON_X2MASK = SDL_BUTTON.(::SDL2::SDL_BUTTON_X2)
10
+
11
+ def any_button? = button != 0
12
+
13
+ def left_button? = SDL_BUTTON_LMASK & button != 0
14
+
15
+ def middle_button? = SDL_BUTTON_MMASK & button != 0
16
+
17
+ def right_button? = SDL_BUTTON_RMASK & button != 0
18
+
19
+ def x1_button? = SDL_BUTTON_X1MASK & button != 0
20
+
21
+ def x2_button? = SDL_BUTTON_X2MASK & button != 0
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,33 @@
1
+ module RbSDL2
2
+ module Mouse
3
+ class MouseClass
4
+ require 'singleton'
5
+ include Singleton
6
+
7
+ def initialize
8
+ @button = 0
9
+ @x_ptr, @y_ptr = Array.new(2) { ::FFI::MemoryPointer.new(:int) }
10
+ end
11
+
12
+ attr_reader :button
13
+ private attr_writer :button
14
+
15
+ require_relative 'mouse_button'
16
+ include MouseButton
17
+
18
+ def position = [x, y]
19
+
20
+ # 継承先のクラスではこのメソッドをオーバーライドすること。
21
+ # 戻り値は self が戻ることが期待されている。
22
+ def update = self
23
+
24
+ private attr_reader :x_ptr
25
+
26
+ def x = x_ptr.read_int
27
+
28
+ private attr_reader :y_ptr
29
+
30
+ def y = y_ptr.read_int
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ module RbSDL2
2
+ module Mouse
3
+ class MouseWheel
4
+ @timestamp = @x = @y = 0
5
+ @mutex = Mutex.new
6
+
7
+ MOUSE_WHEEL_EVENT_WATCH = -> (event, _) {
8
+ if event.mouse_wheel?
9
+ a = event[:direction] == ::SDL2::SDL_MOUSEWHEEL_FLIPPED ? -1 : 1
10
+ @timestamp = event[:timestamp]
11
+ @x = event[:x] * a
12
+ @y = event[:y] * a
13
+ end
14
+ }
15
+
16
+ class << self
17
+ attr_reader :timestamp, :x, :y
18
+
19
+ require_relative '../event'
20
+
21
+ def wheel=(bool)
22
+ @mutex.synchronize do
23
+ if bool
24
+ Event.add_watch_callback(MOUSE_WHEEL_EVENT_WATCH)
25
+ else
26
+ Event.remove_watch_callback(MOUSE_WHEEL_EVENT_WATCH)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ require 'singleton'
33
+ include Singleton
34
+
35
+ def initialize
36
+ @timestamp = @x = @y = 0
37
+ end
38
+
39
+ def position = [x, y]
40
+
41
+ def update
42
+ if @timestamp != MouseWheel.timestamp
43
+ @x, @y, @timestamp = MouseWheel.x, MouseWheel.y, MouseWheel.timestamp
44
+ else
45
+ @x, @y = 0, 0
46
+ end
47
+ self
48
+ end
49
+
50
+ attr_reader :x, :y
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,12 @@
1
+ module RbSDL2
2
+ module Mouse
3
+ require_relative 'mouse_class'
4
+
5
+ class RelativeMouse < MouseClass
6
+ def update
7
+ self.button = ::SDL2.SDL_GetRelativeMouseState(x_ptr, y_ptr)
8
+ self
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ module RbSDL2
2
+ module Mouse
3
+ require_relative 'mouse_class'
4
+
5
+ class WindowMouse < MouseClass
6
+ def position=(*x_y)
7
+ ptr = ::SDL2.SDL_GetMouseFocus
8
+ ::SDL2.SDL_WarpMouseInWindow(ptr, *x_y) unless ptr.null?
9
+ end
10
+
11
+ def update
12
+ self.button = ::SDL2.SDL_GetMouseState(x_ptr, y_ptr)
13
+ self
14
+ end
15
+ end
16
+ end
17
+ end