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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +17 -0
- data/README.md +40 -0
- data/lib/rb_sdl2/audio/audio_allowed_changes.rb +17 -0
- data/lib/rb_sdl2/audio/audio_buffer.rb +49 -0
- data/lib/rb_sdl2/audio/audio_device.rb +44 -0
- data/lib/rb_sdl2/audio/audio_format.rb +23 -0
- data/lib/rb_sdl2/audio/audio_spec.rb +29 -0
- data/lib/rb_sdl2/audio.rb +132 -0
- data/lib/rb_sdl2/clipboard.rb +44 -0
- data/lib/rb_sdl2/cpu_info.rb +39 -0
- data/lib/rb_sdl2/cursor/color_cursor.rb +19 -0
- data/lib/rb_sdl2/cursor/cursor_class.rb +24 -0
- data/lib/rb_sdl2/cursor/cursor_pointer.rb +12 -0
- data/lib/rb_sdl2/cursor/default_cursor.rb +18 -0
- data/lib/rb_sdl2/cursor/system_cursor.rb +45 -0
- data/lib/rb_sdl2/cursor.rb +39 -0
- data/lib/rb_sdl2/display.rb +103 -0
- data/lib/rb_sdl2/display_mode.rb +35 -0
- data/lib/rb_sdl2/error.rb +9 -0
- data/lib/rb_sdl2/event/event_filter.rb +83 -0
- data/lib/rb_sdl2/event/event_queue.rb +137 -0
- data/lib/rb_sdl2/event/event_type.rb +271 -0
- data/lib/rb_sdl2/event.rb +161 -0
- data/lib/rb_sdl2/filesystem.rb +40 -0
- data/lib/rb_sdl2/hint.rb +24 -0
- data/lib/rb_sdl2/keyboard/key_mod.rb +37 -0
- data/lib/rb_sdl2/keyboard/keyboard_state.rb +34 -0
- data/lib/rb_sdl2/keyboard.rb +50 -0
- data/lib/rb_sdl2/message_box.rb +121 -0
- data/lib/rb_sdl2/mouse/global_mouse.rb +18 -0
- data/lib/rb_sdl2/mouse/mouse_button.rb +24 -0
- data/lib/rb_sdl2/mouse/mouse_class.rb +33 -0
- data/lib/rb_sdl2/mouse/mouse_wheel.rb +53 -0
- data/lib/rb_sdl2/mouse/relative_mouse.rb +12 -0
- data/lib/rb_sdl2/mouse/window_mouse.rb +17 -0
- data/lib/rb_sdl2/mouse.rb +74 -0
- data/lib/rb_sdl2/palette.rb +70 -0
- data/lib/rb_sdl2/pixel_format_enum.rb +73 -0
- data/lib/rb_sdl2/platform.rb +7 -0
- data/lib/rb_sdl2/power_info.rb +36 -0
- data/lib/rb_sdl2/rect.rb +14 -0
- data/lib/rb_sdl2/ref_count_pointer.rb +20 -0
- data/lib/rb_sdl2/rw_ops/rw_operator.rb +102 -0
- data/lib/rb_sdl2/rw_ops.rb +124 -0
- data/lib/rb_sdl2/screen_saver.rb +11 -0
- data/lib/rb_sdl2/sdl.rb +32 -0
- data/lib/rb_sdl2/surface/blend_mode.rb +41 -0
- data/lib/rb_sdl2/surface/pixel_format.rb +89 -0
- data/lib/rb_sdl2/surface.rb +244 -0
- data/lib/rb_sdl2/text_input.rb +15 -0
- data/lib/rb_sdl2/timer.rb +22 -0
- data/lib/rb_sdl2/version.rb +13 -0
- data/lib/rb_sdl2/video.rb +25 -0
- data/lib/rb_sdl2/window/dialog.rb +19 -0
- data/lib/rb_sdl2/window/display.rb +85 -0
- data/lib/rb_sdl2/window/grab.rb +23 -0
- data/lib/rb_sdl2/window/hit_test.rb +52 -0
- data/lib/rb_sdl2/window/position.rb +38 -0
- data/lib/rb_sdl2/window/shape.rb +76 -0
- data/lib/rb_sdl2/window/size.rb +59 -0
- data/lib/rb_sdl2/window/window_flags.rb +78 -0
- data/lib/rb_sdl2/window.rb +242 -0
- data/lib/rb_sdl2.rb +53 -0
- data/rb_sdl2.gemspec +19 -0
- 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
|
data/lib/rb_sdl2/hint.rb
ADDED
@@ -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,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
|