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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 101f794f351d219f8cc70180a9f02cd57c11c507e8542fad5d83c2e4635ea7cd
4
+ data.tar.gz: fc7e00d5985c46af575e81cdbd2bfd177464f14eeb111e2dfae41cdaffb75e51
5
+ SHA512:
6
+ metadata.gz: 0dd39b4cb9da230fd7d63556a3db53be3d28eeee972a5a54aa945cffa246501d3684112d72b8f8b5f3f76cee58d47e21a226998ae791390c4f98d53222aa4cda
7
+ data.tar.gz: dca9330ed249b66a6b21ad6bc42a42a14cd014f95757529d3f9e14f927f0ca89c9989960e3403511ecb58d315ed28829ca8fa9a61e30a934250ee3cefc22d207
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-10-10
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,17 @@
1
+ Copyright (C) 2021 shinokaro
2
+
3
+ This software is provided 'as-is', without any express or implied
4
+ warranty. In no event will the authors be held liable for any damages
5
+ arising from the use of this software.
6
+
7
+ Permission is granted to anyone to use this software for any purpose,
8
+ including commercial applications, and to alter it and redistribute it
9
+ freely, subject to the following restrictions:
10
+
11
+ 1. The origin of this software must not be misrepresented; you must not
12
+ claim that you wrote the original software. If you use this software
13
+ in a product, an acknowledgment in the product documentation would be
14
+ appreciated but is not required.
15
+ 2. Altered source versions must be plainly marked as such, and must not be
16
+ misrepresented as being the original software.
17
+ 3. This notice may not be removed or altered from any source distribution.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # RbSDL2
2
+
3
+ RbSDL2 は [SDL](https://www.libsdl.org) (Simple DirectMedia Layer) の機能を
4
+ Ruby オブジェクトとして提供します。
5
+
6
+ SDL 2.0.16 移行を対象としています。この Gem には SDL は含まれていません。
7
+ 利用する際は環境に合わせて SDL を用意してください。
8
+
9
+ ## Installation
10
+
11
+ $ gem install rb_sdl2
12
+
13
+ ## Usage
14
+
15
+ ```ruby
16
+ require 'rb_sdl2'
17
+
18
+ include RbSDL2
19
+
20
+ RbSDL2.load('SDL2 Library path') # SDL を読み込みます。必ず必要です。
21
+ RbSDL2.init # SDL を初期化します。
22
+
23
+ image = Surface.load("image.bmp") # 画像をメモリーに読み込みます。画像形式は BMP のみです。
24
+ window = Window.new("Title")
25
+ window.update { |s| s.blit(image) } # Window へ画像を描画します。
26
+
27
+ RbSDL2.loop do
28
+ break if Event.quit? # ユーザがウィンドウを閉じたことを検出します。
29
+ # キーボードの A キーを押したか調べます。押していればダイアログボックスを表示します。
30
+ window.info_alert("Message") if Keyboard.key?("A")
31
+ # マウスのボタン(種類問わず)を押したか調べます。押していればダイアログボックスを表示します。
32
+ window.error_alert("Message from mouse") if Mouse.any_button?
33
+ Timer.delay(33) # SDL のタイマーを使用して 33 ミリ秒間、停止します。
34
+ end
35
+ # 終了の際に SDL から得たポインターのことは考えなくてよい。 SDL_Quit() も不要。
36
+ ```
37
+
38
+ ## License
39
+
40
+ Zlib
@@ -0,0 +1,17 @@
1
+ module RbSDL2
2
+ class Audio
3
+ module AudioAllowedChanges
4
+ class << self
5
+ def to_num(any_change: false, channels_change: false, format_change: false,
6
+ frequency_change: false, samples_change: false)
7
+ 0 |
8
+ (any_change ? ::SDL2::SDL_AUDIO_ALLOW_ANY_CHANGE : 0) |
9
+ (channels_change ? ::SDL2::SDL_AUDIO_ALLOW_CHANNELS_CHANGE : 0) |
10
+ (format_change ? ::SDL2::SDL_AUDIO_ALLOW_FORMAT_CHANGE : 0) |
11
+ (frequency_change ? ::SDL2::SDL_AUDIO_ALLOW_FREQUENCY_CHANGE : 0) |
12
+ (samples_change ? ::SDL2::SDL_AUDIO_ALLOW_SAMPLES_CHANGE : 0)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,49 @@
1
+ module RbSDL2
2
+ class Audio
3
+ class AudioBuffer
4
+ class AudioBufferPointer < ::FFI::AutoPointer
5
+ def self.release(ptr) = ::SDL2::SDL_FreeWAV(ptr)
6
+ end
7
+
8
+ class << self
9
+ require_relative '../rw_ops'
10
+
11
+ def load(file) = RWOps.new(file, "rb") { |rw| load_rw(rw) }
12
+
13
+ require_relative 'audio_spec'
14
+
15
+ # load_rw は与えられたオブジェクトをオートクローズしません。
16
+ def load_rw(rw)
17
+ spec = AudioSpec.new
18
+ buf = ::FFI::MemoryPointer.new(:pointer)
19
+ len = ::FFI::MemoryPointer.new(:uint32)
20
+ err = ::SDL2::SDL_LoadWAV_RW(rw, 0, spec, buf, len)
21
+ raise RbSDL2Error if err.null?
22
+ new(AudioBufferPointer.new(buf.read_pointer), len.read_uint32, spec)
23
+ end
24
+ end
25
+
26
+ def initialize(ptr, size, spec)
27
+ @ptr = ptr
28
+ @size = size
29
+ @spec = spec
30
+ end
31
+
32
+ attr_reader :size
33
+ alias length size
34
+
35
+ attr_reader :spec
36
+
37
+ require 'forwardable'
38
+ extend Forwardable
39
+ def_delegators :spec, *%i(channels format frequency)
40
+
41
+ require_relative 'audio_format'
42
+ include AudioFormat
43
+
44
+ def to_ptr = @ptr
45
+
46
+ def to_str = @ptr.read_bytes(size)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,44 @@
1
+ module RbSDL2
2
+ class Audio
3
+ class AudioDevice
4
+ class << self
5
+ def devices(capture = false)
6
+ is_capture = IS_CAPTURE.(capture)
7
+ ::SDL2.SDL_GetNumAudioDevices(is_capture).times.map do |num|
8
+ ptr = ::SDL2.SDL_GetAudioDeviceName(num, is_capture)
9
+ raise RbSDL2Error if ptr.null?
10
+
11
+ spec = AudioSpec.new
12
+ # SDL_GetAudioDeviceSpec は SDL_GetNumAudioDevices の最新の呼び出しを反映する。
13
+ err = ::SDL2.SDL_GetAudioDeviceSpec(num, is_capture, spec)
14
+ raise RbSDL2Error if err != 0
15
+
16
+ new(ptr.read_string.force_encoding(Encoding::UTF_8), capture, spec)
17
+ end
18
+ end
19
+ end
20
+
21
+ def initialize(name, capture, spec)
22
+ @capture = CAPTURE.(capture)
23
+ @name = name
24
+ @spec = spec
25
+ end
26
+
27
+ attr_reader :capture
28
+
29
+ attr_reader :name
30
+ alias to_s name
31
+
32
+ attr_reader :spec
33
+
34
+ require 'forwardable'
35
+ extend Forwardable
36
+ def_delegators :spec, *%i(channels format frequency)
37
+
38
+ require_relative 'audio_format'
39
+ include AudioFormat
40
+
41
+ def unknown? = channels == 0 && format == 0 && frequency == 0
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,23 @@
1
+ module RbSDL2
2
+ class Audio
3
+ module AudioFormat
4
+ class << self
5
+ def to_num(signed: true, big_endian: false, bit_size: 16, float: false)
6
+ num = 0 |
7
+ (big_endian ? ::SDL2::SDL_AUDIO_MASK_ENDIAN : 0) |
8
+ (bit_size & ::SDL2::SDL_AUDIO_MASK_BITSIZE) |
9
+ (signed ? ::SDL2::SDL_AUDIO_MASK_SIGNED : 0)
10
+ num | (float ? ::SDL2::SDL_AUDIO_MASK_DATATYPE & ::SDL2::SDL_AUDIO_MASK_SIGNED : 0)
11
+ end
12
+ end
13
+
14
+ def big_endian? = ::SDL2::SDL_AUDIO_MASK_ENDIAN & format != 0
15
+
16
+ def bit_size = ::SDL2::SDL_AUDIO_MASK_BITSIZE & format
17
+
18
+ def float? = ::SDL2::SDL_AUDIO_MASK_DATATYPE & format != 0
19
+
20
+ def signed? = ::SDL2::SDL_AUDIO_MASK_SIGNED & format != 0
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ module RbSDL2
2
+ class Audio
3
+ class AudioSpec
4
+ require_relative 'audio_format'
5
+
6
+ def initialize(channels: 0, format: 0, freq: 0, frequency: freq, samples: 0)
7
+ @st = ::SDL2::SDL_AudioSpec.new
8
+ @st[:channels] = channels
9
+ @st[:format] = format
10
+ @st[:freq] = frequency
11
+ @st[:samples] = samples
12
+ end
13
+
14
+ def size = @st[:size]
15
+ alias chunk_size size
16
+
17
+ def channels = @st[:channels]
18
+
19
+ def format = @st[:format]
20
+
21
+ def freq = @st[:freq]
22
+ alias frequency freq
23
+
24
+ def samples = @st[:samples]
25
+
26
+ def to_ptr = @st.to_ptr
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,132 @@
1
+ module RbSDL2
2
+ class Audio
3
+ IS_CAPTURE = -> (capture) { capture ? 1 : 0 }
4
+ CAPTURE = -> (capture) { capture ? true : false }
5
+ # AudioStream, AudioCallback の実装は行っていない。
6
+ # AudioCallback は Ruby 側からまともに使う方法がない。
7
+ # AudioStream はリアルタイムなリサンプラー、リフォーマッターとして使えるがこれが本当に必要か判らない。
8
+ class << self
9
+ def current
10
+ ptr = ::SDL2.SDL_GetCurrentAudioDriver
11
+ raise RbSDL2Error, "Audio subsystem has not been initialized" if ptr.null?
12
+ ptr.read_string
13
+ end
14
+
15
+ require_relative 'audio/audio_device'
16
+
17
+ def devices = AudioDevice.devices
18
+
19
+ def drivers
20
+ ::SDL2.SDL_GetNumAudioDrivers.times.map do |num|
21
+ ptr = ::SDL2.SDL_GetAudioDriver(num)
22
+ raise RbSDL2Error if ptr.null?
23
+ ptr.read_string
24
+ end
25
+ end
26
+
27
+ def init(driver)
28
+ raise RbSDL2Error if ::SDL2.SDL_AudioInit(driver) < 0
29
+ end
30
+
31
+ require_relative 'audio/audio_buffer'
32
+
33
+ def load(...) = AudioBuffer.load(...)
34
+
35
+ require_relative 'audio/audio_allowed_changes'
36
+ require_relative 'audio/audio_format'
37
+ require_relative 'audio/audio_spec'
38
+
39
+ def new(device = nil, capture = false, allowed_changes: {}, channels: 2, format: {},
40
+ frequency: 48_000, samples: 0)
41
+ want = AudioSpec.new(channels: channels, format: AudioFormat.to_num(**format),
42
+ frequency: frequency, samples: samples)
43
+ have = AudioSpec.new
44
+ id = ::SDL2.SDL_OpenAudioDevice(device&.to_s, IS_CAPTURE.(capture), want, have,
45
+ AudioAllowedChanges.to_num(**allowed_changes))
46
+ raise RbSDL2Error if id == 0
47
+ super(id, capture, have)
48
+ end
49
+ alias open new
50
+
51
+ def quit = ::SDL2.SDL_AudioQuit
52
+ end
53
+
54
+ def initialize(id, capture, spec)
55
+ @capture = CAPTURE.(capture)
56
+ @closed = false
57
+ @id = id
58
+ @spec = spec
59
+ end
60
+
61
+ def capture? = @capture
62
+
63
+ def clear
64
+ raise IOError if closed?
65
+ ::SDL2.SDL_ClearQueuedAudio(self)
66
+ end
67
+
68
+ def close
69
+ ::SDL2.SDL_CloseAudioDevice(id) unless @closed
70
+ @closed = true
71
+ nil
72
+ end
73
+
74
+ def closed? = @closed
75
+
76
+ attr_reader :id
77
+ alias to_int id
78
+
79
+ def pause
80
+ raise IOError if closed?
81
+ ::SDL2.SDL_PauseAudioDevice(id, 1)
82
+ end
83
+
84
+ def play
85
+ raise IOError if closed?
86
+ ::SDL2.SDL_PauseAudioDevice(id, 0)
87
+ end
88
+
89
+ def read(len)
90
+ raise IOError if closed?
91
+ ptr = ::FFI::MemoryPointer.new(len)
92
+ size = ::SDL2.SDL_DequeueAudio(id, ptr, len)
93
+ ptr.read_bytes(size)
94
+ end
95
+
96
+ def size
97
+ raise IOError if closed?
98
+ ::SDL2.SDL_GetQueuedAudioSize(id)
99
+ end
100
+ alias length size
101
+
102
+ attr_reader :spec
103
+
104
+ require 'forwardable'
105
+ extend Forwardable
106
+ def_delegators :spec, *%i(channels chunk_size format frequency samples)
107
+
108
+ require_relative 'audio/audio_format'
109
+ include AudioFormat
110
+
111
+ def status
112
+ raise IOError if closed?
113
+ ::SDL2.SDL_GetAudioDeviceStatus(id)
114
+ end
115
+
116
+ module AudioStatus
117
+ def paused? = ::SDL2::SDL_AUDIO_PAUSED == status
118
+
119
+ def playing? = ::SDL2::SDL_AUDIO_PLAYING == status
120
+
121
+ def stopped? = ::SDL2::SDL_AUDIO_STOPPED == status
122
+ end
123
+ include AudioStatus
124
+
125
+ def write(data)
126
+ raise IOError if closed?
127
+ err = ::SDL2.SDL_QueueAudio(id, data, data.size)
128
+ raise RbSDL2Error if err < 0
129
+ data.size
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,44 @@
1
+ module RbSDL2
2
+ module Clipboard
3
+ class << self
4
+ def clear = self.text = nil
5
+
6
+ # イベントを pump したとき外部からのクリップボード取得が行われる。
7
+ # その際に SDL_CLIPBOARDUPDATE イベントがキューに入る。これはイベントコールバックで捕獲できる。
8
+ # 読み出し時にクリップボードの内容はクリアーされない。
9
+ def text
10
+ # SDL_GetClipboardText は NULL ポインターを戻さない。返すべき内容がない場合は空の文字列を戻す。
11
+ ::SDL2.SDL_GetClipboardText.read_string.force_encoding(Encoding::UTF_8)
12
+ ensure
13
+ # SDL_GetClipboardText は呼び出されるたびに OS から取得したクリップボードの内容を SDL 内部に保存し、
14
+ # それをさらにコピーしたものを戻す。
15
+ # アプリケーション側は取得した文字列ポインタを必ず開放しなければならない。
16
+ ::SDL2.SDL_free(ptr)
17
+ end
18
+
19
+ # クリップボードの内容を戻す。text メソッドと違い読み出し後にクリップボードの内容をクリアーする。
20
+ def text!
21
+ str = text
22
+ clear
23
+ str
24
+ end
25
+
26
+ # クリップボードへ書き込む。クリップボードはパブリックであり他のアプリケーションから取得できる。
27
+ # 当然、SDL もこの書き込みを補足する。
28
+ # クリップボードの読み出しを行わずに、大量のクリップボード書き込みを行うと SDL はエラーを戻す。
29
+ # このエラーは SDL からではなく OS から戻るエラーである。書き込み上限や既に書き込んだ回数を知る方法はない。
30
+ # また、アプリケーション側でクリップボードを読み出しても制限を回避できない。
31
+ # (OS に保存されたクリップボードはパブリックなコピーでありユーザが取り出さなければそのコピーは消えないだろう)
32
+ def text=(obj)
33
+ raise RbSDL2Error if ::SDL2.SDL_SetClipboardText(obj&.to_s&.encode(Encoding::UTF_8)) < 0
34
+ end
35
+
36
+ # クリップボードに内容があるか確認をする。
37
+ # これはアプリケーションがクリップボードを読み出しても内容はクリアーされないため、
38
+ # クリップボードの更新を知るためには実装を工夫する必要がある。
39
+ # もっとも簡単な方法は、クリップボードの内容を読み込み度に Clipboard.text = nil を実行し、
40
+ # クリップボードをクリアする。
41
+ def text? = ::SDL2.SDL_HasClipboardText == ::SDL2::SDL_TRUE
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,39 @@
1
+ module RbSDL2
2
+ module CPUInfo
3
+ class << self
4
+ def cpu_count = ::SDL2.SDL_GetCPUCount
5
+
6
+ def cpu_cache_line_size = ::SDL2.SDL_GetCPUCacheLineSize
7
+
8
+ def system_ram = ::SDL2.SDL_GetSystemRAM
9
+
10
+ def rdtsc? = ::SDL2.SDL_HasRDTSC == ::SDL2::SDL_TRUE
11
+
12
+ def altivec? = ::SDL2.SDL_HasAltiVec == ::SDL2::SDL_TRUE
13
+
14
+ def mmx? = ::SDL2.SDL_HasMMX == ::SDL2::SDL_TRUE
15
+
16
+ def _3dnow? = ::SDL2.SDL_Has3DNow == ::SDL2::SDL_TRUE
17
+
18
+ def sse? = ::SDL2.SDL_HasSSE == ::SDL2::SDL_TRUE
19
+
20
+ def sse2? = ::SDL2.SDL_HasSSE2 == ::SDL2::SDL_TRUE
21
+
22
+ def sse3? = ::SDL2.SDL_HasSSE3 == ::SDL2::SDL_TRUE
23
+
24
+ def sse41? = ::SDL2.SDL_HasSSE41 == ::SDL2::SDL_TRUE
25
+
26
+ def sse42? = ::SDL2.SDL_HasSSE42 == ::SDL2::SDL_TRUE
27
+
28
+ def avx? = ::SDL2.SDL_HasAVX == ::SDL2::SDL_TRUE
29
+
30
+ def avx2? = ::SDL2.SDL_HasAVX2 == ::SDL2::SDL_TRUE
31
+
32
+ def avx512f = ::SDL2.SDL_HasAVX512F == ::SDL2::SDL_TRUE
33
+
34
+ def armsimd? = ::SDL2.SDL_HasARMSIMD == ::SDL2::SDL_TRUE
35
+
36
+ def neon? = ::SDL2.SDL_HasNEON == ::SDL2::SDL_TRUE
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ module RbSDL2
2
+ module Cursor
3
+ require_relative 'cursor_class'
4
+
5
+ class ColorCursor < CursorClass
6
+ class << self
7
+ require_relative 'cursor_pointer'
8
+
9
+ def new(surface, hot_x = 0, hot_y = 0)
10
+ # SDL_CreateColorCursor は与えられた surface をコピーする。
11
+ # 呼び出し後に引数に与えた surface オブジェクトは安全に開放できる。
12
+ ptr = CursorPointer.new(::SDL2.SDL_CreateColorCursor(surface, hot_x, hot_y))
13
+ raise RbSDL2Error if ptr.null?
14
+ super(ptr)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ module RbSDL2
2
+ module Cursor
3
+ class CursorClass
4
+ def initialize(ptr)
5
+ @ptr = ptr
6
+ end
7
+
8
+ def current! = Cursor.current = self
9
+
10
+ def current? = Cursor.current?(self)
11
+
12
+ def hide = Cursor.hide
13
+
14
+ def show
15
+ current!
16
+ Cursor.show
17
+ end
18
+
19
+ def shown? = Cursor.shown? && current?
20
+
21
+ def to_ptr = @ptr
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ module RbSDL2
2
+ module Cursor
3
+ class CursorPointer < ::FFI::AutoPointer
4
+ class << self
5
+ # カーソルポインターはいつでも開放してよい。
6
+ # SDL はカレントカーソルが開放されたときはデフォルトカーソルをカレントカーソルに設定する。
7
+ # デフォルトカーソルは SDL_FreeCursorを呼び出しても開放されない。
8
+ def release(ptr) = ::SDL2.SDL_FreeCursor(ptr)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ module RbSDL2
2
+ module Cursor
3
+ require_relative 'cursor_class'
4
+
5
+ class DefaultCursor < CursorClass
6
+ class << self
7
+ def new
8
+ ptr = ::SDL2.SDL_GetDefaultCursor
9
+ raise RbSDL2Error if ptr.null?
10
+ super(ptr)
11
+ end
12
+ end
13
+
14
+ require 'singleton'
15
+ include Singleton
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ module RbSDL2
2
+ module Cursor
3
+ require_relative 'cursor_class'
4
+
5
+ class SystemCursor < CursorClass
6
+ require_relative 'cursor_pointer'
7
+
8
+ @cursors = Array.new(::SDL2::SDL_NUM_SYSTEM_CURSORS)
9
+
10
+ class << self
11
+ private def new(id)
12
+ return @cursors[id] if @cursors[id]
13
+
14
+ ptr = CursorPointer.new(::SDL2.SDL_CreateSystemCursor(id))
15
+ raise RbSDL2Error if ptr.null?
16
+ @cursors[id] = super(ptr)
17
+ end
18
+
19
+ def arrow_cursor = new(::SDL2::SDL_SYSTEM_CURSOR_ARROW)
20
+
21
+ def crosshair_cursor = new(::SDL2::SDL_SYSTEM_CURSOR_CROSSHAIR)
22
+
23
+ def hand_cursor = new(::SDL2::SDL_SYSTEM_CURSOR_HAND)
24
+
25
+ def i_beam_cursor = new(::SDL2::SDL_SYSTEM_CURSOR_IBEAM)
26
+
27
+ def no_cursor = new(::SDL2::SDL_SYSTEM_CURSOR_NO)
28
+
29
+ def size_all_cursor = new(SDL_SYSTEM_CURSOR_SIZEALL)
30
+
31
+ def size_ne_sw_cursor = new(::SDL2::SDL_SYSTEM_CURSOR_SIZENESW)
32
+
33
+ def size_ns_cursor = new(::SDL2::SDL_SYSTEM_CURSOR_SIZENS)
34
+
35
+ def size_nw_se_cursor = new(::SDL2::SDL_SYSTEM_CURSOR_SIZENWSE)
36
+
37
+ def size_we_cursor = new(::SDL2::SDL_SYSTEM_CURSOR_SIZEWE)
38
+
39
+ def wait_cursor = new(::SDL2::SDL_SYSTEM_CURSOR_WAIT)
40
+
41
+ def wait_arrow_cursor = new(::SDL2::SDL_SYSTEM_CURSOR_WAITARROW)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,39 @@
1
+ module RbSDL2
2
+ module Cursor
3
+ # カーソルポインターが外部から設定される場合があるため、現在のカーソルを取得する方法を提供しない。
4
+ class << self
5
+ require_relative 'cursor/color_cursor'
6
+ def color_cursor(...) = ColorCursor.new(...)
7
+
8
+ def current=(cursor)
9
+ ::SDL2.SDL_SetCursor(cursor)
10
+ end
11
+
12
+ def current?(cursor)
13
+ ::SDL2.SDL_GetCursor == cursor.to_ptr
14
+ end
15
+
16
+ require_relative 'cursor/default_cursor'
17
+ def default_cursor = DefaultCursor.instance
18
+
19
+ def hide = ::SDL2.SDL_ShowCursor(::SDL2::SDL_DISABLE)
20
+
21
+ def show = ::SDL2.SDL_ShowCursor(::SDL2::SDL_ENABLE)
22
+
23
+ def shown? = ::SDL2.SDL_ShowCursor(::SDL2::SDL_QUERY) == ::SDL2::SDL_ENABLE
24
+
25
+ def update
26
+ self.current = nil
27
+ self
28
+ end
29
+ end
30
+
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)
38
+ end
39
+ end