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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +101 -0
- data/LICENSE.txt +21 -0
- data/README.md +245 -0
- data/ext/ruby-sfml/extconf.rb +69 -0
- data/lib/sfml/assets/fonts/DejaVuSans.LICENSE.txt +78 -0
- data/lib/sfml/assets/fonts/DejaVuSans.ttf +0 -0
- data/lib/sfml/assets.rb +121 -0
- data/lib/sfml/audio/listener.rb +55 -0
- data/lib/sfml/audio/music.rb +88 -0
- data/lib/sfml/audio/sound.rb +102 -0
- data/lib/sfml/audio/sound_buffer.rb +38 -0
- data/lib/sfml/audio/sound_buffer_recorder.rb +71 -0
- data/lib/sfml/audio/sound_recorder.rb +30 -0
- data/lib/sfml/c/audio.rb +106 -0
- data/lib/sfml/c/graphics.rb +425 -0
- data/lib/sfml/c/network.rb +79 -0
- data/lib/sfml/c/system.rb +43 -0
- data/lib/sfml/c/window.rb +186 -0
- data/lib/sfml/c.rb +72 -0
- data/lib/sfml/game.rb +101 -0
- data/lib/sfml/graphics/blend_mode.rb +108 -0
- data/lib/sfml/graphics/circle_shape.rb +67 -0
- data/lib/sfml/graphics/color.rb +89 -0
- data/lib/sfml/graphics/convex_shape.rb +82 -0
- data/lib/sfml/graphics/font.rb +67 -0
- data/lib/sfml/graphics/image.rb +125 -0
- data/lib/sfml/graphics/rectangle_shape.rb +62 -0
- data/lib/sfml/graphics/render_states.rb +56 -0
- data/lib/sfml/graphics/render_target.rb +146 -0
- data/lib/sfml/graphics/render_texture.rb +72 -0
- data/lib/sfml/graphics/render_window.rb +154 -0
- data/lib/sfml/graphics/shader.rb +132 -0
- data/lib/sfml/graphics/sprite.rb +75 -0
- data/lib/sfml/graphics/text.rb +144 -0
- data/lib/sfml/graphics/texture.rb +79 -0
- data/lib/sfml/graphics/transform.rb +150 -0
- data/lib/sfml/graphics/transformable.rb +74 -0
- data/lib/sfml/graphics/vertex.rb +53 -0
- data/lib/sfml/graphics/vertex_array.rb +114 -0
- data/lib/sfml/graphics/view.rb +126 -0
- data/lib/sfml/network/ip_address.rb +67 -0
- data/lib/sfml/network/tcp_listener.rb +61 -0
- data/lib/sfml/network/tcp_socket.rb +74 -0
- data/lib/sfml/network/udp_socket.rb +71 -0
- data/lib/sfml/system/clock.rb +44 -0
- data/lib/sfml/system/rect.rb +64 -0
- data/lib/sfml/system/time.rb +48 -0
- data/lib/sfml/system/vector2.rb +66 -0
- data/lib/sfml/system/vector3.rb +63 -0
- data/lib/sfml/version.rb +19 -0
- data/lib/sfml/window/clipboard.rb +38 -0
- data/lib/sfml/window/cursor.rb +68 -0
- data/lib/sfml/window/event.rb +133 -0
- data/lib/sfml/window/joystick.rb +90 -0
- data/lib/sfml/window/keyboard.rb +60 -0
- data/lib/sfml/window/mouse.rb +71 -0
- data/lib/sfml/window/video_mode.rb +37 -0
- data/lib/sfml/window/window.rb +149 -0
- data/lib/sfml.rb +98 -0
- data/ruby-sfml.gemspec +38 -0
- metadata +163 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
# Global gamepad / joystick state — peer to Keyboard and Mouse.
|
|
3
|
+
#
|
|
4
|
+
# if SFML::Joystick.connected?(0)
|
|
5
|
+
# x = SFML::Joystick.axis_position(0, :x) # -100.0 .. 100.0
|
|
6
|
+
# fire = SFML::Joystick.button_pressed?(0, 0)
|
|
7
|
+
# info = SFML::Joystick.identification(0)
|
|
8
|
+
# # => { name: "Xbox Controller", vendor_id: 0x045e, product_id: 0x028e }
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
# SFML supports up to MAX_COUNT joysticks (0..MAX_COUNT-1). Axes are
|
|
12
|
+
# addressed by symbol (:x, :y, :z, :r, :u, :v, :pov_x, :pov_y) so callers
|
|
13
|
+
# don't have to remember the sfJoystickAxis enum order.
|
|
14
|
+
module Joystick
|
|
15
|
+
# Order matches sfJoystickAxis in CSFML/Window/Joystick.h.
|
|
16
|
+
AXES = %i[x y z r u v pov_x pov_y].freeze
|
|
17
|
+
AXIS_INDEX = AXES.each_with_index.to_h.freeze
|
|
18
|
+
|
|
19
|
+
# Friendly aliases — POV axes are also called "hat" or "dpad" in some
|
|
20
|
+
# gamepad APIs.
|
|
21
|
+
AXIS_ALIASES = {
|
|
22
|
+
hat_x: :pov_x, hat_y: :pov_y,
|
|
23
|
+
dpad_x: :pov_x, dpad_y: :pov_y,
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
MAX_COUNT = 8
|
|
27
|
+
MAX_BUTTON_COUNT = 32
|
|
28
|
+
MAX_AXIS_COUNT = AXES.length
|
|
29
|
+
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
def connected?(joystick)
|
|
33
|
+
C::Window.sfJoystick_isConnected(_id(joystick))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def button_count(joystick)
|
|
37
|
+
C::Window.sfJoystick_getButtonCount(_id(joystick))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def has_axis?(joystick, axis)
|
|
41
|
+
C::Window.sfJoystick_hasAxis(_id(joystick), _axis_code(axis))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Axis value in the range [-100.0, 100.0]. Returns 0.0 for axes that
|
|
45
|
+
# don't exist on the device, so it's safe to call without first
|
|
46
|
+
# checking has_axis?.
|
|
47
|
+
def axis_position(joystick, axis)
|
|
48
|
+
C::Window.sfJoystick_getAxisPosition(_id(joystick), _axis_code(axis))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def button_pressed?(joystick, button)
|
|
52
|
+
C::Window.sfJoystick_isButtonPressed(_id(joystick), Integer(button))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Returns a Hash {name:, vendor_id:, product_id:} describing the
|
|
56
|
+
# device, or nil if the joystick isn't connected.
|
|
57
|
+
def identification(joystick)
|
|
58
|
+
return nil unless connected?(joystick)
|
|
59
|
+
ident = C::Window.sfJoystick_getIdentification(_id(joystick))
|
|
60
|
+
name_ptr = ident[:name]
|
|
61
|
+
{
|
|
62
|
+
name: name_ptr.null? ? "" : name_ptr.read_string.force_encoding("UTF-8"),
|
|
63
|
+
vendor_id: ident[:vendor_id],
|
|
64
|
+
product_id: ident[:product_id],
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Refresh joystick state. SFML auto-updates as part of pollEvent, so
|
|
69
|
+
# most code never needs this. Call it explicitly only if you're
|
|
70
|
+
# polling joystick state without an active event loop.
|
|
71
|
+
def update
|
|
72
|
+
C::Window.sfJoystick_update
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @!visibility private
|
|
76
|
+
def _id(joystick)
|
|
77
|
+
n = Integer(joystick)
|
|
78
|
+
raise ArgumentError, "Joystick id must be in 0..#{MAX_COUNT - 1}, got #{n}" if n < 0 || n >= MAX_COUNT
|
|
79
|
+
n
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @!visibility private
|
|
83
|
+
def _axis_code(axis)
|
|
84
|
+
sym = AXIS_ALIASES.fetch(axis, axis)
|
|
85
|
+
AXIS_INDEX.fetch(sym) do
|
|
86
|
+
raise ArgumentError, "Unknown joystick axis: #{axis.inspect}. Expected: #{AXES.inspect}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
# Keyboard key code <-> symbol translation, plus Keyboard.key_pressed?(:esc).
|
|
3
|
+
#
|
|
4
|
+
# The KEY_CODES array order is load-bearing: it matches the sfKeyCode enum
|
|
5
|
+
# in CSFML/Window/Keyboard.h exactly. sfKeyUnknown is -1 and represented
|
|
6
|
+
# here by :unknown returned via #code_to_symbol.
|
|
7
|
+
module Keyboard
|
|
8
|
+
KEY_CODES = %i[
|
|
9
|
+
a b c d e f g h i j k l m n o p q r s t u v w x y z
|
|
10
|
+
num0 num1 num2 num3 num4 num5 num6 num7 num8 num9
|
|
11
|
+
escape
|
|
12
|
+
l_control l_shift l_alt l_system
|
|
13
|
+
r_control r_shift r_alt r_system
|
|
14
|
+
menu
|
|
15
|
+
l_bracket r_bracket
|
|
16
|
+
semicolon comma period apostrophe slash backslash grave equal hyphen
|
|
17
|
+
space enter backspace tab
|
|
18
|
+
page_up page_down end_key home insert delete
|
|
19
|
+
add subtract multiply divide
|
|
20
|
+
left right up down
|
|
21
|
+
numpad0 numpad1 numpad2 numpad3 numpad4
|
|
22
|
+
numpad5 numpad6 numpad7 numpad8 numpad9
|
|
23
|
+
f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15
|
|
24
|
+
pause
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
SYMBOL_TO_CODE = KEY_CODES.each_with_index.to_h.freeze
|
|
28
|
+
|
|
29
|
+
# Friendly aliases users might reach for naturally.
|
|
30
|
+
ALIASES = {
|
|
31
|
+
esc: :escape,
|
|
32
|
+
lctrl: :l_control,
|
|
33
|
+
rctrl: :r_control,
|
|
34
|
+
lshift: :l_shift,
|
|
35
|
+
rshift: :r_shift,
|
|
36
|
+
space_bar: :space,
|
|
37
|
+
return: :enter,
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
40
|
+
module_function
|
|
41
|
+
|
|
42
|
+
def code_to_symbol(code)
|
|
43
|
+
return :unknown if code < 0 || code >= KEY_CODES.length
|
|
44
|
+
KEY_CODES[code]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def symbol_to_code(symbol)
|
|
48
|
+
symbol = ALIASES.fetch(symbol, symbol)
|
|
49
|
+
SYMBOL_TO_CODE.fetch(symbol) do
|
|
50
|
+
raise ArgumentError, "Unknown key symbol: #{symbol.inspect}. " \
|
|
51
|
+
"See SFML::Keyboard::KEY_CODES."
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# SFML::Keyboard.key_pressed?(:escape)
|
|
56
|
+
def key_pressed?(symbol)
|
|
57
|
+
C::Window.sfKeyboard_isKeyPressed(symbol_to_code(symbol))
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
# Global mouse state — peer to SFML::Keyboard. Use it for "is this
|
|
3
|
+
# button held right now?" and for current pointer coordinates outside
|
|
4
|
+
# of the event loop.
|
|
5
|
+
#
|
|
6
|
+
# SFML::Mouse.button_pressed?(:left) #=> true while LMB is held
|
|
7
|
+
# SFML::Mouse.position #=> Vector2 — desktop coords
|
|
8
|
+
# SFML::Mouse.position(window) #=> Vector2 — relative to window
|
|
9
|
+
# SFML::Mouse.set_position([400, 300], window)
|
|
10
|
+
#
|
|
11
|
+
# Buttons are addressed by symbol; the raw sfMouseButton enum order is
|
|
12
|
+
# exposed via BUTTONS for users who need it.
|
|
13
|
+
module Mouse
|
|
14
|
+
BUTTONS = %i[left right middle extra1 extra2].freeze
|
|
15
|
+
BUTTON_INDEX = BUTTONS.each_with_index.to_h.freeze
|
|
16
|
+
|
|
17
|
+
# Friendly aliases — SFML 2 used "X-button" terminology; some users
|
|
18
|
+
# still reach for it.
|
|
19
|
+
ALIASES = {
|
|
20
|
+
x1: :extra1,
|
|
21
|
+
x2: :extra2,
|
|
22
|
+
x_button1: :extra1,
|
|
23
|
+
x_button2: :extra2,
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
# Returns true if the named mouse button is currently held.
|
|
29
|
+
def button_pressed?(button)
|
|
30
|
+
C::Window.sfMouse_isButtonPressed(_code(button))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Pointer position. With no argument, returns desktop-relative
|
|
34
|
+
# coordinates. With a RenderWindow, returns coordinates relative to
|
|
35
|
+
# that window's client area (top-left = 0, 0).
|
|
36
|
+
def position(window = nil)
|
|
37
|
+
vec = if window
|
|
38
|
+
C::Graphics.sfMouse_getPositionRenderWindow(window.handle)
|
|
39
|
+
else
|
|
40
|
+
C::Window.sfMouse_getPosition(nil)
|
|
41
|
+
end
|
|
42
|
+
Vector2.new(vec[:x], vec[:y])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Move the OS pointer. Without `window`, the coordinates are desktop-
|
|
46
|
+
# relative. Useful for FPS-style mouse-look (warp the cursor back to
|
|
47
|
+
# screen centre each frame).
|
|
48
|
+
def set_position(point, window = nil)
|
|
49
|
+
px, py = point.is_a?(Vector2) ? [point.x, point.y] : point
|
|
50
|
+
vec = C::System::Vector2i.new
|
|
51
|
+
vec[:x] = Integer(px)
|
|
52
|
+
vec[:y] = Integer(py)
|
|
53
|
+
|
|
54
|
+
if window
|
|
55
|
+
C::Graphics.sfMouse_setPositionRenderWindow(vec, window.handle)
|
|
56
|
+
else
|
|
57
|
+
C::Window.sfMouse_setPosition(vec, nil)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @!visibility private
|
|
62
|
+
def _code(button)
|
|
63
|
+
sym = ALIASES.fetch(button, button)
|
|
64
|
+
BUTTON_INDEX.fetch(sym) do
|
|
65
|
+
raise ArgumentError,
|
|
66
|
+
"Unknown mouse button: #{button.inspect}. " \
|
|
67
|
+
"Expected one of: #{BUTTONS.inspect}"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
# Video mode = (width, height, bits-per-pixel). Used when creating windows.
|
|
3
|
+
#
|
|
4
|
+
# SFML::VideoMode.new(800, 600)
|
|
5
|
+
# SFML::VideoMode.desktop_mode
|
|
6
|
+
class VideoMode
|
|
7
|
+
attr_reader :width, :height, :bits_per_pixel
|
|
8
|
+
|
|
9
|
+
def initialize(width, height, bits_per_pixel = 32)
|
|
10
|
+
@width = Integer(width)
|
|
11
|
+
@height = Integer(height)
|
|
12
|
+
@bits_per_pixel = Integer(bits_per_pixel)
|
|
13
|
+
freeze
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.desktop_mode
|
|
17
|
+
from_native(C::Window.sfVideoMode_getDesktopMode)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def size = Vector2.new(@width, @height)
|
|
21
|
+
|
|
22
|
+
def to_s = "#<SFML::VideoMode #{@width}x#{@height}@#{@bits_per_pixel}>"
|
|
23
|
+
alias inspect to_s
|
|
24
|
+
|
|
25
|
+
def self.from_native(struct) # :nodoc:
|
|
26
|
+
new(struct[:size][:x], struct[:size][:y], struct[:bits_per_pixel])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_native # :nodoc:
|
|
30
|
+
C::Window::VideoMode.new.tap do |m|
|
|
31
|
+
m[:size][:x] = @width
|
|
32
|
+
m[:size][:y] = @height
|
|
33
|
+
m[:bits_per_pixel] = @bits_per_pixel
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
# A bare window with input + an OpenGL context, **without** SFML's
|
|
3
|
+
# 2D batcher. Use this when you want raw OpenGL (or another
|
|
4
|
+
# rendering library) and just need SFML to manage the platform-level
|
|
5
|
+
# window and event loop. For 2D drawing with SFML's API, you want
|
|
6
|
+
# SFML::RenderWindow instead.
|
|
7
|
+
#
|
|
8
|
+
# win = SFML::Window.new(800, 600, "GL")
|
|
9
|
+
# while win.open?
|
|
10
|
+
# win.each_event do |event|
|
|
11
|
+
# case event
|
|
12
|
+
# in {type: :closed} then win.close
|
|
13
|
+
# in {type: :key_pressed, code: :escape} then win.close
|
|
14
|
+
# else
|
|
15
|
+
# end
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# # ... draw with raw OpenGL calls here ...
|
|
19
|
+
#
|
|
20
|
+
# win.display
|
|
21
|
+
# end
|
|
22
|
+
class Window
|
|
23
|
+
DEFAULT_STYLE = C::Window::Style::DEFAULT
|
|
24
|
+
|
|
25
|
+
def initialize(*args, **opts)
|
|
26
|
+
mode, title = parse_args(args)
|
|
27
|
+
style = opts.fetch(:style, DEFAULT_STYLE)
|
|
28
|
+
state = opts[:fullscreen] ? :fullscreen : :windowed
|
|
29
|
+
|
|
30
|
+
ptr = C::Window.sfWindow_create(
|
|
31
|
+
mode.to_native,
|
|
32
|
+
title.to_s,
|
|
33
|
+
style,
|
|
34
|
+
C::Window::State[state],
|
|
35
|
+
nil,
|
|
36
|
+
)
|
|
37
|
+
raise Error, "sfWindow_create returned NULL" if ptr.null?
|
|
38
|
+
|
|
39
|
+
@handle = FFI::AutoPointer.new(ptr, C::Window.method(:sfWindow_destroy))
|
|
40
|
+
@event_buffer = C::Window::Event.new
|
|
41
|
+
|
|
42
|
+
self.framerate_limit = opts[:framerate] if opts[:framerate]
|
|
43
|
+
self.vsync = opts[:vsync] unless opts[:vsync].nil?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def open?
|
|
47
|
+
C::Window.sfWindow_isOpen(@handle)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def close
|
|
51
|
+
C::Window.sfWindow_close(@handle)
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def display
|
|
56
|
+
C::Window.sfWindow_display(@handle)
|
|
57
|
+
self
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns the next pending Event or nil. Same shape as RenderWindow#poll_event.
|
|
61
|
+
def poll_event
|
|
62
|
+
return nil unless C::Window.sfWindow_pollEvent(@handle, @event_buffer)
|
|
63
|
+
Event.from_native(@event_buffer)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def each_event
|
|
67
|
+
return enum_for(:each_event) unless block_given?
|
|
68
|
+
while (event = poll_event)
|
|
69
|
+
yield event
|
|
70
|
+
end
|
|
71
|
+
self
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def title=(value)
|
|
75
|
+
C::Window.sfWindow_setTitle(@handle, value.to_s)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def size
|
|
79
|
+
v = C::Window.sfWindow_getSize(@handle)
|
|
80
|
+
Vector2.new(v[:x], v[:y])
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def size=(value)
|
|
84
|
+
vec = value.is_a?(Vector2) ? value : Vector2.new(*value)
|
|
85
|
+
v = C::System::Vector2u.new
|
|
86
|
+
v[:x] = Integer(vec.x); v[:y] = Integer(vec.y)
|
|
87
|
+
C::Window.sfWindow_setSize(@handle, v)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def position
|
|
91
|
+
v = C::Window.sfWindow_getPosition(@handle)
|
|
92
|
+
Vector2.new(v[:x], v[:y])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def position=(value)
|
|
96
|
+
vec = value.is_a?(Vector2) ? value : Vector2.new(*value)
|
|
97
|
+
v = C::System::Vector2i.new
|
|
98
|
+
v[:x] = Integer(vec.x); v[:y] = Integer(vec.y)
|
|
99
|
+
C::Window.sfWindow_setPosition(@handle, v)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def visible=(value)
|
|
103
|
+
C::Window.sfWindow_setVisible(@handle, value ? true : false)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def framerate_limit=(value)
|
|
107
|
+
C::Window.sfWindow_setFramerateLimit(@handle, Integer(value))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def vsync=(enabled)
|
|
111
|
+
C::Window.sfWindow_setVerticalSyncEnabled(@handle, enabled ? true : false)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def key_repeat_enabled=(value)
|
|
115
|
+
C::Window.sfWindow_setKeyRepeatEnabled(@handle, value ? true : false)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def request_focus
|
|
119
|
+
C::Window.sfWindow_requestFocus(@handle)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def focused?
|
|
123
|
+
C::Window.sfWindow_hasFocus(@handle)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Make this window's GL context current on the calling thread.
|
|
127
|
+
# Useful when juggling multiple windows / off-screen contexts.
|
|
128
|
+
def active=(value)
|
|
129
|
+
C::Window.sfWindow_setActive(@handle, value ? true : false)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
attr_reader :handle # :nodoc:
|
|
133
|
+
|
|
134
|
+
private
|
|
135
|
+
|
|
136
|
+
def parse_args(args)
|
|
137
|
+
case args.length
|
|
138
|
+
when 2
|
|
139
|
+
[args[0], args[1]]
|
|
140
|
+
when 3
|
|
141
|
+
[VideoMode.new(args[0], args[1]), args[2]]
|
|
142
|
+
else
|
|
143
|
+
raise ArgumentError,
|
|
144
|
+
"Window.new takes either (video_mode, title) or " \
|
|
145
|
+
"(width, height, title), got #{args.length} positional arg(s)"
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
data/lib/sfml.rb
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require "sfml/version"
|
|
2
|
+
|
|
3
|
+
module SFML
|
|
4
|
+
class Error < StandardError; end
|
|
5
|
+
class LoadError < Error; end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Tame process-exit teardown so CSFML doesn't crash.
|
|
9
|
+
#
|
|
10
|
+
# Two things race during a normal Ruby exit:
|
|
11
|
+
#
|
|
12
|
+
# 1. ObjectSpace finalizers run in non-deterministic order. CSFML's
|
|
13
|
+
# GL-bound resources (RenderWindow's GL context, Font glyph atlases,
|
|
14
|
+
# View copies) freed in the wrong sequence segfault inside the
|
|
15
|
+
# SFML/GL stack. Setting autorelease = false on every live
|
|
16
|
+
# FFI::AutoPointer skips the destruction; the OS reclaims memory
|
|
17
|
+
# anyway, so this costs nothing.
|
|
18
|
+
#
|
|
19
|
+
# 2. SFML's audio thread is still pumping samples while Ruby starts to
|
|
20
|
+
# tear the interpreter down. If a `Sound` or `Music` is mid-loop,
|
|
21
|
+
# OpenAL is reading buffers we're about to free → segfault inside
|
|
22
|
+
# libopenal. Stopping every live source first quiets the audio
|
|
23
|
+
# thread before anything starts disappearing.
|
|
24
|
+
#
|
|
25
|
+
# Unreferenced objects still get cleaned up at runtime via the normal
|
|
26
|
+
# GC cycle; we only neuter the *teardown-time* pass.
|
|
27
|
+
at_exit do
|
|
28
|
+
# 1. Capture the desired exit status before we tamper with anything.
|
|
29
|
+
status =
|
|
30
|
+
if $!.is_a?(SystemExit) then $!.status
|
|
31
|
+
elsif $!.nil? then 0
|
|
32
|
+
else 1
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# 2. Quiet the audio thread before anything else — OpenAL holds onto
|
|
36
|
+
# sample buffers and crashes if Ruby starts freeing them while
|
|
37
|
+
# a Sound/Music is mid-loop.
|
|
38
|
+
ObjectSpace.each_object(SFML::Sound) { |s| s.stop rescue nil } if defined?(SFML::Sound)
|
|
39
|
+
ObjectSpace.each_object(SFML::Music) { |m| m.stop rescue nil } if defined?(SFML::Music)
|
|
40
|
+
|
|
41
|
+
# 3. Bypass Ruby's natural finalizer pass entirely. Process memory is
|
|
42
|
+
# about to be reclaimed by the kernel anyway, and Ruby's
|
|
43
|
+
# non-deterministic destruction order races with CSFML's GL/audio
|
|
44
|
+
# internals — segfaulting inside libopenal/libGL is the typical
|
|
45
|
+
# failure mode. exit! kills the interpreter cleanly via _exit(2).
|
|
46
|
+
#
|
|
47
|
+
# Caveat: any user `at_exit` hook registered *before* `require "sfml"`
|
|
48
|
+
# won't run. Hooks registered after the require run first (LIFO),
|
|
49
|
+
# then our hook, so the common case is unaffected.
|
|
50
|
+
exit!(status)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
require "sfml/c"
|
|
54
|
+
require "sfml/system/time"
|
|
55
|
+
require "sfml/system/clock"
|
|
56
|
+
require "sfml/system/vector2"
|
|
57
|
+
require "sfml/system/vector3"
|
|
58
|
+
require "sfml/system/rect"
|
|
59
|
+
require "sfml/window/keyboard"
|
|
60
|
+
require "sfml/window/mouse"
|
|
61
|
+
require "sfml/window/joystick"
|
|
62
|
+
require "sfml/window/cursor"
|
|
63
|
+
require "sfml/window/clipboard"
|
|
64
|
+
require "sfml/window/video_mode"
|
|
65
|
+
require "sfml/window/event"
|
|
66
|
+
require "sfml/window/window"
|
|
67
|
+
require "sfml/graphics/color"
|
|
68
|
+
require "sfml/graphics/transformable"
|
|
69
|
+
require "sfml/graphics/image"
|
|
70
|
+
require "sfml/graphics/texture"
|
|
71
|
+
require "sfml/graphics/sprite"
|
|
72
|
+
require "sfml/graphics/circle_shape"
|
|
73
|
+
require "sfml/graphics/rectangle_shape"
|
|
74
|
+
require "sfml/graphics/convex_shape"
|
|
75
|
+
require "sfml/graphics/vertex"
|
|
76
|
+
require "sfml/graphics/vertex_array"
|
|
77
|
+
require "sfml/graphics/font"
|
|
78
|
+
require "sfml/graphics/text"
|
|
79
|
+
require "sfml/graphics/view"
|
|
80
|
+
require "sfml/graphics/blend_mode"
|
|
81
|
+
require "sfml/graphics/shader"
|
|
82
|
+
require "sfml/graphics/transform"
|
|
83
|
+
require "sfml/graphics/render_states"
|
|
84
|
+
require "sfml/graphics/render_target"
|
|
85
|
+
require "sfml/graphics/render_window"
|
|
86
|
+
require "sfml/graphics/render_texture"
|
|
87
|
+
require "sfml/audio/sound_buffer"
|
|
88
|
+
require "sfml/audio/sound"
|
|
89
|
+
require "sfml/audio/music"
|
|
90
|
+
require "sfml/audio/listener"
|
|
91
|
+
require "sfml/audio/sound_recorder"
|
|
92
|
+
require "sfml/audio/sound_buffer_recorder"
|
|
93
|
+
require "sfml/network/ip_address"
|
|
94
|
+
require "sfml/network/tcp_socket"
|
|
95
|
+
require "sfml/network/tcp_listener"
|
|
96
|
+
require "sfml/network/udp_socket"
|
|
97
|
+
require "sfml/assets"
|
|
98
|
+
require "sfml/game"
|
data/ruby-sfml.gemspec
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require_relative "lib/sfml/version"
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = "ruby-sfml"
|
|
5
|
+
spec.version = SFML::VERSION
|
|
6
|
+
spec.authors = ["Mykhailo Melnyk"]
|
|
7
|
+
spec.email = ["m1kh41l.melnyk@gmail.com"]
|
|
8
|
+
|
|
9
|
+
spec.summary = "Modern Ruby bindings for SFML 3.x"
|
|
10
|
+
spec.description = "Idiomatic Ruby bindings for SFML 3 via CSFML and FFI. Build games and multimedia apps in Ruby."
|
|
11
|
+
spec.homepage = "https://github.com/sOM2H/ruby-sfml"
|
|
12
|
+
spec.license = "MIT"
|
|
13
|
+
spec.required_ruby_version = ">= 3.2.0"
|
|
14
|
+
|
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
16
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
17
|
+
spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
|
|
18
|
+
spec.metadata["documentation_uri"] = "https://www.rubydoc.info/gems/#{spec.name}"
|
|
19
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
20
|
+
|
|
21
|
+
spec.files = Dir[
|
|
22
|
+
"lib/**/*.rb",
|
|
23
|
+
"lib/sfml/assets/**/*",
|
|
24
|
+
"ext/**/*.rb",
|
|
25
|
+
"README.md",
|
|
26
|
+
"CHANGELOG.md",
|
|
27
|
+
"LICENSE.txt",
|
|
28
|
+
"ruby-sfml.gemspec"
|
|
29
|
+
]
|
|
30
|
+
spec.require_paths = ["lib"]
|
|
31
|
+
spec.extensions = ["ext/ruby-sfml/extconf.rb"]
|
|
32
|
+
|
|
33
|
+
spec.add_dependency "ffi", "~> 1.16"
|
|
34
|
+
|
|
35
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
36
|
+
spec.add_development_dependency "rspec", "~> 3.13"
|
|
37
|
+
spec.add_development_dependency "rdoc", "~> 7.0"
|
|
38
|
+
end
|