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.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +342 -0
  3. data/README.md +51 -14
  4. data/TODO.md +199 -0
  5. data/ext/audio/audio_enums.c +131 -0
  6. data/ext/audio/audio_enums.h +18 -0
  7. data/ext/audio/effect_processor.c +233 -0
  8. data/ext/audio/effect_processor.h +25 -0
  9. data/ext/audio/listener.c +83 -0
  10. data/ext/audio/listener.h +8 -0
  11. data/ext/audio/music.c +174 -0
  12. data/ext/audio/music.h +14 -0
  13. data/ext/audio/sound.c +139 -0
  14. data/ext/audio/sound.h +14 -0
  15. data/ext/audio/sound_buffer.c +274 -0
  16. data/ext/audio/sound_buffer.h +18 -0
  17. data/ext/audio/sound_buffer_recorder.c +109 -0
  18. data/ext/audio/sound_buffer_recorder.h +14 -0
  19. data/ext/audio/sound_recorder.c +268 -0
  20. data/ext/audio/sound_recorder.h +14 -0
  21. data/ext/audio/sound_source.h +13 -0
  22. data/ext/audio/sound_source.inc +271 -0
  23. data/ext/audio/sound_source_cone.c +138 -0
  24. data/ext/audio/sound_source_cone.h +20 -0
  25. data/ext/audio/sound_stream.c +294 -0
  26. data/ext/audio/sound_stream.h +14 -0
  27. data/ext/{exceptions.c → core/exceptions.c} +10 -8
  28. data/ext/{ext → core}/exceptions.h +3 -3
  29. data/ext/core/foreign_thread.c +149 -0
  30. data/ext/core/foreign_thread.h +31 -0
  31. data/ext/core/macros.h +8 -0
  32. data/ext/{ext → core}/sfml.h +5 -3
  33. data/ext/core/unicode.c +46 -0
  34. data/ext/core/unicode.h +25 -0
  35. data/ext/ext.c +125 -14
  36. data/ext/extconf.rb +51 -6
  37. data/ext/graphics/blend_mode.c +261 -0
  38. data/ext/graphics/blend_mode.h +18 -0
  39. data/ext/graphics/circle.c +319 -0
  40. data/ext/{ext/klass/drawable → graphics}/circle.h +3 -3
  41. data/ext/graphics/color.c +318 -0
  42. data/ext/graphics/color.h +37 -0
  43. data/ext/{drawable.c → graphics/drawable.c} +11 -11
  44. data/ext/graphics/drawable.h +10 -0
  45. data/ext/graphics/enums.c +123 -0
  46. data/ext/graphics/enums.h +20 -0
  47. data/ext/graphics/font.c +148 -0
  48. data/ext/graphics/font.h +14 -0
  49. data/ext/graphics/glyph.c +60 -0
  50. data/ext/graphics/glyph.h +14 -0
  51. data/ext/graphics/image.c +205 -0
  52. data/ext/graphics/image.h +16 -0
  53. data/ext/graphics/polygon.c +300 -0
  54. data/ext/graphics/polygon.h +10 -0
  55. data/ext/graphics/rect.c +287 -0
  56. data/ext/graphics/rect.h +30 -0
  57. data/ext/graphics/rectangle.c +302 -0
  58. data/ext/graphics/rectangle.h +10 -0
  59. data/ext/graphics/render_state.c +204 -0
  60. data/ext/graphics/render_state.h +18 -0
  61. data/ext/graphics/render_target.inc +181 -0
  62. data/ext/graphics/render_texture.c +182 -0
  63. data/ext/graphics/render_texture.h +14 -0
  64. data/ext/graphics/shader.c +540 -0
  65. data/ext/graphics/shader.h +14 -0
  66. data/ext/graphics/shape.c +311 -0
  67. data/ext/graphics/shape.h +10 -0
  68. data/ext/graphics/sprite.c +249 -0
  69. data/ext/graphics/sprite.h +10 -0
  70. data/ext/graphics/stencil_mode.c +211 -0
  71. data/ext/graphics/stencil_mode.h +18 -0
  72. data/ext/graphics/target.c +133 -0
  73. data/ext/graphics/target.h +40 -0
  74. data/ext/graphics/text.c +344 -0
  75. data/ext/graphics/text.h +10 -0
  76. data/ext/graphics/texture.c +379 -0
  77. data/ext/graphics/texture.h +16 -0
  78. data/ext/graphics/transform.c +372 -0
  79. data/ext/graphics/transform.h +35 -0
  80. data/ext/{transformable.c → graphics/transformable.c} +168 -144
  81. data/ext/{ext/klass → graphics}/transformable.h +3 -3
  82. data/ext/graphics/vertex.c +179 -0
  83. data/ext/graphics/vertex.h +23 -0
  84. data/ext/graphics/vertex_array.c +127 -0
  85. data/ext/graphics/vertex_array.h +10 -0
  86. data/ext/graphics/vertex_buffer.c +211 -0
  87. data/ext/graphics/vertex_buffer.h +14 -0
  88. data/ext/{view.c → graphics/view.c} +168 -169
  89. data/ext/{ext/klass → graphics}/view.h +3 -3
  90. data/ext/network/ftp.c +405 -0
  91. data/ext/network/ftp.h +20 -0
  92. data/ext/network/http.c +243 -0
  93. data/ext/network/http.h +18 -0
  94. data/ext/network/ip_address.c +164 -0
  95. data/ext/network/ip_address.h +20 -0
  96. data/ext/network/network_enums.c +298 -0
  97. data/ext/network/network_enums.h +26 -0
  98. data/ext/network/packet.c +189 -0
  99. data/ext/network/packet.h +14 -0
  100. data/ext/network/socket_selector.c +142 -0
  101. data/ext/network/socket_selector.h +14 -0
  102. data/ext/network/tcp_listener.c +115 -0
  103. data/ext/network/tcp_listener.h +14 -0
  104. data/ext/network/tcp_socket.c +199 -0
  105. data/ext/network/tcp_socket.h +17 -0
  106. data/ext/network/udp_socket.c +190 -0
  107. data/ext/network/udp_socket.h +14 -0
  108. data/ext/ports.rb +97 -21
  109. data/ext/system/buffer.c +77 -0
  110. data/ext/system/buffer.h +14 -0
  111. data/ext/system/clock.c +100 -0
  112. data/ext/{ext/klass → system}/clock.h +3 -3
  113. data/ext/system/input_stream.c +229 -0
  114. data/ext/system/input_stream.h +23 -0
  115. data/ext/system/sleep.c +16 -0
  116. data/ext/system/sleep.h +8 -0
  117. data/ext/system/time.c +218 -0
  118. data/ext/system/time.h +18 -0
  119. data/ext/system/vec2.c +251 -0
  120. data/ext/system/vec2.h +55 -0
  121. data/ext/system/vec3.c +219 -0
  122. data/ext/system/vec3.h +18 -0
  123. data/ext/window/clipboard.c +89 -0
  124. data/ext/window/clipboard.h +8 -0
  125. data/ext/window/context.c +70 -0
  126. data/ext/window/context.h +8 -0
  127. data/ext/window/context_settings.c +220 -0
  128. data/ext/window/context_settings.h +18 -0
  129. data/ext/window/cursor.c +56 -0
  130. data/ext/window/cursor.h +14 -0
  131. data/ext/window/event.c +216 -0
  132. data/ext/window/event.h +14 -0
  133. data/ext/{event_name.c → window/event_name.c} +2 -2
  134. data/ext/window/event_name.h +8 -0
  135. data/ext/window/input_enums.c +261 -0
  136. data/ext/window/input_enums.h +36 -0
  137. data/ext/window/joystick.c +60 -0
  138. data/ext/window/joystick.h +8 -0
  139. data/ext/{keyboard.c → window/keyboard.c} +74 -2
  140. data/ext/window/keyboard.h +12 -0
  141. data/ext/window/mouse.c +70 -0
  142. data/ext/window/mouse.h +8 -0
  143. data/ext/window/sensor.c +30 -0
  144. data/ext/window/sensor.h +8 -0
  145. data/ext/window/touch.c +49 -0
  146. data/ext/window/touch.h +8 -0
  147. data/ext/window/video_mode.c +133 -0
  148. data/ext/window/video_mode.h +14 -0
  149. data/ext/window/vulkan.c +44 -0
  150. data/ext/window/vulkan.h +8 -0
  151. data/ext/window/window.c +414 -0
  152. data/ext/{ext/klass → window}/window.h +3 -3
  153. data/lib/sfml/version.rb +4 -2
  154. data/lib/sfml.rb +2 -0
  155. metadata +163 -56
  156. data/ext/circle.c +0 -198
  157. data/ext/clock.c +0 -65
  158. data/ext/color.c +0 -43
  159. data/ext/event.c +0 -148
  160. data/ext/ext/klass/color.h +0 -26
  161. data/ext/ext/klass/drawable/image.h +0 -4
  162. data/ext/ext/klass/drawable/polygon.h +0 -4
  163. data/ext/ext/klass/drawable/rectangle.h +0 -4
  164. data/ext/ext/klass/drawable/sprite.h +0 -4
  165. data/ext/ext/klass/drawable/texture.h +0 -4
  166. data/ext/ext/klass/event/event_name.h +0 -8
  167. data/ext/ext/klass/event.h +0 -12
  168. data/ext/ext/klass/keyboard.h +0 -8
  169. data/ext/ext/klass/render_state.h +0 -16
  170. data/ext/ext/klass/target.h +0 -18
  171. data/ext/ext/klass/transform.h +0 -17
  172. data/ext/ext/klass/video_mode.h +0 -12
  173. data/ext/ext/module/drawable/drawable.h +0 -10
  174. data/ext/ext/module/transform.h +0 -18
  175. data/ext/ext/module.h +0 -10
  176. data/ext/ext/rect.h +0 -16
  177. data/ext/ext/style_name.h +0 -11
  178. data/ext/ext/vec2.h +0 -38
  179. data/ext/image.c +0 -5
  180. data/ext/polygon.c +0 -5
  181. data/ext/rect.c +0 -26
  182. data/ext/rectangle.c +0 -5
  183. data/ext/render_state.c +0 -109
  184. data/ext/sprite.c +0 -5
  185. data/ext/style_name.c +0 -15
  186. data/ext/target.c +0 -106
  187. data/ext/texture.c +0 -5
  188. data/ext/transform.c +0 -107
  189. data/ext/vec2.c +0 -42
  190. data/ext/video_mode.c +0 -78
  191. 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