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,53 +1,37 @@
1
1
  module RbSDL2
2
2
  module Mouse
3
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
4
+ def initialize(window = nil)
5
+ @wheel_x = @wheel_y = @x = @y = 0
6
+ window_id = window&.window_id
7
+ proc = -> (event) do
8
+ if event.mouse_wheel? && window_id && event[:windowID] == window_id
9
+ a = event[:direction] == ::SDL::MOUSEWHEEL_FLIPPED ? -1 : 1
10
+ @x += event[:x] * a
11
+ @y += event[:y] * a
28
12
  end
29
13
  end
14
+ @watch = EventFilter.new(proc)
30
15
  end
31
16
 
32
- require 'singleton'
33
- include Singleton
34
-
35
- def initialize
36
- @timestamp = @x = @y = 0
17
+ def update
18
+ @wheel_x, @wheel_y = @x, @y
19
+ @x = @y = 0
20
+ self
37
21
  end
38
22
 
39
- def position = [x, y]
40
-
41
- def update
42
- if @timestamp != MouseWheel.timestamp
43
- @x, @y, @timestamp = MouseWheel.x, MouseWheel.y, MouseWheel.timestamp
23
+ def wheel=(bool)
24
+ if bool
25
+ EventFilter.define_watch(@watch)
44
26
  else
45
- @x, @y = 0, 0
27
+ EventFilter.undefine_watch(@watch)
46
28
  end
47
- self
29
+ @wheel_x = @wheel_y = @x = @y = 0
48
30
  end
49
31
 
50
- attr_reader :x, :y
32
+ def wheel_position = [@wheel_x, @wheel_y]
33
+
34
+ attr_reader :wheel_x, :wheel_y, :window_id
51
35
  end
52
36
  end
53
37
  end
@@ -3,8 +3,22 @@ module RbSDL2
3
3
  require_relative 'mouse_class'
4
4
 
5
5
  class RelativeMouse < MouseClass
6
+ require 'singleton'
7
+ include Singleton
8
+
9
+ @_x = ::FFI::MemoryPointer.new(:int)
10
+ @_y = ::FFI::MemoryPointer.new(:int)
11
+
12
+ class << self
13
+ def update
14
+ self.button, self.x, self.y =
15
+ ::SDL.GetRelativeMouseState(@_x, @_y), @_x.read_int, @_y.read_int
16
+ self
17
+ end
18
+ end
19
+
6
20
  def update
7
- self.button = ::SDL2.SDL_GetRelativeMouseState(x_ptr, y_ptr)
21
+ self.class.update
8
22
  self
9
23
  end
10
24
  end
@@ -4,9 +4,9 @@ module RbSDL2
4
4
 
5
5
  class PalettePointer < RefCountPointer
6
6
  class << self
7
- def release(ptr) = ::SDL2.SDL_FreePalette(ptr)
7
+ def release(ptr) = ::SDL.FreePalette(ptr)
8
8
 
9
- def entity_class = ::SDL2::SDL_Palette
9
+ def entity_class = ::SDL::Palette
10
10
  end
11
11
  end
12
12
 
@@ -18,7 +18,9 @@ module RbSDL2
18
18
  end
19
19
 
20
20
  def new(num_colors)
21
- ptr = PalettePointer.new(::SDL2.SDL_AllocPalette(num_colors))
21
+ # SDL_AllocPalette() のエラーは引数が < 1、または必要なメモリー領域がない場合。
22
+ # パレットのカラーは作成時に全て [255, 255, 255, 255] で埋められる。
23
+ ptr = PalettePointer.new(::SDL.AllocPalette(num_colors))
22
24
  raise RbSDL2Error if ptr.null?
23
25
  super(ptr)
24
26
  end
@@ -31,34 +33,36 @@ module RbSDL2
31
33
  end
32
34
 
33
35
  def initialize(ptr)
34
- @st = ::SDL2::SDL_Palette.new(ptr)
36
+ @st = ::SDL::Palette.new(ptr)
35
37
  end
36
38
 
37
39
  def ==(other)
38
- other.respond_to?(:to_ptr) && to_ptr == other.to_ptr
40
+ other.respond_to?(:to_ptr) && other.to_ptr == to_ptr
39
41
  end
40
42
 
41
43
  def [](nth)
42
44
  raise ArgumentError if nth < 0 || length <= nth
43
- ::SDL2::SDL_Color.new(@st[:colors] + ::SDL2::SDL_Color.size * nth).values
45
+ ::SDL::Color.new(@st[:colors] + ::SDL::Color.size * nth).values
44
46
  end
45
47
 
46
48
  # color 引数には 3要素以上の配列であること。4要素目以降は無視される。
47
49
  # color 引数は内部で splat する。これに対応していれば配列以外のオブジェクトでもよい。
48
- # パレットのカラーが描画に使われるときはアルファ値は無視されて不透明(SDL_ALPHA_OPAQUE)として扱わられる。
49
- # パレットのカラーは作成時に全て [255, 255, 255, 255] で埋められている。
50
+ # パレットのカラーが描画に使われるときはアルファ値は無視されて不透明(ALPHA_OPAQUE)として扱う。
50
51
  def []=(nth, color)
51
52
  raise ArgumentError if nth < 0 || length <= nth
52
- c = ::SDL2::SDL_Color.new
53
+ c = ::SDL::Color.new
53
54
  c[:r], c[:g], c[:b] = color
54
- err = ::SDL2.SDL_SetPaletteColors(self, c, nth, 1)
55
+ err = ::SDL.SetPaletteColors(self, c, nth, 1)
55
56
  raise RbSDL2Error if err < 0
56
57
  end
57
58
 
58
59
  def each = length.times { |nth| yield(self[nth]) }
59
60
 
60
- def length = @st[:ncolors]
61
+ def inspect
62
+ "#<#{self.class.name} colors=#{length}>"
63
+ end
61
64
 
65
+ def length = @st[:ncolors]
62
66
  alias size length
63
67
 
64
68
  def to_a = to_enum.to_a
@@ -1,73 +1,105 @@
1
1
  module RbSDL2
2
2
  module PixelFormatEnum
3
- names = ::SDL2.constants.grep(/\ASDL_PIXELFORMAT_/)
4
- values = names.map { |n| ::SDL2.const_get(n) }
5
- FORMAT_MAP = names.zip(values).to_h.freeze
6
-
7
- INDEXED_TYPES = [::SDL2::SDL_PIXELTYPE_INDEX1, ::SDL2::SDL_PIXELTYPE_INDEX4,
8
- ::SDL2::SDL_PIXELTYPE_INDEX8].freeze
9
-
10
- PACKED_TYPES = [::SDL2::SDL_PIXELTYPE_PACKED8, ::SDL2::SDL_PIXELTYPE_PACKED16,
11
- ::SDL2::SDL_PIXELTYPE_PACKED32].freeze
12
-
13
- ARRAY_TYPES = [::SDL2::SDL_PIXELTYPE_ARRAYU8, ::SDL2::SDL_PIXELTYPE_ARRAYU16,
14
- ::SDL2::SDL_PIXELTYPE_ARRAYU32, ::SDL2::SDL_PIXELTYPE_ARRAYF16,
15
- ::SDL2::SDL_PIXELTYPE_ARRAYF32].freeze
16
-
17
- PACKED_ORDERS = [::SDL2::SDL_PACKEDORDER_ARGB, ::SDL2::SDL_PACKEDORDER_RGBA,
18
- ::SDL2::SDL_PACKEDORDER_ABGR, ::SDL2::SDL_PACKEDORDER_BGRA].freeze
19
-
20
- ARRAY_ORDERS = [::SDL2::SDL_ARRAYORDER_ARGB, ::SDL2::SDL_ARRAYORDER_RGBA,
21
- ::SDL2::SDL_ARRAYORDER_ABGR, ::SDL2::SDL_ARRAYORDER_BGRA].freeze
3
+ # SDL の PixelFormatEnum はピクセルフォーマットをビット列にパックしたものである。
4
+ # ビット操作を通じてピクセルフォーマットを構築、分析できる。
5
+ # しかし、SDL API は事前に定義されているフォーマット以外を与えるとエラーになる。
6
+ # ユーザ側で自由なフォーマット定義を行うことはできない。
7
+ # Ruby 側でフォーマットの詳細を知るため目的に応じたフォーマット集合を作成することにした。
8
+ # これはコードのメンテナンスを考慮したためである。
9
+ #
10
+ # [[sym, num, type, alpha], ...]
11
+ table = [
12
+ [:index1lsb, ::SDL::PIXELFORMAT_INDEX1LSB, :indexed, false],
13
+ [:index1msb, ::SDL::PIXELFORMAT_INDEX1MSB, :indexed, false],
14
+ [:index4lsb, ::SDL::PIXELFORMAT_INDEX4LSB, :indexed, false],
15
+ [:index4msb, ::SDL::PIXELFORMAT_INDEX4MSB, :indexed, false],
16
+ [:index8, ::SDL::PIXELFORMAT_INDEX8, :indexed, false],
17
+ [:rgb332, ::SDL::PIXELFORMAT_RGB332, :packed, false],
18
+ [:xrgb4444, ::SDL::PIXELFORMAT_XRGB4444, :packed, false],
19
+ [:rgb444, ::SDL::PIXELFORMAT_RGB444, :packed, false],
20
+ [:xbgr4444, ::SDL::PIXELFORMAT_XBGR4444, :packed, false],
21
+ [:bgr444, ::SDL::PIXELFORMAT_BGR444, :packed, false],
22
+ [:xrgb1555, ::SDL::PIXELFORMAT_XRGB1555, :packed, false],
23
+ [:rgb555, ::SDL::PIXELFORMAT_RGB555, :packed, false],
24
+ [:xbgr1555, ::SDL::PIXELFORMAT_XBGR1555, :packed, false],
25
+ [:bgr555, ::SDL::PIXELFORMAT_BGR555, :packed, false],
26
+ [:argb4444, ::SDL::PIXELFORMAT_ARGB4444, :packed, true],
27
+ [:rgba4444, ::SDL::PIXELFORMAT_RGBA4444, :packed, true],
28
+ [:abgr4444, ::SDL::PIXELFORMAT_ABGR4444, :packed, true],
29
+ [:bgra4444, ::SDL::PIXELFORMAT_BGRA4444, :packed, true],
30
+ [:argb1555, ::SDL::PIXELFORMAT_ARGB1555, :packed, true],
31
+ [:rgba5551, ::SDL::PIXELFORMAT_RGBA5551, :packed, true],
32
+ [:abgr1555, ::SDL::PIXELFORMAT_ABGR1555, :packed, true],
33
+ [:bgra5551, ::SDL::PIXELFORMAT_BGRA5551, :packed, true],
34
+ [:rgb565, ::SDL::PIXELFORMAT_RGB565, :packed, false],
35
+ [:bgr565, ::SDL::PIXELFORMAT_BGR565, :packed, false],
36
+ [:rgb24, ::SDL::PIXELFORMAT_RGB24, :array, false],
37
+ [:bgr24, ::SDL::PIXELFORMAT_BGR24, :array, false],
38
+ [:xrgb8888, ::SDL::PIXELFORMAT_XRGB8888, :packed, false],
39
+ [:rgb888, ::SDL::PIXELFORMAT_RGB888, :packed, false],
40
+ [:rgbx8888, ::SDL::PIXELFORMAT_RGBX8888, :packed, false],
41
+ [:xbgr8888, ::SDL::PIXELFORMAT_XBGR8888, :packed, false],
42
+ [:bgr888, ::SDL::PIXELFORMAT_BGR888, :packed, false],
43
+ [:bgrx8888, ::SDL::PIXELFORMAT_BGRX8888, :packed, false],
44
+ [:argb8888, ::SDL::PIXELFORMAT_ARGB8888, :packed, true],
45
+ [:rgba8888, ::SDL::PIXELFORMAT_RGBA8888, :packed, true],
46
+ [:abgr8888, ::SDL::PIXELFORMAT_ABGR8888, :packed, true],
47
+ [:bgra8888, ::SDL::PIXELFORMAT_BGRA8888, :packed, true],
48
+ [:argb2101010, ::SDL::PIXELFORMAT_ARGB2101010, :packed, true],
49
+ [:rgba32, ::SDL::PIXELFORMAT_RGBA32, :packed, true],
50
+ [:argb32, ::SDL::PIXELFORMAT_ARGB32, :packed, true],
51
+ [:bgra32, ::SDL::PIXELFORMAT_BGRA32, :packed, true],
52
+ [:abgr32, ::SDL::PIXELFORMAT_ABGR32, :packed, true],
53
+ [:yv12, ::SDL::PIXELFORMAT_YV12, :fourcc, false],
54
+ [:iyuv, ::SDL::PIXELFORMAT_IYUV, :fourcc, false],
55
+ [:yuy2, ::SDL::PIXELFORMAT_YUY2, :fourcc, false],
56
+ [:uyvy, ::SDL::PIXELFORMAT_UYVY, :fourcc, false],
57
+ [:yvyu, ::SDL::PIXELFORMAT_YVYU, :fourcc, false],
58
+ [:nv12, ::SDL::PIXELFORMAT_NV12, :fourcc, false],
59
+ [:nv21, ::SDL::PIXELFORMAT_NV21, :fourcc, false],
60
+ [:external_oes, ::SDL::PIXELFORMAT_EXTERNAL_OES, :fourcc, false],
61
+ ]
62
+
63
+ FORMAT_MAP = table.map { |sym, n, _, _| [sym, n] }.to_h.freeze
64
+
65
+ INDEXED_TYPES = table.inject([]) { |a, (_, n, type, _)| a << n if type == :indexed; a }.freeze
66
+
67
+ WITH_ALPHA = table.inject([]) { |a, (_, n, _, alpha)| a << n if alpha; a }.freeze
22
68
 
23
69
  class << self
24
- def array_type?(num) = !fourcc?(num) && ARRAY_TYPES.include?(to_type(num))
25
-
26
- def fourcc?(num) = (num >> 28) & 0x0F != 1
27
-
28
- def indexed_type?(num) = !fourcc?(num) && INDEXED_TYPES.include?(to_type(num))
29
-
30
- def packed_type?(num) = !fourcc?(num) && PACKED_TYPES.include?(to_type(num))
31
-
32
- def to_fourcc(num)
33
- 4.times.inject([]) { |n, i| n << (num >> i * 8) % 0x100 }.pack("c4") if fourcc?
70
+ def to_name(num)
71
+ ::SDL.GetPixelFormatName(num).read_string.delete_prefix("SDL_PIXELFORMAT_")
34
72
  end
35
73
 
36
- def to_name(num) = ::SDL2.SDL_GetPixelFormatName(num).read_string
37
-
38
- # obj は SDL の SDL_PIXELFORMAT_* 定数のどれか、または定数名でもよい。
39
- # 定数名は RGBA32 のような短縮した名前でもよい。"UNKNOWN" も受け取れる(値は 0)。
40
- # 該当するフォーマットがない場合は 0 (SDL_PIXELFORMAT_UNKNOWN) を戻す。
41
- # SDL はオリジナルのフォーマットを処理しないことに注意。
74
+ # obj に与えたピクセルフォーマットに応じた整数値を戻す。
42
75
  def to_num(obj)
43
- name = if Symbol === obj || String === obj
44
- obj.match?(/\ASDL_PIXELFORMAT_/) ? obj : "SDL_PIXELFORMAT_#{obj.upcase}"
45
- else
46
- to_name(obj)
47
- end
48
- FORMAT_MAP[name.to_sym].to_i
49
- end
50
-
51
- def to_order(num) = fourcc?(num) ? 0 : (num >> 20) & 0x0F
52
-
53
- def to_type(num) = fourcc?(num) ? 0 : (num >> 24) & 0x0F
54
-
55
- def with_alpha?(num)
56
- packed_type?(num) && PACKED_ORDERS.include?(to_order(num)) ||
57
- array_type?(num) && ARRAY_ORDERS.include?(to_order(num))
76
+ case obj
77
+ when Symbol, String
78
+ sym = obj.to_sym.downcase
79
+ if FORMAT_MAP.key?(sym)
80
+ FORMAT_MAP[sym]
81
+ else
82
+ raise ArgumentError, "Invalid format name"
83
+ end
84
+ else
85
+ raise ArgumentError
86
+ end
58
87
  end
59
88
  end
60
89
 
61
- def fourcc = PixelFormatEnum.to_fourcc(format)
90
+ # FOURCC が無い場合は nil を戻す。
91
+ def fourcc
92
+ 4.times.inject([]) { |n, i| n << (num >> i * 8) % 0x100 }.pack("c4") if fourcc?
93
+ end
62
94
 
63
- def fourcc? = PixelFormatEnum.fourcc?(format)
95
+ def fourcc? = (format >> 28) & 0x0F != 1
64
96
 
65
- def format_name = PixelFormatEnum.to_name(format)
97
+ def format_name = PixelFormatEnum.to_num(format)
66
98
 
67
- def indexed_color? = PixelFormatEnum.indexed_type?(format)
99
+ def indexed_color? = PixelFormatEnum::INDEXED_TYPES.include?(format)
68
100
 
69
101
  def rgb? = !(fourcc? || indexed_color? || rgba?)
70
102
 
71
- def rgba? = PixelFormatEnum.with_alpha?(format)
103
+ def rgba? = PixelFormatEnum::WITH_ALPHA.include?(format)
72
104
  end
73
105
  end
@@ -1,7 +1,9 @@
1
1
  module RbSDL2
2
2
  module Platform
3
3
  class << self
4
- def platform = ::SDL2.SDL_GetPlatform.read_string
4
+ # プラットフォーム名(動作環境)を文字列で返します。
5
+ # ここでの動作環境は SDL ライブラリが認識しているものです。
6
+ def platform = ::SDL.GetPlatform.read_string
5
7
  end
6
8
  end
7
9
  end
@@ -1,36 +1,47 @@
1
1
  module RbSDL2
2
- module PowerInfo
3
- @secs_ptr, @pct_ptr = Array.new(2) { ::FFI::MemoryPointer.new(:int) }
2
+ class PowerInfo
3
+ def initialize
4
+ @battery_time = ::FFI::MemoryPointer.new(:int)
5
+ @battery_percentage = ::FFI::MemoryPointer.new(:int)
6
+ update
7
+ end
8
+
9
+ # バッテリーの残り容量(パーセント)を戻します。
10
+ # 残り容量を特定できない, またはバッテリーで動作していない場合 nil を戻します。
11
+ def battery_percentage = (num = @battery_percentage.read_int) >= 0 ? num : nil
12
+
13
+ # バッテリーの残り時間(秒)を戻します。
14
+ # 残り時間を特定できない、またはバッテリーで動作していない場合は nil を戻します。
15
+ def battery_time = (num = @battery_time.read_int) >= 0 ? num : nil
4
16
 
5
- class << self
6
- attr_reader :battery_capacity, :battery_time, :state
17
+ # バッテリーが搭載されているか?
18
+ def battery? = [::SDL::POWERSTATE_CHARGING,
19
+ ::SDL::POWERSTATE_CHARGED,
20
+ ::SDL::POWERSTATE_ON_BATTERY].include?(state)
7
21
 
8
- # バッテリーが搭載されているか?
9
- def battery? = on_battery? || battery_charging? || battery_charged?
22
+ # 電源あり、バッテリー満充電
23
+ def charged? = ::SDL::POWERSTATE_CHARGED == state
10
24
 
11
- # 電源あり、バッテリー満充電
12
- def battery_charged? = ::SDL2::SDL_POWERSTATE_CHARGED == state
25
+ # 電源あり、バッテリー充電中
26
+ def charging? = ::SDL::POWERSTATE_CHARGING == state
13
27
 
14
- # 電源あり、バッテリー充電中
15
- def battery_charging? = ::SDL2::SDL_POWERSTATE_CHARGING == state
28
+ # 電源あり、バッテリー非搭載(デスクトップパソコンなど)
29
+ def no_battery? = ::SDL::POWERSTATE_NO_BATTERY == state
16
30
 
17
- # 電源あり、バッテリー非搭載(デスクトップパソコンなど)
18
- def no_battery? = ::SDL2::SDL_POWERSTATE_NO_BATTERY == state
31
+ # 電源なし、バッテリー使用中
32
+ def on_battery? = ::SDL::POWERSTATE_ON_BATTERY == state
19
33
 
20
- # 電源なし、バッテリー使用中
21
- def on_battery? = ::SDL2::SDL_POWERSTATE_ON_BATTERY == state
34
+ # 電源に接続されているか?
35
+ def plugged_in? = !on_battery?
22
36
 
23
- # 電源に接続されているか?
24
- def plugged_in? = no_battery? || battery_charging? || battery_charged?
37
+ attr_reader :state
25
38
 
26
- def update
27
- @state = ::SDL2.SDL_GetPowerInfo(@secs_ptr, @pct_ptr)
28
- @battery_time, @battery_capacity = @secs_ptr.read_int, @pct_ptr.read_int
29
- self
30
- end
39
+ # 電源、バッテリーの情報なし
40
+ def unknown? = ::SDL::POWERSTATE_UNKNOWN == state
31
41
 
32
- # 電源、バッテリーの情報なし
33
- def unknown? = ::SDL2::SDL_POWERSTATE_UNKNOWN == state
42
+ def update
43
+ @state = ::SDL.GetPowerInfo(@battery_time, @battery_percentage)
44
+ self
34
45
  end
35
46
  end
36
47
  end
data/lib/rb_sdl2/rect.rb CHANGED
@@ -1,7 +1,13 @@
1
1
  module RbSDL2
2
2
  class Rect
3
+ class << self
4
+ def to_ary(ptr)
5
+ ::SDL::Rect.new(ptr).values
6
+ end
7
+ end
8
+
3
9
  def initialize(x = 0, y = 0, w = 0, h = 0)
4
- @st = ::SDL2::SDL_Rect.new
10
+ @st = ::SDL::Rect.new
5
11
  @st[:x], @st[:y], @st[:w], @st[:h] = x, y, w, h
6
12
  end
7
13
 
@@ -1,20 +1,35 @@
1
1
  module RbSDL2
2
2
  class RefCountPointer < ::FFI::AutoPointer
3
+ # 備考:SDL では参照カウンターを操作する際にロックを行っていない。
4
+ # SDL_AtomicAdd() を使用した理由はオブジェクトの生成をなるべく行わないようにするため。
3
5
  class << self
4
- def entity_class
5
- raise NotImplementedError
6
- end
6
+ def entity_class = raise NotImplementedError
7
+
8
+ def offset_of_refcount = entity_class.offset_of(:refcount)
9
+
10
+ def dec_ref(ptr) = ::SDL.SDL_AtomicAdd(ptr + offset_of_refcount, -1)
11
+
12
+ def inc_ref(ptr) = ::SDL.SDL_AtomicAdd(ptr + offset_of_refcount, 1)
7
13
 
8
14
  def to_ptr(ptr)
9
- raise ArgumentError, 'Invalid pointer, ptr is NULL' if ptr.null?
10
- # refcount の増加を AutoPointer 化する前に成功させる必要がある。
11
- # AutoPointer になった後に失敗すると、GC に回収されたとき refcount が実際の参照数より少なくなる。
12
- entity_class.new(ptr)[:refcount] += 1
13
- new(ptr)
15
+ obj = new(ptr)
16
+
17
+ # ポインターの参照カウントを増加する際に例外が発生すると、
18
+ # ポインターが GC に回収され refcount が実際の参照数より少なくなる。
19
+ # 例外を補足してポインターのファイナライザーを解除する。
20
+ unless ptr.null?
21
+ begin
22
+ inc_ref(ptr)
23
+ rescue => e
24
+ obj.autorelease = false
25
+ raise e
26
+ end
27
+ end
28
+
29
+ obj
14
30
  end
15
31
  end
16
32
 
17
- # for debug
18
- def refcount = self.class.entity_class.new(self)[:refcount]
33
+ def refcount = get_int(self.class.offset_of_refcount)
19
34
  end
20
35
  end
@@ -0,0 +1,19 @@
1
+ module RbSDL2
2
+ require_relative 'rw_ops_pointer'
3
+
4
+ class RWFile < RWOps
5
+ def initialize(path, mode = "r")
6
+ @path = path
7
+ ptr = ::SDL.RWFromFile(SDL.str_to_sdl(path), SDL.str_to_sdl(mode))
8
+ raise RbSDL2Error if ptr.null?
9
+ super(RWOpsPointer.new(ptr))
10
+ end
11
+
12
+ def inspect
13
+ "#<#{self.class.name}:#{path}#{closed? ? " (closed)" : nil}>"
14
+ end
15
+
16
+ attr_reader :path
17
+ alias to_path path
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module RbSDL2
2
+ require_relative 'rw_ops_pointer'
3
+
4
+ class RWMemory < RWOps
5
+ def initialize(obj, readonly: false, size: nil)
6
+ raise TypeError, "obj is NULL" if obj.nil? || obj.null?
7
+ size = size || obj.size
8
+ ptr = readonly ? ::SDL.RWFromConstMem(obj, size) : ::SDL.RWFromMem(obj, size)
9
+ raise RbSDL2Error if ptr.null?
10
+ super(RWOpsPointer.new(ptr))
11
+ @obj = obj
12
+ end
13
+
14
+ def inspect
15
+ "#<#{self.class.name}:memory#{closed? ? " (closed)" : nil}>"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,126 @@
1
+ module RbSDL2
2
+ class RWObject < RWOps
3
+ class CloseCallback < ::FFI::Function
4
+ def initialize
5
+ # int (* close) (struct RWops * context);
6
+ super(:int, [:pointer]) do |context|
7
+ yield
8
+ 0
9
+ rescue => e
10
+ raise e if $DEBUG
11
+ Error.last_error_message = e.message
12
+ -1
13
+ ensure
14
+ # SDL_RWclose() の仕様により成功、失敗問わずポインターを開放する。
15
+ ::SDL.FreeRW(context)
16
+ end
17
+ end
18
+ end
19
+
20
+ class ReadCallback < ::FFI::Function
21
+ def initialize
22
+ # size_t (* read) (struct RWops * context, void *ptr, size_t size, size_t maxnum);
23
+ super(:size_t, [:pointer, :pointer, :size_t, :size_t]) do |_context, ptr, size, max_num|
24
+ num = size * max_num
25
+ str = yield(num)
26
+ next 0 if str.nil? # EOF
27
+ len = str.size
28
+ if len > num # 要求より多い文字数を返している。obj.read が壊れている。
29
+ raise RbSDL2Error, "The return value of read method is corrupted"
30
+ end
31
+ ptr.write_bytes(str, 0, len)
32
+ len / size
33
+ rescue => e
34
+ raise e if $DEBUG
35
+ Error.last_error_message = e.message
36
+ 0
37
+ end
38
+ end
39
+ end
40
+
41
+ class SeekCallback < ::FFI::Function
42
+ def initialize
43
+ # Sint64 (* seek) (struct RWops * context, Sint64 offset, int whence);
44
+ super(:int64, [:pointer, :int64, :int]) do |_context, offset, whence|
45
+ # SDL_RWseek() の仕様によりシーク後の現在位置を戻す必要がある。
46
+ yield(offset, whence)
47
+ rescue => e
48
+ raise e if $DEBUG
49
+ Error.last_error_message = e.message
50
+ -1
51
+ end
52
+ end
53
+ end
54
+
55
+ class SizeCallback < ::FFI::Function
56
+ def initialize
57
+ # Sint64 (* size) (struct RWops * context);
58
+ super(:int64, [:pointer]) do |_context|
59
+ # Ruby ではサイズが不明な IO は size メソッドが無い(NoMethodError)。
60
+ yield
61
+ rescue => e
62
+ raise e if $DEBUG
63
+ Error.last_error_message = e.message
64
+ # 不明な時は -1。
65
+ -1
66
+ end
67
+ end
68
+ end
69
+
70
+ class WriteCallback < ::FFI::Function
71
+ def initialize
72
+ # size_t (* write) (struct RWops * context, const void *ptr, size_t size, size_t num);
73
+ super(:size_t, [:pointer, :pointer, :size_t, :size_t]) do |_context, ptr, size, max_num|
74
+ str = ptr.read_bytes(size * max_num)
75
+ yield(str) / size
76
+ rescue => e
77
+ raise e if $DEBUG
78
+ Error.last_error_message = e.message
79
+ 0
80
+ end
81
+ end
82
+ end
83
+
84
+ require_relative 'rw_ops_pointer'
85
+
86
+ # obj 引数には Ruby の IO オブジェクトのように振る舞うオブジェクトを与える。
87
+ # 期待される振る舞いは close, read, seek, size, tell, write メソッド呼び出しに応答すること。
88
+ # メソッドが無い場合やメソッドから例外が出た場合は SDL 側にエラー値を戻す。
89
+ # autoclose 引数に true を与えた場合 close メソッドの呼び出し、GC の回収時に obj 引数に与えたオブジェクトを
90
+ # クローズする。
91
+ # C のスコープへ渡す場合、ポインターが利用されている間 RWOps オブジェクトを生存させる必要がある。
92
+ def initialize(obj)
93
+ ptr = ::SDL.AllocRW
94
+ raise RbSDL2Error if ptr.null?
95
+ super(
96
+ begin
97
+ # メンバーの関数ポインターが NULL であってはならない。
98
+ @st = ::SDL::RWops.new(ptr)
99
+ # 引数に与えた proc 内で例外が出たときは SDL へ例外メッセージがセットされエラーを表す戻り値が渡される。
100
+ # デバッグモードの時は Ruby 側へ例外を出す。
101
+ # close_proc: 引数はなし。戻り値は無視される。RWops ポインタの開放を行う必要はない。
102
+ # read_proc: 引数に読み出すバイト数。戻り値は文字列。
103
+ # seek_proc: 引数に offset, whence。IO#seek を参照。戻り値は現在位置(IO#tell)。
104
+ # size_proc: 引数はなし。戻り値はバイト数。不明な場合は何らかの例外を出す。
105
+ # write_proc: 引数に書き込む文字列。戻り値は実際に書き込んだバイト数。
106
+ @st[:close] = @close_callback = CloseCallback.new {} # obj.close しない。autoclose=false
107
+ @st[:read] = @read_callback = ReadCallback.new { |length| obj.read(length) }
108
+ @st[:seek] = @seek_callback = SeekCallback.new { |offset, whence| obj.seek(offset, whence); obj.tell }
109
+ @st[:size] = @size_callback = SizeCallback.new { obj.size }
110
+ @st[:write] = @write_callback = WriteCallback.new { |str| obj.write(str) }
111
+ RWOpsPointer.new(ptr)
112
+ rescue => e
113
+ ::SDL.FreeRW(ptr)
114
+ raise e
115
+ end
116
+ )
117
+ @obj = obj
118
+ end
119
+
120
+ def __getobj__ = @obj
121
+
122
+ def inspect
123
+ "#<#{self.class.name}:#{@obj.inspect}#{closed? ? " (closed)" : nil}>"
124
+ end
125
+ end
126
+ end