ruby-sfml 3.0.0.2 → 3.0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8136e14f0f8d0f777f7649e15a61e6017e386f7e80e023d5a2b8000191db9213
4
- data.tar.gz: f6fed33adc435dcbe67777499d0aae14e58ae670a7a04fc94cb58b23805e8fc8
3
+ metadata.gz: 3261668821d3742648968ea78730bf09a83c72a428e69fe9fc5f2492f13f8557
4
+ data.tar.gz: 860208fab91bf28c6825948892bca54478defebdc29c596cb30088e394a7f516
5
5
  SHA512:
6
- metadata.gz: 62d6d8965915bfe6e95161ae8580a3b2d84eca6334a33387238073de069839dae4f08829f82f42042f43850899ed24329e3d49610ac6ec95092b53685ed49d2e
7
- data.tar.gz: a4f6805ba848b00c623ae4f1b0029b97423f154058e472e3208c48873c9f2be229707aed0ac2934483256c03f048fc06bf3f6c9c8001712ab7e764d8aa23bba4
6
+ metadata.gz: 2cae71d47e55c1d1fa1b39c8c18d7362fff927224ae410449ef33444fe07060ef2cf8c8c3c6b0b65beffd7d2f9c99e6cd89c6d2558a5573817150c636493375b
7
+ data.tar.gz: 5a4e5a8f78ae23ed08d72bc62df15587787e172000abd53b8fe1b9869952f98348d9c775f37c606555f25e5c5b26f2009685e136a26b788ae4ce9123817090be
data/CHANGELOG.md CHANGED
@@ -8,6 +8,83 @@ ruby-sfml's own patch level.
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [3.0.0.3] — 2026-05-09
12
+
13
+ ### Added — typography
14
+ - `Font#family` — human-readable family name from `sfFont_getInfo`.
15
+ - `Font#has_glyph?(codepoint)` — accepts an Integer codepoint or
16
+ a single-character String.
17
+ - `Font#kerning(a, b, character_size:, bold: false)` — wraps
18
+ `sfFont_getKerning` / `getBoldKerning` for accurate glyph
19
+ pairing. Used in advanced text layout.
20
+ - `Font#line_spacing(size)`, `Font#underline_position(size)`,
21
+ `Font#underline_thickness(size)` — typography metrics for the
22
+ given character size.
23
+ - `Font#texture(size)` — borrowed `SFML::Texture` of the glyph
24
+ atlas (handy for debug visualisations of what's been rasterised).
25
+ - `Font#dup` / `#clone` — independent deep copy.
26
+ - `Font.from_memory(bytes)` — load from a Ruby String (embedded
27
+ assets, network responses, `data:` URLs).
28
+ - `Text#find_character_pos(index)` — exact `Vector2` position of
29
+ the character at the given byte index. Critical for caret /
30
+ selection rendering in text inputs.
31
+ - `Text#letter_spacing` / `#line_spacing` getters (the setters
32
+ were already there).
33
+ - `Text#dup` / `#clone` — independent copy with the same string
34
+ and font reference.
35
+ - `Text#transform` / `#inverse_transform` — the `SFML::Transform`
36
+ the renderer applies when drawing this Text.
37
+
38
+ ### Added — Image / Texture / RenderTexture
39
+ - `Image.from_memory(bytes)` — decode PNG / JPG / BMP / TGA /
40
+ GIF / HDR / PSD from a Ruby String. Mirror of
41
+ `Image#save_to_memory`.
42
+ - `Image#copy_from(source, at:, source_rect: nil, apply_alpha: false)`
43
+ — stamp a region of `source` into self. Useful for hand-built
44
+ texture atlases or composite images.
45
+ - `Texture.create(width, height)` — allocate a blank GPU
46
+ texture; pair with `Texture#update(image)` to stream pixels.
47
+ - `Texture.maximum_size` / `Texture.unbind` — class-level
48
+ helpers.
49
+ - `Texture#bind(coord:)` — manually bind for raw OpenGL interop.
50
+ - `Texture#srgb?`, `Texture#generate_mipmap`, `Texture#dup`.
51
+ - `RenderTexture.maximum_anti_aliasing_level`,
52
+ `RenderTexture#srgb?`, `RenderTexture#generate_mipmap`.
53
+ - `RenderTexture#active=`, `#push_gl_states`, `#pop_gl_states`,
54
+ `#reset_gl_states` — same GL-state machinery as RenderWindow.
55
+
56
+ ### Added — Window / RenderWindow polish
57
+ - `RenderWindow#focused?` / `#request_focus` / `#position` /
58
+ `#position=` / `#srgb?`.
59
+ - `RenderWindow#visible=`, `#key_repeat_enabled=`,
60
+ `#joystick_threshold=`.
61
+ - `RenderWindow#active=`, `#push_gl_states`, `#pop_gl_states`,
62
+ `#reset_gl_states` — for mixing raw OpenGL calls with SFML
63
+ rendering. Surround custom GL with push/pop so SFML's
64
+ internal state survives.
65
+ - `RenderWindow#wait_event(timeout:)` — block until the next
66
+ event or `timeout` elapses (a `SFML::Time`). For low-power /
67
+ event-driven apps that don't need a 60Hz update loop.
68
+ - `Window#cursor=`, `#cursor_visible=`, `#cursor_grabbed=` —
69
+ parity with the same setters that already existed on
70
+ `RenderWindow`.
71
+ - `Window#joystick_threshold=`, `Window#context_settings`,
72
+ `Window#wait_event(timeout:)`.
73
+
74
+ ### Added — Shader
75
+ - `Shader#bind`, `Shader.unbind`, `Shader#native_handle` — for
76
+ raw GL interop and debug introspection.
77
+
78
+ ### Fixed
79
+ - `SFML::App._dispatch` now forwards `:resized` events to *both*
80
+ `on_resize(width, height)` *and* `on_event(event)`. The 3.0.0.2
81
+ refactor accidentally routed `:resized` only through the
82
+ structured hook, which broke any app that forwarded
83
+ `on_event` to a sub-system (e.g. `def on_event(e) =
84
+ @gui.on_event(e)`) — the GUI never got told to refresh its
85
+ view + reflow on resize. Both hooks now receive the event;
86
+ apps that already use `on_resize` are unaffected.
87
+
11
88
  ## [3.0.0.2] — 2026-05-09
12
89
 
13
90
  ### Added
data/lib/sfml/app.rb CHANGED
@@ -224,7 +224,12 @@ module SFML
224
224
  in {type: :closed}
225
225
  quit
226
226
  in {type: :resized, size: {x:, y:}}
227
+ # `:resized` fires BOTH the structured hook and the generic
228
+ # event sink — apps that forward `on_event` to a sub-system
229
+ # (`@gui.on_event(event)`, etc.) keep getting the event,
230
+ # while apps that just want clean (w, h) override the hook.
227
231
  on_resize(x, y)
232
+ on_event(event)
228
233
  in {type: :key_pressed, code:}
229
234
  # Scene-level bindings win over app-level (CSS-style cascade:
230
235
  # the more-specific layer overrides the more-general one).
@@ -108,6 +108,19 @@ module SFML
108
108
  attach_function :sfRenderWindow_setMaximumSize, [:render_window_t, :pointer], :void
109
109
  attach_function :sfRenderWindow_createFromHandle, [:pointer, :pointer], :render_window_t
110
110
  attach_function :sfRenderWindow_getNativeHandle, [:render_window_t], :pointer
111
+ attach_function :sfRenderWindow_hasFocus, [:render_window_t], :bool
112
+ attach_function :sfRenderWindow_requestFocus, [:render_window_t], :void
113
+ attach_function :sfRenderWindow_getPosition, [:render_window_t], System::Vector2i.by_value
114
+ attach_function :sfRenderWindow_setPosition, [:render_window_t, System::Vector2i.by_value], :void
115
+ attach_function :sfRenderWindow_setVisible, [:render_window_t, :bool], :void
116
+ attach_function :sfRenderWindow_setKeyRepeatEnabled, [:render_window_t, :bool], :void
117
+ attach_function :sfRenderWindow_setJoystickThreshold, [:render_window_t, :float], :void
118
+ attach_function :sfRenderWindow_setActive, [:render_window_t, :bool], :bool
119
+ attach_function :sfRenderWindow_pushGLStates, [:render_window_t], :void
120
+ attach_function :sfRenderWindow_popGLStates, [:render_window_t], :void
121
+ attach_function :sfRenderWindow_resetGLStates, [:render_window_t], :void
122
+ attach_function :sfRenderWindow_isSrgb, [:render_window_t], :bool
123
+ attach_function :sfRenderWindow_waitEvent, [:render_window_t, System::Time.by_value, :pointer], :bool
111
124
 
112
125
  typedef :pointer, :texture_t
113
126
  typedef :pointer, :render_texture_t
@@ -205,8 +218,16 @@ module SFML
205
218
  System::Vector2i.by_value
206
219
 
207
220
  # ---- Texture ----
221
+ attach_function :sfTexture_create, [System::Vector2u.by_value], :texture_t
208
222
  attach_function :sfTexture_createFromFile, [:string, :pointer], :texture_t
209
223
  attach_function :sfTexture_createFromImage,[:image_t, :pointer], :texture_t
224
+ attach_function :sfTexture_copy, [:texture_t], :texture_t
225
+ attach_function :sfTexture_isSrgb, [:texture_t], :bool
226
+ attach_function :sfTexture_generateMipmap, [:texture_t], :bool
227
+ attach_function :sfTexture_getMaximumSize, [], :uint
228
+ # `sfTexture_bind` takes a CoordinateType enum (0 = normalised,
229
+ # 1 = pixels). Pass NULL to unbind.
230
+ attach_function :sfTexture_bind, [:texture_t, :int], :void
210
231
  attach_function :sfTexture_destroy, [:texture_t], :void
211
232
  attach_function :sfTexture_getSize, [:texture_t], System::Vector2u.by_value
212
233
  attach_function :sfTexture_setSmooth, [:texture_t, :bool], :void
@@ -223,7 +244,9 @@ module SFML
223
244
  attach_function :sfImage_createFromColor, [System::Vector2u.by_value, Color.by_value], :image_t
224
245
  attach_function :sfImage_createFromPixels, [System::Vector2u.by_value, :pointer], :image_t
225
246
  attach_function :sfImage_createFromFile, [:string], :image_t
247
+ attach_function :sfImage_createFromMemory, [:pointer, :size_t], :image_t
226
248
  attach_function :sfImage_copy, [:image_t], :image_t
249
+ attach_function :sfImage_copyImage, [:image_t, :image_t, System::Vector2u.by_value, IntRect.by_value, :bool], :bool
227
250
  attach_function :sfImage_destroy, [:image_t], :void
228
251
  attach_function :sfImage_saveToFile, [:image_t, :string], :bool
229
252
  attach_function :sfImage_saveToMemory, [:image_t, :pointer, :string], :bool
@@ -356,6 +379,12 @@ module SFML
356
379
  attach_function :sfRenderTexture_isSmooth, [:render_texture_t], :bool
357
380
  attach_function :sfRenderTexture_setRepeated, [:render_texture_t, :bool], :void
358
381
  attach_function :sfRenderTexture_isRepeated, [:render_texture_t], :bool
382
+ attach_function :sfRenderTexture_isSrgb, [:render_texture_t], :bool
383
+ attach_function :sfRenderTexture_generateMipmap, [:render_texture_t], :bool
384
+ attach_function :sfRenderTexture_getMaximumAntiAliasingLevel, [], :uint
385
+ attach_function :sfRenderTexture_pushGLStates, [:render_texture_t], :void
386
+ attach_function :sfRenderTexture_popGLStates, [:render_texture_t], :void
387
+ attach_function :sfRenderTexture_resetGLStates, [:render_texture_t], :void
359
388
 
360
389
  attach_function :sfRenderTexture_mapPixelToCoords,
361
390
  [:render_texture_t, System::Vector2i.by_value, :view_t],
@@ -386,6 +415,8 @@ module SFML
386
415
  attach_function :sfShader_createFromMemory, [:string, :string, :string], :shader_t
387
416
  attach_function :sfShader_destroy, [:shader_t], :void
388
417
  attach_function :sfShader_isAvailable, [], :bool
418
+ attach_function :sfShader_bind, [:shader_t], :void
419
+ attach_function :sfShader_getNativeHandle, [:shader_t], :uint
389
420
  attach_function :sfShader_isGeometryAvailable, [], :bool
390
421
 
391
422
  attach_function :sfShader_setFloatUniform, [:shader_t, :string, :float], :void
@@ -429,10 +460,28 @@ module SFML
429
460
  attach_function :sfShader_setVec4UniformArray, [:shader_t, :string, :pointer, :size_t], :void
430
461
 
431
462
  # ---- Font ----
432
- attach_function :sfFont_createFromFile, [:string], :font_t
433
- attach_function :sfFont_destroy, [:font_t], :void
434
- attach_function :sfFont_setSmooth, [:font_t, :bool], :void
435
- attach_function :sfFont_isSmooth, [:font_t], :bool
463
+ # `sfFontInfo` carries one field — the human-readable font
464
+ # family. CSFML 3 expanded this struct's surface area only
465
+ # through the existing `getInfo` getter; new fields would
466
+ # arrive as additional struct members.
467
+ class FontInfo < FFI::Struct
468
+ layout :family, :pointer # C string; copy out before the font is destroyed
469
+ end
470
+
471
+ attach_function :sfFont_createFromFile, [:string], :font_t
472
+ attach_function :sfFont_createFromMemory, [:pointer, :size_t], :font_t
473
+ attach_function :sfFont_copy, [:font_t], :font_t
474
+ attach_function :sfFont_destroy, [:font_t], :void
475
+ attach_function :sfFont_setSmooth, [:font_t, :bool], :void
476
+ attach_function :sfFont_isSmooth, [:font_t], :bool
477
+ attach_function :sfFont_getInfo, [:font_t], FontInfo.by_value
478
+ attach_function :sfFont_hasGlyph, [:font_t, :uint32], :bool
479
+ attach_function :sfFont_getKerning, [:font_t, :uint32, :uint32, :uint], :float
480
+ attach_function :sfFont_getBoldKerning, [:font_t, :uint32, :uint32, :uint], :float
481
+ attach_function :sfFont_getLineSpacing, [:font_t, :uint], :float
482
+ attach_function :sfFont_getUnderlinePosition, [:font_t, :uint], :float
483
+ attach_function :sfFont_getUnderlineThickness, [:font_t, :uint], :float
484
+ attach_function :sfFont_getTexture, [:font_t, :uint], :texture_t
436
485
 
437
486
  # ---- Text ----
438
487
  attach_function :sfText_create, [:font_t], :text_t
@@ -455,7 +504,15 @@ module SFML
455
504
  attach_function :sfText_setStyle, [:text_t, :uint32], :void
456
505
  attach_function :sfText_getStyle, [:text_t], :uint32
457
506
  attach_function :sfText_setLetterSpacing, [:text_t, :float], :void
507
+ attach_function :sfText_getLetterSpacing, [:text_t], :float
458
508
  attach_function :sfText_setLineSpacing, [:text_t, :float], :void
509
+ attach_function :sfText_getLineSpacing, [:text_t], :float
510
+ attach_function :sfText_findCharacterPos, [:text_t, :size_t], System::Vector2f.by_value
511
+ attach_function :sfText_getFont, [:text_t], :font_t
512
+ attach_function :sfText_copy, [:text_t], :text_t
513
+ attach_function :sfText_getString, [:text_t], :string
514
+ attach_function :sfText_getTransform, [:text_t], Transform.by_value
515
+ attach_function :sfText_getInverseTransform,[:text_t], Transform.by_value
459
516
  attach_function :sfText_setPosition, [:text_t, System::Vector2f.by_value], :void
460
517
  attach_function :sfText_getPosition, [:text_t], System::Vector2f.by_value
461
518
  attach_function :sfText_setRotation, [:text_t, :float], :void
data/lib/sfml/c/window.rb CHANGED
@@ -241,8 +241,15 @@ module SFML
241
241
  attach_function :sfWindow_setKeyRepeatEnabled, [:raw_window_t, :bool], :void
242
242
  attach_function :sfWindow_requestFocus, [:raw_window_t], :void
243
243
  attach_function :sfWindow_hasFocus, [:raw_window_t], :bool
244
+ attach_function :sfWindow_requestFocus, [:raw_window_t], :void
244
245
  attach_function :sfWindow_setActive, [:raw_window_t, :bool], :bool
245
246
  attach_function :sfWindow_setIcon, [:raw_window_t, System::Vector2u.by_value, :pointer], :void
247
+ attach_function :sfWindow_setMouseCursor, [:raw_window_t, :pointer], :void
248
+ attach_function :sfWindow_setMouseCursorVisible, [:raw_window_t, :bool], :void
249
+ attach_function :sfWindow_setMouseCursorGrabbed, [:raw_window_t, :bool], :void
250
+ attach_function :sfWindow_setJoystickThreshold, [:raw_window_t, :float], :void
251
+ attach_function :sfWindow_getSettings, [:raw_window_t], ContextSettings.by_value
252
+ attach_function :sfWindow_waitEvent, [:raw_window_t, System::Time.by_value, :pointer], :bool
246
253
  # NULL pointer clears the limit. Pass a Vector2u* to set it.
247
254
  attach_function :sfWindow_setMinimumSize, [:raw_window_t, :pointer], :void
248
255
  attach_function :sfWindow_setMaximumSize, [:raw_window_t, :pointer], :void
@@ -27,6 +27,23 @@ module SFML
27
27
  font
28
28
  end
29
29
 
30
+ # Load a font from a Ruby String of bytes — useful when the
31
+ # font lives inside a `data:` URL, an embedded asset, or a
32
+ # network response. The bytes are copied by SFML before this
33
+ # call returns; the caller's String can be GC'd safely.
34
+ def self.from_memory(bytes)
35
+ raise ArgumentError, "expected a String, got #{bytes.class}" unless bytes.is_a?(String)
36
+
37
+ buf = FFI::MemoryPointer.new(:uint8, bytes.bytesize)
38
+ buf.write_bytes(bytes)
39
+ ptr = C::Graphics.sfFont_createFromMemory(buf, bytes.bytesize)
40
+ raise Error, "sfFont_createFromMemory returned NULL" if ptr.null?
41
+
42
+ font = allocate
43
+ font.send(:_take_ownership, ptr)
44
+ font
45
+ end
46
+
30
47
  # The default font bundled with ruby-sfml. Use this when you don't
31
48
  # care which typeface as long as you can render text — examples,
32
49
  # debug HUDs, prototypes. Memoized so subsequent calls return the
@@ -56,6 +73,69 @@ module SFML
56
73
  C::Graphics.sfFont_setSmooth(@handle, !!value)
57
74
  end
58
75
 
76
+ # Human-readable family name (e.g. "DejaVu Sans"). Read once
77
+ # via `sfFont_getInfo` — CSFML returns a static C string from
78
+ # FreeType so we copy out into a Ruby String.
79
+ def family
80
+ info = C::Graphics.sfFont_getInfo(@handle)
81
+ info[:family].null? ? nil : info[:family].read_string
82
+ end
83
+
84
+ # `true` if the font has a glyph for the given Unicode codepoint
85
+ # (Integer) or single-character String.
86
+ def has_glyph?(codepoint)
87
+ cp = codepoint.is_a?(String) ? codepoint.codepoints.first : Integer(codepoint)
88
+ C::Graphics.sfFont_hasGlyph(@handle, cp || 0)
89
+ end
90
+
91
+ # Horizontal kerning offset between two adjacent glyphs at the
92
+ # given character size. Float, in pixels (often negative — the
93
+ # kern pulls the second glyph leftward).
94
+ def kerning(first, second, character_size:, bold: false)
95
+ a = first.is_a?(String) ? first.codepoints.first : Integer(first)
96
+ b = second.is_a?(String) ? second.codepoints.first : Integer(second)
97
+ fn = bold ? :sfFont_getBoldKerning : :sfFont_getKerning
98
+ C::Graphics.send(fn, @handle, a || 0, b || 0, Integer(character_size))
99
+ end
100
+
101
+ # Distance between two consecutive baselines for the given
102
+ # character size. Float, in pixels.
103
+ def line_spacing(character_size)
104
+ C::Graphics.sfFont_getLineSpacing(@handle, Integer(character_size))
105
+ end
106
+
107
+ # Vertical offset of the underline from the baseline (positive
108
+ # values point downward). Float, in pixels.
109
+ def underline_position(character_size)
110
+ C::Graphics.sfFont_getUnderlinePosition(@handle, Integer(character_size))
111
+ end
112
+
113
+ # Thickness of the underline stroke. Float, in pixels.
114
+ def underline_thickness(character_size)
115
+ C::Graphics.sfFont_getUnderlineThickness(@handle, Integer(character_size))
116
+ end
117
+
118
+ # The internal glyph atlas as a `SFML::Texture` (read-only — we
119
+ # don't own the pointer; CSFML keeps it alive as long as the
120
+ # font does).
121
+ def texture(character_size)
122
+ ptr = C::Graphics.sfFont_getTexture(@handle, Integer(character_size))
123
+ return nil if ptr.null?
124
+ Texture.send(:_borrow, ptr)
125
+ end
126
+
127
+ # Deep copy. The returned font has its own atlas state; mutate
128
+ # one without affecting the other.
129
+ def dup
130
+ ptr = C::Graphics.sfFont_copy(@handle)
131
+ raise Error, "sfFont_copy returned NULL" if ptr.null?
132
+
133
+ font = self.class.allocate
134
+ font.send(:_take_ownership, ptr)
135
+ font
136
+ end
137
+ alias clone dup
138
+
59
139
  attr_reader :handle # :nodoc:
60
140
 
61
141
  private
@@ -41,6 +41,23 @@ module SFML
41
41
  img
42
42
  end
43
43
 
44
+ # Decode an image from a Ruby String of bytes (PNG, JPG, BMP,
45
+ # TGA, GIF, HDR, PSD — whatever stb_image / SFML's loader
46
+ # supports). Mirror of `Image#save_to_memory` for round-trips
47
+ # without touching the disk.
48
+ def self.from_memory(bytes)
49
+ raise ArgumentError, "expected a String, got #{bytes.class}" unless bytes.is_a?(String)
50
+
51
+ buf = FFI::MemoryPointer.new(:uint8, bytes.bytesize)
52
+ buf.write_bytes(bytes)
53
+ ptr = C::Graphics.sfImage_createFromMemory(buf, bytes.bytesize)
54
+ raise Error, "sfImage_createFromMemory returned NULL — unsupported format?" if ptr.null?
55
+
56
+ img = allocate
57
+ img.send(:_take_ownership, ptr)
58
+ img
59
+ end
60
+
44
61
  # Build an image from a raw RGBA byte string. `pixels` must be
45
62
  # exactly width*height*4 bytes, row-major from the top-left.
46
63
  def self.from_pixels(width, height, pixels)
@@ -90,6 +107,36 @@ module SFML
90
107
  ptr.read_bytes(width * height * 4)
91
108
  end
92
109
 
110
+ # Copy a rectangular region of `source` into this image. The
111
+ # source region is `source_rect` (a `SFML::Rect` or `[x, y, w,
112
+ # h]` Array; defaults to the entire source); the destination
113
+ # top-left in this image is `at` (`[x, y]`). When `apply_alpha`
114
+ # is true, the source's alpha channel blends with this image's
115
+ # existing pixels; otherwise the copy is opaque.
116
+ #
117
+ # Useful for stamping sprites onto an atlas, blitting one
118
+ # image into a sub-rect of another, or hand-building a
119
+ # composite before uploading to GPU.
120
+ def copy_from(source, at:, source_rect: nil, apply_alpha: false)
121
+ raise ArgumentError, "Image#copy_from needs a SFML::Image" unless source.is_a?(Image)
122
+
123
+ dest = C::System::Vector2u.new
124
+ dest[:x] = Integer(at[0]); dest[:y] = Integer(at[1])
125
+
126
+ sr_struct = C::Graphics::IntRect.new
127
+ x, y, w, h =
128
+ case source_rect
129
+ when nil then [0, 0, 0, 0] # zeroes = "entire source" in CSFML
130
+ when Rect then [source_rect.x, source_rect.y, source_rect.width, source_rect.height]
131
+ when Array then source_rect
132
+ else raise ArgumentError, "source_rect must be a SFML::Rect or [x, y, w, h]"
133
+ end
134
+ sr_struct[:position][:x] = Integer(x); sr_struct[:position][:y] = Integer(y)
135
+ sr_struct[:size][:x] = Integer(w); sr_struct[:size][:y] = Integer(h)
136
+ C::Graphics.sfImage_copyImage(@handle, source.handle, dest, sr_struct, !!apply_alpha)
137
+ self
138
+ end
139
+
93
140
  def save(path)
94
141
  ok = C::Graphics.sfImage_saveToFile(@handle, path.to_s)
95
142
  raise Error, "Could not save image to #{path.inspect}" unless ok
@@ -51,6 +51,33 @@ module SFML
51
51
  C::Graphics.sfRenderTexture_setRepeated(@handle, !!value)
52
52
  end
53
53
 
54
+ # `true` if the framebuffer is sRGB-capable.
55
+ def srgb? = C::Graphics.sfRenderTexture_isSrgb(@handle)
56
+
57
+ # Generate mipmaps for the underlying texture. Useful when the
58
+ # texture will be sampled at a downscaled size — without
59
+ # mipmaps you get aliasing on minification. Returns `true` if
60
+ # the GPU honoured the request (NPOT or unsupported drivers
61
+ # return `false`).
62
+ def generate_mipmap
63
+ C::Graphics.sfRenderTexture_generateMipmap(@handle)
64
+ end
65
+
66
+ # Maximum MSAA level the driver can apply to a RenderTexture
67
+ # at this moment. Class-level — independent of the instance.
68
+ def self.maximum_anti_aliasing_level
69
+ C::Graphics.sfRenderTexture_getMaximumAntiAliasingLevel
70
+ end
71
+
72
+ # ---- GL interop (mirror of RenderWindow's) ----
73
+
74
+ def active=(value)
75
+ C::Graphics.sfRenderTexture_setActive(@handle, value ? true : false)
76
+ end
77
+ def push_gl_states = C::Graphics.sfRenderTexture_pushGLStates(@handle)
78
+ def pop_gl_states = C::Graphics.sfRenderTexture_popGLStates(@handle)
79
+ def reset_gl_states = C::Graphics.sfRenderTexture_resetGLStates(@handle)
80
+
54
81
  # The Texture this RenderTexture is rendering into. Borrowed — its
55
82
  # lifetime is bounded by `self`. Memoised so repeated calls return
56
83
  # the same Ruby wrapper.
@@ -162,6 +162,68 @@ module SFML
162
162
  C::Graphics.sfRenderWindow_getNativeHandle(@handle)
163
163
  end
164
164
 
165
+ # ---- Focus ----
166
+ def focused? = C::Graphics.sfRenderWindow_hasFocus(@handle)
167
+ def request_focus = C::Graphics.sfRenderWindow_requestFocus(@handle)
168
+
169
+ # ---- OS-window state ----
170
+ def visible=(value)
171
+ C::Graphics.sfRenderWindow_setVisible(@handle, value ? true : false)
172
+ end
173
+
174
+ def key_repeat_enabled=(value)
175
+ C::Graphics.sfRenderWindow_setKeyRepeatEnabled(@handle, value ? true : false)
176
+ end
177
+
178
+ def joystick_threshold=(value)
179
+ C::Graphics.sfRenderWindow_setJoystickThreshold(@handle, Float(value))
180
+ end
181
+
182
+ # Top-left corner in desktop coordinates.
183
+ def position
184
+ Vector2.from_native(C::Graphics.sfRenderWindow_getPosition(@handle))
185
+ end
186
+
187
+ def position=(value)
188
+ vec = value.is_a?(Vector2) ? value : Vector2.new(*value)
189
+ v = C::System::Vector2i.new
190
+ v[:x] = Integer(vec.x); v[:y] = Integer(vec.y)
191
+ C::Graphics.sfRenderWindow_setPosition(@handle, v)
192
+ end
193
+
194
+ # `true` if the framebuffer is sRGB-capable (i.e. the GL
195
+ # context was created with the sRGB attribute and the driver
196
+ # honoured it).
197
+ def srgb? = C::Graphics.sfRenderWindow_isSrgb(@handle)
198
+
199
+ # ---- GL interop ----
200
+ #
201
+ # When mixing raw OpenGL calls with SFML rendering, surround
202
+ # the OpenGL block with `push_gl_states` / `pop_gl_states` so
203
+ # SFML's internal state survives. `reset_gl_states` is a
204
+ # heavier "throw away whatever's been changed" reset.
205
+ # `active=` toggles the GL context's activation on the
206
+ # current thread — the only way to use SFML rendering from a
207
+ # non-main thread.
208
+
209
+ def active=(value)
210
+ C::Graphics.sfRenderWindow_setActive(@handle, value ? true : false)
211
+ end
212
+ def push_gl_states = C::Graphics.sfRenderWindow_pushGLStates(@handle)
213
+ def pop_gl_states = C::Graphics.sfRenderWindow_popGLStates(@handle)
214
+ def reset_gl_states = C::Graphics.sfRenderWindow_resetGLStates(@handle)
215
+
216
+ # Block until an event arrives or `timeout` (a SFML::Time)
217
+ # elapses. Returns the next pending Event or nil on timeout.
218
+ # Useful for low-power apps that don't need to redraw at
219
+ # 60fps — wake on input.
220
+ def wait_event(timeout: nil)
221
+ t = timeout || Time.zero
222
+ ok = C::Graphics.sfRenderWindow_waitEvent(@handle, t.to_native, @event_buffer)
223
+ return nil unless ok
224
+ Event.from_native(@event_buffer)
225
+ end
226
+
165
227
  # Wrap an existing OS-level window. `handle` is a platform native
166
228
  # handle (Integer address or FFI::Pointer). Useful for embedding
167
229
  # the renderer inside another framework's window (Qt, Gtk, raw
@@ -139,6 +139,19 @@ module SFML
139
139
  C::Graphics.sfShader_setFloatUniformArray(@handle, name.to_s, buf, values.length)
140
140
  end
141
141
 
142
+ # Bind this shader as the active GL program. Useful when you
143
+ # want to issue raw GL draw calls under SFML's context. Pair
144
+ # with `Shader.unbind` to restore SFML's default. Most users
145
+ # don't need this — just pass the shader to `target.draw(...,
146
+ # render_states: SFML::RenderStates.new(shader: self))`.
147
+ def bind = C::Graphics.sfShader_bind(@handle)
148
+
149
+ def self.unbind = C::Graphics.sfShader_bind(nil)
150
+
151
+ # The OpenGL program ID. Useful for debug printf / interop
152
+ # with raw GL libraries.
153
+ def native_handle = C::Graphics.sfShader_getNativeHandle(@handle)
154
+
142
155
  attr_reader :handle # :nodoc:
143
156
 
144
157
  private
@@ -123,6 +123,53 @@ module SFML
123
123
  C::Graphics.sfText_setStyle(@handle, bits)
124
124
  end
125
125
 
126
+ # The character spacing modifier — a multiplier on the font's
127
+ # natural advance. Read with no args, set with a Float (1.0 =
128
+ # default).
129
+ def letter_spacing = C::Graphics.sfText_getLetterSpacing(@handle)
130
+
131
+ def letter_spacing=(value)
132
+ C::Graphics.sfText_setLetterSpacing(@handle, Float(value))
133
+ end
134
+
135
+ # The line-spacing multiplier (1.0 = default).
136
+ def line_spacing = C::Graphics.sfText_getLineSpacing(@handle)
137
+
138
+ def line_spacing=(value)
139
+ C::Graphics.sfText_setLineSpacing(@handle, Float(value))
140
+ end
141
+
142
+ # The screen-space position of the character at byte index
143
+ # `index` in the Text's current string. Returns a `Vector2`.
144
+ # Useful for positioning a caret in a text field, drawing
145
+ # selection highlights, or anchoring tooltips.
146
+ def find_character_pos(index)
147
+ Vector2.from_native(C::Graphics.sfText_findCharacterPos(@handle, Integer(index)))
148
+ end
149
+
150
+ # Combined translation/scale/rotation as a SFML::Transform.
151
+ # Same matrix the renderer uses to draw the Text.
152
+ def transform
153
+ C::Graphics.sfText_getTransform(@handle)
154
+ end
155
+
156
+ def inverse_transform
157
+ C::Graphics.sfText_getInverseTransform(@handle)
158
+ end
159
+
160
+ # Deep copy. The returned Text holds the same Font reference
161
+ # (Fonts are shareable; each owns its own glyph atlas).
162
+ def dup
163
+ ptr = C::Graphics.sfText_copy(@handle)
164
+ raise Error, "sfText_copy returned NULL" if ptr.null?
165
+
166
+ copy = self.class.allocate
167
+ copy.instance_variable_set(:@handle, FFI::AutoPointer.new(ptr, C::Graphics.method(:sfText_destroy)))
168
+ copy.instance_variable_set(:@font, @font)
169
+ copy
170
+ end
171
+ alias clone dup
172
+
126
173
  # Bounding box of the text in its own (untransformed) coordinate system.
127
174
  # Use this to centre or align glyphs precisely:
128
175
  # text.origin = [text.local_bounds.width / 2, 0]
@@ -16,6 +16,21 @@ module SFML
16
16
  tex
17
17
  end
18
18
 
19
+ # Allocate a blank texture on the GPU at the given size — use
20
+ # `update(image)` afterwards to upload pixels. Useful when
21
+ # you'll be filling the texture from a procedurally-generated
22
+ # Image or repeatedly streaming pixel data into it.
23
+ def self.create(width, height)
24
+ size = C::System::Vector2u.new
25
+ size[:x] = Integer(width); size[:y] = Integer(height)
26
+ ptr = C::Graphics.sfTexture_create(size)
27
+ raise Error, "sfTexture_create returned NULL — out of GPU memory?" if ptr.null?
28
+
29
+ tex = allocate
30
+ tex.send(:_take_ownership, ptr)
31
+ tex
32
+ end
33
+
19
34
  # Upload a CPU-side SFML::Image to the GPU as a new Texture. Keeps
20
35
  # the RGBA byte order and dimensions of the source image.
21
36
  def self.from_image(image, smooth: false, repeated: false)
@@ -68,8 +83,58 @@ module SFML
68
83
  C::Graphics.sfTexture_setRepeated(@handle, !!value)
69
84
  end
70
85
 
86
+ def srgb? = C::Graphics.sfTexture_isSrgb(@handle)
87
+
88
+ # Generate mipmaps for this texture. Returns `true` if the
89
+ # GPU honoured it. Required for the `_MIPMAP_*` minification
90
+ # filters; otherwise downscaled samples alias.
91
+ def generate_mipmap = C::Graphics.sfTexture_generateMipmap(@handle)
92
+
93
+ # Bind this texture to the active OpenGL texture unit. `coord`
94
+ # is one of `:normalized` (default — UVs in [0..1]) or
95
+ # `:pixels` (UVs in [0..size]). Useful when mixing raw
96
+ # OpenGL with SFML rendering. Pass `nil` to unbind:
97
+ # `SFML::Texture.unbind`.
98
+ COORDINATE_TYPES = {normalized: 0, pixels: 1}.freeze
99
+
100
+ def bind(coord: :normalized)
101
+ raise ArgumentError, "coord must be :normalized or :pixels" unless COORDINATE_TYPES.key?(coord)
102
+ C::Graphics.sfTexture_bind(@handle, COORDINATE_TYPES[coord])
103
+ end
104
+
105
+ def self.unbind
106
+ C::Graphics.sfTexture_bind(nil, 0)
107
+ end
108
+
109
+ # Maximum texture dimension the driver will allocate. Tied to
110
+ # the GL state, so it's a class-level call (no instance).
111
+ def self.maximum_size
112
+ C::Graphics.sfTexture_getMaximumSize
113
+ end
114
+
115
+ # Deep copy. The returned texture has its own GPU memory.
116
+ def dup
117
+ ptr = C::Graphics.sfTexture_copy(@handle)
118
+ raise Error, "sfTexture_copy returned NULL" if ptr.null?
119
+
120
+ tex = self.class.allocate
121
+ tex.send(:_take_ownership, ptr)
122
+ tex
123
+ end
124
+ alias clone dup
125
+
71
126
  attr_reader :handle # :nodoc:
72
127
 
128
+ # Internal — borrow a CSFML-owned `sfTexture*` (e.g. one
129
+ # returned by `sfFont_getTexture`) without registering an
130
+ # auto-destroy hook. The owning object is responsible for
131
+ # outliving any draw call that uses this borrowed handle.
132
+ def self._borrow(ptr)
133
+ tex = allocate
134
+ tex.instance_variable_set(:@handle, ptr)
135
+ tex
136
+ end
137
+
73
138
  private
74
139
 
75
140
  def _take_ownership(ptr)
data/lib/sfml/version.rb CHANGED
@@ -15,5 +15,5 @@ module SFML
15
15
  # "3.0.1.0" — CSFML 3.0.1 ships, we re-cut from upstream
16
16
  # "3.0.1.1" — our patch on top of CSFML 3.0.1
17
17
  # "3.1.0.0" — CSFML 3.1.0 ships, we add new bindings
18
- VERSION = "3.0.0.2"
18
+ VERSION = "3.0.0.3"
19
19
  end
@@ -129,6 +129,44 @@ module SFML
129
129
  C::Window.sfWindow_setActive(@handle, value ? true : false)
130
130
  end
131
131
 
132
+ # ---- Mouse cursor ----
133
+
134
+ def cursor=(cursor)
135
+ raise ArgumentError, "Window#cursor= requires a SFML::Cursor" unless cursor.is_a?(Cursor)
136
+ C::Window.sfWindow_setMouseCursor(@handle, cursor.handle)
137
+ @cursor = cursor # keep alive
138
+ end
139
+
140
+ def cursor_visible=(visible)
141
+ C::Window.sfWindow_setMouseCursorVisible(@handle, visible ? true : false)
142
+ end
143
+
144
+ def cursor_grabbed=(grabbed)
145
+ C::Window.sfWindow_setMouseCursorGrabbed(@handle, grabbed ? true : false)
146
+ end
147
+
148
+ # ---- Misc state ----
149
+
150
+ def joystick_threshold=(value)
151
+ C::Window.sfWindow_setJoystickThreshold(@handle, Float(value))
152
+ end
153
+
154
+ # The actual GL context settings the driver gave us — may differ
155
+ # from what was requested via `ContextSettings`.
156
+ def context_settings
157
+ ContextSettings.from_native(C::Window.sfWindow_getSettings(@handle))
158
+ end
159
+
160
+ # Block until the next event arrives or `timeout` (a SFML::Time)
161
+ # elapses. `nil` timeout = wait forever (matches CSFML's
162
+ # `sfTime_Zero` / no-timeout convention).
163
+ def wait_event(timeout: nil)
164
+ t = timeout || Time.zero
165
+ ok = C::Window.sfWindow_waitEvent(@handle, t.to_native, @event_buffer)
166
+ return nil unless ok
167
+ Event.from_native(@event_buffer)
168
+ end
169
+
132
170
  # Replace the window's title-bar / taskbar icon with the pixels from
133
171
  # the given SFML::Image. The OS scales it as needed; 32×32 RGBA
134
172
  # is the typical sweet spot.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-sfml
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.2
4
+ version: 3.0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mykhailo Melnyk