ruby-sfml 3.0.0.0

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 (62) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +101 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +245 -0
  5. data/ext/ruby-sfml/extconf.rb +69 -0
  6. data/lib/sfml/assets/fonts/DejaVuSans.LICENSE.txt +78 -0
  7. data/lib/sfml/assets/fonts/DejaVuSans.ttf +0 -0
  8. data/lib/sfml/assets.rb +121 -0
  9. data/lib/sfml/audio/listener.rb +55 -0
  10. data/lib/sfml/audio/music.rb +88 -0
  11. data/lib/sfml/audio/sound.rb +102 -0
  12. data/lib/sfml/audio/sound_buffer.rb +38 -0
  13. data/lib/sfml/audio/sound_buffer_recorder.rb +71 -0
  14. data/lib/sfml/audio/sound_recorder.rb +30 -0
  15. data/lib/sfml/c/audio.rb +106 -0
  16. data/lib/sfml/c/graphics.rb +425 -0
  17. data/lib/sfml/c/network.rb +79 -0
  18. data/lib/sfml/c/system.rb +43 -0
  19. data/lib/sfml/c/window.rb +186 -0
  20. data/lib/sfml/c.rb +72 -0
  21. data/lib/sfml/game.rb +101 -0
  22. data/lib/sfml/graphics/blend_mode.rb +108 -0
  23. data/lib/sfml/graphics/circle_shape.rb +67 -0
  24. data/lib/sfml/graphics/color.rb +89 -0
  25. data/lib/sfml/graphics/convex_shape.rb +82 -0
  26. data/lib/sfml/graphics/font.rb +67 -0
  27. data/lib/sfml/graphics/image.rb +125 -0
  28. data/lib/sfml/graphics/rectangle_shape.rb +62 -0
  29. data/lib/sfml/graphics/render_states.rb +56 -0
  30. data/lib/sfml/graphics/render_target.rb +146 -0
  31. data/lib/sfml/graphics/render_texture.rb +72 -0
  32. data/lib/sfml/graphics/render_window.rb +154 -0
  33. data/lib/sfml/graphics/shader.rb +132 -0
  34. data/lib/sfml/graphics/sprite.rb +75 -0
  35. data/lib/sfml/graphics/text.rb +144 -0
  36. data/lib/sfml/graphics/texture.rb +79 -0
  37. data/lib/sfml/graphics/transform.rb +150 -0
  38. data/lib/sfml/graphics/transformable.rb +74 -0
  39. data/lib/sfml/graphics/vertex.rb +53 -0
  40. data/lib/sfml/graphics/vertex_array.rb +114 -0
  41. data/lib/sfml/graphics/view.rb +126 -0
  42. data/lib/sfml/network/ip_address.rb +67 -0
  43. data/lib/sfml/network/tcp_listener.rb +61 -0
  44. data/lib/sfml/network/tcp_socket.rb +74 -0
  45. data/lib/sfml/network/udp_socket.rb +71 -0
  46. data/lib/sfml/system/clock.rb +44 -0
  47. data/lib/sfml/system/rect.rb +64 -0
  48. data/lib/sfml/system/time.rb +48 -0
  49. data/lib/sfml/system/vector2.rb +66 -0
  50. data/lib/sfml/system/vector3.rb +63 -0
  51. data/lib/sfml/version.rb +19 -0
  52. data/lib/sfml/window/clipboard.rb +38 -0
  53. data/lib/sfml/window/cursor.rb +68 -0
  54. data/lib/sfml/window/event.rb +133 -0
  55. data/lib/sfml/window/joystick.rb +90 -0
  56. data/lib/sfml/window/keyboard.rb +60 -0
  57. data/lib/sfml/window/mouse.rb +71 -0
  58. data/lib/sfml/window/video_mode.rb +37 -0
  59. data/lib/sfml/window/window.rb +149 -0
  60. data/lib/sfml.rb +98 -0
  61. data/ruby-sfml.gemspec +38 -0
  62. metadata +163 -0
@@ -0,0 +1,150 @@
1
+ module SFML
2
+ # A 2D affine transformation. Wraps the 3×3 matrix that SFML uses to
3
+ # combine translation, rotation, scaling, and skew. Useful when you
4
+ # want to:
5
+ #
6
+ # - Apply the same transform across many drawables (push it via
7
+ # `RenderStates.new(transform: …)` instead of repeating
8
+ # `position=`/`rotation=` on every shape).
9
+ # - Build a transform from primitive ops without holding a Drawable.
10
+ #
11
+ # t = SFML::Transform.identity
12
+ # .translate([400, 300])
13
+ # .rotate(30)
14
+ # .scale([2, 2])
15
+ #
16
+ # t.transform_point([10, 0]) #=> Vector2(world coord after t)
17
+ # inv = t.inverse #=> reverse mapping
18
+ #
19
+ # Methods are chainable and **mutate in place**, returning self. To
20
+ # work with a fresh copy use `t.dup`. The CSFML constant for the
21
+ # identity is exposed via `Transform.identity`.
22
+ class Transform
23
+ def initialize
24
+ @struct = C::Graphics::Transform.new
25
+ # Start as identity by copying CSFML's sfTransform_Identity.
26
+ identity_bytes = C::Graphics.sfTransform_Identity.pointer.read_bytes(C::Graphics::Transform.size)
27
+ @struct.pointer.write_bytes(identity_bytes)
28
+ end
29
+
30
+ def self.identity
31
+ new
32
+ end
33
+
34
+ # Build from a flat row-major 9-element float array. Lays out as:
35
+ # [a, b, c,
36
+ # d, e, f,
37
+ # g, h, i] # SFML stores the 3x3 conceptually as a 4x4 padded
38
+ def self.from_matrix(arr)
39
+ raise ArgumentError, "expected 9-element matrix, got #{arr.length}" if arr.length != 9
40
+ t = new
41
+ 9.times { |i| t.struct[:matrix][i] = arr[i].to_f }
42
+ t
43
+ end
44
+
45
+ def initialize_dup(other)
46
+ super
47
+ @struct = C::Graphics::Transform.new
48
+ @struct.pointer.write_bytes(other.struct.pointer.read_bytes(C::Graphics::Transform.size))
49
+ end
50
+
51
+ # Apply a translation. Mutates self.
52
+ def translate(offset)
53
+ vec = _vec2(offset)
54
+ C::Graphics.sfTransform_translate(@struct.pointer, vec.to_native_f)
55
+ self
56
+ end
57
+
58
+ # Apply a rotation by `degrees`. Optional `center` to pivot around
59
+ # a non-origin point.
60
+ def rotate(degrees, center: nil)
61
+ if center
62
+ c = _vec2(center)
63
+ C::Graphics.sfTransform_rotateWithCenter(@struct.pointer, degrees.to_f, c.to_native_f)
64
+ else
65
+ C::Graphics.sfTransform_rotate(@struct.pointer, degrees.to_f)
66
+ end
67
+ self
68
+ end
69
+
70
+ # Apply a scale. With `center:`, scales around that pivot.
71
+ def scale(factors, center: nil)
72
+ f = _vec2(factors)
73
+ if center
74
+ c = _vec2(center)
75
+ C::Graphics.sfTransform_scaleWithCenter(@struct.pointer, f.to_native_f, c.to_native_f)
76
+ else
77
+ C::Graphics.sfTransform_scale(@struct.pointer, f.to_native_f)
78
+ end
79
+ self
80
+ end
81
+
82
+ # Multiply this transform by another. `t.combine(t2)` is `t * t2`
83
+ # in left-to-right order (apply t2's transformation, then t's).
84
+ def combine(other)
85
+ raise ArgumentError, "Transform#combine needs another SFML::Transform" unless other.is_a?(Transform)
86
+ C::Graphics.sfTransform_combine(@struct.pointer, other.struct.pointer)
87
+ self
88
+ end
89
+
90
+ # Return a new Transform that's the inverse of this one — applying
91
+ # both in sequence yields the identity.
92
+ def inverse
93
+ result = C::Graphics.sfTransform_getInverse(@struct.pointer)
94
+ t = allocate_new_with(result)
95
+ t
96
+ end
97
+
98
+ # Map a point through the transform. Returns a Vector2.
99
+ def transform_point(point)
100
+ vec = _vec2(point)
101
+ r = C::Graphics.sfTransform_transformPoint(@struct.pointer, vec.to_native_f)
102
+ Vector2.new(r[:x], r[:y])
103
+ end
104
+
105
+ # Map a SFML::Rect through the transform. Returns a new Rect.
106
+ def transform_rect(rect)
107
+ raise ArgumentError, "Transform#transform_rect needs a SFML::Rect" unless rect.is_a?(Rect)
108
+ native = C::Graphics::FloatRect.new
109
+ native[:position][:x] = rect.x.to_f
110
+ native[:position][:y] = rect.y.to_f
111
+ native[:size][:x] = rect.width.to_f
112
+ native[:size][:y] = rect.height.to_f
113
+
114
+ r = C::Graphics.sfTransform_transformRect(@struct.pointer, native)
115
+ Rect.from_native(r)
116
+ end
117
+
118
+ # Read the matrix as a flat 9-element Array of floats (row-major).
119
+ def matrix
120
+ (0...9).map { |i| @struct[:matrix][i] }
121
+ end
122
+
123
+ def ==(other)
124
+ return false unless other.is_a?(Transform)
125
+ C::Graphics.sfTransform_equal(@struct.pointer, other.struct.pointer)
126
+ end
127
+ alias eql? ==
128
+ def hash = matrix.hash
129
+
130
+ def to_s = "Transform(#{matrix.map { |v| v.round(3) }.inspect})"
131
+ alias inspect to_s
132
+
133
+ # @!visibility private
134
+ attr_reader :struct
135
+
136
+ private
137
+
138
+ def allocate_new_with(native_struct)
139
+ t = self.class.allocate
140
+ new_buf = C::Graphics::Transform.new
141
+ new_buf.pointer.write_bytes(native_struct.pointer.read_bytes(C::Graphics::Transform.size))
142
+ t.instance_variable_set(:@struct, new_buf)
143
+ t
144
+ end
145
+
146
+ def _vec2(value)
147
+ value.is_a?(Vector2) ? value : Vector2.new(*value)
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,74 @@
1
+ module SFML
2
+ module Graphics
3
+ # Shared transform interface for Sprite, CircleShape, RectangleShape
4
+ # (and Text once added). Including class must:
5
+ # 1. Define a constant CSFML_PREFIX (Symbol), e.g. :sfCircleShape
6
+ # 2. Hold the FFI handle in @handle
7
+ #
8
+ # All wrappers go through C::Graphics.public_send so the same Ruby code
9
+ # picks up sfSprite_*, sfCircleShape_*, sfRectangleShape_* etc. without
10
+ # repetition. The cost (~1µs of method dispatch on a typical call) is
11
+ # negligible against any GPU work, but the wins on maintainability are
12
+ # large — adding a new shape becomes ~5 lines.
13
+ module Transformable
14
+ def position
15
+ Vector2.from_native(_csfml(:getPosition, @handle))
16
+ end
17
+
18
+ def position=(value)
19
+ _csfml(:setPosition, @handle, _coerce_vec2(value).to_native_f)
20
+ end
21
+
22
+ def rotation
23
+ _csfml(:getRotation, @handle)
24
+ end
25
+
26
+ def rotation=(degrees)
27
+ _csfml(:setRotation, @handle, degrees.to_f)
28
+ end
29
+
30
+ def scale
31
+ Vector2.from_native(_csfml(:getScale, @handle))
32
+ end
33
+
34
+ def scale=(value)
35
+ _csfml(:setScale, @handle, _coerce_vec2(value).to_native_f)
36
+ end
37
+
38
+ def origin
39
+ Vector2.from_native(_csfml(:getOrigin, @handle))
40
+ end
41
+
42
+ def origin=(value)
43
+ _csfml(:setOrigin, @handle, _coerce_vec2(value).to_native_f)
44
+ end
45
+
46
+ def move(offset)
47
+ _csfml(:move, @handle, _coerce_vec2(offset).to_native_f)
48
+ self
49
+ end
50
+
51
+ def rotate(degrees)
52
+ _csfml(:rotate, @handle, degrees.to_f)
53
+ self
54
+ end
55
+
56
+ def scale_by(factors)
57
+ _csfml(:scale, @handle, _coerce_vec2(factors).to_native_f)
58
+ self
59
+ end
60
+
61
+ private
62
+
63
+ def _csfml(suffix, *args)
64
+ C::Graphics.public_send(:"#{self.class::CSFML_PREFIX}_#{suffix}", *args)
65
+ end
66
+
67
+ # Accept a Vector2 or any [x, y] pair so users can pass a literal:
68
+ # shape.position = [400, 300]
69
+ def _coerce_vec2(value)
70
+ value.is_a?(Vector2) ? value : Vector2.new(*value)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,53 @@
1
+ module SFML
2
+ # A single point of geometry: position, colour, and texture coordinate.
3
+ # Plain Ruby value object — VertexArray copies it into / out of CSFML
4
+ # storage, so mutating a Vertex after appending doesn't propagate.
5
+ #
6
+ # SFML::Vertex.new([10, 20])
7
+ # SFML::Vertex.new([10, 20], color: SFML::Color.red)
8
+ # SFML::Vertex.new([10, 20], color: SFML::Color.red, tex_coords: [0, 0])
9
+ class Vertex
10
+ attr_accessor :position, :color, :tex_coords
11
+
12
+ def initialize(position = Vector2.zero, color: Color::WHITE, tex_coords: Vector2.zero)
13
+ @position = _coerce_vec2(position)
14
+ @color = color
15
+ @tex_coords = _coerce_vec2(tex_coords)
16
+ end
17
+
18
+ def to_s
19
+ "Vertex(#{@position.x}, #{@position.y})"
20
+ end
21
+ alias inspect to_s
22
+
23
+ # @!visibility private
24
+ def to_native
25
+ v = C::Graphics::Vertex.new
26
+ v[:position][:x] = @position.x.to_f
27
+ v[:position][:y] = @position.y.to_f
28
+ v[:color][:r] = @color.r
29
+ v[:color][:g] = @color.g
30
+ v[:color][:b] = @color.b
31
+ v[:color][:a] = @color.a
32
+ v[:tex_coords][:x] = @tex_coords.x.to_f
33
+ v[:tex_coords][:y] = @tex_coords.y.to_f
34
+ v
35
+ end
36
+
37
+ # @!visibility private
38
+ def self.from_native(struct)
39
+ new(
40
+ Vector2.new(struct[:position][:x], struct[:position][:y]),
41
+ color: Color.new(struct[:color][:r], struct[:color][:g],
42
+ struct[:color][:b], struct[:color][:a]),
43
+ tex_coords: Vector2.new(struct[:tex_coords][:x], struct[:tex_coords][:y]),
44
+ )
45
+ end
46
+
47
+ private
48
+
49
+ def _coerce_vec2(value)
50
+ value.is_a?(Vector2) ? value : Vector2.new(*value)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,114 @@
1
+ module SFML
2
+ # Batched geometry: a single CSFML draw call shipping many vertices to
3
+ # the GPU at once. This is how you draw thousands of particles, custom
4
+ # meshes, or tile maps without the per-shape overhead.
5
+ #
6
+ # va = SFML::VertexArray.new(:triangles)
7
+ # va << SFML::Vertex.new([100, 100], color: SFML::Color.red)
8
+ # va << SFML::Vertex.new([200, 100], color: SFML::Color.green)
9
+ # va << SFML::Vertex.new([150, 200], color: SFML::Color.blue)
10
+ # window.draw(va)
11
+ #
12
+ # Primitive types control how vertices form geometry:
13
+ # :points one isolated point per vertex
14
+ # :lines pairs of vertices form line segments
15
+ # :line_strip consecutive vertices form a chain of segments
16
+ # :triangles triples form independent triangles
17
+ # :triangle_strip each new vertex extends the strip
18
+ # :triangle_fan every vertex shares a fan-out with vertex 0
19
+ class VertexArray
20
+ include Enumerable
21
+
22
+ # Order matches sfPrimitiveType in CSFML/Graphics/PrimitiveType.h.
23
+ PRIMITIVE_TYPES = %i[points lines line_strip triangles triangle_strip triangle_fan].freeze
24
+ PRIMITIVE_INDEX = PRIMITIVE_TYPES.each_with_index.to_h.freeze
25
+
26
+ def initialize(primitive_type = :points, vertices = nil)
27
+ ptr = C::Graphics.sfVertexArray_create
28
+ raise Error, "sfVertexArray_create returned NULL" if ptr.null?
29
+ @handle = FFI::AutoPointer.new(ptr, C::Graphics.method(:sfVertexArray_destroy))
30
+
31
+ self.primitive_type = primitive_type
32
+ vertices&.each { |v| append(v) }
33
+ end
34
+
35
+ def primitive_type
36
+ PRIMITIVE_TYPES[C::Graphics.sfVertexArray_getPrimitiveType(@handle)] || :unknown
37
+ end
38
+
39
+ def primitive_type=(type)
40
+ code = PRIMITIVE_INDEX.fetch(type) do
41
+ raise ArgumentError,
42
+ "Unknown primitive type: #{type.inspect}. Expected one of: #{PRIMITIVE_TYPES.inspect}"
43
+ end
44
+ C::Graphics.sfVertexArray_setPrimitiveType(@handle, code)
45
+ end
46
+
47
+ def size = C::Graphics.sfVertexArray_getVertexCount(@handle)
48
+ alias length size
49
+ alias count size
50
+
51
+ def empty? = size.zero?
52
+
53
+ def clear
54
+ C::Graphics.sfVertexArray_clear(@handle)
55
+ self
56
+ end
57
+
58
+ def resize(n)
59
+ C::Graphics.sfVertexArray_resize(@handle, Integer(n))
60
+ self
61
+ end
62
+
63
+ def append(vertex)
64
+ C::Graphics.sfVertexArray_append(@handle, vertex.to_native)
65
+ self
66
+ end
67
+ alias << append
68
+
69
+ # Read a vertex by index, or nil if out of range. Returns a fresh
70
+ # SFML::Vertex copy — mutate via `va[i] = new_vertex` to write back,
71
+ # not through the returned object.
72
+ def [](index)
73
+ i = Integer(index)
74
+ return nil if i < 0 || i >= size
75
+ ptr = C::Graphics.sfVertexArray_getVertex(@handle, i)
76
+ Vertex.from_native(C::Graphics::Vertex.new(ptr))
77
+ end
78
+
79
+ def []=(index, vertex)
80
+ i = Integer(index)
81
+ # CSFML's sfVertexArray_getVertex aborts the process on out-of-range
82
+ # access (it asserts), so we have to bounds-check on the Ruby side.
83
+ raise IndexError, "vertex index #{i} out of range (size: #{size})" if i < 0 || i >= size
84
+
85
+ ptr = C::Graphics.sfVertexArray_getVertex(@handle, i)
86
+ cv = C::Graphics::Vertex.new(ptr)
87
+ cv[:position][:x] = vertex.position.x.to_f
88
+ cv[:position][:y] = vertex.position.y.to_f
89
+ cv[:color][:r] = vertex.color.r
90
+ cv[:color][:g] = vertex.color.g
91
+ cv[:color][:b] = vertex.color.b
92
+ cv[:color][:a] = vertex.color.a
93
+ cv[:tex_coords][:x] = vertex.tex_coords.x.to_f
94
+ cv[:tex_coords][:y] = vertex.tex_coords.y.to_f
95
+ vertex
96
+ end
97
+
98
+ def each
99
+ return enum_for(:each) unless block_given?
100
+ size.times { |i| yield self[i] }
101
+ self
102
+ end
103
+
104
+ def bounds
105
+ Rect.from_native(C::Graphics.sfVertexArray_getBounds(@handle))
106
+ end
107
+
108
+ def draw_on(target, states_ptr = nil) # :nodoc:
109
+ target._draw_native(:VertexArray, @handle, states_ptr)
110
+ end
111
+
112
+ attr_reader :handle # :nodoc:
113
+ end
114
+ end
@@ -0,0 +1,126 @@
1
+ module SFML
2
+ # A 2D camera. A View defines what part of the world is visible in the
3
+ # window: a centre point, a size in world units, an optional rotation,
4
+ # and a viewport rectangle (in normalised window coords [0,1]) that
5
+ # places the view inside the window — useful for split-screen or for
6
+ # rendering a mini-map alongside the main camera.
7
+ #
8
+ # Two flows you'll typically use:
9
+ #
10
+ # # 1. Define from a centre + size:
11
+ # camera = SFML::View.new(center: [400, 300], size: [800, 600])
12
+ #
13
+ # # 2. Define from a world rectangle (top-left + size):
14
+ # camera = SFML::View.from_rect(SFML::Rect.new([0, 0], [1600, 1200]))
15
+ #
16
+ # window.view = camera # apply
17
+ # window.draw(stuff) # everything draws through the camera
18
+ # window.view = window.default_view # restore the 1:1 default
19
+ #
20
+ # All coordinate inputs accept either a SFML::Vector2 or a [x, y] array.
21
+ class View
22
+ def initialize(center: nil, size: nil, rotation: nil, viewport: nil, _handle: nil)
23
+ ptr = _handle || C::Graphics.sfView_create
24
+ raise Error, "sfView_create returned NULL" if ptr.null?
25
+ @handle = FFI::AutoPointer.new(ptr, C::Graphics.method(:sfView_destroy))
26
+
27
+ self.center = center if center
28
+ self.size = size if size
29
+ self.rotation = rotation if rotation
30
+ self.viewport = viewport if viewport
31
+ end
32
+
33
+ # Build a View whose rectangle covers the given world Rect.
34
+ def self.from_rect(rect)
35
+ raise ArgumentError, "View.from_rect needs a SFML::Rect" unless rect.is_a?(Rect)
36
+
37
+ native = C::Graphics::FloatRect.new
38
+ native[:position][:x] = rect.x.to_f
39
+ native[:position][:y] = rect.y.to_f
40
+ native[:size][:x] = rect.width.to_f
41
+ native[:size][:y] = rect.height.to_f
42
+
43
+ ptr = C::Graphics.sfView_createFromRect(native)
44
+ raise Error, "sfView_createFromRect returned NULL" if ptr.null?
45
+ new(_handle: ptr)
46
+ end
47
+
48
+ # @!visibility private
49
+ # Wrap a CSFML view pointer that we don't own (e.g. the value returned
50
+ # by sfRenderWindow_getView). We deep-copy via sfView_copy so the Ruby
51
+ # object owns its own lifetime.
52
+ def self.from_borrowed(ptr)
53
+ raise Error, "borrowed view pointer is NULL" if ptr.null?
54
+ copy = C::Graphics.sfView_copy(ptr)
55
+ new(_handle: copy)
56
+ end
57
+
58
+ def center
59
+ Vector2.from_native(C::Graphics.sfView_getCenter(@handle))
60
+ end
61
+
62
+ def center=(value)
63
+ C::Graphics.sfView_setCenter(@handle, _vec2(value).to_native_f)
64
+ end
65
+
66
+ def size
67
+ Vector2.from_native(C::Graphics.sfView_getSize(@handle))
68
+ end
69
+
70
+ def size=(value)
71
+ C::Graphics.sfView_setSize(@handle, _vec2(value).to_native_f)
72
+ end
73
+
74
+ def rotation
75
+ C::Graphics.sfView_getRotation(@handle)
76
+ end
77
+
78
+ def rotation=(degrees)
79
+ C::Graphics.sfView_setRotation(@handle, degrees.to_f)
80
+ end
81
+
82
+ # Viewport in normalised [0,1] window coordinates. Default is the full
83
+ # window (Rect.new([0,0], [1,1])). Use [0, 0, 0.5, 1] for left half,
84
+ # [0.75, 0, 0.25, 0.25] for a top-right mini-map, etc.
85
+ def viewport
86
+ Rect.from_native(C::Graphics.sfView_getViewport(@handle))
87
+ end
88
+
89
+ def viewport=(rect)
90
+ raise ArgumentError, "View#viewport= needs a SFML::Rect" unless rect.is_a?(Rect)
91
+ native = C::Graphics::FloatRect.new
92
+ native[:position][:x] = rect.x.to_f
93
+ native[:position][:y] = rect.y.to_f
94
+ native[:size][:x] = rect.width.to_f
95
+ native[:size][:y] = rect.height.to_f
96
+ C::Graphics.sfView_setViewport(@handle, native)
97
+ end
98
+
99
+ # Pan the camera by an offset in world units.
100
+ def move(offset)
101
+ C::Graphics.sfView_move(@handle, _vec2(offset).to_native_f)
102
+ self
103
+ end
104
+
105
+ # Rotate the camera by `degrees`, relative to current rotation.
106
+ def rotate(degrees)
107
+ C::Graphics.sfView_rotate(@handle, degrees.to_f)
108
+ self
109
+ end
110
+
111
+ # Multiply the visible area by `factor`. Factor < 1.0 zooms in;
112
+ # factor > 1.0 zooms out.
113
+ def zoom(factor)
114
+ C::Graphics.sfView_zoom(@handle, factor.to_f)
115
+ self
116
+ end
117
+
118
+ attr_reader :handle # :nodoc:
119
+
120
+ private
121
+
122
+ def _vec2(value)
123
+ value.is_a?(Vector2) ? value : Vector2.new(*value)
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,67 @@
1
+ module SFML
2
+ module Network
3
+ # An IPv4 address. The CSFML side stores it as a fixed-size 16-byte
4
+ # NUL-terminated dotted-decimal string ("192.168.1.42").
5
+ #
6
+ # SFML::Network::IpAddress.from_string("192.168.1.42")
7
+ # SFML::Network::IpAddress.from_bytes(192, 168, 1, 42)
8
+ # SFML::Network::IpAddress::LOCALHOST # 127.0.0.1
9
+ # SFML::Network::IpAddress::ANY # 0.0.0.0
10
+ # SFML::Network::IpAddress::BROADCAST # 255.255.255.255
11
+ # SFML::Network::IpAddress.local # local network IP
12
+ class IpAddress
13
+ def self.from_string(s)
14
+ wrap(C::Network.sfIpAddress_fromString(s.to_s))
15
+ end
16
+
17
+ def self.from_bytes(a, b, c, d)
18
+ wrap(C::Network.sfIpAddress_fromBytes(Integer(a), Integer(b), Integer(c), Integer(d)))
19
+ end
20
+
21
+ def self.from_integer(n)
22
+ wrap(C::Network.sfIpAddress_fromInteger(Integer(n)))
23
+ end
24
+
25
+ # The IP this machine sees on its local network.
26
+ def self.local
27
+ wrap(C::Network.sfIpAddress_getLocalAddress)
28
+ end
29
+
30
+ # Public-facing IP. Performs an outbound query, so optionally
31
+ # cap with a timeout. Slow — minutes if the network is down.
32
+ def self.public(timeout: SFML::Time.zero)
33
+ wrap(C::Network.sfIpAddress_getPublicAddress(timeout.to_native))
34
+ end
35
+
36
+ # @!visibility private
37
+ def self.wrap(struct)
38
+ ip = allocate
39
+ ip.instance_variable_set(:@struct, struct)
40
+ ip.freeze
41
+ end
42
+
43
+ def to_s
44
+ @struct[:address].to_ptr.read_string_to_null
45
+ end
46
+ alias inspect to_s
47
+
48
+ def to_i
49
+ C::Network.sfIpAddress_toInteger(@struct)
50
+ end
51
+
52
+ def ==(other)
53
+ other.is_a?(IpAddress) && to_s == other.to_s
54
+ end
55
+ alias eql? ==
56
+ def hash = to_s.hash
57
+
58
+ # @!visibility private
59
+ attr_reader :struct
60
+
61
+ NONE = wrap(C::Network.sfIpAddress_None)
62
+ ANY = wrap(C::Network.sfIpAddress_Any)
63
+ LOCALHOST = wrap(C::Network.sfIpAddress_LocalHost)
64
+ BROADCAST = wrap(C::Network.sfIpAddress_Broadcast)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,61 @@
1
+ module SFML
2
+ module Network
3
+ # The server side of TCP. Listens on a port; #accept blocks until a
4
+ # client connects, then returns a fresh TcpSocket for that peer.
5
+ #
6
+ # listener = SFML::Network::TcpListener.new
7
+ # listener.listen(port: 8080)
8
+ # loop do
9
+ # status, peer = listener.accept
10
+ # break unless status == :done
11
+ # handle_client(peer)
12
+ # end
13
+ class TcpListener
14
+ def initialize
15
+ ptr = C::Network.sfTcpListener_create
16
+ raise Error, "sfTcpListener_create returned NULL" if ptr.null?
17
+ @handle = FFI::AutoPointer.new(ptr, C::Network.method(:sfTcpListener_destroy))
18
+ end
19
+
20
+ # Bind the listener to a port and start listening.
21
+ # `address` (default: any local interface) restricts which interface
22
+ # to listen on — pass IpAddress::LOCALHOST for loopback-only.
23
+ def listen(port:, address: IpAddress::ANY)
24
+ addr = address.is_a?(IpAddress) ? address : IpAddress.from_string(address)
25
+ code = C::Network.sfTcpListener_listen(@handle, Integer(port), addr.struct)
26
+ C::Network::STATUSES[code]
27
+ end
28
+
29
+ def close
30
+ C::Network.sfTcpListener_close(@handle)
31
+ self
32
+ end
33
+
34
+ # Returns [status, TcpSocket] — the socket is non-nil only when
35
+ # status == :done.
36
+ def accept
37
+ out_ptr = FFI::MemoryPointer.new(:pointer)
38
+ code = C::Network.sfTcpListener_accept(@handle, out_ptr)
39
+ status = C::Network::STATUSES[code]
40
+
41
+ return [status, nil] unless status == :done
42
+ sock_ptr = out_ptr.read_pointer
43
+ return [status, nil] if sock_ptr.null?
44
+
45
+ sock = TcpSocket.allocate
46
+ sock.instance_variable_set(:@handle, FFI::AutoPointer.new(sock_ptr, C::Network.method(:sfTcpSocket_destroy)))
47
+ [status, sock]
48
+ end
49
+
50
+ def blocking? = C::Network.sfTcpListener_isBlocking(@handle)
51
+
52
+ def blocking=(value)
53
+ C::Network.sfTcpListener_setBlocking(@handle, value ? true : false)
54
+ end
55
+
56
+ def local_port = C::Network.sfTcpListener_getLocalPort(@handle)
57
+
58
+ attr_reader :handle # :nodoc:
59
+ end
60
+ end
61
+ end