sfml3-rb 0.2.2 → 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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +9 -0
  3. data/CHANGELOG.md +67 -16
  4. data/README.md +123 -32
  5. data/TODO.md +18 -11
  6. data/ext/audio/audio_enums.c +58 -20
  7. data/ext/audio/listener.c +89 -6
  8. data/ext/audio/music.c +212 -5
  9. data/ext/audio/sound.c +218 -9
  10. data/ext/audio/sound_buffer.c +197 -47
  11. data/ext/audio/sound_buffer_recorder.c +90 -18
  12. data/ext/audio/sound_recorder.c +188 -58
  13. data/ext/audio/sound_source_cone.c +127 -33
  14. data/ext/audio/sound_stream.c +204 -21
  15. data/ext/core/exceptions.c +10 -8
  16. data/ext/core/unicode.c +12 -2
  17. data/ext/ext.c +8 -0
  18. data/ext/graphics/blend_mode.c +218 -60
  19. data/ext/graphics/circle.c +300 -16
  20. data/ext/graphics/color.c +253 -94
  21. data/ext/graphics/drawable.c +16 -4
  22. data/ext/graphics/font.c +144 -25
  23. data/ext/graphics/glyph.c +38 -8
  24. data/ext/graphics/image.c +205 -41
  25. data/ext/graphics/polygon.c +317 -30
  26. data/ext/graphics/rect.c +225 -60
  27. data/ext/graphics/rectangle.c +307 -35
  28. data/ext/graphics/render_state.c +124 -12
  29. data/ext/graphics/render_texture.c +191 -14
  30. data/ext/graphics/render_window.c +55 -0
  31. data/ext/graphics/render_window.h +10 -0
  32. data/ext/graphics/shader.c +289 -17
  33. data/ext/graphics/shape.c +265 -32
  34. data/ext/graphics/sprite.c +245 -30
  35. data/ext/graphics/stencil_mode.c +163 -46
  36. data/ext/graphics/target.c +121 -43
  37. data/ext/graphics/target.h +31 -17
  38. data/ext/graphics/text.c +339 -19
  39. data/ext/graphics/texture.c +291 -6
  40. data/ext/graphics/transform.c +178 -5
  41. data/ext/graphics/transformable.c +127 -12
  42. data/ext/graphics/vertex.c +98 -6
  43. data/ext/graphics/vertex_array.c +142 -21
  44. data/ext/graphics/vertex_buffer.c +154 -12
  45. data/ext/graphics/view.c +140 -6
  46. data/ext/network/ftp.c +278 -12
  47. data/ext/network/http.c +179 -44
  48. data/ext/network/ip_address.c +127 -35
  49. data/ext/network/network_enums.c +248 -141
  50. data/ext/network/packet.c +280 -41
  51. data/ext/network/socket_selector.c +98 -21
  52. data/ext/network/tcp_listener.c +82 -18
  53. data/ext/network/tcp_socket.c +135 -27
  54. data/ext/network/udp_socket.c +129 -28
  55. data/ext/ports.rb +7 -0
  56. data/ext/system/buffer.c +47 -16
  57. data/ext/system/clock.c +77 -12
  58. data/ext/system/input_stream.c +113 -51
  59. data/ext/system/sleep.c +10 -2
  60. data/ext/system/time.c +182 -40
  61. data/ext/system/vec2.c +166 -39
  62. data/ext/system/vec3.c +177 -39
  63. data/ext/window/clipboard.c +47 -17
  64. data/ext/window/context.c +66 -13
  65. data/ext/window/context_settings.c +132 -42
  66. data/ext/window/cursor.c +45 -9
  67. data/ext/window/event.c +164 -53
  68. data/ext/window/joystick.c +69 -3
  69. data/ext/window/keyboard.c +168 -116
  70. data/ext/window/mouse.c +64 -13
  71. data/ext/window/sensor.c +28 -2
  72. data/ext/window/touch.c +32 -12
  73. data/ext/window/video_mode.c +93 -31
  74. data/ext/window/vulkan.c +34 -6
  75. data/ext/window/window.c +228 -229
  76. data/ext/window/window.h +4 -4
  77. data/ext/window/window_base.c +251 -0
  78. data/ext/window/window_base.h +43 -0
  79. data/ext/window/window_base.inc +236 -0
  80. data/lib/sfml/version.rb +1 -1
  81. data/sig/audio/audio_enums.rbs +30 -0
  82. data/sig/audio/listener.rbs +16 -0
  83. data/sig/audio/music.rbs +56 -0
  84. data/sig/audio/sound.rbs +51 -0
  85. data/sig/audio/sound_buffer.rbs +17 -0
  86. data/sig/audio/sound_buffer_recorder.rbs +14 -0
  87. data/sig/audio/sound_recorder.rbs +17 -0
  88. data/sig/audio/sound_source_cone.rbs +14 -0
  89. data/sig/audio/sound_stream.rbs +51 -0
  90. data/sig/graphics/blend_mode.rbs +29 -0
  91. data/sig/graphics/circle.rbs +44 -0
  92. data/sig/graphics/color.rbs +39 -0
  93. data/sig/graphics/drawable.rbs +5 -0
  94. data/sig/graphics/font.rbs +20 -0
  95. data/sig/graphics/glyph.rbs +7 -0
  96. data/sig/graphics/image.rbs +22 -0
  97. data/sig/graphics/polygon.rbs +42 -0
  98. data/sig/graphics/rect.rbs +33 -0
  99. data/sig/graphics/rectangle.rbs +42 -0
  100. data/sig/graphics/render_state.rbs +21 -0
  101. data/sig/graphics/render_target.rbs +18 -0
  102. data/sig/graphics/render_texture.rbs +39 -0
  103. data/sig/graphics/render_window.rbs +7 -0
  104. data/sig/graphics/shader.rbs +39 -0
  105. data/sig/graphics/shape.rbs +38 -0
  106. data/sig/graphics/sprite.rbs +34 -0
  107. data/sig/graphics/stencil_mode.rbs +18 -0
  108. data/sig/graphics/target.rbs +8 -0
  109. data/sig/graphics/text.rbs +48 -0
  110. data/sig/graphics/texture.rbs +34 -0
  111. data/sig/graphics/transform.rbs +37 -0
  112. data/sig/graphics/transformable.rbs +25 -0
  113. data/sig/graphics/vertex.rbs +17 -0
  114. data/sig/graphics/vertex_array.rbs +17 -0
  115. data/sig/graphics/vertex_buffer.rbs +22 -0
  116. data/sig/graphics/view.rbs +23 -0
  117. data/sig/network/ftp.rbs +46 -0
  118. data/sig/network/http.rbs +27 -0
  119. data/sig/network/ip_address.rbs +22 -0
  120. data/sig/network/network_enums.rbs +95 -0
  121. data/sig/network/packet.rbs +40 -0
  122. data/sig/network/socket_selector.rbs +14 -0
  123. data/sig/network/tcp_listener.rbs +13 -0
  124. data/sig/network/tcp_socket.rbs +18 -0
  125. data/sig/network/udp_socket.rbs +17 -0
  126. data/sig/sfml/version.rbs +3 -0
  127. data/sig/system/buffer.rbs +11 -0
  128. data/sig/system/clock.rbs +13 -0
  129. data/sig/system/input_stream.rbs +7 -0
  130. data/sig/system/sleep.rbs +3 -0
  131. data/sig/system/time.rbs +28 -0
  132. data/sig/system/vec2.rbs +26 -0
  133. data/sig/system/vec3.rbs +27 -0
  134. data/sig/window/clipboard.rbs +8 -0
  135. data/sig/window/context.rbs +11 -0
  136. data/sig/window/context_settings.rbs +20 -0
  137. data/sig/window/cursor.rbs +6 -0
  138. data/sig/window/event.rbs +20 -0
  139. data/sig/window/joystick.rbs +15 -0
  140. data/sig/window/keyboard.rbs +10 -0
  141. data/sig/window/mouse.rbs +9 -0
  142. data/sig/window/sensor.rbs +8 -0
  143. data/sig/window/touch.rbs +6 -0
  144. data/sig/window/video_mode.rbs +16 -0
  145. data/sig/window/vulkan.rbs +7 -0
  146. data/sig/window/window.rbs +34 -0
  147. data/sig/window/window_base.rbs +33 -0
  148. metadata +76 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc41a984a71094455d29e95d4c529a1e228e5c5442333aca6eb57b9b7c9d3f5e
4
- data.tar.gz: 835286043ece71ba5dc8c1790cdd6ab0370ae0924823771e03a811cd8acba1af
3
+ metadata.gz: f98dbb27451c7780e8dbd1ab1f677a9590d5d42b55db97cad407eee15fdd3ef8
4
+ data.tar.gz: 6ffe42d972c73f477b642ad6e1bfc1e9c4d5585f40bd9a9402bc08dfb03834ef
5
5
  SHA512:
6
- metadata.gz: f75f7ce8244a36a0a8e93903ea0d323513753cfdf3e82f79364b3081a3d5ac651609897a0d4911c2509aac1e5107c37ab9ea9341eede85bb630a05228273ed3d
7
- data.tar.gz: 68a989c7d4e1147cd5585bf8db85be15c3c1419bbefaeca95791fa37575900d8b841d1b33dd5009f9611e3b5a365b7410c7687ca8eecf96d188a05feb6de269f
6
+ metadata.gz: 7fde721522979c1446f14a18ce2433dcf6c8a1009c40cedb6771926f87a759bee9f44f338a47d8205336e51624ee80d919a18ea7172e9815c434194e814459c9
7
+ data.tar.gz: c2fde3b6034c72f50cf8d480a77bee01f79d505b510ca7b354c618bcf0f694307776d2409c00ab0b173253c3c797b2498e5f0c50cbd94256d902f7f96c816d91
data/.yardopts ADDED
@@ -0,0 +1,9 @@
1
+ --markup markdown
2
+ --output-dir doc
3
+ --title sfml3-rb
4
+ --readme README.md
5
+ ext/**/*.c
6
+ -
7
+ CHANGELOG.md
8
+ TODO.md
9
+ LICENSE.md
data/CHANGELOG.md CHANGED
@@ -6,26 +6,77 @@ 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
+
9
11
  ### Added
10
- * **Full YARD API documentation and RBS type signatures**, covering every
11
- bound class, module, method and constant. Doc comments live in-place in
12
- the `ext/**/*.c` sources (YARD's C parser, the same convention RDoc uses),
13
- so they're published automatically to
14
- [rubydoc.info](https://rubydoc.info/gems/sfml3-rb) on release; `rake yard`
15
- builds them locally into `doc/`. `sig/**/*.rbs` ships in the gem alongside
16
- the extension sources, for IDE completion (Solargraph, RubyMine) and
17
- static type-checking (Sorbet, Steep). Run `rake rbs` to validate the
18
- signatures.
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`.
19
33
 
20
- ## [0.2.2]
34
+ ## [0.3.0] - 2026-09-19
35
+
36
+ ### Added
37
+ * **Four more experimental binary-gem targets**: `aarch64-linux-musl`, `arm-linux-gnu`
38
+ (ARMv7 hard-float), `arm-linux-musl`, and `aarch64-mingw-ucrt` (64-bit Windows on ARM).
39
+ All four ride the same rake-compiler-dock cross-compilation this project already uses for
40
+ its other targets; none has been run on its target hardware yet, so all are
41
+ `experimental` in `publish.yaml` like `aarch64-linux-gnu`, `x86_64-darwin` and
42
+ `arm64-darwin` already were. `script/provision.sh` gained matching cases for the two new
43
+ glibc/musl Linux targets.
44
+ * `rakelib/package.rake`'s `EXPECTED_ABIS` gained an entry for `aarch64-mingw-ucrt`: its
45
+ rake-compiler-dock image carries no cross Ruby older than 3.4, so that gem has no floor
46
+ below it — the same kind of upstream gap `x86-mingw32` already has at the top end.
47
+ * `aarch64-linux-gnu` is now additionally run — not just cross-compiled — by a new
48
+ `test-arm64.yaml` workflow, on GitHub's hosted `ubuntu-24.04-arm` runner. It stays
49
+ `experimental` in `publish.yaml` for now; this is the evidence that will eventually
50
+ justify dropping that flag.
51
+ * ARMv6 (32-bit), RISC-V (rv64gc), PowerPC (ppc64le), and any BSD are deliberately not
52
+ covered by a binary gem: rake-compiler-dock ships no cross-compilation image for any of
53
+ them, and building custom cross-toolchain infrastructure for them is a much larger,
54
+ separate undertaking. The source-gem fallback remains the only path there, unchanged.
21
55
 
22
56
  ### Fixed
23
- * **The macOS binary gems now build.** SFML 3.0.2 defaults
24
- `CMAKE_OSX_DEPLOYMENT_TARGET` to 13.0, but the osxcross SDK in the
25
- rake-compiler-dock image is 11.1 and rejects a newer target, so both Darwin
26
- builds aborted while configuring SFML. The ports toolchain now pins the
27
- deployment target to macOS 11.0, which is the floor Apple Silicon requires
28
- anyway.
57
+ * **A GC race in the audio bindings aborted the process on arm64.** `Sound`, `Music` and
58
+ `SoundStream` released the GVL inside their `dfree`, but their data types were marked
59
+ `RUBY_TYPED_FREE_IMMEDIATELY`, so the free could run *during* garbage collection: another
60
+ thread (Ruby's `Timeout` thread, in the test suite) could then allocate while GC was mid-cycle
61
+ and Ruby aborted with "object allocation during garbage collection phase". Dropping the flag
62
+ defers the free to a safe point; releasing the GVL is still what keeps `sf*_destroy` from
63
+ deadlocking against the audio thread.
64
+
65
+ ### Documentation
66
+ * **Every public class, module, method and constant is documented**, with the
67
+ result rendered on [rubydoc.info](https://rubydoc.info/gems/sfml3-rb). The
68
+ comments live beside each binding in `ext/**/*.c`; `rake yard` builds them
69
+ into `doc/`. Previously the comments carried `call-seq` and `@return` tags
70
+ but little prose, so rubydoc.info showed a signature with a blank
71
+ description; every such method now has a sentence of its own.
72
+ * RBS type signatures for the same surface ship in `sig/**/*.rbs`, for IDE
73
+ completion (Solargraph, RubyMine) and static checking (Sorbet, Steep);
74
+ `rake rbs` validates them.
75
+ * `.yardopts` ships in the gem, so rubydoc.info generates with the same title,
76
+ README and extra files as `rake yard`.
77
+ * `rake doc:undoc` fails when a method is left without a description, so the
78
+ coverage cannot regress silently (`yard stats` uses `blank?` and passes for
79
+ tag-only comments, which is how the blanks went unnoticed).
29
80
 
30
81
  ## [0.2.1]
31
82
 
data/README.md CHANGED
@@ -2,17 +2,42 @@
2
2
 
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
- ## Status
6
-
7
- Latest release: **0.2.2**. Bound against **CSFML 3**. See [TODO.md](TODO.md) for which parts of the
8
- SFML 3 API are ported so far, and [CHANGELOG.md](CHANGELOG.md) for what changed recently.
9
-
10
- API docs (every class, module, method and constant) are on
11
- [rubydoc.info](https://rubydoc.info/gems/sfml3-rb). The gem also ships RBS type signatures
12
- (`sig/**/*.rbs`) alongside the extension sources, for IDE completion (Solargraph, RubyMine) and
13
- static type-checking (Sorbet, Steep).
14
-
15
- ## Install
5
+ [![Ruby](https://img.shields.io/badge/ruby-3.1%2B-red?style=flat-square)](https://www.ruby-lang.org/)
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/)
9
+ [![Gem Version](https://img.shields.io/gem/v/sfml3-rb?style=flat-square)](https://rubygems.org/gems/sfml3-rb)
10
+ [![Gem Downloads](https://img.shields.io/gem/dt/sfml3-rb?style=flat-square)](https://rubygems.org/gems/sfml3-rb)
11
+ [![License](https://img.shields.io/badge/license-0BSD-green?style=flat-square)](LICENSE.md)
12
+
13
+ Latest release: **0.3.1**, bound against **CSFML 3**.
14
+
15
+ ## Features
16
+
17
+ * **Broad coverage of SFML 3**, bound through CSFML: windows and events, graphics, audio, network,
18
+ and the system layer, with every `sf*` entry point tracked in [TODO.md](TODO.md).
19
+ * **Precompiled binary gems** for common platforms, with FreeType, SFML 3 and CSFML 3 statically
20
+ linked in — no toolchain and nothing to install system-wide.
21
+ * **A source fallback everywhere else**, which downloads and builds the pinned, checksum-verified
22
+ dependencies at install time, so the gem works on macOS, ARM and the BSDs out of the box.
23
+ * **Complete API documentation and types**: every class, module, method and constant is documented
24
+ on the [docs site](https://algaves.github.io/sfml3.rb/) and covered by RBS signatures shipped in
25
+ the gem.
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.
29
+
30
+ ## Table of Contents
31
+
32
+ * [Installation](#installation)
33
+ * [Quick Start](#quick-start)
34
+ * [Documentation](#documentation)
35
+ * [Development](#development)
36
+ * [Contributing](#contributing)
37
+ * [Acknowledgements](#acknowledgements)
38
+ * [License](#license)
39
+
40
+ ## Installation
16
41
 
17
42
  ```sh
18
43
  gem install sfml3-rb
@@ -29,11 +54,18 @@ already linked in — no toolchain, no build, nothing to install system-wide:
29
54
  | `x86-linux-musl` | 3.1 – 4.0 | 32-bit musl |
30
55
  | `x64-mingw-ucrt` | 3.1 – 4.0 | 64-bit Windows, RubyInstaller 3.1+ |
31
56
  | `x86-mingw32` | 3.1 – **3.4** | 32-bit Windows |
32
- | `aarch64-linux-gnu`, `x86_64-darwin`, `arm64-darwin` | 3.1 – 4.0 | experimental, not yet built |
57
+ | `aarch64-linux-gnu` | 3.1 – 4.0 | experimental, cross-built and run on a native arm64 CI runner |
58
+ | `aarch64-linux-musl`, `arm-linux-gnu`, `arm-linux-musl` | 3.1 – 4.0 | experimental, not yet built |
59
+ | `aarch64-mingw-ucrt` | 3.4 – **4.0** | experimental, 64-bit Windows on ARM |
60
+ | `x86_64-darwin`, `arm64-darwin` | 3.1 – 4.0 | experimental, not yet built |
33
61
 
34
- Each gem carries one extension per Ruby ABI. Two gaps come from upstream rather than from this
35
- project: **RubyInstaller publishes no 32-bit Ruby 4.0**, so `x86-mingw32` stops at 3.4; and 64-bit
36
- Windows before Ruby 3.1 used a different platform (`x64-mingw32`), which is not built.
62
+ Each gem carries one extension per Ruby ABI. Three gaps come from upstream rather than from this
63
+ project: **RubyInstaller publishes no 32-bit Ruby 4.0**, so `x86-mingw32` stops at 3.4; 64-bit
64
+ Windows before Ruby 3.1 used a different platform (`x64-mingw32`), which is not built; and the
65
+ cross-compilation image for `aarch64-mingw-ucrt` carries no cross Ruby older than 3.4, so that
66
+ gem has no floor below it.
67
+
68
+ ### Building from source
37
69
 
38
70
  Anywhere else — macOS, ARM, the BSDs — RubyGems falls back to the source gem, which downloads and
39
71
  builds FreeType, SFML 3 and CSFML 3 from pinned, checksum-verified tarballs at install time. That
@@ -42,36 +74,39 @@ takes a few minutes and needs:
42
74
  * Ruby >= 3.1
43
75
  * A C/C++ toolchain and CMake >= 3.22
44
76
  * On Linux, the X11/udev/OpenGL development headers SFML links against — these can't be bundled.
45
- On Fedora:
46
77
 
47
- ```sh
48
- sudo dnf install cmake gcc-c++ libX11-devel \
49
- libXrandr-devel libXcursor-devel libXi-devel systemd-devel libglvnd-devel
50
- ```
78
+ On Fedora:
79
+
80
+ ```sh
81
+ sudo dnf install cmake gcc-c++ libX11-devel \
82
+ libXrandr-devel libXcursor-devel libXi-devel systemd-devel libglvnd-devel
83
+ ```
51
84
 
52
- or on Debian/Ubuntu:
85
+ On Debian/Ubuntu:
53
86
 
54
- ```sh
55
- sudo apt-get install cmake build-essential libx11-dev \
56
- libxrandr-dev libxcursor-dev libxi-dev libudev-dev libgl1-mesa-dev
57
- ```
87
+ ```sh
88
+ sudo apt-get install cmake build-essential libx11-dev \
89
+ libxrandr-dev libxcursor-dev libxi-dev libudev-dev libgl1-mesa-dev
90
+ ```
58
91
 
59
92
  Each installed gem version builds its own copy; there's no build cache shared across versions.
60
93
 
94
+ ### Linking against system libraries
95
+
61
96
  To link against a system CSFML 3 instead (no download, no build):
62
97
 
63
98
  ```sh
64
99
  gem install sfml3-rb -- --enable-system-libraries
65
100
  ```
66
101
 
67
- ## Usage
102
+ ## Quick Start
68
103
 
69
104
  ```ruby
70
105
  require 'sfml'
71
106
  include SFML
72
107
 
73
108
  window = Window.new VideoMode.new(640, 480, 32), 'SFML'
74
- event = Event.new
109
+ event = Event.new
75
110
 
76
111
  while window.is_open?
77
112
  while window.poll_event! event
@@ -83,7 +118,37 @@ while window.is_open?
83
118
  end
84
119
  ```
85
120
 
86
- See [`test/hello-world.rb`](test/hello-world.rb) for a fuller example with shapes and transforms.
121
+ Event types are strings (`'closed'`, `'resized'`, `'key-pressed'`, ...); keys and buttons are enums.
122
+
123
+ See [`test/hello-world.rb`](test/hello-world.rb) for a fuller example with shapes and transforms,
124
+ and [`test/matrix-transformable.rb`](test/matrix-transformable.rb) for a visual demo. Neither is
125
+ part of the test suite.
126
+
127
+ ## Documentation
128
+
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.
135
+ * [CHANGELOG.md](CHANGELOG.md) — what changed in each release.
136
+ * [TODO.md](TODO.md) — module-by-module porting coverage and what is deliberately unbound.
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/`.
87
152
 
88
153
  ## Development
89
154
 
@@ -94,6 +159,7 @@ rake test # compile, then run the test suite
94
159
  rake gem # build the source gem into pkg/
95
160
  rake yard # build API docs into doc/
96
161
  rake rbs # validate sig/**/*.rbs
162
+ rake steep # type-check lib/ against sig/**/*.rbs
97
163
  ```
98
164
 
99
165
  `rake githooks:install` points your checkout at the committed `.githooks/` pre-commit hook, which
@@ -109,9 +175,12 @@ side by side: `core/` (CSFML umbrella header, macros, exceptions, UTF-32 convers
109
175
  devices), `graphics/` (shapes, Color, Transform, View, Texture, Text, Shader, the render targets),
110
176
  `audio/` and `network/`. Includes are subsystem-relative, e.g. `#include "graphics/circle.h"`.
111
177
 
112
- Two `.inc` files hold method bodies shared by several classes and are included once per class with
113
- a different macro prefix: `audio/sound_source.inc` (Sound, Music, SoundStream) and
114
- `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.
115
184
 
116
185
  Note that mkmf flattens object files to their basenames, so every `.c` filename has to stay
117
186
  unique across the whole tree — and that `$srcs` is baked into the generated Makefile, so after
@@ -138,9 +207,31 @@ mingw toolchain and the osxcross SDK already provide.
138
207
  Binary gems carry one extension per Ruby ABI under `lib/sfml/<major.minor>/`; `lib/sfml.rb` prefers
139
208
  that and falls back to the single `lib/sfml/sfml_ext.so` a source build installs.
140
209
 
210
+ ## Contributing
211
+
212
+ Bug reports and pull requests are welcome on
213
+ [GitHub](https://github.com/algaves/sfml3.rb/issues). Before opening a pull request:
214
+
215
+ ```sh
216
+ bundle install
217
+ bundle exec rake test # build the extension and run the suite
218
+ bundle exec rubocop # lint Ruby (CI enforces this)
219
+ ```
220
+
221
+ Install the pre-commit hook with `bundle exec rake githooks:install` so staged Ruby is linted and
222
+ staged C is formatted automatically. The C build is warning-clean under `SFML_STRICT=1`; keep it
223
+ that way.
224
+
225
+ ## Acknowledgements
226
+
227
+ This gem would not exist without [SFML](https://www.sfml-dev.org/) and its C binding,
228
+ [CSFML](https://github.com/SFML/CSFML), both maintained by the SFML team. The source build also
229
+ vendors and links FreeType, Ogg, Vorbis and FLAC; see [`ext/ports.rb`](ext/ports.rb) and the
230
+ [LICENSE](LICENSE.md) for their terms.
231
+
141
232
  ## License
142
233
 
143
- [0BSD](LICENSE.md)
234
+ This project is licensed under the BSD Zero Clause License (0BSD) - see the [LICENSE](LICENSE.md) file for details.
144
235
 
145
236
  ---
146
237
 
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. |
@@ -3,12 +3,12 @@
3
3
  #include <ruby.h>
4
4
  #include <string.h>
5
5
 
6
- static const char *sound_status_names[] = {"stopped", "paused", "playing"};
6
+ static const char* sound_status_names[] = {"stopped", "paused", "playing"};
7
7
 
8
8
  /* Designated initializers, indexed by the enum constant rather than by
9
9
  position: CSFML 3 defines the channel order, and a positional table would
10
10
  silently return the wrong name if a future release inserted a value. */
11
- static const char *sound_channel_names[] = {
11
+ static const char* sound_channel_names[] = {
12
12
  [sfSoundChannelUnspecified] = "unspecified",
13
13
  [sfSoundChannelMono] = "mono",
14
14
  [sfSoundChannelFrontLeft] = "front_left",
@@ -28,11 +28,10 @@ static const char *sound_channel_names[] = {
28
28
  [sfSoundChannelTopFrontCenter] = "top_front_center",
29
29
  [sfSoundChannelTopBackLeft] = "top_back_left",
30
30
  [sfSoundChannelTopBackRight] = "top_back_right",
31
- [sfSoundChannelTopBackCenter] = "top_back_center"
32
- };
31
+ [sfSoundChannelTopBackCenter] = "top_back_center"};
33
32
 
34
- static int symbol_index(VALUE rb_symbol, const char *const *names, size_t count) {
35
- const char *name;
33
+ static int symbol_index(VALUE rb_symbol, const char* const* names, size_t count) {
34
+ const char* name;
36
35
  size_t i;
37
36
 
38
37
  if (!SYMBOL_P(rb_symbol)) {
@@ -43,14 +42,14 @@ static int symbol_index(VALUE rb_symbol, const char *const *names, size_t count)
43
42
 
44
43
  for (i = 0; i < count; i++) {
45
44
  if (names[i] != NULL && strcmp(name, names[i]) == 0) {
46
- return (int) i;
45
+ return (int)i;
47
46
  }
48
47
  }
49
48
 
50
49
  return -1;
51
50
  }
52
51
 
53
- const char *sound_status_name(sfSoundStatus status) {
52
+ const char* sound_status_name(sfSoundStatus status) {
54
53
  if (status >= sfStopped && status <= sfPlaying) {
55
54
  return sound_status_names[status];
56
55
  }
@@ -62,7 +61,7 @@ sfSoundStatus sound_status_from_rb(VALUE rb_status) {
62
61
  int index;
63
62
 
64
63
  if (RB_INTEGER_TYPE_P(rb_status)) {
65
- return (sfSoundStatus) NUM2INT(rb_status);
64
+ return (sfSoundStatus)NUM2INT(rb_status);
66
65
  }
67
66
 
68
67
  index = symbol_index(rb_status, sound_status_names, 3);
@@ -71,10 +70,10 @@ sfSoundStatus sound_status_from_rb(VALUE rb_status) {
71
70
  rb_raise(rb_eArgError, "unknown sound status");
72
71
  }
73
72
 
74
- return (sfSoundStatus) index;
73
+ return (sfSoundStatus)index;
75
74
  }
76
75
 
77
- const char *sound_channel_name(sfSoundChannel channel) {
76
+ const char* sound_channel_name(sfSoundChannel channel) {
78
77
  if (channel >= sfSoundChannelUnspecified && channel <= sfSoundChannelTopBackCenter &&
79
78
  sound_channel_names[channel] != NULL) {
80
79
  return sound_channel_names[channel];
@@ -87,7 +86,7 @@ sfSoundChannel sound_channel_from_rb(VALUE rb_channel) {
87
86
  int index;
88
87
 
89
88
  if (RB_INTEGER_TYPE_P(rb_channel)) {
90
- return (sfSoundChannel) NUM2INT(rb_channel);
89
+ return (sfSoundChannel)NUM2INT(rb_channel);
91
90
  }
92
91
 
93
92
  index = symbol_index(rb_channel, sound_channel_names,
@@ -97,35 +96,74 @@ sfSoundChannel sound_channel_from_rb(VALUE rb_channel) {
97
96
  rb_raise(rb_eArgError, "unknown sound channel");
98
97
  }
99
98
 
100
- return (sfSoundChannel) index;
99
+ return (sfSoundChannel)index;
101
100
  }
102
101
 
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
-
102
+ /* Document-module: SFML::SoundStatus
103
+ * Playback state constants returned by SoundSource#status (Sound, Music,
104
+ * SoundStream). Mirrored as symbols (+:stopped+, +:paused+, +:playing+) by
105
+ * that method rather than these Integer constants, but both refer to the
106
+ * same underlying values.
107
+ */
108
+
109
+ /* Document-module: SFML::SoundChannel
110
+ * Speaker position constants used in a channel map (SoundBuffer#channel_map,
111
+ * Music#channel_map, SoundStream#channel_map, SoundRecorder#channel_map),
112
+ * mirrored as symbols (e.g. +:front_left+) rather than these Integer
113
+ * constants in those methods.
114
+ */
115
+ void Init_AudioEnums(VALUE rb_mSFML) {
116
+ VALUE rb_mSoundStatus = rb_define_module_under(rb_mSFML, "SoundStatus");
117
+ VALUE rb_mSoundChannel = rb_define_module_under(rb_mSFML, "SoundChannel");
118
+
119
+ /* The source is not playing. */
107
120
  rb_define_const(rb_mSoundStatus, "STOPPED", INT2NUM(sfStopped));
121
+ /* The source is paused; #playing_offset stays where it was paused. */
108
122
  rb_define_const(rb_mSoundStatus, "PAUSED", INT2NUM(sfPaused));
123
+ /* The source is currently playing. */
109
124
  rb_define_const(rb_mSoundStatus, "PLAYING", INT2NUM(sfPlaying));
110
125
 
126
+ /* No channel position specified. */
111
127
  rb_define_const(rb_mSoundChannel, "UNSPECIFIED", INT2NUM(sfSoundChannelUnspecified));
128
+ /* A single, centered channel. */
112
129
  rb_define_const(rb_mSoundChannel, "MONO", INT2NUM(sfSoundChannelMono));
130
+ /* Front left speaker. */
113
131
  rb_define_const(rb_mSoundChannel, "FRONT_LEFT", INT2NUM(sfSoundChannelFrontLeft));
132
+ /* Front right speaker. */
114
133
  rb_define_const(rb_mSoundChannel, "FRONT_RIGHT", INT2NUM(sfSoundChannelFrontRight));
134
+ /* Front center speaker. */
115
135
  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));
136
+ /* Front left-of-center speaker. */
137
+ rb_define_const(rb_mSoundChannel, "FRONT_LEFT_OF_CENTER",
138
+ INT2NUM(sfSoundChannelFrontLeftOfCenter));
139
+ /* Front right-of-center speaker. */
140
+ rb_define_const(rb_mSoundChannel, "FRONT_RIGHT_OF_CENTER",
141
+ INT2NUM(sfSoundChannelFrontRightOfCenter));
142
+ /* Low-frequency effects (subwoofer) channel. */
143
+ rb_define_const(rb_mSoundChannel, "LOW_FREQUENCY_EFFECTS",
144
+ INT2NUM(sfSoundChannelLowFrequencyEffects));
145
+ /* Back left speaker. */
119
146
  rb_define_const(rb_mSoundChannel, "BACK_LEFT", INT2NUM(sfSoundChannelBackLeft));
147
+ /* Back right speaker. */
120
148
  rb_define_const(rb_mSoundChannel, "BACK_RIGHT", INT2NUM(sfSoundChannelBackRight));
149
+ /* Back center speaker. */
121
150
  rb_define_const(rb_mSoundChannel, "BACK_CENTER", INT2NUM(sfSoundChannelBackCenter));
151
+ /* Side left speaker. */
122
152
  rb_define_const(rb_mSoundChannel, "SIDE_LEFT", INT2NUM(sfSoundChannelSideLeft));
153
+ /* Side right speaker. */
123
154
  rb_define_const(rb_mSoundChannel, "SIDE_RIGHT", INT2NUM(sfSoundChannelSideRight));
155
+ /* Top center speaker. */
124
156
  rb_define_const(rb_mSoundChannel, "TOP_CENTER", INT2NUM(sfSoundChannelTopCenter));
157
+ /* Top front left speaker. */
125
158
  rb_define_const(rb_mSoundChannel, "TOP_FRONT_LEFT", INT2NUM(sfSoundChannelTopFrontLeft));
159
+ /* Top front right speaker. */
126
160
  rb_define_const(rb_mSoundChannel, "TOP_FRONT_RIGHT", INT2NUM(sfSoundChannelTopFrontRight));
161
+ /* Top front center speaker. */
127
162
  rb_define_const(rb_mSoundChannel, "TOP_FRONT_CENTER", INT2NUM(sfSoundChannelTopFrontCenter));
163
+ /* Top back left speaker. */
128
164
  rb_define_const(rb_mSoundChannel, "TOP_BACK_LEFT", INT2NUM(sfSoundChannelTopBackLeft));
165
+ /* Top back right speaker. */
129
166
  rb_define_const(rb_mSoundChannel, "TOP_BACK_RIGHT", INT2NUM(sfSoundChannelTopBackRight));
167
+ /* Top back center speaker. */
130
168
  rb_define_const(rb_mSoundChannel, "TOP_BACK_CENTER", INT2NUM(sfSoundChannelTopBackCenter));
131
169
  }