sfml3-rb 0.0.1 → 0.2.2
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 +4 -4
- data/CHANGELOG.md +342 -0
- data/README.md +51 -14
- data/TODO.md +199 -0
- data/ext/audio/audio_enums.c +131 -0
- data/ext/audio/audio_enums.h +18 -0
- data/ext/audio/effect_processor.c +233 -0
- data/ext/audio/effect_processor.h +25 -0
- data/ext/audio/listener.c +83 -0
- data/ext/audio/listener.h +8 -0
- data/ext/audio/music.c +174 -0
- data/ext/audio/music.h +14 -0
- data/ext/audio/sound.c +139 -0
- data/ext/audio/sound.h +14 -0
- data/ext/audio/sound_buffer.c +274 -0
- data/ext/audio/sound_buffer.h +18 -0
- data/ext/audio/sound_buffer_recorder.c +109 -0
- data/ext/audio/sound_buffer_recorder.h +14 -0
- data/ext/audio/sound_recorder.c +268 -0
- data/ext/audio/sound_recorder.h +14 -0
- data/ext/audio/sound_source.h +13 -0
- data/ext/audio/sound_source.inc +271 -0
- data/ext/audio/sound_source_cone.c +138 -0
- data/ext/audio/sound_source_cone.h +20 -0
- data/ext/audio/sound_stream.c +294 -0
- data/ext/audio/sound_stream.h +14 -0
- data/ext/{exceptions.c → core/exceptions.c} +10 -8
- data/ext/{ext → core}/exceptions.h +3 -3
- data/ext/core/foreign_thread.c +149 -0
- data/ext/core/foreign_thread.h +31 -0
- data/ext/core/macros.h +8 -0
- data/ext/{ext → core}/sfml.h +5 -3
- data/ext/core/unicode.c +46 -0
- data/ext/core/unicode.h +25 -0
- data/ext/ext.c +125 -14
- data/ext/extconf.rb +51 -6
- data/ext/graphics/blend_mode.c +261 -0
- data/ext/graphics/blend_mode.h +18 -0
- data/ext/graphics/circle.c +319 -0
- data/ext/{ext/klass/drawable → graphics}/circle.h +3 -3
- data/ext/graphics/color.c +318 -0
- data/ext/graphics/color.h +37 -0
- data/ext/{drawable.c → graphics/drawable.c} +11 -11
- data/ext/graphics/drawable.h +10 -0
- data/ext/graphics/enums.c +123 -0
- data/ext/graphics/enums.h +20 -0
- data/ext/graphics/font.c +148 -0
- data/ext/graphics/font.h +14 -0
- data/ext/graphics/glyph.c +60 -0
- data/ext/graphics/glyph.h +14 -0
- data/ext/graphics/image.c +205 -0
- data/ext/graphics/image.h +16 -0
- data/ext/graphics/polygon.c +300 -0
- data/ext/graphics/polygon.h +10 -0
- data/ext/graphics/rect.c +287 -0
- data/ext/graphics/rect.h +30 -0
- data/ext/graphics/rectangle.c +302 -0
- data/ext/graphics/rectangle.h +10 -0
- data/ext/graphics/render_state.c +204 -0
- data/ext/graphics/render_state.h +18 -0
- data/ext/graphics/render_target.inc +181 -0
- data/ext/graphics/render_texture.c +182 -0
- data/ext/graphics/render_texture.h +14 -0
- data/ext/graphics/shader.c +540 -0
- data/ext/graphics/shader.h +14 -0
- data/ext/graphics/shape.c +311 -0
- data/ext/graphics/shape.h +10 -0
- data/ext/graphics/sprite.c +249 -0
- data/ext/graphics/sprite.h +10 -0
- data/ext/graphics/stencil_mode.c +211 -0
- data/ext/graphics/stencil_mode.h +18 -0
- data/ext/graphics/target.c +133 -0
- data/ext/graphics/target.h +40 -0
- data/ext/graphics/text.c +344 -0
- data/ext/graphics/text.h +10 -0
- data/ext/graphics/texture.c +379 -0
- data/ext/graphics/texture.h +16 -0
- data/ext/graphics/transform.c +372 -0
- data/ext/graphics/transform.h +35 -0
- data/ext/{transformable.c → graphics/transformable.c} +168 -144
- data/ext/{ext/klass → graphics}/transformable.h +3 -3
- data/ext/graphics/vertex.c +179 -0
- data/ext/graphics/vertex.h +23 -0
- data/ext/graphics/vertex_array.c +127 -0
- data/ext/graphics/vertex_array.h +10 -0
- data/ext/graphics/vertex_buffer.c +211 -0
- data/ext/graphics/vertex_buffer.h +14 -0
- data/ext/{view.c → graphics/view.c} +168 -169
- data/ext/{ext/klass → graphics}/view.h +3 -3
- data/ext/network/ftp.c +405 -0
- data/ext/network/ftp.h +20 -0
- data/ext/network/http.c +243 -0
- data/ext/network/http.h +18 -0
- data/ext/network/ip_address.c +164 -0
- data/ext/network/ip_address.h +20 -0
- data/ext/network/network_enums.c +298 -0
- data/ext/network/network_enums.h +26 -0
- data/ext/network/packet.c +189 -0
- data/ext/network/packet.h +14 -0
- data/ext/network/socket_selector.c +142 -0
- data/ext/network/socket_selector.h +14 -0
- data/ext/network/tcp_listener.c +115 -0
- data/ext/network/tcp_listener.h +14 -0
- data/ext/network/tcp_socket.c +199 -0
- data/ext/network/tcp_socket.h +17 -0
- data/ext/network/udp_socket.c +190 -0
- data/ext/network/udp_socket.h +14 -0
- data/ext/ports.rb +97 -21
- data/ext/system/buffer.c +77 -0
- data/ext/system/buffer.h +14 -0
- data/ext/system/clock.c +100 -0
- data/ext/{ext/klass → system}/clock.h +3 -3
- data/ext/system/input_stream.c +229 -0
- data/ext/system/input_stream.h +23 -0
- data/ext/system/sleep.c +16 -0
- data/ext/system/sleep.h +8 -0
- data/ext/system/time.c +218 -0
- data/ext/system/time.h +18 -0
- data/ext/system/vec2.c +251 -0
- data/ext/system/vec2.h +55 -0
- data/ext/system/vec3.c +219 -0
- data/ext/system/vec3.h +18 -0
- data/ext/window/clipboard.c +89 -0
- data/ext/window/clipboard.h +8 -0
- data/ext/window/context.c +70 -0
- data/ext/window/context.h +8 -0
- data/ext/window/context_settings.c +220 -0
- data/ext/window/context_settings.h +18 -0
- data/ext/window/cursor.c +56 -0
- data/ext/window/cursor.h +14 -0
- data/ext/window/event.c +216 -0
- data/ext/window/event.h +14 -0
- data/ext/{event_name.c → window/event_name.c} +2 -2
- data/ext/window/event_name.h +8 -0
- data/ext/window/input_enums.c +261 -0
- data/ext/window/input_enums.h +36 -0
- data/ext/window/joystick.c +60 -0
- data/ext/window/joystick.h +8 -0
- data/ext/{keyboard.c → window/keyboard.c} +74 -2
- data/ext/window/keyboard.h +12 -0
- data/ext/window/mouse.c +70 -0
- data/ext/window/mouse.h +8 -0
- data/ext/window/sensor.c +30 -0
- data/ext/window/sensor.h +8 -0
- data/ext/window/touch.c +49 -0
- data/ext/window/touch.h +8 -0
- data/ext/window/video_mode.c +133 -0
- data/ext/window/video_mode.h +14 -0
- data/ext/window/vulkan.c +44 -0
- data/ext/window/vulkan.h +8 -0
- data/ext/window/window.c +414 -0
- data/ext/{ext/klass → window}/window.h +3 -3
- data/lib/sfml/version.rb +4 -2
- data/lib/sfml.rb +2 -0
- metadata +163 -56
- data/ext/circle.c +0 -198
- data/ext/clock.c +0 -65
- data/ext/color.c +0 -43
- data/ext/event.c +0 -148
- data/ext/ext/klass/color.h +0 -26
- data/ext/ext/klass/drawable/image.h +0 -4
- data/ext/ext/klass/drawable/polygon.h +0 -4
- data/ext/ext/klass/drawable/rectangle.h +0 -4
- data/ext/ext/klass/drawable/sprite.h +0 -4
- data/ext/ext/klass/drawable/texture.h +0 -4
- data/ext/ext/klass/event/event_name.h +0 -8
- data/ext/ext/klass/event.h +0 -12
- data/ext/ext/klass/keyboard.h +0 -8
- data/ext/ext/klass/render_state.h +0 -16
- data/ext/ext/klass/target.h +0 -18
- data/ext/ext/klass/transform.h +0 -17
- data/ext/ext/klass/video_mode.h +0 -12
- data/ext/ext/module/drawable/drawable.h +0 -10
- data/ext/ext/module/transform.h +0 -18
- data/ext/ext/module.h +0 -10
- data/ext/ext/rect.h +0 -16
- data/ext/ext/style_name.h +0 -11
- data/ext/ext/vec2.h +0 -38
- data/ext/image.c +0 -5
- data/ext/polygon.c +0 -5
- data/ext/rect.c +0 -26
- data/ext/rectangle.c +0 -5
- data/ext/render_state.c +0 -109
- data/ext/sprite.c +0 -5
- data/ext/style_name.c +0 -15
- data/ext/target.c +0 -106
- data/ext/texture.c +0 -5
- data/ext/transform.c +0 -107
- data/ext/vec2.c +0 -42
- data/ext/video_mode.c +0 -78
- data/ext/window.c +0 -274
data/TODO.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Roadmap: SFML 3 API port to Ruby
|
|
2
|
+
|
|
3
|
+
Tracks porting coverage of the SFML 3 API into this gem, module by module and class by class.
|
|
4
|
+
|
|
5
|
+
This binding wraps **CSFML 3** (the C API), not SFML's C++ API directly, so the scope below is
|
|
6
|
+
CSFML 3.0.0's actual header set — not every C++-only construct in the
|
|
7
|
+
[reference docs](https://www.sfml-dev.org/documentation/3.1.0/annotated.html). Out of scope for that
|
|
8
|
+
reason: `String`/`Utf`/`Literals` (SFML C++ uses `std::string`; CSFML takes plain C strings),
|
|
9
|
+
`Exception` (CSFML reports errors via return codes, not C++ exceptions), and C++-utility-only types
|
|
10
|
+
(`SuspendAwareClock`, `TimeoutWithPredicate`, `U8StringCharTraits`). `Glsl` is handled as plain
|
|
11
|
+
`set_*_uniform` calls (its types are CSFML structs, not a templated class), and `InputStream`'s C
|
|
12
|
+
struct form *is* bound (`SFML::InputStream`). `Sftp` is also out of scope: it was added to SFML in
|
|
13
|
+
3.1, and the CSFML 3.0.0 this gem vendors predates it.
|
|
14
|
+
|
|
15
|
+
Status legend: `[x]` bound and tested · `[~]` compiled but not exposed to Ruby, or exposed as an
|
|
16
|
+
internal helper only · `[ ]` not started.
|
|
17
|
+
|
|
18
|
+
**Coverage is tracked per function, not per class.** Every `sf*` entry point in
|
|
19
|
+
`ports/<target>/include/CSFML` is accounted for: bound, or listed in "Deliberately unbound" at the
|
|
20
|
+
bottom. Re-derive the list with:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
grep -rhoE '\bsf[A-Za-z0-9_]+\s*\(' ports/<target>/include/CSFML --include='*.h' \
|
|
24
|
+
| tr -d ' (' | sort -u > /tmp/api.txt
|
|
25
|
+
grep -rhoE '\bsf[A-Za-z0-9_]+' ext --include='*.c' --include='*.h' --include='*.inc' \
|
|
26
|
+
| sort -u > /tmp/used.txt
|
|
27
|
+
comm -23 /tmp/api.txt /tmp/used.txt
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Mind the false positives: functions reached through token paste (`sfSound_*`, `sfMusic_*`,
|
|
31
|
+
`sfSoundStream_*` from `ext/audio/sound_source.inc`; `sfPacket_read*`/`write*` from the macro in
|
|
32
|
+
`ext/network/packet.c`; the shared `sfRenderWindow_*`/`sfRenderTexture_*` render-target methods
|
|
33
|
+
from `ext/graphics/render_target.inc`) never appear literally in the sources and so show up as
|
|
34
|
+
"missing" every time.
|
|
35
|
+
|
|
36
|
+
## System
|
|
37
|
+
|
|
38
|
+
Base module: time, vectors, clocks, streams.
|
|
39
|
+
|
|
40
|
+
- [x] **Clock** — `SFML::Clock` (`ext/system/clock.c`); `elapsed_time`, `restart!`, `reset!`,
|
|
41
|
+
`start!`, `stop!`, `running?`, `copy`
|
|
42
|
+
- [x] **Vector2** — `SFML::Vector2` (`ext/system/vec2.c`); setters still accept `[x, y]`
|
|
43
|
+
- [x] **Vector3** — `SFML::Vector3` (`ext/system/vec3.c`)
|
|
44
|
+
- [x] **Time** — `SFML::Time` (`ext/system/time.c`); arithmetic, comparison, unit conversions
|
|
45
|
+
- [x] **Sleep** — `SFML.sleep` (`ext/system/sleep.c`)
|
|
46
|
+
- [x] **InputStream** — `SFML::InputStream` (`ext/system/input_stream.c`); wraps any `#read`-able
|
|
47
|
+
object and is accepted by every `from_stream` constructor
|
|
48
|
+
- [x] **Buffer** — `SFML::Buffer` (`ext/system/buffer.c`); returned by `Image#save_to_memory`
|
|
49
|
+
|
|
50
|
+
## Window
|
|
51
|
+
|
|
52
|
+
OpenGL-based windows, events, input handling.
|
|
53
|
+
|
|
54
|
+
- [x] **Window** — `SFML::Window` (`ext/window/window.c`); wraps `sfRenderWindow`, so this single
|
|
55
|
+
class covers what SFML splits into `WindowBase` + `Window` + `RenderWindow`. Accepts style,
|
|
56
|
+
state and `ContextSettings`; exposes min/max size, icon, cursor, native handle, settings.
|
|
57
|
+
- [x] **VideoMode** — `SFML::VideoMode` (`ext/window/video_mode.c`); includes `desktop_mode` and
|
|
58
|
+
`fullscreen_modes`
|
|
59
|
+
- [x] **Event** — `SFML::Event` (`ext/window/event.c`, `ext/window/event_name.c`); every payload is
|
|
60
|
+
exposed — text, key, mouse move/raw/button/wheel, joystick move/button/connect, touch, sensor
|
|
61
|
+
- [x] **Keyboard** — `SFML::Keyboard` (`ext/window/keyboard.c`); real-time `pressed?`,
|
|
62
|
+
`scancode_pressed?`, `localize`, `delocalize`, `description`, virtual-keyboard toggle
|
|
63
|
+
- [x] **Mouse** — `SFML::Mouse` (`ext/window/mouse.c`)
|
|
64
|
+
- [x] **Joystick** — `SFML::Joystick` (`ext/window/joystick.c`)
|
|
65
|
+
- [x] **Touch** — `SFML::Touch` (`ext/window/touch.c`)
|
|
66
|
+
- [x] **Sensor** — `SFML::Sensor` (`ext/window/sensor.c`)
|
|
67
|
+
- [x] **Clipboard** — `SFML::Clipboard` (`ext/window/clipboard.c`)
|
|
68
|
+
- [x] **Cursor** — `SFML::Cursor` (`ext/window/cursor.c`)
|
|
69
|
+
- [x] **Context** / **ContextSettings** — `SFML::Context`, `SFML::ContextSettings`
|
|
70
|
+
(`ext/window/context.c`, `ext/window/context_settings.c`)
|
|
71
|
+
- [x] **Vulkan** — `SFML::Vulkan` (`ext/window/vulkan.c`)
|
|
72
|
+
|
|
73
|
+
## Graphics
|
|
74
|
+
|
|
75
|
+
2D rendering: shapes, sprites, text, render targets.
|
|
76
|
+
|
|
77
|
+
- [x] **Transform** — `SFML::Transform` (`ext/graphics/transform.c`); a class wrapping
|
|
78
|
+
`sfTransform` by value. `identity`/`IDENTITY` (frozen), `from_a`, `to_a`/`matrix` (3x3),
|
|
79
|
+
`gl_matrix` (the 16-float 4x4 for `glLoadMatrixf`), `==`, `translate!`, `rotate!`, `scale!`
|
|
80
|
+
(both taking an optional centre), non-mutating `translate`/`rotate`/`scale`,
|
|
81
|
+
`transform_point`, `transform_rect`, `combine!`, `*`, `inverse`, `copy`. The old module
|
|
82
|
+
functions `Transform.combine`/`Transform.inverse` remain, Array-in/Array-out. Every class
|
|
83
|
+
that exposes `#transform` still returns a plain 9-element Array, and anything that *takes*
|
|
84
|
+
a transform accepts either form
|
|
85
|
+
- [x] **Transformable** — `SFML::Transformable` (`ext/graphics/transformable.c`); including
|
|
86
|
+
`inverse_transform` and `copy`
|
|
87
|
+
- [x] **Drawable** — `SFML::Drawable` mixin (`ext/graphics/drawable.c`)
|
|
88
|
+
- [x] **RenderStates** — `SFML::RenderState` (`ext/graphics/render_state.c`); blend mode, stencil
|
|
89
|
+
mode, coordinate type, texture, shader and transform are all settable
|
|
90
|
+
- [x] **RenderTarget** — `SFML::Target` (`ext/graphics/target.c`) dispatches to `sfRenderWindow_*`
|
|
91
|
+
or `sfRenderTexture_*` at runtime for `Drawable#draw`. The methods each concrete target owns
|
|
92
|
+
directly — `map_pixel_to_coords`, `map_coords_to_pixel`, `push_gl_states`, `pop_gl_states`,
|
|
93
|
+
`reset_gl_states`, `draw_primitives`, `draw_vertex_buffer_range`, `clear_stencil`,
|
|
94
|
+
`clear_color_and_stencil`, `viewport`, `scissor`, `srgb?` — are generated once for both
|
|
95
|
+
`Window` and `RenderTexture` from `ext/graphics/render_target.inc`
|
|
96
|
+
- [x] **RenderWindow** — folded into `SFML::Window` (see Window module above)
|
|
97
|
+
- [x] **RenderTexture** — `SFML::RenderTexture` (`ext/graphics/render_texture.c`)
|
|
98
|
+
- [x] **View** — `SFML::View` (`ext/graphics/view.c`); including `View.from_rect` and
|
|
99
|
+
`scissor`/`scissor=`
|
|
100
|
+
- [x] **CircleShape** — `SFML::Circle` (`ext/graphics/circle.c`)
|
|
101
|
+
- [x] **RectangleShape** — `SFML::RectangleShape` (`ext/graphics/rectangle.c`)
|
|
102
|
+
- [x] **ConvexShape** — `SFML::ConvexShape` (`ext/graphics/polygon.c`)
|
|
103
|
+
- [x] **Shape** — `SFML::Shape` (`ext/graphics/shape.c`); subclass and define `point_count`/`point`
|
|
104
|
+
- [x] **Sprite** — `SFML::Sprite` (`ext/graphics/sprite.c`)
|
|
105
|
+
- [x] **Texture** — `SFML::Texture` (`ext/graphics/texture.c`); every constructor in both linear
|
|
106
|
+
and sRGB form, plus `resize`/`resize_srgb` and `swap`
|
|
107
|
+
- [x] **Image** — `SFML::Image` (`ext/graphics/image.c`)
|
|
108
|
+
- [x] **Font** — `SFML::Font` (`ext/graphics/font.c`)
|
|
109
|
+
- [x] **Text** — `SFML::Text` (`ext/graphics/text.c`); `#string` goes through the UTF-32 entry
|
|
110
|
+
points, so non-ASCII round-trips exactly
|
|
111
|
+
- [x] **Glyph** — `SFML::Glyph` (`ext/graphics/glyph.c`)
|
|
112
|
+
- [x] **Shader** — `SFML::Shader` (`ext/graphics/shader.c`); scalar/vector/color/int/bool/matrix
|
|
113
|
+
uniforms, all six array uniforms (float, vec2-4, mat3, mat4), plus a generic `uniform=`
|
|
114
|
+
- [x] **Color** — `SFML::Color` (`ext/graphics/color.c`)
|
|
115
|
+
- [x] **Rect** — `SFML::Rect` (`ext/graphics/rect.c`); `sfFloatRect` and `sfIntRect`
|
|
116
|
+
- [x] **BlendMode** — `SFML::BlendMode` (`ext/graphics/blend_mode.c`)
|
|
117
|
+
- [x] **StencilMode** — `SFML::StencilMode` (`ext/graphics/stencil_mode.c`)
|
|
118
|
+
- [x] **Vertex** / **VertexArray** / **VertexBuffer** — `ext/graphics/vertex*.c`
|
|
119
|
+
|
|
120
|
+
## Audio
|
|
121
|
+
|
|
122
|
+
Sounds, streaming, recording, spatialization.
|
|
123
|
+
|
|
124
|
+
- [x] **Listener** — `SFML::Listener` (`ext/audio/listener.c`); global volume plus
|
|
125
|
+
position/direction/velocity/up-vector/cone
|
|
126
|
+
- [x] **ListenerCone** / **SoundSourceCone** — `SFML::SoundSourceCone` (`ext/audio/sound_source_cone.c`)
|
|
127
|
+
- [x] **SoundSource** — the shared play/pause/stop/status, pitch, pan, volume, spatialization,
|
|
128
|
+
position/direction/velocity, cone, doppler/directional-attenuation, min/max distance/gain,
|
|
129
|
+
attenuation, playing-offset and effect-processor surface, generated per class from
|
|
130
|
+
`ext/audio/sound_source.inc`
|
|
131
|
+
- [x] **SoundStatus** / **SoundChannel** — `SFML::SoundStatus`, `SFML::SoundChannel`
|
|
132
|
+
(`ext/audio/audio_enums.c`)
|
|
133
|
+
- [x] **SoundBuffer** — `SFML::SoundBuffer` (`ext/audio/sound_buffer.c`); loading from file, memory,
|
|
134
|
+
stream and raw samples, saving, sample access and channel map
|
|
135
|
+
- [x] **Sound** — `SFML::Sound` (`ext/audio/sound.c`)
|
|
136
|
+
- [x] **SoundStream** — `SFML::SoundStream` (`ext/audio/sound_stream.c`); subclass and implement
|
|
137
|
+
`#on_get_data` (and optionally `#on_seek`)
|
|
138
|
+
- [x] **SoundBufferRecorder** — `SFML::SoundBufferRecorder` (`ext/audio/sound_buffer_recorder.c`)
|
|
139
|
+
- [x] **SoundRecorder** — `SFML::SoundRecorder` (`ext/audio/sound_recorder.c`); subclass and
|
|
140
|
+
implement `#on_process` (and optionally `#on_start`/`#on_stop`)
|
|
141
|
+
- [x] **Music** — `SFML::Music` (`ext/audio/music.c`); file, memory and stream sources, loop points
|
|
142
|
+
- [x] **EffectProcessor** — `SFML::SoundSource#effect_processor=`; a Ruby proc is dispatched through
|
|
143
|
+
a bounded pool of C thunks (`ext/audio/effect_processor.c`), because `sfEffectProcessor` has
|
|
144
|
+
no `userData` to identify the source
|
|
145
|
+
- [x] **Build support** — `ext/ports.rb` builds Ogg 1.3.5, Vorbis 1.3.7 and FLAC 1.4.3 as pinned,
|
|
146
|
+
static, position-independent ports (SFML 3's audio backend is miniaudio, so no OpenAL), and
|
|
147
|
+
enables `SFML_BUILD_AUDIO` / `CSFML_BUILD_AUDIO`
|
|
148
|
+
|
|
149
|
+
## Network
|
|
150
|
+
|
|
151
|
+
Socket-based communication and higher-level protocols.
|
|
152
|
+
|
|
153
|
+
- [x] **IpAddress** — `SFML::IpAddress` (`ext/network/ip_address.c`); string/bytes/integer
|
|
154
|
+
constructors, `NONE`/`ANY`/`LOCAL_HOST`/`BROADCAST`, local and public address lookup
|
|
155
|
+
- [x] **Packet** — `SFML::Packet` (`ext/network/packet.c`); raw data plus every typed reader/writer
|
|
156
|
+
(booleans, all integer widths, floats, string)
|
|
157
|
+
- [x] **SocketSelector** — `SFML::SocketSelector` (`ext/network/socket_selector.c`); add/remove for
|
|
158
|
+
each socket type, wait and readiness checks
|
|
159
|
+
- [x] **TcpSocket** / **TcpListener** — `SFML::TcpSocket`, `SFML::TcpListener`
|
|
160
|
+
(`ext/network/tcp_socket.c`, `ext/network/tcp_listener.c`); connect/accept, blocking control,
|
|
161
|
+
raw and packet sends/receives
|
|
162
|
+
- [x] **UdpSocket** — `SFML::UdpSocket` (`ext/network/udp_socket.c`); bind/send/receive and packet
|
|
163
|
+
variants, datagram size and any-port helpers
|
|
164
|
+
- [x] **Http** — `SFML::Http`, `SFML::HttpRequest`, `SFML::HttpResponse` (`ext/network/http.c`)
|
|
165
|
+
- [x] **Ftp** — `SFML::Ftp`, `SFML::FtpResponse`, `SFML::FtpDirectoryResponse`,
|
|
166
|
+
`SFML::FtpListingResponse` (`ext/network/ftp.c`)
|
|
167
|
+
- [x] **SocketStatus** / **HttpMethod** / **HttpStatus** / **FtpStatus** / **FtpTransferMode** —
|
|
168
|
+
enum modules (`ext/network/network_enums.c`)
|
|
169
|
+
- [x] **Build support** — `ext/ports.rb` enables `SFML_BUILD_NETWORK` / `CSFML_BUILD_NETWORK`; the
|
|
170
|
+
module needs no external dependency (just `ws2_32` on Windows)
|
|
171
|
+
|
|
172
|
+
Note that CSFML exposes no `sfSocket` base class, so there is nothing to bind for "Socket base" —
|
|
173
|
+
only the concrete TCP/UDP sockets and the selector.
|
|
174
|
+
|
|
175
|
+
## Deliberately unbound
|
|
176
|
+
|
|
177
|
+
These CSFML entry points have no Ruby surface on purpose. Listed so a coverage diff can be read
|
|
178
|
+
without re-deriving the reasoning each time.
|
|
179
|
+
|
|
180
|
+
| Symbols | Why |
|
|
181
|
+
| --- | --- |
|
|
182
|
+
| `sfWindow_*`, `sfWindowBase_*` (~55 functions) | `SFML::Window` wraps `sfRenderWindow`, which subsumes both a plain window and a base window. |
|
|
183
|
+
| `sfMouse_*WindowBase`, `sfTouch_getPositionWindowBase` | Superseded by the `*RenderWindow` forms, which need no downcast. |
|
|
184
|
+
| `sfRenderWindow_create`, `sfText_getString`/`setString`, `sfRenderWindow_setTitle`, `sfFtpDirectoryResponse_getDirectory` | Superseded by their `*Unicode` counterparts; the narrow forms decode through the C locale and mangle non-ASCII. |
|
|
185
|
+
| `sfColor_add`/`subtract`/`modulate`/`fromRGB`/`fromRGBA`/`fromInteger`/`toInteger`, `sfIntRect_contains`/`intersects` | Reimplemented directly in C in `color.c` / `rect.c`; the Ruby methods exist. |
|
|
186
|
+
| `sfSprite_getTexture`, `sfText_getFont`, `sf*Shape_getTexture` | The Ruby getters return the cached wrapper object. CSFML returns a non-owning pointer, so re-wrapping it would hand Ruby an object it must not free. |
|
|
187
|
+
| `sfShape_getPoint`, `sfShape_getPointCount` | `SFML::Shape` reads these from the Ruby subclass, which is where they are defined. |
|
|
188
|
+
| `sfTexture_updateFromWindow` | `Texture#update_from_window` uses `sfTexture_updateFromRenderWindow`, since `SFML::Window` is a render window. |
|
|
189
|
+
| `sfFree` | CSFML's allocator hook; nothing in a Ruby binding should call it. |
|
|
190
|
+
| `sfGlslVec4_fromsfColor`, `sfGlslIvec4_fromsfColor` | Documentation-only helpers; `Shader#set_color` does the conversion. |
|
|
191
|
+
|
|
192
|
+
## References
|
|
193
|
+
|
|
194
|
+
- [SFML 3.1.0 module topics](https://www.sfml-dev.org/documentation/3.1.0/topics.html)
|
|
195
|
+
- [SFML 3.1.0 class index](https://www.sfml-dev.org/documentation/3.1.0/annotated.html)
|
|
196
|
+
- [SFML 3.1.0 namespace index](https://www.sfml-dev.org/documentation/3.1.0/namespaces.html)
|
|
197
|
+
- [CSFML 3.0.0 headers](https://github.com/SFML/CSFML/tree/3.0.0/include/CSFML) — the actual C API
|
|
198
|
+
surface this gem binds against, vendored locally under `ports/<target>/include/CSFML/`
|
|
199
|
+
once `rake ports` has run
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#include "audio/audio_enums.h"
|
|
2
|
+
|
|
3
|
+
#include <ruby.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
|
|
6
|
+
static const char *sound_status_names[] = {"stopped", "paused", "playing"};
|
|
7
|
+
|
|
8
|
+
/* Designated initializers, indexed by the enum constant rather than by
|
|
9
|
+
position: CSFML 3 defines the channel order, and a positional table would
|
|
10
|
+
silently return the wrong name if a future release inserted a value. */
|
|
11
|
+
static const char *sound_channel_names[] = {
|
|
12
|
+
[sfSoundChannelUnspecified] = "unspecified",
|
|
13
|
+
[sfSoundChannelMono] = "mono",
|
|
14
|
+
[sfSoundChannelFrontLeft] = "front_left",
|
|
15
|
+
[sfSoundChannelFrontRight] = "front_right",
|
|
16
|
+
[sfSoundChannelFrontCenter] = "front_center",
|
|
17
|
+
[sfSoundChannelFrontLeftOfCenter] = "front_left_of_center",
|
|
18
|
+
[sfSoundChannelFrontRightOfCenter] = "front_right_of_center",
|
|
19
|
+
[sfSoundChannelLowFrequencyEffects] = "low_frequency_effects",
|
|
20
|
+
[sfSoundChannelBackLeft] = "back_left",
|
|
21
|
+
[sfSoundChannelBackRight] = "back_right",
|
|
22
|
+
[sfSoundChannelBackCenter] = "back_center",
|
|
23
|
+
[sfSoundChannelSideLeft] = "side_left",
|
|
24
|
+
[sfSoundChannelSideRight] = "side_right",
|
|
25
|
+
[sfSoundChannelTopCenter] = "top_center",
|
|
26
|
+
[sfSoundChannelTopFrontLeft] = "top_front_left",
|
|
27
|
+
[sfSoundChannelTopFrontRight] = "top_front_right",
|
|
28
|
+
[sfSoundChannelTopFrontCenter] = "top_front_center",
|
|
29
|
+
[sfSoundChannelTopBackLeft] = "top_back_left",
|
|
30
|
+
[sfSoundChannelTopBackRight] = "top_back_right",
|
|
31
|
+
[sfSoundChannelTopBackCenter] = "top_back_center"
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
static int symbol_index(VALUE rb_symbol, const char *const *names, size_t count) {
|
|
35
|
+
const char *name;
|
|
36
|
+
size_t i;
|
|
37
|
+
|
|
38
|
+
if (!SYMBOL_P(rb_symbol)) {
|
|
39
|
+
return -1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
name = rb_id2name(SYM2ID(rb_symbol));
|
|
43
|
+
|
|
44
|
+
for (i = 0; i < count; i++) {
|
|
45
|
+
if (names[i] != NULL && strcmp(name, names[i]) == 0) {
|
|
46
|
+
return (int) i;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return -1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const char *sound_status_name(sfSoundStatus status) {
|
|
54
|
+
if (status >= sfStopped && status <= sfPlaying) {
|
|
55
|
+
return sound_status_names[status];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return "stopped";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
sfSoundStatus sound_status_from_rb(VALUE rb_status) {
|
|
62
|
+
int index;
|
|
63
|
+
|
|
64
|
+
if (RB_INTEGER_TYPE_P(rb_status)) {
|
|
65
|
+
return (sfSoundStatus) NUM2INT(rb_status);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
index = symbol_index(rb_status, sound_status_names, 3);
|
|
69
|
+
|
|
70
|
+
if (index < 0) {
|
|
71
|
+
rb_raise(rb_eArgError, "unknown sound status");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (sfSoundStatus) index;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const char *sound_channel_name(sfSoundChannel channel) {
|
|
78
|
+
if (channel >= sfSoundChannelUnspecified && channel <= sfSoundChannelTopBackCenter &&
|
|
79
|
+
sound_channel_names[channel] != NULL) {
|
|
80
|
+
return sound_channel_names[channel];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return "unspecified";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
sfSoundChannel sound_channel_from_rb(VALUE rb_channel) {
|
|
87
|
+
int index;
|
|
88
|
+
|
|
89
|
+
if (RB_INTEGER_TYPE_P(rb_channel)) {
|
|
90
|
+
return (sfSoundChannel) NUM2INT(rb_channel);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
index = symbol_index(rb_channel, sound_channel_names,
|
|
94
|
+
sizeof(sound_channel_names) / sizeof(sound_channel_names[0]));
|
|
95
|
+
|
|
96
|
+
if (index < 0) {
|
|
97
|
+
rb_raise(rb_eArgError, "unknown sound channel");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return (sfSoundChannel) index;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
void Init_AudioEnums(VALUE rb_module) {
|
|
104
|
+
VALUE rb_mSoundStatus = rb_define_module_under(rb_module, "SoundStatus");
|
|
105
|
+
VALUE rb_mSoundChannel = rb_define_module_under(rb_module, "SoundChannel");
|
|
106
|
+
|
|
107
|
+
rb_define_const(rb_mSoundStatus, "STOPPED", INT2NUM(sfStopped));
|
|
108
|
+
rb_define_const(rb_mSoundStatus, "PAUSED", INT2NUM(sfPaused));
|
|
109
|
+
rb_define_const(rb_mSoundStatus, "PLAYING", INT2NUM(sfPlaying));
|
|
110
|
+
|
|
111
|
+
rb_define_const(rb_mSoundChannel, "UNSPECIFIED", INT2NUM(sfSoundChannelUnspecified));
|
|
112
|
+
rb_define_const(rb_mSoundChannel, "MONO", INT2NUM(sfSoundChannelMono));
|
|
113
|
+
rb_define_const(rb_mSoundChannel, "FRONT_LEFT", INT2NUM(sfSoundChannelFrontLeft));
|
|
114
|
+
rb_define_const(rb_mSoundChannel, "FRONT_RIGHT", INT2NUM(sfSoundChannelFrontRight));
|
|
115
|
+
rb_define_const(rb_mSoundChannel, "FRONT_CENTER", INT2NUM(sfSoundChannelFrontCenter));
|
|
116
|
+
rb_define_const(rb_mSoundChannel, "FRONT_LEFT_OF_CENTER", INT2NUM(sfSoundChannelFrontLeftOfCenter));
|
|
117
|
+
rb_define_const(rb_mSoundChannel, "FRONT_RIGHT_OF_CENTER", INT2NUM(sfSoundChannelFrontRightOfCenter));
|
|
118
|
+
rb_define_const(rb_mSoundChannel, "LOW_FREQUENCY_EFFECTS", INT2NUM(sfSoundChannelLowFrequencyEffects));
|
|
119
|
+
rb_define_const(rb_mSoundChannel, "BACK_LEFT", INT2NUM(sfSoundChannelBackLeft));
|
|
120
|
+
rb_define_const(rb_mSoundChannel, "BACK_RIGHT", INT2NUM(sfSoundChannelBackRight));
|
|
121
|
+
rb_define_const(rb_mSoundChannel, "BACK_CENTER", INT2NUM(sfSoundChannelBackCenter));
|
|
122
|
+
rb_define_const(rb_mSoundChannel, "SIDE_LEFT", INT2NUM(sfSoundChannelSideLeft));
|
|
123
|
+
rb_define_const(rb_mSoundChannel, "SIDE_RIGHT", INT2NUM(sfSoundChannelSideRight));
|
|
124
|
+
rb_define_const(rb_mSoundChannel, "TOP_CENTER", INT2NUM(sfSoundChannelTopCenter));
|
|
125
|
+
rb_define_const(rb_mSoundChannel, "TOP_FRONT_LEFT", INT2NUM(sfSoundChannelTopFrontLeft));
|
|
126
|
+
rb_define_const(rb_mSoundChannel, "TOP_FRONT_RIGHT", INT2NUM(sfSoundChannelTopFrontRight));
|
|
127
|
+
rb_define_const(rb_mSoundChannel, "TOP_FRONT_CENTER", INT2NUM(sfSoundChannelTopFrontCenter));
|
|
128
|
+
rb_define_const(rb_mSoundChannel, "TOP_BACK_LEFT", INT2NUM(sfSoundChannelTopBackLeft));
|
|
129
|
+
rb_define_const(rb_mSoundChannel, "TOP_BACK_RIGHT", INT2NUM(sfSoundChannelTopBackRight));
|
|
130
|
+
rb_define_const(rb_mSoundChannel, "TOP_BACK_CENTER", INT2NUM(sfSoundChannelTopBackCenter));
|
|
131
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#ifndef SFML_RB_AUDIO_AUDIO_ENUMS_H
|
|
2
|
+
#define SFML_RB_AUDIO_AUDIO_ENUMS_H
|
|
3
|
+
|
|
4
|
+
#include <ruby.h>
|
|
5
|
+
|
|
6
|
+
#include "core/sfml.h"
|
|
7
|
+
|
|
8
|
+
void Init_AudioEnums(VALUE rb_module);
|
|
9
|
+
|
|
10
|
+
const char *sound_status_name(sfSoundStatus status);
|
|
11
|
+
|
|
12
|
+
sfSoundStatus sound_status_from_rb(VALUE rb_status);
|
|
13
|
+
|
|
14
|
+
const char *sound_channel_name(sfSoundChannel channel);
|
|
15
|
+
|
|
16
|
+
sfSoundChannel sound_channel_from_rb(VALUE rb_channel);
|
|
17
|
+
|
|
18
|
+
#endif //SFML_RB_AUDIO_AUDIO_ENUMS_H
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#include "audio/effect_processor.h"
|
|
2
|
+
|
|
3
|
+
#include <ruby.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
|
|
6
|
+
#include "core/foreign_thread.h"
|
|
7
|
+
|
|
8
|
+
/* Slots are process-global because a C callback cannot carry context. Each
|
|
9
|
+
entry is either Qnil (free) or the Proc assigned to that slot. */
|
|
10
|
+
static VALUE effect_processors[EFFECT_PROCESSOR_SLOTS];
|
|
11
|
+
|
|
12
|
+
typedef struct {
|
|
13
|
+
int slot;
|
|
14
|
+
const float* input;
|
|
15
|
+
unsigned int input_count;
|
|
16
|
+
float* output;
|
|
17
|
+
unsigned int output_count;
|
|
18
|
+
unsigned int requested_output_count;
|
|
19
|
+
unsigned int channels;
|
|
20
|
+
} EffectContext;
|
|
21
|
+
|
|
22
|
+
/* Fills the output buffer with silence for the full frame count the audio
|
|
23
|
+
engine asked for. Used whenever the processor cannot produce real output
|
|
24
|
+
(an exception, or a timed-out worker): reporting output_count 0 instead of
|
|
25
|
+
this was observed to make miniaudio's node graph re-invoke this callback
|
|
26
|
+
in an immediate, unbounded retry loop (tens of thousands of calls/second)
|
|
27
|
+
rather than treat it as one completed (silent) buffer - which in turn
|
|
28
|
+
starved SFML's AudioDevice::waitForReadingComplete() on the main thread,
|
|
29
|
+
since the audio thread never stopped re-acquiring its internal mutex. */
|
|
30
|
+
static void effect_output_silence(EffectContext* ctx) {
|
|
31
|
+
unsigned int i;
|
|
32
|
+
|
|
33
|
+
for (i = 0; i < ctx->requested_output_count * ctx->channels; i++) {
|
|
34
|
+
ctx->output[i] = 0.0f;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
ctx->output_count = ctx->requested_output_count;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#define EFFECT_JOB_TIMEOUT_MS 50
|
|
41
|
+
|
|
42
|
+
void Init_EffectProcessor(void) {
|
|
43
|
+
int i;
|
|
44
|
+
|
|
45
|
+
for (i = 0; i < EFFECT_PROCESSOR_SLOTS; i++) {
|
|
46
|
+
effect_processors[i] = Qnil;
|
|
47
|
+
rb_gc_register_address(&effect_processors[i]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static VALUE effect_yield(VALUE raw) {
|
|
52
|
+
EffectContext* ctx = (EffectContext*)raw;
|
|
53
|
+
VALUE rb_input;
|
|
54
|
+
VALUE rb_result;
|
|
55
|
+
unsigned int i;
|
|
56
|
+
unsigned int c;
|
|
57
|
+
long produced;
|
|
58
|
+
|
|
59
|
+
rb_input = rb_ary_new_capa((long)ctx->input_count);
|
|
60
|
+
|
|
61
|
+
for (i = 0; i < ctx->input_count; i++) {
|
|
62
|
+
VALUE frame = rb_ary_new_capa((long)ctx->channels);
|
|
63
|
+
|
|
64
|
+
for (c = 0; c < ctx->channels; c++) {
|
|
65
|
+
rb_ary_push(frame, DBL2NUM((double)ctx->input[i * ctx->channels + c]));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
rb_ary_push(rb_input, frame);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
rb_result = rb_funcall(effect_processors[ctx->slot], rb_intern("call"), 2, rb_input,
|
|
72
|
+
UINT2NUM(ctx->channels));
|
|
73
|
+
|
|
74
|
+
if (!RB_TYPE_P(rb_result, T_ARRAY)) {
|
|
75
|
+
rb_raise(rb_eTypeError, "effect processor must return an Array of frames");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
produced = RARRAY_LEN(rb_result);
|
|
79
|
+
|
|
80
|
+
if (produced > (long)ctx->output_count) {
|
|
81
|
+
produced = (long)ctx->output_count;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (i = 0; i < (unsigned int)produced; i++) {
|
|
85
|
+
VALUE frame = rb_ary_entry(rb_result, (long)i);
|
|
86
|
+
|
|
87
|
+
if (!RB_TYPE_P(frame, T_ARRAY)) {
|
|
88
|
+
rb_raise(rb_eTypeError, "effect processor frames must be Arrays");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (c = 0; c < ctx->channels; c++) {
|
|
92
|
+
ctx->output[i * ctx->channels + c] = (float)NUM2DBL(rb_ary_entry(frame, (long)c));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
ctx->output_count = (unsigned int)produced;
|
|
97
|
+
|
|
98
|
+
return Qnil;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Runs on the shared foreign-thread worker, which holds the GVL. */
|
|
102
|
+
static void effect_run(void* raw) {
|
|
103
|
+
EffectContext* ctx = (EffectContext*)raw;
|
|
104
|
+
int state = 0;
|
|
105
|
+
|
|
106
|
+
rb_protect(effect_yield, (VALUE)ctx, &state);
|
|
107
|
+
|
|
108
|
+
if (state) {
|
|
109
|
+
/* An exception must not unwind through the audio engine: drop the
|
|
110
|
+
output and let playback continue silently. */
|
|
111
|
+
rb_set_errinfo(Qnil);
|
|
112
|
+
effect_output_silence(ctx);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static void effect_invoke(int slot, const float* input, unsigned int* input_count, float* output,
|
|
117
|
+
unsigned int* output_count, unsigned int channels) {
|
|
118
|
+
EffectContext ctx;
|
|
119
|
+
|
|
120
|
+
if (slot < 0 || slot >= EFFECT_PROCESSOR_SLOTS || NIL_P(effect_processors[slot]) ||
|
|
121
|
+
*output_count == 0) {
|
|
122
|
+
*output_count = 0;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
ctx.slot = slot;
|
|
127
|
+
ctx.input = input;
|
|
128
|
+
ctx.input_count = (input != NULL) ? *input_count : 0;
|
|
129
|
+
ctx.output = output;
|
|
130
|
+
ctx.output_count = *output_count;
|
|
131
|
+
ctx.requested_output_count = *output_count;
|
|
132
|
+
ctx.channels = channels;
|
|
133
|
+
|
|
134
|
+
if (!run_on_ruby_thread(effect_run, &ctx, EFFECT_JOB_TIMEOUT_MS)) {
|
|
135
|
+
/* Timed out before the worker even claimed the job (e.g. GVL
|
|
136
|
+
contention): degrade to silence for this callback rather than
|
|
137
|
+
stall the audio thread. */
|
|
138
|
+
effect_output_silence(&ctx);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* The processor consumed every input frame it was offered. */
|
|
142
|
+
*input_count = ctx.input_count;
|
|
143
|
+
*output_count = ctx.output_count;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
#define EFFECT_THUNK(n) \
|
|
147
|
+
static void effect_thunk_##n(const float* input, unsigned int* input_count, float* output, \
|
|
148
|
+
unsigned int* output_count, unsigned int channels) { \
|
|
149
|
+
effect_invoke(n, input, input_count, output, output_count, channels); \
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
EFFECT_THUNK(0)
|
|
153
|
+
EFFECT_THUNK(1)
|
|
154
|
+
EFFECT_THUNK(2)
|
|
155
|
+
EFFECT_THUNK(3)
|
|
156
|
+
EFFECT_THUNK(4)
|
|
157
|
+
EFFECT_THUNK(5)
|
|
158
|
+
EFFECT_THUNK(6)
|
|
159
|
+
EFFECT_THUNK(7)
|
|
160
|
+
EFFECT_THUNK(8)
|
|
161
|
+
EFFECT_THUNK(9)
|
|
162
|
+
EFFECT_THUNK(10)
|
|
163
|
+
EFFECT_THUNK(11)
|
|
164
|
+
EFFECT_THUNK(12)
|
|
165
|
+
EFFECT_THUNK(13)
|
|
166
|
+
EFFECT_THUNK(14)
|
|
167
|
+
EFFECT_THUNK(15)
|
|
168
|
+
EFFECT_THUNK(16)
|
|
169
|
+
EFFECT_THUNK(17)
|
|
170
|
+
EFFECT_THUNK(18)
|
|
171
|
+
EFFECT_THUNK(19)
|
|
172
|
+
EFFECT_THUNK(20)
|
|
173
|
+
EFFECT_THUNK(21)
|
|
174
|
+
EFFECT_THUNK(22)
|
|
175
|
+
EFFECT_THUNK(23)
|
|
176
|
+
EFFECT_THUNK(24)
|
|
177
|
+
EFFECT_THUNK(25)
|
|
178
|
+
EFFECT_THUNK(26)
|
|
179
|
+
EFFECT_THUNK(27)
|
|
180
|
+
EFFECT_THUNK(28)
|
|
181
|
+
EFFECT_THUNK(29)
|
|
182
|
+
EFFECT_THUNK(30)
|
|
183
|
+
EFFECT_THUNK(31)
|
|
184
|
+
|
|
185
|
+
static sfEffectProcessor effect_thunks[EFFECT_PROCESSOR_SLOTS] = {
|
|
186
|
+
effect_thunk_0, effect_thunk_1, effect_thunk_2, effect_thunk_3, effect_thunk_4,
|
|
187
|
+
effect_thunk_5, effect_thunk_6, effect_thunk_7, effect_thunk_8, effect_thunk_9,
|
|
188
|
+
effect_thunk_10, effect_thunk_11, effect_thunk_12, effect_thunk_13, effect_thunk_14,
|
|
189
|
+
effect_thunk_15, effect_thunk_16, effect_thunk_17, effect_thunk_18, effect_thunk_19,
|
|
190
|
+
effect_thunk_20, effect_thunk_21, effect_thunk_22, effect_thunk_23, effect_thunk_24,
|
|
191
|
+
effect_thunk_25, effect_thunk_26, effect_thunk_27, effect_thunk_28, effect_thunk_29,
|
|
192
|
+
effect_thunk_30, effect_thunk_31};
|
|
193
|
+
|
|
194
|
+
sfEffectProcessor effect_processor_acquire(VALUE rb_proc, int* slot) {
|
|
195
|
+
int i;
|
|
196
|
+
|
|
197
|
+
/* Replacing the processor on a source that already has one reuses its
|
|
198
|
+
slot, so a source never leaks slots across repeated assignment. */
|
|
199
|
+
if (*slot >= 0 && *slot < EFFECT_PROCESSOR_SLOTS) {
|
|
200
|
+
if (NIL_P(rb_proc)) {
|
|
201
|
+
effect_processors[*slot] = Qnil;
|
|
202
|
+
return NULL;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
effect_processors[*slot] = rb_proc;
|
|
206
|
+
|
|
207
|
+
return effect_thunks[*slot];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (NIL_P(rb_proc)) {
|
|
211
|
+
*slot = -1;
|
|
212
|
+
return NULL;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
for (i = 0; i < EFFECT_PROCESSOR_SLOTS; i++) {
|
|
216
|
+
if (NIL_P(effect_processors[i])) {
|
|
217
|
+
effect_processors[i] = rb_proc;
|
|
218
|
+
*slot = i;
|
|
219
|
+
|
|
220
|
+
return effect_thunks[i];
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
rb_raise(rb_eRuntimeError, "all %d effect-processor slots are in use", EFFECT_PROCESSOR_SLOTS);
|
|
225
|
+
|
|
226
|
+
return NULL;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
void effect_processor_release(int slot) {
|
|
230
|
+
if (slot >= 0 && slot < EFFECT_PROCESSOR_SLOTS) {
|
|
231
|
+
effect_processors[slot] = Qnil;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#ifndef SFML_RB_AUDIO_EFFECT_PROCESSOR_H
|
|
2
|
+
#define SFML_RB_AUDIO_EFFECT_PROCESSOR_H
|
|
3
|
+
|
|
4
|
+
#include <ruby.h>
|
|
5
|
+
|
|
6
|
+
#include "core/sfml.h"
|
|
7
|
+
|
|
8
|
+
/* sfEffectProcessor carries no userData, so a single C callback cannot tell
|
|
9
|
+
which sound source invoked it. Instead the binding keeps a fixed pool of
|
|
10
|
+
identical C thunks, each hard-wired to one slot; assigning a processor to a
|
|
11
|
+
source hands it the thunk for that source's slot. Slots are bounded, and
|
|
12
|
+
exceeding the pool raises rather than silently sharing one callback. */
|
|
13
|
+
#define EFFECT_PROCESSOR_SLOTS 32
|
|
14
|
+
|
|
15
|
+
/* Registers every slot as a GC root. Must run before any acquire. */
|
|
16
|
+
void Init_EffectProcessor(void);
|
|
17
|
+
|
|
18
|
+
/* Stores rb_proc in a free slot, writes its index to *slot and returns the
|
|
19
|
+
matching thunk. Passing nil returns NULL (no processor). */
|
|
20
|
+
sfEffectProcessor effect_processor_acquire(VALUE rb_proc, int *slot);
|
|
21
|
+
|
|
22
|
+
/* Frees a slot previously handed out, clearing the Ruby reference. */
|
|
23
|
+
void effect_processor_release(int slot);
|
|
24
|
+
|
|
25
|
+
#endif //SFML_RB_AUDIO_EFFECT_PROCESSOR_H
|