rb_sdl2 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +1 -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 +11 -4
  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 +12 -12
  34. data/lib/rb_sdl2/pixel_format_enum.rb +87 -55
  35. data/lib/rb_sdl2/platform.rb +1 -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 +67 -28
  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 -265
  49. data/lib/rb_sdl2/text_input.rb +21 -15
  50. data/lib/rb_sdl2/timer.rb +36 -31
  51. data/lib/rb_sdl2/version.rb +3 -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 +4 -4
  62. metadata +31 -35
  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 -105
  78. data/lib/rb_sdl2/rw_ops.rb +0 -149
  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,13 +1,20 @@
1
1
  module RbSDL2
2
- module Error
2
+ class Error < StandardError
3
3
  class << self
4
4
  # SDL が設定したエラーメッセージをクリアします。
5
- def clear = ::SDL2.SDL_ClearError
5
+ def clear = ::SDL.ClearError
6
6
 
7
7
  # SDL からのエラーメッセージを文字列で返します。
8
- # SDL からエラーが通知されてもえらメッセージがあるとは限りません。
8
+ # SDL からエラーが通知されてもエラーメッセージがあるとは限りません。
9
9
  # SDL の関数はエラーの状態を示してもエラーメッセージをセットしない場合があります。
10
- def message = ::SDL2.SDL_GetError.read_string
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
11
18
  end
12
19
  end
13
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