ruby-sfml 3.0.0.0 → 3.0.0.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.
@@ -0,0 +1,117 @@
1
+ module SFML
2
+ module Network
3
+ # CSFML's tiny HTTP/1.x client. Useful when an SFML 3 game wants
4
+ # to talk to a leaderboard / asset server without pulling in
5
+ # `Net::HTTP`. For anything more involved (TLS, redirects,
6
+ # streaming, retries, JSON), Ruby's stdlib `Net::HTTP` is the
7
+ # better tool — this wrapper exists for parity with CSFML, not
8
+ # because we recommend it.
9
+ #
10
+ # http = SFML::Network::Http.new("http://localhost", port: 8080)
11
+ # resp = http.send_request(method: :get, uri: "/")
12
+ # resp.status #=> 200
13
+ # resp.body #=> "Hello world\n"
14
+ #
15
+ # `send_request` accepts:
16
+ # method: :get / :post / :head / :put / :delete (default :get)
17
+ # uri: path string (default "/")
18
+ # fields: Hash of header name → value
19
+ # body: String body (POST/PUT)
20
+ # http_version: [major, minor] (default [1, 0])
21
+ # timeout: SFML::Time or seconds (default 0 = no timeout)
22
+ class Http
23
+ DEFAULT_TIMEOUT = SFML::Time.zero
24
+ DEFAULT_VERSION = [1, 0].freeze
25
+
26
+ def initialize(host, port: 0)
27
+ ptr = C::Network.sfHttp_create
28
+ raise Error, "sfHttp_create returned NULL" if ptr.null?
29
+
30
+ @handle = FFI::AutoPointer.new(ptr, C::Network.method(:sfHttp_destroy))
31
+ C::Network.sfHttp_setHost(@handle, host.to_s, Integer(port))
32
+ end
33
+
34
+ def send_request(method: :get, uri: "/", fields: nil, body: nil,
35
+ http_version: DEFAULT_VERSION, timeout: DEFAULT_TIMEOUT)
36
+ request_ptr = C::Network.sfHttpRequest_create
37
+ raise Error, "sfHttpRequest_create returned NULL" if request_ptr.null?
38
+
39
+ begin
40
+ method_idx = C::Network::HTTP_METHODS.index(method) ||
41
+ raise(ArgumentError, "Unknown HTTP method: #{method.inspect} " \
42
+ "(expected one of #{C::Network::HTTP_METHODS.inspect})")
43
+ C::Network.sfHttpRequest_setMethod(request_ptr, method_idx)
44
+ C::Network.sfHttpRequest_setUri(request_ptr, uri.to_s)
45
+ C::Network.sfHttpRequest_setHttpVersion(request_ptr, Integer(http_version[0]), Integer(http_version[1]))
46
+ C::Network.sfHttpRequest_setBody(request_ptr, body.to_s) if body
47
+ fields&.each_pair do |name, value|
48
+ C::Network.sfHttpRequest_setField(request_ptr, name.to_s, value.to_s)
49
+ end
50
+
51
+ t = timeout.is_a?(Time) ? timeout : Time.seconds(timeout.to_f)
52
+ response_ptr = C::Network.sfHttp_sendRequest(@handle, request_ptr, t.to_native)
53
+ raise Error, "sfHttp_sendRequest returned NULL" if response_ptr.null?
54
+
55
+ Response.send(:_take_ownership, response_ptr)
56
+ ensure
57
+ C::Network.sfHttpRequest_destroy(request_ptr)
58
+ end
59
+ end
60
+
61
+ attr_reader :handle # :nodoc:
62
+
63
+ # Read-only view onto a CSFML sfHttpResponse. Wraps the C pointer
64
+ # in a Ruby object that destroys the response when GC'd.
65
+ class Response
66
+ # Status mappings — most map onto standard HTTP codes; the four
67
+ # at the bottom are SFML-side transport errors above the
68
+ # standard range (≥ 1000).
69
+ STATUS_NAMES = {
70
+ 200 => :ok, 201 => :created, 202 => :accepted, 204 => :no_content,
71
+ 205 => :reset_content, 206 => :partial_content,
72
+ 301 => :multiple_choices, 302 => :moved_permanently, 303 => :moved_temporarily,
73
+ 304 => :not_modified,
74
+ 400 => :bad_request, 401 => :unauthorized, 403 => :forbidden, 404 => :not_found,
75
+ 405 => :range_not_satisfiable,
76
+ 500 => :internal_server_error, 501 => :not_implemented, 502 => :bad_gateway,
77
+ 503 => :service_not_available, 504 => :gateway_timeout, 505 => :version_not_supported,
78
+ 1000 => :invalid_response, 1001 => :connection_failed,
79
+ }.freeze
80
+
81
+ def initialize
82
+ raise NoMethodError, "use SFML::Network::Http#send_request to create a Response"
83
+ end
84
+
85
+ def status
86
+ C::Network.sfHttpResponse_getStatus(@handle)
87
+ end
88
+
89
+ # The status as a symbol when CSFML maps it, otherwise the
90
+ # raw integer. Useful for `case resp.status_symbol in :ok`.
91
+ def status_symbol
92
+ STATUS_NAMES[status] || status
93
+ end
94
+
95
+ def body = C::Network.sfHttpResponse_getBody(@handle).to_s
96
+ def field(name) = C::Network.sfHttpResponse_getField(@handle, name.to_s)
97
+
98
+ def http_version
99
+ [
100
+ C::Network.sfHttpResponse_getMajorVersion(@handle),
101
+ C::Network.sfHttpResponse_getMinorVersion(@handle),
102
+ ]
103
+ end
104
+
105
+ attr_reader :handle # :nodoc:
106
+
107
+ # @!visibility private
108
+ def self._take_ownership(ptr)
109
+ obj = allocate
110
+ obj.instance_variable_set(:@handle,
111
+ FFI::AutoPointer.new(ptr, C::Network.method(:sfHttpResponse_destroy)))
112
+ obj
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,93 @@
1
+ module SFML
2
+ module Network
3
+ # Multiplex many sockets onto a single blocking #wait. Useful for
4
+ # one-thread network servers — register every socket / listener
5
+ # you care about, call #wait, then ask each one whether it's
6
+ # `ready?`. Ready means there's data to read or, for a listener,
7
+ # an incoming connection waiting.
8
+ #
9
+ # selector = SFML::Network::SocketSelector.new
10
+ # selector.add(listener)
11
+ # clients.each { |c| selector.add(c) }
12
+ #
13
+ # if selector.wait(timeout: 1.0)
14
+ # if selector.ready?(listener)
15
+ # # accept() will not block
16
+ # end
17
+ # clients.each do |c|
18
+ # next unless selector.ready?(c)
19
+ # # receive() will return data
20
+ # end
21
+ # end
22
+ #
23
+ # Ruby's `IO.select` covers a similar use case for plain Ruby IO
24
+ # objects; this binding exists for game code that's already using
25
+ # `SFML::Network::Tcp{Socket,Listener}` / `UdpSocket`.
26
+ class SocketSelector
27
+ def initialize
28
+ ptr = C::Network.sfSocketSelector_create
29
+ raise Error, "sfSocketSelector_create returned NULL" if ptr.null?
30
+
31
+ @handle = FFI::AutoPointer.new(ptr, C::Network.method(:sfSocketSelector_destroy))
32
+ end
33
+
34
+ def add(socket)
35
+ case socket
36
+ when TcpListener then C::Network.sfSocketSelector_addTcpListener(@handle, socket.handle)
37
+ when TcpSocket then C::Network.sfSocketSelector_addTcpSocket(@handle, socket.handle)
38
+ when UdpSocket then C::Network.sfSocketSelector_addUdpSocket(@handle, socket.handle)
39
+ else
40
+ raise ArgumentError,
41
+ "SocketSelector#add expects TcpListener, TcpSocket, or UdpSocket (got #{socket.class})"
42
+ end
43
+ self
44
+ end
45
+
46
+ def remove(socket)
47
+ case socket
48
+ when TcpListener then C::Network.sfSocketSelector_removeTcpListener(@handle, socket.handle)
49
+ when TcpSocket then C::Network.sfSocketSelector_removeTcpSocket(@handle, socket.handle)
50
+ when UdpSocket then C::Network.sfSocketSelector_removeUdpSocket(@handle, socket.handle)
51
+ else
52
+ raise ArgumentError,
53
+ "SocketSelector#remove expects TcpListener, TcpSocket, or UdpSocket (got #{socket.class})"
54
+ end
55
+ self
56
+ end
57
+
58
+ def clear
59
+ C::Network.sfSocketSelector_clear(@handle)
60
+ self
61
+ end
62
+
63
+ # Block until at least one registered socket has activity, or
64
+ # `timeout` elapses. `timeout` may be a SFML::Time, a Numeric
65
+ # (seconds), or nil / SFML::Time.zero (no timeout — block
66
+ # forever). Returns true if a socket is ready, false on timeout.
67
+ def wait(timeout: nil)
68
+ t =
69
+ case timeout
70
+ when nil then SFML::Time.zero
71
+ when Time then timeout
72
+ when Numeric then SFML::Time.seconds(timeout.to_f)
73
+ else
74
+ raise ArgumentError, "timeout must be SFML::Time, Numeric, or nil"
75
+ end
76
+ C::Network.sfSocketSelector_wait(@handle, t.to_native)
77
+ end
78
+
79
+ def ready?(socket)
80
+ case socket
81
+ when TcpListener then C::Network.sfSocketSelector_isTcpListenerReady(@handle, socket.handle)
82
+ when TcpSocket then C::Network.sfSocketSelector_isTcpSocketReady(@handle, socket.handle)
83
+ when UdpSocket then C::Network.sfSocketSelector_isUdpSocketReady(@handle, socket.handle)
84
+ else
85
+ raise ArgumentError,
86
+ "SocketSelector#ready? expects TcpListener, TcpSocket, or UdpSocket (got #{socket.class})"
87
+ end
88
+ end
89
+
90
+ attr_reader :handle # :nodoc:
91
+ end
92
+ end
93
+ end
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.0"
18
+ VERSION = "3.0.0.1"
19
19
  end
@@ -122,6 +122,20 @@ module SFML
122
122
  c = C::Window::JoystickConnectEvent.new(ptr)
123
123
  { joystick_id: c[:joystick_id] }
124
124
 
125
+ when :touch_began, :touch_moved, :touch_ended
126
+ t = C::Window::TouchEvent.new(ptr)
127
+ {
128
+ finger: t[:finger],
129
+ position: Vector2.new(t[:position][:x], t[:position][:y]),
130
+ }
131
+
132
+ when :sensor_changed
133
+ s = C::Window::SensorEvent.new(ptr)
134
+ {
135
+ sensor: Sensor::TYPES[s[:sensor]] || :unknown,
136
+ value: Vector3.new(s[:value][:x], s[:value][:y], s[:value][:z]),
137
+ }
138
+
125
139
  else
126
140
  EMPTY
127
141
  end
@@ -0,0 +1,50 @@
1
+ module SFML
2
+ # Mobile-platform inertial / environment sensors. SFML treats every
3
+ # device the same way: identify the sensor by symbol, ask if it's
4
+ # available, enable it before reading, then poll a Vector3 value
5
+ # whose components depend on the sensor type (e.g. acceleration in
6
+ # m/s² for `:accelerometer`, angular velocity in deg/s for
7
+ # `:gyroscope`, magnetic field in µT for `:magnetometer`).
8
+ #
9
+ # if SFML::Sensor.available?(:accelerometer)
10
+ # SFML::Sensor.enable(:accelerometer)
11
+ # gravity = SFML::Sensor.value(:accelerometer)
12
+ # end
13
+ #
14
+ # On desktop platforms without sensor hardware, `available?` returns
15
+ # `false` and `value` returns the zero vector — calls don't raise.
16
+ #
17
+ # Sensor data also surfaces through the event loop as
18
+ # `{type: :sensor_changed, sensor: :accelerometer, value: Vector3}`.
19
+ module Sensor
20
+ TYPES = C::Window::SENSOR_TYPES
21
+ TYPE_INDEX = TYPES.each_with_index.to_h.freeze
22
+
23
+ module_function
24
+
25
+ def available?(type)
26
+ C::Window.sfSensor_isAvailable(_index(type))
27
+ end
28
+
29
+ def enable(type)
30
+ C::Window.sfSensor_setEnabled(_index(type), true)
31
+ end
32
+
33
+ def disable(type)
34
+ C::Window.sfSensor_setEnabled(_index(type), false)
35
+ end
36
+
37
+ def value(type)
38
+ v = C::Window.sfSensor_getValue(_index(type))
39
+ Vector3.new(v[:x], v[:y], v[:z])
40
+ end
41
+
42
+ # @!visibility private
43
+ def _index(type)
44
+ TYPE_INDEX.fetch(type) do
45
+ raise ArgumentError, "Unknown sensor type: #{type.inspect} " \
46
+ "(expected one of #{TYPES.inspect})"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,39 @@
1
+ module SFML
2
+ # Polling API for touchscreen input. Each finger is identified by an
3
+ # integer (0 = first contact, 1 = second, etc.). The same fingers
4
+ # also surface through the event loop as `:touch_began`,
5
+ # `:touch_moved`, `:touch_ended` events with `finger:` and
6
+ # `position:` fields.
7
+ #
8
+ # if SFML::Touch.down?(0)
9
+ # pos = SFML::Touch.position(0, relative_to: window)
10
+ # ...
11
+ # end
12
+ #
13
+ # On desktop platforms without touchscreen hardware these always
14
+ # return `false` / `[0, 0]`.
15
+ module Touch
16
+ module_function
17
+
18
+ # True while finger `n` is currently in contact with the screen.
19
+ def down?(finger = 0)
20
+ C::Window.sfTouch_isDown(Integer(finger))
21
+ end
22
+
23
+ # Position of finger `n`. Without `relative_to:`, returns
24
+ # desktop-relative coordinates; pass a Window or RenderWindow to
25
+ # get window-local coordinates.
26
+ def position(finger = 0, relative_to: nil)
27
+ f = Integer(finger)
28
+ vec =
29
+ case relative_to
30
+ when nil then C::Window.sfTouch_getPosition(f, nil)
31
+ when RenderWindow then C::Graphics.sfTouch_getPositionRenderWindow(f, relative_to.handle)
32
+ when Window then C::Window.sfTouch_getPosition(f, relative_to.handle)
33
+ else
34
+ raise ArgumentError, "relative_to: must be SFML::Window, SFML::RenderWindow, or nil"
35
+ end
36
+ Vector2.new(vec[:x], vec[:y])
37
+ end
38
+ end
39
+ end
@@ -129,10 +129,67 @@ module SFML
129
129
  C::Window.sfWindow_setActive(@handle, value ? true : false)
130
130
  end
131
131
 
132
+ # Replace the window's title-bar / taskbar icon with the pixels from
133
+ # the given SFML::Image. The OS scales it as needed; 32×32 RGBA
134
+ # is the typical sweet spot.
135
+ def icon=(image)
136
+ raise ArgumentError, "Window#icon= requires a SFML::Image" unless image.is_a?(SFML::Image)
137
+
138
+ size = C::System::Vector2u.new
139
+ size[:x] = image.width
140
+ size[:y] = image.height
141
+ C::Window.sfWindow_setIcon(@handle, size, C::Graphics.sfImage_getPixelsPtr(image.handle))
142
+ end
143
+
144
+ # Constrain user-driven resizes. Accepts a [w, h] Array, a Vector2,
145
+ # or nil to clear the limit. When set, the OS won't let the user
146
+ # drag the window smaller (or larger) than this — programmatic
147
+ # `size=` is not affected.
148
+ def minimum_size=(value)
149
+ C::Window.sfWindow_setMinimumSize(@handle, _vec2u_or_nil(value))
150
+ end
151
+
152
+ def maximum_size=(value)
153
+ C::Window.sfWindow_setMaximumSize(@handle, _vec2u_or_nil(value))
154
+ end
155
+
156
+ # OS-specific native handle for the underlying window — `HWND` on
157
+ # Windows, `NSView*` on macOS, X11 `Window` xid on Linux.
158
+ # Returns an FFI::Pointer; cast or read as the platform expects.
159
+ def native_handle
160
+ C::Window.sfWindow_getNativeHandle(@handle)
161
+ end
162
+
163
+ # Wrap an existing OS-level window. `handle` is a platform native
164
+ # handle (Integer address or FFI::Pointer). Useful when SFML is
165
+ # being embedded inside another framework (Qt, Gtk, raw Win32,
166
+ # Cocoa NSView). The framework owns the window's lifecycle; SFML
167
+ # only renders into it.
168
+ def self.from_handle(handle)
169
+ ptr = handle.is_a?(FFI::Pointer) ? handle : FFI::Pointer.new(:void, Integer(handle))
170
+ raw = C::Window.sfWindow_createFromHandle(ptr, nil)
171
+ raise Error, "sfWindow_createFromHandle returned NULL" if raw.null?
172
+
173
+ win = allocate
174
+ win.instance_variable_set(:@handle,
175
+ FFI::AutoPointer.new(raw, C::Window.method(:sfWindow_destroy)))
176
+ win.instance_variable_set(:@event_buffer, C::Window::Event.new)
177
+ win
178
+ end
179
+
132
180
  attr_reader :handle # :nodoc:
133
181
 
134
182
  private
135
183
 
184
+ def _vec2u_or_nil(value)
185
+ return nil if value.nil?
186
+
187
+ vec = value.is_a?(Vector2) ? value : Vector2.new(*value)
188
+ v = C::System::Vector2u.new
189
+ v[:x] = Integer(vec.x); v[:y] = Integer(vec.y)
190
+ v
191
+ end
192
+
136
193
  def parse_args(args)
137
194
  case args.length
138
195
  when 2
data/lib/sfml.rb CHANGED
@@ -32,13 +32,28 @@ at_exit do
32
32
  else 1
33
33
  end
34
34
 
35
- # 2. Quiet the audio thread before anything else OpenAL holds onto
35
+ # 2. If we're exiting because of an unhandled exception, print it
36
+ # ourselves — `exit!` below skips Ruby's terminal exception
37
+ # reporter, so without this the user sees a silent exit instead
38
+ # of the stack trace they'd normally get.
39
+ if $! && !$!.is_a?(SystemExit)
40
+ err = $!
41
+ warn "#{err.backtrace.first}: #{err.message} (#{err.class})"
42
+ err.backtrace.drop(1).each { |line| warn "\tfrom #{line}" }
43
+ end
44
+
45
+ # 3. Quiet the audio thread before anything else — OpenAL holds onto
36
46
  # sample buffers and crashes if Ruby starts freeing them while
37
47
  # a Sound/Music is mid-loop.
38
48
  ObjectSpace.each_object(SFML::Sound) { |s| s.stop rescue nil } if defined?(SFML::Sound)
39
49
  ObjectSpace.each_object(SFML::Music) { |m| m.stop rescue nil } if defined?(SFML::Music)
50
+ # SoundStream isn't stopped from this hook on purpose — by the time
51
+ # at_exit runs, finalizer ordering can have already destroyed
52
+ # the underlying CSFML stream, and CSFML asserts on a stale handle.
53
+ # Either explicitly #stop your SoundStream before exit, or rely on
54
+ # exit!() below to skip the audio thread's natural teardown.
40
55
 
41
- # 3. Bypass Ruby's natural finalizer pass entirely. Process memory is
56
+ # 4. Bypass Ruby's natural finalizer pass entirely. Process memory is
42
57
  # about to be reclaimed by the kernel anyway, and Ruby's
43
58
  # non-deterministic destruction order races with CSFML's GL/audio
44
59
  # internals — segfaulting inside libopenal/libGL is the typical
@@ -59,6 +74,8 @@ require "sfml/system/rect"
59
74
  require "sfml/window/keyboard"
60
75
  require "sfml/window/mouse"
61
76
  require "sfml/window/joystick"
77
+ require "sfml/window/touch"
78
+ require "sfml/window/sensor"
62
79
  require "sfml/window/cursor"
63
80
  require "sfml/window/clipboard"
64
81
  require "sfml/window/video_mode"
@@ -74,25 +91,33 @@ require "sfml/graphics/rectangle_shape"
74
91
  require "sfml/graphics/convex_shape"
75
92
  require "sfml/graphics/vertex"
76
93
  require "sfml/graphics/vertex_array"
94
+ require "sfml/graphics/vertex_buffer"
77
95
  require "sfml/graphics/font"
78
96
  require "sfml/graphics/text"
79
97
  require "sfml/graphics/view"
80
98
  require "sfml/graphics/blend_mode"
99
+ require "sfml/graphics/stencil_mode"
81
100
  require "sfml/graphics/shader"
82
101
  require "sfml/graphics/transform"
83
102
  require "sfml/graphics/render_states"
84
103
  require "sfml/graphics/render_target"
85
104
  require "sfml/graphics/render_window"
86
105
  require "sfml/graphics/render_texture"
106
+ require "sfml/audio/internal"
87
107
  require "sfml/audio/sound_buffer"
108
+ require "sfml/audio/sound_cone"
88
109
  require "sfml/audio/sound"
89
110
  require "sfml/audio/music"
90
111
  require "sfml/audio/listener"
91
112
  require "sfml/audio/sound_recorder"
92
113
  require "sfml/audio/sound_buffer_recorder"
114
+ require "sfml/audio/sound_stream"
93
115
  require "sfml/network/ip_address"
94
116
  require "sfml/network/tcp_socket"
95
117
  require "sfml/network/tcp_listener"
96
118
  require "sfml/network/udp_socket"
119
+ require "sfml/network/socket_selector"
120
+ require "sfml/network/http"
121
+ require "sfml/network/ftp"
97
122
  require "sfml/assets"
98
123
  require "sfml/game"
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.0
4
+ version: 3.0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mykhailo Melnyk
@@ -82,12 +82,15 @@ files:
82
82
  - lib/sfml/assets.rb
83
83
  - lib/sfml/assets/fonts/DejaVuSans.LICENSE.txt
84
84
  - lib/sfml/assets/fonts/DejaVuSans.ttf
85
+ - lib/sfml/audio/internal.rb
85
86
  - lib/sfml/audio/listener.rb
86
87
  - lib/sfml/audio/music.rb
87
88
  - lib/sfml/audio/sound.rb
88
89
  - lib/sfml/audio/sound_buffer.rb
89
90
  - lib/sfml/audio/sound_buffer_recorder.rb
91
+ - lib/sfml/audio/sound_cone.rb
90
92
  - lib/sfml/audio/sound_recorder.rb
93
+ - lib/sfml/audio/sound_stream.rb
91
94
  - lib/sfml/c.rb
92
95
  - lib/sfml/c/audio.rb
93
96
  - lib/sfml/c/graphics.rb
@@ -108,14 +111,19 @@ files:
108
111
  - lib/sfml/graphics/render_window.rb
109
112
  - lib/sfml/graphics/shader.rb
110
113
  - lib/sfml/graphics/sprite.rb
114
+ - lib/sfml/graphics/stencil_mode.rb
111
115
  - lib/sfml/graphics/text.rb
112
116
  - lib/sfml/graphics/texture.rb
113
117
  - lib/sfml/graphics/transform.rb
114
118
  - lib/sfml/graphics/transformable.rb
115
119
  - lib/sfml/graphics/vertex.rb
116
120
  - lib/sfml/graphics/vertex_array.rb
121
+ - lib/sfml/graphics/vertex_buffer.rb
117
122
  - lib/sfml/graphics/view.rb
123
+ - lib/sfml/network/ftp.rb
124
+ - lib/sfml/network/http.rb
118
125
  - lib/sfml/network/ip_address.rb
126
+ - lib/sfml/network/socket_selector.rb
119
127
  - lib/sfml/network/tcp_listener.rb
120
128
  - lib/sfml/network/tcp_socket.rb
121
129
  - lib/sfml/network/udp_socket.rb
@@ -131,6 +139,8 @@ files:
131
139
  - lib/sfml/window/joystick.rb
132
140
  - lib/sfml/window/keyboard.rb
133
141
  - lib/sfml/window/mouse.rb
142
+ - lib/sfml/window/sensor.rb
143
+ - lib/sfml/window/touch.rb
134
144
  - lib/sfml/window/video_mode.rb
135
145
  - lib/sfml/window/window.rb
136
146
  - ruby-sfml.gemspec