rb_sdl2 0.1.0 → 0.2.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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +69 -0
  3. data/README.md +15 -1
  4. data/lib/rb_sdl2/audio/audio.rb +162 -0
  5. data/lib/rb_sdl2/audio/audio_buffer.rb +12 -19
  6. data/lib/rb_sdl2/audio/audio_device.rb +17 -23
  7. data/lib/rb_sdl2/audio/audio_spec.rb +38 -6
  8. data/lib/rb_sdl2/audio/audio_spec_reader.rb +10 -0
  9. data/lib/rb_sdl2/clipboard.rb +31 -31
  10. data/lib/rb_sdl2/cpu_info.rb +34 -17
  11. data/lib/rb_sdl2/cursor.rb +105 -24
  12. data/lib/rb_sdl2/display.rb +16 -16
  13. data/lib/rb_sdl2/display_mode.rb +1 -1
  14. data/lib/rb_sdl2/error.rb +14 -3
  15. data/lib/rb_sdl2/errors.rb +7 -0
  16. data/lib/rb_sdl2/event/event.rb +130 -0
  17. data/lib/rb_sdl2/event/event_filter.rb +34 -70
  18. data/lib/rb_sdl2/event/event_pointer.rb +26 -0
  19. data/lib/rb_sdl2/event/event_queue.rb +97 -120
  20. data/lib/rb_sdl2/event/event_type.rb +147 -205
  21. data/lib/rb_sdl2/filesystem.rb +8 -9
  22. data/lib/rb_sdl2/hint.rb +26 -24
  23. data/lib/rb_sdl2/keyboard/keyboard.rb +50 -0
  24. data/lib/rb_sdl2/keyboard/mod_state.rb +68 -0
  25. data/lib/rb_sdl2/keyboard/state.rb +39 -0
  26. data/lib/rb_sdl2/message_box.rb +69 -102
  27. data/lib/rb_sdl2/mouse/global_mouse.rb +10 -3
  28. data/lib/rb_sdl2/mouse/mouse.rb +64 -0
  29. data/lib/rb_sdl2/mouse/mouse_button.rb +13 -11
  30. data/lib/rb_sdl2/mouse/mouse_class.rb +9 -17
  31. data/lib/rb_sdl2/mouse/mouse_wheel.rb +21 -37
  32. data/lib/rb_sdl2/mouse/relative_mouse.rb +15 -1
  33. data/lib/rb_sdl2/palette.rb +15 -11
  34. data/lib/rb_sdl2/pixel_format_enum.rb +87 -55
  35. data/lib/rb_sdl2/platform.rb +3 -1
  36. data/lib/rb_sdl2/power_info.rb +34 -23
  37. data/lib/rb_sdl2/rect.rb +7 -1
  38. data/lib/rb_sdl2/ref_count_pointer.rb +25 -10
  39. data/lib/rb_sdl2/rw_ops/rw_file.rb +19 -0
  40. data/lib/rb_sdl2/rw_ops/rw_memory.rb +18 -0
  41. data/lib/rb_sdl2/rw_ops/rw_object.rb +126 -0
  42. data/lib/rb_sdl2/rw_ops/rw_ops.rb +104 -0
  43. data/lib/rb_sdl2/rw_ops/rw_ops_pointer.rb +14 -0
  44. data/lib/rb_sdl2/sdl.rb +72 -21
  45. data/lib/rb_sdl2/sdl_pointer.rb +22 -0
  46. data/lib/rb_sdl2/surface/blend_mode.rb +41 -41
  47. data/lib/rb_sdl2/surface/pixel_format.rb +34 -33
  48. data/lib/rb_sdl2/surface.rb +280 -244
  49. data/lib/rb_sdl2/text_input.rb +21 -15
  50. data/lib/rb_sdl2/timer.rb +36 -22
  51. data/lib/rb_sdl2/version.rb +5 -3
  52. data/lib/rb_sdl2/video.rb +17 -5
  53. data/lib/rb_sdl2/window/accessor.rb +135 -0
  54. data/lib/rb_sdl2/window/display.rb +8 -8
  55. data/lib/rb_sdl2/window/flash.rb +22 -0
  56. data/lib/rb_sdl2/window/hit_test.rb +22 -43
  57. data/lib/rb_sdl2/window/shape.rb +55 -40
  58. data/lib/rb_sdl2/window/state.rb +178 -0
  59. data/lib/rb_sdl2/window/window.rb +149 -0
  60. data/lib/rb_sdl2.rb +79 -17
  61. data/rb_sdl2.gemspec +12 -4
  62. metadata +38 -37
  63. data/lib/rb_sdl2/audio/audio_allowed_changes.rb +0 -17
  64. data/lib/rb_sdl2/audio/audio_format.rb +0 -23
  65. data/lib/rb_sdl2/audio.rb +0 -132
  66. data/lib/rb_sdl2/cursor/color_cursor.rb +0 -19
  67. data/lib/rb_sdl2/cursor/cursor_class.rb +0 -24
  68. data/lib/rb_sdl2/cursor/cursor_pointer.rb +0 -12
  69. data/lib/rb_sdl2/cursor/default_cursor.rb +0 -18
  70. data/lib/rb_sdl2/cursor/system_cursor.rb +0 -45
  71. data/lib/rb_sdl2/event.rb +0 -161
  72. data/lib/rb_sdl2/keyboard/key_mod.rb +0 -37
  73. data/lib/rb_sdl2/keyboard/keyboard_state.rb +0 -34
  74. data/lib/rb_sdl2/keyboard.rb +0 -50
  75. data/lib/rb_sdl2/mouse/window_mouse.rb +0 -17
  76. data/lib/rb_sdl2/mouse.rb +0 -74
  77. data/lib/rb_sdl2/rw_ops/rw_operator.rb +0 -102
  78. data/lib/rb_sdl2/rw_ops.rb +0 -124
  79. data/lib/rb_sdl2/screen_saver.rb +0 -11
  80. data/lib/rb_sdl2/window/dialog.rb +0 -19
  81. data/lib/rb_sdl2/window/grab.rb +0 -23
  82. data/lib/rb_sdl2/window/position.rb +0 -38
  83. data/lib/rb_sdl2/window/size.rb +0 -59
  84. data/lib/rb_sdl2/window/window_flags.rb +0 -78
  85. data/lib/rb_sdl2/window.rb +0 -242
@@ -1,39 +1,120 @@
1
1
  module RbSDL2
2
- module Cursor
3
- # カーソルポインターが外部から設定される場合があるため、現在のカーソルを取得する方法を提供しない。
4
- class << self
5
- require_relative 'cursor/color_cursor'
6
- def color_cursor(...) = ColorCursor.new(...)
2
+ class Cursor
3
+ class CursorPointer < ::FFI::AutoPointer
4
+ class << self
5
+ # カーソルポインターはいつでも(表示中であっても)安全に開放できる。
6
+ # SDL はカレントカーソルが開放されたときはデフォルトカーソルをカレントカーソルに設定する。
7
+ # デフォルトカーソルは FreeCursorを呼び出しても開放されない。
8
+ def release(ptr) = ::SDL.FreeCursor(ptr)
9
+ end
10
+ end
7
11
 
12
+ @current = nil # カレントカーソルのオブジェクトを GC に回収されないように保持するために使用。
13
+
14
+ class << self
15
+ # nil を与えた場合デフォルトカーソルが設定されます。
8
16
  def current=(cursor)
9
- ::SDL2.SDL_SetCursor(cursor)
17
+ @current = if cursor.nil?
18
+ ::SDL.SetCursor(::SDL.GetDefaultCursor) # => nil
19
+ elsif Cursor === cursor
20
+ ::SDL.SetCursor(cursor)
21
+ cursor
22
+ else
23
+ raise TypeError
24
+ end
10
25
  end
11
26
 
12
- def current?(cursor)
13
- ::SDL2.SDL_GetCursor == cursor.to_ptr
14
- end
27
+ def current?(cursor) = ::SDL.GetCursor == cursor.to_ptr
15
28
 
16
- require_relative 'cursor/default_cursor'
17
- def default_cursor = DefaultCursor.instance
29
+ def hide
30
+ ::SDL.ShowCursor(::SDL::DISABLE)
31
+ nil
32
+ end
18
33
 
19
- def hide = ::SDL2.SDL_ShowCursor(::SDL2::SDL_DISABLE)
34
+ def show
35
+ ::SDL.ShowCursor(::SDL::ENABLE)
36
+ nil
37
+ end
20
38
 
21
- def show = ::SDL2.SDL_ShowCursor(::SDL2::SDL_ENABLE)
39
+ def shown? = ::SDL.ShowCursor(::SDL::QUERY) == ::SDL::ENABLE
22
40
 
23
- def shown? = ::SDL2.SDL_ShowCursor(::SDL2::SDL_QUERY) == ::SDL2::SDL_ENABLE
41
+ # カーソルの再描画を行います。
42
+ def update = ::SDL.SetCursor(nil); self
24
43
 
25
- def update
26
- self.current = nil
27
- self
44
+ # obj が Surface オブジェクトの時はカラーカーソルを作成します。
45
+ # その際に hot 引数にカーソルの判定位置を与えることができます。値は [x, y] です。
46
+ # 引数に与えた Surface オブジェクトは SDL 側にコピーされるため呼び出し後に安全に開放できます。
47
+ # obj が Symbol の時はシステムカーソルを作成します。
48
+ # Symbol は :arrow, :i_beam, :wait, :crosshair, :wait_arrow, :size_nw_se, :size_ne_sw,
49
+ # :size_we, :sie_ns, :size_all, :no, :hand が指定できます。
50
+ # obj が nil の場合はデフォルトカーソルを作成します。
51
+ def new(obj = nil, hot: nil)
52
+ ptr = CursorPointer.new(
53
+ case obj
54
+ when Surface
55
+ hot_x, hot_y = hot
56
+ # SDL_CreateColorCursor() は与えられた surface をコピーする。
57
+ # 呼び出し後に引数に与えた surface オブジェクトは安全に開放できる。
58
+ ::SDL.CreateColorCursor(obj, hot_x, hot_y)
59
+ when Symbol
60
+ id = case obj
61
+ when :arrow then ::SDL::SYSTEM_CURSOR_ARROW
62
+ when :i_beam then ::SDL::SYSTEM_CURSOR_IBEAM
63
+ when :wait then ::SDL::SYSTEM_CURSOR_WAIT
64
+ when :crosshair then ::SDL::SYSTEM_CURSOR_CROSSHAIR
65
+ when :wait_arrow then ::SDL::SYSTEM_CURSOR_WAITARROW
66
+ when :size_nw_se then ::SDL::SYSTEM_CURSOR_SIZENWSE
67
+ when :size_ne_sw then ::SDL::SYSTEM_CURSOR_SIZENESW
68
+ when :size_we then ::SDL::SYSTEM_CURSOR_SIZEWE
69
+ when :size_ns then ::SDL::SYSTEM_CURSOR_SIZENS
70
+ when :size_all then ::SDL::SYSTEM_CURSOR_SIZEALL
71
+ when :no then ::SDL::SYSTEM_CURSOR_NO
72
+ when :hand then ::SDL::SYSTEM_CURSOR_HAND
73
+ else raise ArgumentError
74
+ end
75
+ ::SDL.CreateSystemCursor(id)
76
+ when nil
77
+ # SDL 側にあるデフォルトカーソルのポインターを戻す。このポインターはシングルトンである。
78
+ # ポインターを SDL_FreeCursor() へ与えても安全である。SDL 内部では開放されない。
79
+ ::SDL.GetDefaultCursor
80
+ else
81
+ raise ArgumentError
82
+ end
83
+ )
84
+ raise RbSDL2Error if ptr.null?
85
+ super(ptr)
28
86
  end
29
87
  end
30
88
 
31
- require 'forwardable'
32
- extend SingleForwardable
33
- require_relative 'cursor/system_cursor'
34
- def_single_delegators :SystemCursor,
35
- *%i(arrow_cursor crosshair_cursor hand_cursor i_beam_cursor no_cursor
36
- size_all_cursor size_ne_sw_cursor size_ns_cursor size_nw_se_cursor
37
- size_we_cursor wait_cursor wait_arrow_cursor)
89
+ def initialize(ptr)
90
+ @ptr = ptr
91
+ end
92
+
93
+ def ==(other)
94
+ other.respond_to?(:to_ptr) && other.to_ptr == @ptr
95
+ end
96
+
97
+ # 自身をカレントカーソルに設定します。カーソルの表示状態は変更されません。
98
+ def current! = Cursor.current = self
99
+
100
+ # 自身がカレントカーソルの場合に true を戻します。
101
+ def current? = Cursor.current?(self)
102
+
103
+ # 自身がカレントカーソルの場合のみカーソルを非表示にします。
104
+ def hide
105
+ current? && Cursor.hide
106
+ self
107
+ end
108
+
109
+ # 自身を表示カーソルにします。この時カレントカーソルは自身に設定されています。
110
+ def show
111
+ current! && Cursor.show
112
+ self
113
+ end
114
+
115
+ # 自身がカレントカーソルの場合かつカーソル表示中の時に true を戻します。
116
+ def shown? = current? && Cursor.shown?
117
+
118
+ def to_ptr = @ptr
38
119
  end
39
120
  end
@@ -2,7 +2,7 @@ module RbSDL2
2
2
  class Display
3
3
  class << self
4
4
  def displays
5
- count = ::SDL2.SDL_GetNumVideoDisplays
5
+ count = ::SDL.GetNumVideoDisplays
6
6
  raise RbSDL2Error if count < 0
7
7
  count.times.map { |num| Display.new(num) }
8
8
  end
@@ -14,7 +14,7 @@ module RbSDL2
14
14
 
15
15
  def bounds
16
16
  rect = Rect.new
17
- err = ::SDL2.SDL_GetDisplayBounds(index, rect)
17
+ err = ::SDL.GetDisplayBounds(index, rect)
18
18
  raise RbSDL2Error if err < 0
19
19
  rect.to_a
20
20
  end
@@ -24,7 +24,7 @@ module RbSDL2
24
24
  def closest_display_mode(**display_mode)
25
25
  mode ||= DisplayMode.new(**display_mode)
26
26
  closest = DisplayMode.new
27
- err = ::SDL2.SDL_GetClosestDisplayMode(self, mode, closest)
27
+ err = ::SDL.GetClosestDisplayMode(self, mode, closest)
28
28
  raise RbSDL2Error if err.null?
29
29
  closest
30
30
  # 利用可能なディスプレイモードが検索され, 要求と最も近いモードがclosestに代入される.
@@ -35,24 +35,24 @@ module RbSDL2
35
35
 
36
36
  def current_display_mode
37
37
  obj = DisplayMode.new
38
- err = ::SDL2.SDL_GetCurrentDisplayMode(index, obj)
38
+ err = ::SDL.GetCurrentDisplayMode(index, obj)
39
39
  raise RbSDL2Error if err < 0
40
40
  obj
41
41
  end
42
42
 
43
43
  def desktop_display_mode
44
44
  obj = DisplayMode.new
45
- err = ::SDL2.SDL_GetDesktopDisplayMode(index, obj)
45
+ err = ::SDL.GetDesktopDisplayMode(index, obj)
46
46
  raise RbSDL2Error if err < 0
47
47
  obj
48
48
  end
49
49
 
50
50
  def display_modes
51
- num = ::SDL2.SDL_GetNumDisplayModes(index)
51
+ num = ::SDL.GetNumDisplayModes(index)
52
52
  raise RbSDL2Error if num < 0
53
53
  num.times.map do |mode_index|
54
54
  obj = DisplayMode.new
55
- err = ::SDL2.SDL_GetDisplayMode(index, mode_index, obj)
55
+ err = ::SDL.GetDisplayMode(index, mode_index, obj)
56
56
  raise RbSDL2Error if err < 0
57
57
  obj
58
58
  end
@@ -61,7 +61,7 @@ module RbSDL2
61
61
  # ディスプレイピクセルの斜め、水平、垂直方向の DPI を配列で戻す。
62
62
  def dpi
63
63
  d_h_v_dpi = Array.new(3) { ::FFI::MemoryPointer.new(:float) }
64
- err = ::SDL2.SDL_GetDisplayDPI(index, *d_h_v_dpi)
64
+ err = ::SDL.GetDisplayDPI(index, *d_h_v_dpi)
65
65
  raise RbSDL2Error if err < 0
66
66
  d_h_v_dpi.map { |v| v.read_float }
67
67
  end
@@ -74,28 +74,28 @@ module RbSDL2
74
74
  end
75
75
 
76
76
  def name
77
- ptr = ::SDL2.SDL_GetDisplayName(index)
77
+ ptr = ::SDL.GetDisplayName(index)
78
78
  raise RbSDL2Error if ptr.null?
79
- ptr.read_string.force_encoding(Encoding::UTF_8)
79
+ SDL.ptr_to_str(ptr)
80
80
  end
81
81
  alias to_s name
82
82
 
83
- def orientation = ::SDL2.SDL_GetDisplayOrientation(index)
83
+ def orientation = ::SDL.GetDisplayOrientation(index)
84
84
 
85
85
  module DisplayOrientation
86
- def flipped_landscape? = ::SDL2::SDL_ORIENTATION_LANDSCAPE_FLIPPED == orientation
86
+ def flipped_landscape? = ::SDL::ORIENTATION_LANDSCAPE_FLIPPED == orientation
87
87
 
88
- def flipped_portrait? = ::SDL2::SDL_ORIENTATION_PORTRAIT_FLIPPED == orientation
88
+ def flipped_portrait? = ::SDL::ORIENTATION_PORTRAIT_FLIPPED == orientation
89
89
 
90
- def landscape? = ::SDL2::SDL_ORIENTATION_LANDSCAPE == orientation
90
+ def landscape? = ::SDL::ORIENTATION_LANDSCAPE == orientation
91
91
 
92
- def portrait? = ::SDL2::SDL_ORIENTATION_PORTRAIT == orientation
92
+ def portrait? = ::SDL::ORIENTATION_PORTRAIT == orientation
93
93
  end
94
94
  include DisplayOrientation
95
95
 
96
96
  def usable_bounds
97
97
  rect = Rect.new
98
- err = ::SDL2.SDL_GetDisplayUsableBounds(index, rect)
98
+ err = ::SDL.GetDisplayUsableBounds(index, rect)
99
99
  raise RbSDL2Error if err < 0
100
100
  rect.to_a
101
101
  end
@@ -1,7 +1,7 @@
1
1
  module RbSDL2
2
2
  class DisplayMode
3
3
  def initialize(format: 0, h: 0, height: h, refresh_rate: 0, w: 0, width: w)
4
- @st = ::SDL2::SDL_DisplayMode.new
4
+ @st = ::SDL::DisplayMode.new
5
5
  @st[:format] = PixelFormatEnum.to_num(format)
6
6
  @st[:w] = width
7
7
  @st[:h] = height
data/lib/rb_sdl2/error.rb CHANGED
@@ -1,9 +1,20 @@
1
1
  module RbSDL2
2
- module Error
2
+ class Error < StandardError
3
3
  class << self
4
- def clear = ::SDL2.SDL_ClearError
4
+ # SDL が設定したエラーメッセージをクリアします。
5
+ def clear = ::SDL.ClearError
5
6
 
6
- def message = ::SDL2.SDL_GetError.read_string
7
+ # SDL からのエラーメッセージを文字列で返します。
8
+ # SDL からエラーが通知されてもエラーメッセージがあるとは限りません。
9
+ # SDL の関数はエラーの状態を示してもエラーメッセージをセットしない場合があります。
10
+ def last_error_message = SDL.ptr_to_str(::SDL.GetError)
11
+
12
+ def last_error_message=(error_message)
13
+ # SDL_SetError() の第一引数は sprintf フォーマットである。
14
+ # このメソッドのデザインの都合上、可変長引数を与える方法が無い。
15
+ # "%" をエスケープすることで第二引数を無視させてメモリー参照を行わないようにする。
16
+ ::SDL.SetError(SDL.str_to_sdl(error_message.gsub(/%/, "%%")))
17
+ end
7
18
  end
8
19
  end
9
20
  end
@@ -0,0 +1,7 @@
1
+ module RbSDL2
2
+ class RbSDL2Error < StandardError
3
+ def initialize(error_message = nil)
4
+ super(error_message || Error.last_error_message)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,130 @@
1
+ module RbSDL2
2
+ class Event
3
+ # SDL_SYSWMEVENT の msg メンバーの実装はしない。
4
+ # msg ポインターが指す SDL_SysWMmsg は構造体サイズが環境に依存するためコピーを行うことができない。
5
+ # SDL_SYSWMEVENT イベントの作成はできない、受け取りはできるが msg メンバーへのアクセスはできない。
6
+ # SDL は SDL_SYSWMEVENT にある SDL_SysWMmsg をマネージしている。
7
+ # イベントへの追加ではコピーを行い、取り出しの際にはテンポラリ領域へコピーしてからアプリケーションへ渡す。
8
+ # アプリケーションが SDL_SYSWMEVENT を無視してもメモリーリークはしない。
9
+
10
+ require 'forwardable'
11
+ extend SingleForwardable
12
+
13
+ require_relative 'event_filter'
14
+ def_single_delegators EventFilter, *%i(define_watch filter_callback_defined? undefine_watch)
15
+
16
+ require_relative 'event_queue'
17
+ def_single_delegators EventQueue, *%i(clear exist? poll pump wait)
18
+
19
+ class << self
20
+ def quit?
21
+ pump
22
+ exist?(::SDL::QUIT)
23
+ end
24
+ end
25
+
26
+ require_relative 'event_type'
27
+ def_single_delegators EventType, *%i(disable enable ignore? register_events)
28
+
29
+ include EventType
30
+
31
+ require_relative 'event_pointer'
32
+
33
+ class << self
34
+ def new(type:, **members)
35
+ obj = super(EventPointer.new(type))
36
+ members.each_pair { |sym, val| obj[sym] = val }
37
+ obj
38
+ end
39
+
40
+ def to_ptr(ptr)
41
+ obj = allocate
42
+ obj.__send__(:initialize, ptr)
43
+ obj
44
+ end
45
+ end
46
+
47
+ def initialize(ptr)
48
+ @ptr = ptr
49
+ @entity = nil
50
+ @obj = if drop_file? || drop_text?
51
+ SDLPointer.new(entity[:file])
52
+ elsif text_editing_ext?
53
+ SDLPointer.new(entity[:text])
54
+ elsif sys_wm_event? && entity[:msg].null?
55
+ # msg に NULL があると SDL はこのポインターをチェックせずに読み出す。
56
+ raise TypeError
57
+ end
58
+ end
59
+
60
+ # SDL_SYSWMEVENT の msg はポインターです。このポインターの取り扱いはアプリケーションに委ねられます。
61
+ # SDL_USEREVENT の data1, data2 はポインターです。このポインターの取り扱いはアプリケーションに委ねられます。
62
+ def [](sym)
63
+ obj = entity[sym]
64
+ case sym
65
+ when :data then obj.to_a
66
+ when :file then if drop_file? || drop_text?
67
+ SDL.ptr_to_str(obj)
68
+ else
69
+ # SDL_EVENTBEGIN, SDL_DROPCOMPLETE はこのメンバー使用しない。
70
+ nil
71
+ end
72
+ when :keysym then { scancode: obj[:scancode], sym: obj[:sym], mod: obj[:mod] }
73
+ when :msg then obj
74
+ when :text then SDL.ptr_to_str(obj.to_ptr)
75
+ when :type then type
76
+ else obj
77
+ end
78
+ end
79
+
80
+ # SDL_SYSWMEVENT の msg への書き込みはできません。
81
+ # SDL_USEREVENT の data1, data2 はポインターです。このポインターの取り扱いはアプリケーションに委ねられます。
82
+ def []=(sym, val)
83
+ raise FrozenError if frozen?
84
+
85
+ case sym
86
+ when :data then entity[sym].tap { |st| val.each.with_index { |v, i| st[i] = v } }
87
+ when :file then if drop_file? || drop_text?
88
+ entity[sym] = @obj = SDLPointer.from_string(val)
89
+ else
90
+ # SDL_EVENTBEGIN, SDL_DROPCOMPLETE はこのメンバー使用しない。
91
+ raise TypeError
92
+ end
93
+ when :keysym then entity[sym].tap { |st| val.each { |k, v| st[k] = v } }
94
+ when :msg then raise NotImplementedError
95
+ when :text then if text_editing_ext?
96
+ entity[sym] = @obj = SDLPointer.from_string(val)
97
+ else
98
+ ::SDL.utf8strlcpy(entity[sym].to_ptr, SDL.str_to_sdl(val), entity[sym].size)
99
+ end
100
+ when :type then raise TypeError
101
+ else entity[sym] = val
102
+ end
103
+ end
104
+
105
+ private def entity = @entity ||= EventType::ENTITY_MAP[type].new(@ptr)
106
+
107
+ # clone, dup はディープコピーを行います。
108
+ def initialize_copy(obj)
109
+ super
110
+ initialize(EventPointer.copy(obj.to_ptr))
111
+ end
112
+
113
+ def inspect = "#<#{self.class.name}: #{name}>"
114
+
115
+ def member?(name) = members.include?(name)
116
+
117
+ def members = entity.members.grep_v(/\Apadding/)
118
+
119
+ def name = EventType.to_name(type)
120
+
121
+ def to_h = members.map { |sym| [sym, self[sym]] }.to_h
122
+
123
+ def to_ptr = @ptr
124
+
125
+ def to_s = name.to_s
126
+
127
+ def type = @ptr.type
128
+ alias to_i type
129
+ end
130
+ end
@@ -1,82 +1,46 @@
1
1
  module RbSDL2
2
- class Event
3
- class EventFilter < ::FFI::Function
4
- @filter_callback = nil, nil
5
- @filter_mutex = Mutex.new
2
+ class EventFilter < ::FFI::Function
6
3
 
7
- class << self
8
- def filter_callback_set(obj, userdata = nil)
9
- func = if Proc === obj
10
- # イベントを削除する時にイベントメンバーのポインターを開放(Event#clear)する。
11
- new { |event| obj.call(event) || (event.clear; nil) }
12
- else
13
- obj
14
- end
15
- # func, userdata の対の関係を保つ。
16
- @filter_mutex.synchronize do
17
- ::SDL2.SDL_SetEventFilter(func, userdata)
18
- @filter_callback = [func, userdata]
19
- end
20
- [obj, userdata]
21
- end
4
+ class Releaser
5
+ def initialize(obj) = @obj = obj
22
6
 
23
- # SDL にフィルターコールバック関数が設定されている場合に true を戻す。
24
- def filter_callback_defined?
25
- _func_userdata = Array.new(2) { ::FFI::MemoryPointer.new(:pointer) }
26
- # SDL_GetEventFilter はコールバックのポインター関数が NULL の場合に SDL_FALSE となる。
27
- # userdata ポインターが設定されていても SDL_GetEventFilter の戻り値に関与しない。
28
- ::SDL2.SDL_GetEventFilter(*_func_userdata) == ::SDL2::SDL_TRUE
29
- end
30
- end
7
+ def call(_id) = ::SDL.DelEventWatch(@obj, nil)
8
+ end
9
+
10
+ @watches = []
31
11
 
32
- @watch_callbacks = []
33
- @watch_mutex = Mutex.new
12
+ class << self
13
+ def define_watch(func)
14
+ ::SDL.AddEventWatch(func, nil)
15
+ @watches << func.__id__
16
+ ptr = ::FFI::Pointer.new(func.address)
17
+ ObjectSpace.define_finalizer(func, Releaser.new(ptr))
18
+ end
34
19
 
35
- class << self
36
- def add_watch_callback(obj, userdata = nil)
37
- func = if Proc === obj
38
- new(&obj)
39
- else
40
- obj
41
- end
42
- obj_userdata = [obj, userdata]
43
- @watch_mutex.synchronize do
44
- ::SDL2.SDL_AddEventWatch(func, userdata)
45
- @watch_callbacks << [obj_userdata, func]
46
- end
47
- obj_userdata
48
- end
20
+ def undefine_watch(func)
21
+ func_id = func.__id__
22
+ @watches.delete_if { |id| (id == func_id).tap { ::SDL.DelEventWatch(func, nil) } }
23
+ ObjectSpace.undefine_finalizer(func)
24
+ end
49
25
 
50
- def remove_watch_callback(obj, userdata = nil)
51
- obj_userdata = [obj, userdata]
52
- @watch_mutex.synchronize do
53
- idx = @watch_callbacks.index { |obj| obj.first == obj_userdata }
54
- if idx
55
- _, func = @watch_callbacks.delete_at(idx)
56
- ::SDL2.SDL_DelEventWatch(func, userdata)
57
- end
58
- end
59
- obj_userdata
60
- end
26
+ # SDL にフィルターコールバック関数が設定されている場合に true を戻す。
27
+ def filter_callback_defined?
28
+ ::SDL.GetEventFilter(nil, nil) == ::SDL::TRUE
61
29
  end
30
+ end
62
31
 
63
- require 'delegate'
32
+ require_relative 'event'
33
+ require_relative 'event_pointer'
64
34
 
65
- # 引数ブロックへはコールバック実行時に Event のインスタンス(のデリゲーター)が与えられる。
66
- # 与えられた Event インスタンスは引数ブロック終了後に nil に変化する(デリゲート先を変更している)。
67
- # SDL がイベントコールバックへ与えるイベントへのポインターがイベントキューの一部を直接指しているため
68
- # コールバックを抜けた後にイベント内容の永続性が保証ができない(たぶん別のイベント内容になるだろう)。
69
- # 引数ブロックに与えられたオブジェクトをスコープ外に持ち出しても安全である。
70
- # オブジェクトではなくイベントの内容をスコープ外に持ち出したい場合は
71
- # 与えられたオブジェクトをコピー(clone, dup)すればよい。
72
- def initialize
73
- # typedef int (SDLCALL * SDL_EventFilter) (void *userdata, SDL_Event * event);
74
- super(:int, [:pointer, :pointer]) do |_userdata, ptr|
75
- obj = SimpleDelegator.new(Event.to_ptr(ptr))
76
- yield(obj) ? 1 : 0
77
- ensure
78
- obj.__setobj__(nil)
79
- end
35
+ # コールバックにはコピーしたイベントが与えられます。
36
+ # よって SDL のイベントキューにあるイベントを書き換えることはできません。
37
+ def initialize(proc)
38
+ # int EventFilter(void* userdata, SDL_Event* event);
39
+ super(:int, [:pointer, :pointer]) do |_userdata, ptr|
40
+ # SDL のイベントキューへの参照ポインターを受け取る場合は必ずコピーを取る必要がある。
41
+ # キューの状態は変化するため、次にアクセスする際にポインター先が存在する保証はない。
42
+ # SDL のイベントキューを参照している間は SDL がキューをロックしているためコピーは安全にできる。
43
+ proc.call( Event.to_ptr(EventPointer.copy(ptr)) ) ? 1 : 0
80
44
  end
81
45
  end
82
46
  end
@@ -0,0 +1,26 @@
1
+ module RbSDL2
2
+ class EventPointer < ::FFI::MemoryPointer
3
+ class << self
4
+ def copy(ptr)
5
+ obj = malloc
6
+ ::SDL.memcpy(obj, ptr, size)
7
+ type = obj.type
8
+ if ::SDL::DROPFILE == type || ::SDL::DROPTEXT == type
9
+ ref = obj + ::SDL::DropEvent.offset_of(:file)
10
+ str = ::SDL.strdup(ref.read_pointer)
11
+ raise NoMemoryError if str.null?
12
+ ref.write_pointer(str)
13
+ end
14
+ obj
15
+ end
16
+
17
+ def malloc = new(0)
18
+
19
+ def new(type) = super(size).write_uint32(type)
20
+
21
+ def size = ::SDL::Event.size
22
+ end
23
+
24
+ def type = read_uint32
25
+ end
26
+ end