sfml3-rb 0.3.0 → 0.3.1

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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/README.md +36 -13
  4. data/TODO.md +18 -11
  5. data/ext/audio/music.c +6 -0
  6. data/ext/audio/sound.c +43 -4
  7. data/ext/audio/sound_buffer.c +8 -0
  8. data/ext/audio/sound_buffer_recorder.c +8 -3
  9. data/ext/audio/sound_recorder.c +21 -11
  10. data/ext/audio/sound_source_cone.c +18 -3
  11. data/ext/audio/sound_stream.c +25 -15
  12. data/ext/ext.c +4 -0
  13. data/ext/graphics/blend_mode.c +16 -8
  14. data/ext/graphics/circle.c +30 -13
  15. data/ext/graphics/color.c +17 -8
  16. data/ext/graphics/drawable.c +3 -2
  17. data/ext/graphics/font.c +6 -0
  18. data/ext/graphics/glyph.c +7 -0
  19. data/ext/graphics/image.c +25 -4
  20. data/ext/graphics/polygon.c +31 -11
  21. data/ext/graphics/rect.c +16 -8
  22. data/ext/graphics/rectangle.c +30 -11
  23. data/ext/graphics/render_state.c +8 -10
  24. data/ext/graphics/render_texture.c +27 -11
  25. data/ext/graphics/render_window.c +55 -0
  26. data/ext/graphics/render_window.h +10 -0
  27. data/ext/graphics/shader.c +6 -0
  28. data/ext/graphics/shape.c +23 -16
  29. data/ext/graphics/sprite.c +34 -10
  30. data/ext/graphics/stencil_mode.c +16 -8
  31. data/ext/graphics/target.c +60 -27
  32. data/ext/graphics/target.h +22 -8
  33. data/ext/graphics/text.c +33 -13
  34. data/ext/graphics/texture.c +34 -3
  35. data/ext/graphics/transformable.c +11 -20
  36. data/ext/graphics/vertex.c +18 -3
  37. data/ext/graphics/vertex_array.c +18 -9
  38. data/ext/graphics/vertex_buffer.c +30 -10
  39. data/ext/graphics/view.c +16 -10
  40. data/ext/network/ftp.c +30 -3
  41. data/ext/network/http.c +23 -6
  42. data/ext/network/ip_address.c +25 -14
  43. data/ext/network/packet.c +8 -3
  44. data/ext/network/socket_selector.c +8 -3
  45. data/ext/network/tcp_listener.c +8 -3
  46. data/ext/network/tcp_socket.c +8 -3
  47. data/ext/network/udp_socket.c +8 -3
  48. data/ext/system/buffer.c +13 -11
  49. data/ext/system/clock.c +11 -5
  50. data/ext/system/input_stream.c +21 -11
  51. data/ext/system/time.c +17 -13
  52. data/ext/system/vec2.c +17 -11
  53. data/ext/system/vec3.c +18 -11
  54. data/ext/window/context.c +14 -11
  55. data/ext/window/context_settings.c +16 -4
  56. data/ext/window/cursor.c +6 -0
  57. data/ext/window/event.c +15 -25
  58. data/ext/window/mouse.c +28 -11
  59. data/ext/window/touch.c +12 -10
  60. data/ext/window/video_mode.c +16 -25
  61. data/ext/window/window.c +110 -435
  62. data/ext/window/window.h +4 -4
  63. data/ext/window/window_base.c +251 -0
  64. data/ext/window/window_base.h +43 -0
  65. data/ext/window/window_base.inc +236 -0
  66. data/lib/sfml/version.rb +1 -1
  67. data/sig/audio/sound.rbs +1 -1
  68. data/sig/audio/sound_buffer_recorder.rbs +1 -1
  69. data/sig/audio/sound_recorder.rbs +1 -1
  70. data/sig/audio/sound_source_cone.rbs +1 -1
  71. data/sig/audio/sound_stream.rbs +1 -1
  72. data/sig/graphics/blend_mode.rbs +1 -1
  73. data/sig/graphics/circle.rbs +1 -1
  74. data/sig/graphics/color.rbs +5 -5
  75. data/sig/graphics/image.rbs +1 -1
  76. data/sig/graphics/polygon.rbs +1 -1
  77. data/sig/graphics/rect.rbs +5 -5
  78. data/sig/graphics/rectangle.rbs +1 -1
  79. data/sig/graphics/render_state.rbs +2 -2
  80. data/sig/graphics/render_target.rbs +18 -0
  81. data/sig/graphics/render_texture.rbs +4 -2
  82. data/sig/graphics/render_window.rbs +7 -0
  83. data/sig/graphics/shape.rbs +1 -1
  84. data/sig/graphics/sprite.rbs +2 -2
  85. data/sig/graphics/stencil_mode.rbs +2 -2
  86. data/sig/graphics/target.rbs +1 -3
  87. data/sig/graphics/text.rbs +4 -4
  88. data/sig/graphics/texture.rbs +1 -1
  89. data/sig/graphics/transformable.rbs +1 -1
  90. data/sig/graphics/vertex.rbs +4 -4
  91. data/sig/graphics/vertex_array.rbs +1 -1
  92. data/sig/graphics/vertex_buffer.rbs +4 -4
  93. data/sig/graphics/view.rbs +0 -1
  94. data/sig/network/ftp.rbs +1 -1
  95. data/sig/network/http.rbs +2 -2
  96. data/sig/network/ip_address.rbs +1 -1
  97. data/sig/network/packet.rbs +1 -1
  98. data/sig/network/socket_selector.rbs +1 -1
  99. data/sig/network/tcp_listener.rbs +1 -1
  100. data/sig/network/tcp_socket.rbs +1 -1
  101. data/sig/network/udp_socket.rbs +1 -1
  102. data/sig/sfml/version.rbs +3 -0
  103. data/sig/system/buffer.rbs +1 -1
  104. data/sig/system/clock.rbs +1 -1
  105. data/sig/system/input_stream.rbs +1 -1
  106. data/sig/system/time.rbs +4 -4
  107. data/sig/system/vec2.rbs +4 -4
  108. data/sig/system/vec3.rbs +4 -4
  109. data/sig/window/context.rbs +1 -1
  110. data/sig/window/context_settings.rbs +1 -1
  111. data/sig/window/event.rbs +1 -1
  112. data/sig/window/mouse.rbs +2 -2
  113. data/sig/window/touch.rbs +1 -1
  114. data/sig/window/video_mode.rbs +1 -1
  115. data/sig/window/window.rbs +2 -25
  116. data/sig/window/window_base.rbs +33 -0
  117. metadata +11 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ef7284361fdd31619f9f97c920664262125cfb26936bbc8a6425ff6c585e12c
4
- data.tar.gz: 59baa039d3fa9f7d704469f3204070dc90dc8db48f2be3e9737c7e888554ed7e
3
+ metadata.gz: f98dbb27451c7780e8dbd1ab1f677a9590d5d42b55db97cad407eee15fdd3ef8
4
+ data.tar.gz: 6ffe42d972c73f477b642ad6e1bfc1e9c4d5585f40bd9a9402bc08dfb03834ef
5
5
  SHA512:
6
- metadata.gz: d2d1dbf0f61b9c418e1aa01e81770158390cb28b9f297157195357463bd7ac6ca2e8ac0d2974f295968f4e762dd6e4affa330bf72df4cba5588a4bc30f459c74
7
- data.tar.gz: 2b959e3a75e33a5633ec3cdaf64e2d5c95bdaa62ff58d37e981b114a3e52b0a69df5594c1adbf32a47446c3c1d4bd3f961a99ae06f56dde83ce80af2b6468934
6
+ metadata.gz: 7fde721522979c1446f14a18ce2433dcf6c8a1009c40cedb6771926f87a759bee9f44f338a47d8205336e51624ee80d919a18ea7172e9815c434194e814459c9
7
+ data.tar.gz: c2fde3b6034c72f50cf8d480a77bee01f79d505b510ca7b354c618bcf0f694307776d2409c00ab0b173253c3c797b2498e5f0c50cbd94256d902f7f96c816d91
data/CHANGELOG.md CHANGED
@@ -6,6 +6,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.3.1] - 2026-09-19
10
+
11
+ ### Added
12
+ * **`SFML::WindowBase`, `SFML::RenderWindow` and the `SFML::RenderTarget` module**, mirroring
13
+ SFML 3's own hierarchy. `WindowBase` wraps `sfWindowBase` -- an OS window and event queue with
14
+ no OpenGL context -- and is constructible, with `Window` and `RenderWindow` derived from it.
15
+ `Window` keeps its existing `sfRenderWindow` behaviour; `RenderWindow` is the same renderable
16
+ window under the name that also includes `RenderTarget`. `RenderTexture` includes
17
+ `RenderTarget` too, so a drawable's `#draw` now accepts a `RenderWindow` or a `RenderTexture`
18
+ directly (the legacy `Target` still works). The shared window surface is generated once, in
19
+ `ext/window/window_base.inc`.
20
+ * **RBS signatures are now checked against the code.** `sig/**/*.rbs` declares `SFML::VERSION`, and
21
+ a `Steepfile` plus the `steep` development gem add `rake steep` to type-check `lib/` against the
22
+ signatures. `check.yaml` runs both `rake rbs` and `rake steep`, on `main` and on `release/**`
23
+ PRs, which previously ran no workflow at all.
24
+
25
+ ### Changed
26
+ * `Mouse.position`, `Mouse.set_position` and `Touch.position` dispatch to the
27
+ `*RenderWindow` or `*WindowBase` CSFML entry point depending on whether the argument is a
28
+ Window/RenderWindow or a WindowBase, instead of passing an `sfRenderWindow*` where an
29
+ `sfWindowBase*` is expected.
30
+ * **RBS constructors use `def initialize`.** Every `def self.new` became
31
+ `def initialize: (…) -> void`, the form RubyMine links to `Foo.new` and Steep checks, and which
32
+ matches the C extension, where every constructor is an `initialize`.
33
+
9
34
  ## [0.3.0] - 2026-09-19
10
35
 
11
36
  ### Added
data/README.md CHANGED
@@ -3,13 +3,14 @@
3
3
  Ruby bindings for [SFML 3](https://www.sfml-dev.org/), via its C API, [CSFML](https://github.com/SFML/CSFML).
4
4
 
5
5
  [![Ruby](https://img.shields.io/badge/ruby-3.1%2B-red?style=flat-square)](https://www.ruby-lang.org/)
6
- [![Test](https://github.com/algaves/sfml3.rb/actions/workflows/test.yaml/badge.svg)](https://github.com/algaves/sfml3.rb/actions/workflows/test.yaml)
7
- [![Check](https://github.com/algaves/sfml3.rb/actions/workflows/check.yaml/badge.svg)](https://github.com/algaves/sfml3.rb/actions/workflows/check.yaml)
6
+ [![Test](https://img.shields.io/github/actions/workflow/status/algaves/sfml3.rb/test.yaml?style=flat-square)](https://img.shields.io/github/actions/workflow/status/algaves/sfml3.rb/test.yaml?style=flat-square)
7
+ [![Check](https://img.shields.io/github/actions/workflow/status/algaves/sfml3.rb/check.yaml?style=flat-square)](https://img.shields.io/github/actions/workflow/status/algaves/sfml3.rb/check.yaml?style=flat-square)
8
+ [![Docs](https://img.shields.io/badge/docs-GitHub%20Pages-4c9a2a?style=flat-square)](https://algaves.github.io/sfml3.rb/)
8
9
  [![Gem Version](https://img.shields.io/gem/v/sfml3-rb?style=flat-square)](https://rubygems.org/gems/sfml3-rb)
9
10
  [![Gem Downloads](https://img.shields.io/gem/dt/sfml3-rb?style=flat-square)](https://rubygems.org/gems/sfml3-rb)
10
11
  [![License](https://img.shields.io/badge/license-0BSD-green?style=flat-square)](LICENSE.md)
11
12
 
12
- Latest release: **0.3.0**, bound against **CSFML 3**.
13
+ Latest release: **0.3.1**, bound against **CSFML 3**.
13
14
 
14
15
  ## Features
15
16
 
@@ -20,10 +21,11 @@ Latest release: **0.3.0**, bound against **CSFML 3**.
20
21
  * **A source fallback everywhere else**, which downloads and builds the pinned, checksum-verified
21
22
  dependencies at install time, so the gem works on macOS, ARM and the BSDs out of the box.
22
23
  * **Complete API documentation and types**: every class, module, method and constant is documented
23
- on [rubydoc.info](https://rubydoc.info/gems/sfml3-rb) and covered by RBS signatures shipped in
24
+ on the [docs site](https://algaves.github.io/sfml3.rb/) and covered by RBS signatures shipped in
24
25
  the gem.
25
- * **A close fit to SFML's own model**: classes mirror the C++ types (Window, Texture, Sprite,
26
- Sound, ...) minus the parts that only exist in C++, like `std::string` and exceptions.
26
+ * **A close fit to SFML's own model**: classes mirror the C++ types (WindowBase, Window,
27
+ RenderWindow, Texture, Sprite, Sound, ...) minus the parts that only exist in C++, like
28
+ `std::string` and exceptions.
27
29
 
28
30
  ## Table of Contents
29
31
 
@@ -124,13 +126,30 @@ part of the test suite.
124
126
 
125
127
  ## Documentation
126
128
 
127
- * [API reference](https://rubydoc.info/gems/sfml3-rb) — every class, module, method and constant,
128
- built from the YARD comments in `ext/**/*.c`.
129
- * RBS type signatures (`sig/**/*.rbs`) ship in the gem for IDE completion (Solargraph, RubyMine)
130
- and static type-checking (Sorbet, Steep). Validate them with `rake rbs`.
129
+ * [API reference](https://algaves.github.io/sfml3.rb/) — every class, module, method and constant,
130
+ built from the YARD comments in `ext/**/*.c` and deployed to GitHub Pages by CI. The same docs
131
+ are also generated on [RubyDoc.info](https://rubydoc.info/gems/sfml3-rb).
132
+ * RBS type signatures (`sig/**/*.rbs`) describe the whole API, including the native classes, for
133
+ RBS-aware editors. `rake rbs` validates the signatures and `rake steep` type-checks `lib/`
134
+ against them.
131
135
  * [CHANGELOG.md](CHANGELOG.md) — what changed in each release.
132
136
  * [TODO.md](TODO.md) — module-by-module porting coverage and what is deliberately unbound.
133
137
 
138
+ ### IDE setup (RubyMine)
139
+
140
+ `sig/**/*.rbs` is the only machine-readable description of the API: the native extension cannot be
141
+ introspected, so an editor either reads the signatures or sees nothing. RBS support lives in
142
+ **RubyMine** (and IntelliJ IDEA Ultimate with the Ruby plugin); CLion and other C/C++ IDEs show
143
+ `.rbs` files as plain text.
144
+
145
+ 1. Open the project in RubyMine and point **Settings → Languages & Frameworks → Ruby SDK** at the
146
+ interpreter you build against (Ruby 3.1+). Keep the C sources in CLion/clangd.
147
+ 2. Run `bundle install` so the `rbs` gem (3.2+) is available to that interpreter.
148
+ 3. RubyMine indexes `sig/` automatically; completion, type info (`Ctrl+Shift+P`), parameter info
149
+ and *Navigate → Type Signature* then work for `Window.new` and the rest of the API.
150
+ 4. For a full type check, run `steep check` from *Run anything* (`Ctrl` twice); `Steepfile` points
151
+ it at `lib/` and `sig/`.
152
+
134
153
  ## Development
135
154
 
136
155
  ```sh
@@ -140,6 +159,7 @@ rake test # compile, then run the test suite
140
159
  rake gem # build the source gem into pkg/
141
160
  rake yard # build API docs into doc/
142
161
  rake rbs # validate sig/**/*.rbs
162
+ rake steep # type-check lib/ against sig/**/*.rbs
143
163
  ```
144
164
 
145
165
  `rake githooks:install` points your checkout at the committed `.githooks/` pre-commit hook, which
@@ -155,9 +175,12 @@ side by side: `core/` (CSFML umbrella header, macros, exceptions, UTF-32 convers
155
175
  devices), `graphics/` (shapes, Color, Transform, View, Texture, Text, Shader, the render targets),
156
176
  `audio/` and `network/`. Includes are subsystem-relative, e.g. `#include "graphics/circle.h"`.
157
177
 
158
- Two `.inc` files hold method bodies shared by several classes and are included once per class with
159
- a different macro prefix: `audio/sound_source.inc` (Sound, Music, SoundStream) and
160
- `graphics/render_target.inc` (Window, RenderTexture).
178
+ Three `.inc` files hold method bodies shared by several classes and are included once per class
179
+ with a different macro prefix: `audio/sound_source.inc` (Sound, Music, SoundStream),
180
+ `window/window_base.inc` (WindowBase, Window) and `graphics/render_target.inc` (Window,
181
+ RenderTexture). `SFML::RenderWindow < SFML::Window < SFML::WindowBase` and includes the
182
+ `SFML::RenderTarget` module, so a drawable's `#draw` accepts a `RenderWindow` or a
183
+ `RenderTexture` directly; `SFML::Target` remains as the legacy generic wrapper.
161
184
 
162
185
  Note that mkmf flattens object files to their basenames, so every `.c` filename has to stay
163
186
  unique across the whole tree — and that `$srcs` is baked into the generated Makefile, so after
data/TODO.md CHANGED
@@ -51,9 +51,14 @@ Base module: time, vectors, clocks, streams.
51
51
 
52
52
  OpenGL-based windows, events, input handling.
53
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.
54
+ - [x] **WindowBase** — `SFML::WindowBase` (`ext/window/window_base.c`); wraps `sfWindowBase`, an OS
55
+ window and event queue with no OpenGL context. The methods shared with `Window` are generated
56
+ from `ext/window/window_base.inc`.
57
+ - [x] **Window** — `SFML::Window` (`ext/window/window.c`); wraps `sfRenderWindow` and derives from
58
+ `WindowBase`, overriding every base method with the matching `sfRenderWindow_*` entry point.
59
+ Accepts style, state and `ContextSettings`; exposes min/max size, icon, cursor, native handle,
60
+ settings, display, and the render-target surface. Deliberately still renderable, so existing
61
+ `Window.new(...).clear` code keeps working.
57
62
  - [x] **VideoMode** — `SFML::VideoMode` (`ext/window/video_mode.c`); includes `desktop_mode` and
58
63
  `fullscreen_modes`
59
64
  - [x] **Event** — `SFML::Event` (`ext/window/event.c`, `ext/window/event_name.c`); every payload is
@@ -87,13 +92,16 @@ OpenGL-based windows, events, input handling.
87
92
  - [x] **Drawable** — `SFML::Drawable` mixin (`ext/graphics/drawable.c`)
88
93
  - [x] **RenderStates** — `SFML::RenderState` (`ext/graphics/render_state.c`); blend mode, stencil
89
94
  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`,
95
+ - [x] **RenderTarget** — `SFML::RenderTarget` module (`ext/graphics/target.c`), included by
96
+ `RenderWindow` and `RenderTexture`, so a drawable's `#draw` accepts either directly. The
97
+ methods — `map_pixel_to_coords`, `map_coords_to_pixel`, `push_gl_states`, `pop_gl_states`,
93
98
  `reset_gl_states`, `draw_primitives`, `draw_vertex_buffer_range`, `clear_stencil`,
94
99
  `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)
100
+ `Window` and `RenderTexture` from `ext/graphics/render_target.inc`. `SFML::Target` remains
101
+ as the legacy runtime-dispatch wrapper for `Drawable#draw`.
102
+ - [x] **RenderWindow** — `SFML::RenderWindow` (`ext/graphics/render_window.c`); derives from
103
+ `Window` and includes `RenderTarget`. Creation, events and the window surface come from
104
+ `Window` / `WindowBase`.
97
105
  - [x] **RenderTexture** — `SFML::RenderTexture` (`ext/graphics/render_texture.c`)
98
106
  - [x] **View** — `SFML::View` (`ext/graphics/view.c`); including `View.from_rect` and
99
107
  `scissor`/`scissor=`
@@ -179,9 +187,8 @@ without re-deriving the reasoning each time.
179
187
 
180
188
  | Symbols | Why |
181
189
  | --- | --- |
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. |
190
+ | `sfWindow_*` (~26 functions) | A plain `sfWindow` (an OpenGL context with no render-target surface); `SFML::Window` wraps `sfRenderWindow` and `SFML::WindowBase` wraps `sfWindowBase`. |
191
+ | `sfRenderWindow_create`, `sfWindowBase_create`, `sfText_getString`/`setString`, `sfRenderWindow_setTitle`, `sfWindowBase_setTitle`, `sfFtpDirectoryResponse_getDirectory` | Superseded by their `*Unicode` counterparts; the narrow forms decode through the C locale and mangle non-ASCII. |
185
192
  | `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
193
  | `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
194
  | `sfShape_getPoint`, `sfShape_getPointCount` | `SFML::Shape` reads these from the Ruby subclass, which is where they are defined. |
data/ext/audio/music.c CHANGED
@@ -216,6 +216,11 @@ static VALUE Music_set_loop_points(VALUE self, VALUE rb_span) {
216
216
  #undef SS_FN
217
217
  #undef SS_METHOD
218
218
 
219
+ static VALUE Music_alloc(VALUE klass) {
220
+ (void)klass;
221
+ rb_raise(rb_eNotImpError, "use Music.from_file, Music.from_memory or Music.from_stream");
222
+ }
223
+
219
224
  /* Document-class: SFML::Music
220
225
  * Music streamed from a file, memory buffer or InputStream rather than held
221
226
  * fully decoded in memory, so it's suited to long tracks that would be
@@ -349,6 +354,7 @@ static VALUE Music_set_loop_points(VALUE self, VALUE rb_span) {
349
354
  */
350
355
  void Init_Music(VALUE rb_mSFML) {
351
356
  rb_cMusic = rb_define_class_under(rb_mSFML, "Music", rb_cObject);
357
+ rb_define_alloc_func(rb_cMusic, Music_alloc);
352
358
 
353
359
  rb_define_singleton_method(rb_cMusic, "from_file", Music_from_file, 1);
354
360
  rb_define_singleton_method(rb_cMusic, "from_memory", Music_from_memory, 1);
data/ext/audio/sound.c CHANGED
@@ -39,7 +39,11 @@ static void Sound_free(void* ptr) {
39
39
  thread's effect_processor_acquire could reuse the slot number for an
40
40
  unrelated new source while this source's callback might still be
41
41
  executing against it. */
42
- rb_thread_call_without_gvl(Sound_destroy_without_gvl, sound->source.handle, RUBY_UBF_IO, NULL);
42
+ if (sound->source.handle != NULL) {
43
+ rb_thread_call_without_gvl(Sound_destroy_without_gvl, sound->source.handle, RUBY_UBF_IO,
44
+ NULL);
45
+ }
46
+
43
47
  effect_processor_release(sound->source.effect_slot);
44
48
  free(sound);
45
49
  }
@@ -68,6 +72,25 @@ static VALUE Sound_wrap(VALUE klass, sfSound* handle, VALUE rb_buffer) {
68
72
  return TypedData_Wrap_Struct(klass, &Sound_data_type, ptr);
69
73
  }
70
74
 
75
+ static VALUE Sound_initialize_copy(VALUE self, VALUE other) {
76
+ (void)other;
77
+ rb_raise(rb_eTypeError, "can't copy a %s", rb_obj_classname(self));
78
+ }
79
+
80
+ static VALUE Sound_alloc(VALUE klass) {
81
+ Sound* ptr = malloc(sizeof(Sound));
82
+
83
+ if (ptr == NULL) {
84
+ rb_raise(rb_eNoMemError, "failed to allocate sound");
85
+ }
86
+
87
+ ptr->source.handle = NULL;
88
+ ptr->source.effect_slot = -1;
89
+ ptr->rb_buffer = Qnil;
90
+
91
+ return TypedData_Wrap_Struct(klass, &Sound_data_type, ptr);
92
+ }
93
+
71
94
  /* call-seq:
72
95
  * Sound.new(buffer) -> Sound
73
96
  *
@@ -76,12 +99,26 @@ static VALUE Sound_wrap(VALUE klass, sfSound* handle, VALUE rb_buffer) {
76
99
  * @return [Sound]
77
100
  * @raise [ArgumentError] if +buffer+ is not a SoundBuffer
78
101
  */
79
- static VALUE Sound_new(VALUE klass, VALUE rb_buffer) {
102
+ static VALUE Sound_initialize(VALUE self, VALUE rb_buffer) {
103
+ Sound* sound;
104
+ sfSound* handle;
105
+
80
106
  if (!rb_obj_is_kind_of(rb_buffer, Get_Klass_SoundBuffer())) {
81
107
  raise_invalid_argument_class(Get_Klass_SoundBuffer());
82
108
  }
83
109
 
84
- return Sound_wrap(klass, sfSound_create(Get_SoundBuffer_Struct(rb_buffer)), rb_buffer);
110
+ sound = (Sound*)Get_Sound_Struct(self);
111
+
112
+ handle = sfSound_create(Get_SoundBuffer_Struct(rb_buffer));
113
+
114
+ if (handle == NULL) {
115
+ rb_raise(rb_eRuntimeError, "failed to create sound");
116
+ }
117
+
118
+ sound->source.handle = handle;
119
+ sound->rb_buffer = rb_buffer;
120
+
121
+ return self;
85
122
  }
86
123
 
87
124
  /* call-seq: copy -> Sound
@@ -289,7 +326,9 @@ static VALUE Sound_set_buffer(VALUE self, VALUE rb_buffer) {
289
326
  void Init_Sound(VALUE rb_mSFML) {
290
327
  rb_cSound = rb_define_class_under(rb_mSFML, "Sound", rb_cObject);
291
328
 
292
- rb_define_singleton_method(rb_cSound, "new", Sound_new, 1);
329
+ rb_define_alloc_func(rb_cSound, Sound_alloc);
330
+ rb_define_method(rb_cSound, "initialize", Sound_initialize, 1);
331
+ rb_define_private_method(rb_cSound, "initialize_copy", Sound_initialize_copy, 1);
293
332
 
294
333
  rb_define_method(rb_cSound, "copy", Sound_copy, 0);
295
334
  rb_define_method(rb_cSound, "buffer", Sound_get_buffer, 0);
@@ -381,6 +381,13 @@ static VALUE SoundBuffer_duration(VALUE self) {
381
381
  return time_to_rb(sfSoundBuffer_getDuration(Get_SoundBuffer_Struct(self)));
382
382
  }
383
383
 
384
+ static VALUE SoundBuffer_alloc(VALUE klass) {
385
+ (void)klass;
386
+ rb_raise(rb_eNotImpError,
387
+ "use SoundBuffer.from_file, SoundBuffer.from_memory, SoundBuffer.from_stream or "
388
+ "SoundBuffer.from_samples");
389
+ }
390
+
384
391
  /* Document-class: SFML::SoundBuffer
385
392
  * Audio samples held fully decoded in memory, ready to be played through one
386
393
  * or more Sound instances (a single buffer may back several simultaneous
@@ -389,6 +396,7 @@ static VALUE SoundBuffer_duration(VALUE self) {
389
396
  */
390
397
  void Init_SoundBuffer(VALUE rb_mSFML) {
391
398
  rb_cSoundBuffer = rb_define_class_under(rb_mSFML, "SoundBuffer", rb_cObject);
399
+ rb_define_alloc_func(rb_cSoundBuffer, SoundBuffer_alloc);
392
400
 
393
401
  rb_define_singleton_method(rb_cSoundBuffer, "from_file", SoundBuffer_from_file, 1);
394
402
  rb_define_singleton_method(rb_cSoundBuffer, "from_memory", SoundBuffer_from_memory, 1);
@@ -41,6 +41,10 @@ static VALUE SoundBufferRecorder_wrap(VALUE klass, sfSoundBufferRecorder* handle
41
41
  return TypedData_Wrap_Struct(klass, &SoundBufferRecorder_data_type, ptr);
42
42
  }
43
43
 
44
+ static VALUE SoundBufferRecorder_alloc(VALUE klass) {
45
+ return SoundBufferRecorder_wrap(klass, sfSoundBufferRecorder_create());
46
+ }
47
+
44
48
  /* call-seq:
45
49
  * SoundBufferRecorder.new -> SoundBufferRecorder
46
50
  *
@@ -49,8 +53,8 @@ static VALUE SoundBufferRecorder_wrap(VALUE klass, sfSoundBufferRecorder* handle
49
53
  * @return [SoundBufferRecorder]
50
54
  * @raise [RuntimeError] if no capture device is available
51
55
  */
52
- static VALUE SoundBufferRecorder_new(VALUE klass) {
53
- return SoundBufferRecorder_wrap(klass, sfSoundBufferRecorder_create());
56
+ static VALUE SoundBufferRecorder_initialize(VALUE self) {
57
+ return self;
54
58
  }
55
59
 
56
60
  /* call-seq:
@@ -151,7 +155,8 @@ static VALUE SoundBufferRecorder_set_channel_count(VALUE self, VALUE rb_count) {
151
155
  void Init_SoundBufferRecorder(VALUE rb_mSFML) {
152
156
  rb_cSoundBufferRecorder = rb_define_class_under(rb_mSFML, "SoundBufferRecorder", rb_cObject);
153
157
 
154
- rb_define_singleton_method(rb_cSoundBufferRecorder, "new", SoundBufferRecorder_new, 0);
158
+ rb_define_alloc_func(rb_cSoundBufferRecorder, SoundBufferRecorder_alloc);
159
+ rb_define_method(rb_cSoundBufferRecorder, "initialize", SoundBufferRecorder_initialize, 0);
155
160
 
156
161
  rb_define_method(rb_cSoundBufferRecorder, "start", SoundBufferRecorder_start, 1);
157
162
  rb_define_method(rb_cSoundBufferRecorder, "stop", SoundBufferRecorder_stop, 0);
@@ -154,6 +154,23 @@ static void SoundRecorder_on_stop(void* userData) {
154
154
  }
155
155
  }
156
156
 
157
+ static VALUE SoundRecorder_alloc(VALUE klass) {
158
+ SoundRecorder* ptr = malloc(sizeof(SoundRecorder));
159
+ VALUE self;
160
+
161
+ if (ptr == NULL) {
162
+ rb_raise(rb_eNoMemError, "failed to allocate sound recorder");
163
+ }
164
+
165
+ ptr->rb_self = Qnil;
166
+ ptr->handle = NULL;
167
+
168
+ self = TypedData_Wrap_Struct(klass, &SoundRecorder_data_type, ptr);
169
+ ptr->rb_self = self;
170
+
171
+ return self;
172
+ }
173
+
157
174
  /* call-seq:
158
175
  * SoundRecorder.new -> SoundRecorder
159
176
  *
@@ -167,21 +184,13 @@ static void SoundRecorder_on_stop(void* userData) {
167
184
  * @raise [NotImplementedError] if the subclass does not define #on_process
168
185
  * @raise [RuntimeError] if no capture device is available
169
186
  */
170
- static VALUE SoundRecorder_new(VALUE klass) {
187
+ static VALUE SoundRecorder_initialize(VALUE self) {
171
188
  SoundRecorder* ptr;
172
189
  sfSoundRecorder* handle;
173
- VALUE self;
174
-
175
- ptr = malloc(sizeof(SoundRecorder));
176
- ptr->rb_self = Qnil;
177
- ptr->handle = NULL;
178
190
 
179
- self = TypedData_Wrap_Struct(klass, &SoundRecorder_data_type, ptr);
180
- ptr->rb_self = self;
191
+ TypedData_Get_Struct(self, SoundRecorder, &SoundRecorder_data_type, ptr);
181
192
 
182
193
  if (!rb_respond_to(self, rb_intern("on_process"))) {
183
- /* Ruby already owns the wrapper; SoundRecorder_free handles the NULL
184
- handle, so let GC clean up after the raise. */
185
194
  rb_raise(rb_eNotImpError, "subclass must define #on_process");
186
195
  }
187
196
 
@@ -360,7 +369,8 @@ static VALUE SoundRecorder_channel_map(VALUE self) {
360
369
  void Init_SoundRecorder(VALUE rb_mSFML) {
361
370
  rb_cSoundRecorder = rb_define_class_under(rb_mSFML, "SoundRecorder", rb_cObject);
362
371
 
363
- rb_define_singleton_method(rb_cSoundRecorder, "new", SoundRecorder_new, 0);
372
+ rb_define_alloc_func(rb_cSoundRecorder, SoundRecorder_alloc);
373
+ rb_define_method(rb_cSoundRecorder, "initialize", SoundRecorder_initialize, 0);
364
374
  rb_define_singleton_method(rb_cSoundRecorder, "available?", SoundRecorder_available, 0);
365
375
  rb_define_singleton_method(rb_cSoundRecorder, "available_devices",
366
376
  SoundRecorder_available_devices, 0);
@@ -29,6 +29,18 @@ static VALUE SoundSourceCone_wrap(sfSoundSourceCone cone) {
29
29
  return TypedData_Wrap_Struct(rb_cSoundSourceCone, &SoundSourceCone_data_type, ptr);
30
30
  }
31
31
 
32
+ static VALUE SoundSourceCone_alloc(VALUE klass) {
33
+ SoundSourceCone* ptr = malloc(sizeof(SoundSourceCone));
34
+
35
+ if (ptr == NULL) {
36
+ rb_raise(rb_eNoMemError, "failed to allocate sound source cone");
37
+ }
38
+
39
+ ptr->cone = (sfSoundSourceCone){0.0f, 0.0f, 0.0f};
40
+
41
+ return TypedData_Wrap_Struct(klass, &SoundSourceCone_data_type, ptr);
42
+ }
43
+
32
44
  /* call-seq:
33
45
  * SoundSourceCone.new(inner_angle, outer_angle, outer_gain) -> SoundSourceCone
34
46
  *
@@ -36,12 +48,14 @@ static VALUE SoundSourceCone_wrap(sfSoundSourceCone cone) {
36
48
  *
37
49
  * @return [SoundSourceCone]
38
50
  */
39
- static VALUE SoundSourceCone_new(VALUE klass, VALUE rb_inner, VALUE rb_outer, VALUE rb_gain) {
51
+ static VALUE SoundSourceCone_initialize(VALUE self, VALUE rb_inner, VALUE rb_outer, VALUE rb_gain) {
40
52
  sfSoundSourceCone cone = {.innerAngle = (float)NUM2DBL(rb_inner),
41
53
  .outerAngle = (float)NUM2DBL(rb_outer),
42
54
  .outerGain = (float)NUM2DBL(rb_gain)};
43
55
 
44
- return SoundSourceCone_wrap(cone);
56
+ ((SoundSourceCone*)Get_SoundSourceCone_Struct(self))->cone = cone;
57
+
58
+ return self;
45
59
  }
46
60
 
47
61
  /* call-seq: inner_angle -> Float
@@ -172,7 +186,8 @@ static VALUE SoundSourceCone_eql(VALUE self, VALUE rb_other) {
172
186
  void Init_SoundSourceCone(VALUE rb_mSFML) {
173
187
  rb_cSoundSourceCone = rb_define_class_under(rb_mSFML, "SoundSourceCone", rb_cObject);
174
188
 
175
- rb_define_singleton_method(rb_cSoundSourceCone, "new", SoundSourceCone_new, 3);
189
+ rb_define_alloc_func(rb_cSoundSourceCone, SoundSourceCone_alloc);
190
+ rb_define_method(rb_cSoundSourceCone, "initialize", SoundSourceCone_initialize, 3);
176
191
 
177
192
  rb_define_method(rb_cSoundSourceCone, "inner_angle", SoundSourceCone_get_inner_angle, 0);
178
193
  rb_define_method(rb_cSoundSourceCone, "outer_angle", SoundSourceCone_get_outer_angle, 0);
@@ -193,6 +193,26 @@ static void SoundStream_on_seek(sfTime time, void* userData) {
193
193
  run_on_ruby_thread(SoundStream_seek_run, &ctx, SOUND_STREAM_CALLBACK_TIMEOUT_MS);
194
194
  }
195
195
 
196
+ static VALUE SoundStream_alloc(VALUE klass) {
197
+ SoundStream* ptr = malloc(sizeof(SoundStream));
198
+ VALUE self;
199
+
200
+ if (ptr == NULL) {
201
+ rb_raise(rb_eNoMemError, "failed to allocate sound stream");
202
+ }
203
+
204
+ ptr->source.handle = NULL;
205
+ ptr->source.effect_slot = -1;
206
+ ptr->rb_self = Qnil;
207
+ ptr->samples = NULL;
208
+ ptr->samples_capacity = 0;
209
+
210
+ self = TypedData_Wrap_Struct(klass, &SoundStream_data_type, ptr);
211
+ ptr->rb_self = self;
212
+
213
+ return self;
214
+ }
215
+
196
216
  /* call-seq:
197
217
  * SoundStream.new(channel_count, sample_rate) -> SoundStream
198
218
  * SoundStream.new(channel_count, sample_rate, channel_map) -> SoundStream
@@ -206,14 +226,15 @@ static void SoundStream_on_seek(sfTime time, void* userData) {
206
226
  * @raise [NotImplementedError] if the subclass does not define #on_get_data
207
227
  * @raise [RuntimeError] if the underlying stream could not be created
208
228
  */
209
- static VALUE SoundStream_new(int argc, VALUE* argv, VALUE klass) {
229
+ static VALUE SoundStream_initialize(int argc, VALUE* argv, VALUE self) {
210
230
  VALUE rb_channel_count, rb_sample_rate, rb_channel_map;
211
231
  sfSoundChannel* channel_map = NULL;
212
232
  size_t channel_map_size = 0;
213
233
  SoundStream* ptr;
214
- VALUE self;
215
234
  long i;
216
235
 
236
+ TypedData_Get_Struct(self, SoundStream, &SoundStream_data_type, ptr);
237
+
217
238
  rb_scan_args(argc, argv, "21", &rb_channel_count, &rb_sample_rate, &rb_channel_map);
218
239
 
219
240
  if (!NIL_P(rb_channel_map)) {
@@ -229,19 +250,7 @@ static VALUE SoundStream_new(int argc, VALUE* argv, VALUE klass) {
229
250
  }
230
251
  }
231
252
 
232
- ptr = malloc(sizeof(SoundStream));
233
- ptr->source.handle = NULL;
234
- ptr->source.effect_slot = -1;
235
- ptr->rb_self = Qnil;
236
- ptr->samples = NULL;
237
- ptr->samples_capacity = 0;
238
-
239
- self = TypedData_Wrap_Struct(klass, &SoundStream_data_type, ptr);
240
- ptr->rb_self = self;
241
-
242
253
  if (!rb_respond_to(self, rb_intern("on_get_data"))) {
243
- /* The wrapper is already owned by Ruby; its dfree tolerates a NULL
244
- handle, so raising here just leaves it to be collected. */
245
254
  free(channel_map);
246
255
  rb_raise(rb_eNotImpError, "subclass must define #on_get_data");
247
256
  }
@@ -447,7 +456,8 @@ static VALUE SoundStream_channel_map(VALUE self) {
447
456
  void Init_SoundStream(VALUE rb_mSFML) {
448
457
  rb_cSoundStream = rb_define_class_under(rb_mSFML, "SoundStream", rb_cObject);
449
458
 
450
- rb_define_singleton_method(rb_cSoundStream, "new", SoundStream_new, -1);
459
+ rb_define_alloc_func(rb_cSoundStream, SoundStream_alloc);
460
+ rb_define_method(rb_cSoundStream, "initialize", SoundStream_initialize, -1);
451
461
 
452
462
  rb_define_method(rb_cSoundStream, "channel_count", SoundStream_channel_count, 0);
453
463
  rb_define_method(rb_cSoundStream, "sample_rate", SoundStream_sample_rate, 0);
data/ext/ext.c CHANGED
@@ -22,6 +22,7 @@
22
22
  #include "graphics/target.h"
23
23
  #include "graphics/render_state.h"
24
24
  #include "window/event.h"
25
+ #include "window/window_base.h"
25
26
  #include "window/window.h"
26
27
  #include "window/keyboard.h"
27
28
  #include "window/mouse.h"
@@ -47,6 +48,7 @@
47
48
  #include "graphics/font.h"
48
49
  #include "graphics/text.h"
49
50
  #include "graphics/render_texture.h"
51
+ #include "graphics/render_window.h"
50
52
  #include "graphics/shader.h"
51
53
  #include "audio/effect_processor.h"
52
54
  #include "audio/audio_enums.h"
@@ -127,7 +129,9 @@ void Init_sfml_ext(void) {
127
129
  Init_Event(rb_mExt);
128
130
  Init_VideoMode(rb_mExt);
129
131
  Init_View(rb_mExt);
132
+ Init_WindowBase(rb_mExt);
130
133
  Init_Window(rb_mExt);
134
+ Init_RenderWindow(rb_mExt);
131
135
  Init_Keyboard(rb_mExt);
132
136
  Init_Mouse(rb_mExt);
133
137
  Init_Joystick(rb_mExt);
@@ -80,6 +80,18 @@ static sfBlendEquation equation_from_rb(VALUE rb_equation) {
80
80
  return sfBlendEquationAdd;
81
81
  }
82
82
 
83
+ static VALUE BlendMode_alloc(VALUE klass) {
84
+ BlendMode* ptr = malloc(sizeof(BlendMode));
85
+
86
+ if (ptr == NULL) {
87
+ rb_raise(rb_eNoMemError, "failed to allocate blend mode");
88
+ }
89
+
90
+ ptr->mode = sfBlendAlpha;
91
+
92
+ return TypedData_Wrap_Struct(klass, &BlendMode_data_type, ptr);
93
+ }
94
+
83
95
  /* call-seq:
84
96
  * BlendMode.new -> BlendMode(ALPHA)
85
97
  * BlendMode.new(color_src_factor, color_dst_factor, color_equation, alpha_src_factor,
@@ -95,9 +107,7 @@ static sfBlendEquation equation_from_rb(VALUE rb_equation) {
95
107
  * @return [BlendMode]
96
108
  * @raise [ArgumentError] if given an argument count other than 0 or 6
97
109
  */
98
- static VALUE BlendMode_new(int argc, VALUE* argv, VALUE klass) {
99
- VALUE self;
100
- BlendMode* ptr;
110
+ static VALUE BlendMode_initialize(int argc, VALUE* argv, VALUE self) {
101
111
  sfBlendMode mode = sfBlendAlpha;
102
112
 
103
113
  if (argc == 6) {
@@ -108,10 +118,7 @@ static VALUE BlendMode_new(int argc, VALUE* argv, VALUE klass) {
108
118
  raise_invalid_arguments_excepted(6, argc);
109
119
  }
110
120
 
111
- ptr = malloc(sizeof(BlendMode));
112
- ptr->mode = mode;
113
-
114
- self = TypedData_Wrap_Struct(klass, &BlendMode_data_type, ptr);
121
+ ((BlendMode*)Get_BlendMode_Struct(self))->mode = mode;
115
122
 
116
123
  return self;
117
124
  }
@@ -310,7 +317,8 @@ static VALUE BlendMode_eql(VALUE self, VALUE rb_other) {
310
317
  void Init_BlendMode(VALUE rb_mSFML) {
311
318
  rb_cBlendMode = rb_define_class_under(rb_mSFML, "BlendMode", rb_cObject);
312
319
 
313
- rb_define_singleton_method(rb_cBlendMode, "new", BlendMode_new, -1);
320
+ rb_define_alloc_func(rb_cBlendMode, BlendMode_alloc);
321
+ rb_define_method(rb_cBlendMode, "initialize", BlendMode_initialize, -1);
314
322
 
315
323
  rb_define_method(rb_cBlendMode, "color_src_factor", BlendMode_get_color_src_factor, 0);
316
324
  rb_define_method(rb_cBlendMode, "color_dst_factor", BlendMode_get_color_dst_factor, 0);