rbgl 1.0.0 → 1.0.2
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 +4 -4
- data/.codespellignore +4 -0
- data/.rubocop.yml +3 -0
- data/.yamllint +16 -0
- data/CHANGELOG.md +11 -1
- data/README.md +130 -74
- data/Rakefile +7 -0
- data/bench/renderer_bench.rb +283 -0
- data/docs/.nojekyll +1 -0
- data/docs/index.html +38 -0
- data/examples/array_test.rb +1 -1
- data/examples/color_test.rb +1 -1
- data/examples/cube_spinning.rb +1 -1
- data/examples/gradient.rb +1 -1
- data/examples/multi_return_test.rb +1 -1
- data/examples/plasma.rb +1 -1
- data/examples/sphere_raymarch.rb +1 -1
- data/examples/triangle_window.rb +1 -1
- data/exe/rbgl +42 -0
- data/lib/rbgl/cli/doctor.rb +155 -0
- data/lib/rbgl/engine/buffer.rb +55 -9
- data/lib/rbgl/engine/clip_space_clipper.rb +23 -3
- data/lib/rbgl/engine/context.rb +23 -6
- data/lib/rbgl/engine/framebuffer.rb +146 -41
- data/lib/rbgl/engine/immutable_color.rb +32 -0
- data/lib/rbgl/engine/rasterizer/attribute_interpolator.rb +100 -10
- data/lib/rbgl/engine/rasterizer/line_renderer.rb +30 -14
- data/lib/rbgl/engine/rasterizer/point_renderer.rb +3 -3
- data/lib/rbgl/engine/rasterizer/triangle_renderer.rb +72 -23
- data/lib/rbgl/engine/rasterizer.rb +3 -20
- data/lib/rbgl/engine/shader/base_shader.rb +3 -7
- data/lib/rbgl/engine/shader/builtins.rb +13 -10
- data/lib/rbgl/engine/shader/dynamic_data.rb +18 -10
- data/lib/rbgl/engine/texture.rb +74 -50
- data/lib/rbgl/engine.rb +3 -1
- data/lib/rbgl/gui/backend.rb +12 -7
- data/lib/rbgl/gui/backend_factory.rb +7 -3
- data/lib/rbgl/gui/cocoa/backend.rb +50 -17
- data/lib/rbgl/gui/cocoa/key_mapper.rb +48 -0
- data/lib/rbgl/gui/event.rb +4 -2
- data/lib/rbgl/gui/file_backend/bmp_writer.rb +2 -2
- data/lib/rbgl/gui/file_backend/frame_writer.rb +7 -0
- data/lib/rbgl/gui/file_backend/png_writer.rb +18 -0
- data/lib/rbgl/gui/file_backend/ppm_writer.rb +1 -0
- data/lib/rbgl/gui/file_backend.rb +24 -6
- data/lib/rbgl/gui/wayland/backend.rb +127 -55
- data/lib/rbgl/gui/wayland/codec.rb +7 -11
- data/lib/rbgl/gui/wayland/connection.rb +137 -20
- data/lib/rbgl/gui/wayland/event_dispatcher.rb +103 -12
- data/lib/rbgl/gui/wayland/global_binder.rb +6 -4
- data/lib/rbgl/gui/wayland/input_mapper.rb +61 -0
- data/lib/rbgl/gui/wayland/protocol_objects/arguments.rb +14 -2
- data/lib/rbgl/gui/wayland/protocol_objects/seat.rb +115 -0
- data/lib/rbgl/gui/wayland/protocol_objects/shm.rb +23 -12
- data/lib/rbgl/gui/wayland/protocol_objects/xdg_shell.rb +15 -0
- data/lib/rbgl/gui/wayland/protocol_objects.rb +1 -0
- data/lib/rbgl/gui/window/render_loop.rb +71 -18
- data/lib/rbgl/gui/window.rb +39 -8
- data/lib/rbgl/gui/x11/atom_cache.rb +4 -1
- data/lib/rbgl/gui/x11/backend.rb +66 -28
- data/lib/rbgl/gui/x11/connection.rb +216 -86
- data/lib/rbgl/gui/x11/event_parser.rb +13 -2
- data/lib/rbgl/gui/x11/key_mapper.rb +48 -0
- data/lib/rbgl/gui/x11/pixel_encoder.rb +164 -0
- data/lib/rbgl/gui/x11/request_encoder.rb +29 -28
- data/lib/rbgl/gui/x11/setup_parser.rb +138 -0
- data/lib/rbgl/gui/x11/transport.rb +52 -2
- data/lib/rbgl/gui/x11/x_authority.rb +107 -0
- data/lib/rbgl/gui.rb +2 -5
- data/lib/rbgl/version.rb +1 -1
- metadata +38 -24
- data/examples/chemical_heartbeat.rb +0 -166
- data/examples/dark_transit.rb +0 -166
- data/examples/fractured_orb.rb +0 -428
- data/examples/fractured_orb_rb.rb +0 -598
- data/examples/hexagonal_flow.rb +0 -333
- data/examples/teapot.rb +0 -362
- data/examples/teapot_mcu.rb +0 -344
data/lib/rbgl/gui/window.rb
CHANGED
|
@@ -8,10 +8,11 @@ module RBGL
|
|
|
8
8
|
class Window
|
|
9
9
|
attr_reader :context, :backend, :width, :height
|
|
10
10
|
|
|
11
|
-
def initialize(width:, height:, title: "RBGL", backend: :auto, **options)
|
|
11
|
+
def initialize(width:, height:, title: "RBGL", backend: :auto, target_fps: 60, **options)
|
|
12
12
|
@width = width
|
|
13
13
|
@height = height
|
|
14
14
|
@title = title
|
|
15
|
+
@render_loop = RenderLoop.new(target_fps: target_fps)
|
|
15
16
|
@context = Engine::Context.new(width: width, height: height)
|
|
16
17
|
@backend = build_backend(backend, width: width, height: height, title: title, **options)
|
|
17
18
|
@event_handlers = Hash.new { |h, k| h[k] = [] }
|
|
@@ -20,7 +21,7 @@ module RBGL
|
|
|
20
21
|
event_handlers: @event_handlers,
|
|
21
22
|
on_resize: method(:apply_resize)
|
|
22
23
|
)
|
|
23
|
-
@
|
|
24
|
+
@closed = false
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
def on(event_type, &block)
|
|
@@ -29,6 +30,8 @@ module RBGL
|
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def run(&frame_callback)
|
|
33
|
+
raise RuntimeError, "Window#run is one-shot and cannot be called after the window is closed" if @closed
|
|
34
|
+
|
|
32
35
|
@render_loop.run(
|
|
33
36
|
backend: @backend,
|
|
34
37
|
context: @context,
|
|
@@ -36,7 +39,25 @@ module RBGL
|
|
|
36
39
|
&frame_callback
|
|
37
40
|
)
|
|
38
41
|
ensure
|
|
39
|
-
|
|
42
|
+
original_error = $!
|
|
43
|
+
begin
|
|
44
|
+
close if @backend
|
|
45
|
+
rescue StandardError
|
|
46
|
+
raise unless original_error
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Advances one frame. `now` is seconds on a monotonic timeline.
|
|
51
|
+
def step(now = nil, &frame_callback)
|
|
52
|
+
return false if @closed
|
|
53
|
+
|
|
54
|
+
!!@render_loop.step(
|
|
55
|
+
backend: @backend,
|
|
56
|
+
context: @context,
|
|
57
|
+
process_events: method(:process_events),
|
|
58
|
+
now:,
|
|
59
|
+
&frame_callback
|
|
60
|
+
)
|
|
40
61
|
end
|
|
41
62
|
|
|
42
63
|
def stop
|
|
@@ -53,11 +74,11 @@ module RBGL
|
|
|
53
74
|
end
|
|
54
75
|
|
|
55
76
|
def metal_available?
|
|
56
|
-
@backend.metal_available?
|
|
77
|
+
@backend.respond_to?(:metal_available?) && @backend.metal_available?
|
|
57
78
|
end
|
|
58
79
|
|
|
59
80
|
def native_handle
|
|
60
|
-
@backend.native_handle
|
|
81
|
+
@backend.native_handle if @backend.respond_to?(:native_handle)
|
|
61
82
|
end
|
|
62
83
|
|
|
63
84
|
def should_close?
|
|
@@ -65,11 +86,21 @@ module RBGL
|
|
|
65
86
|
end
|
|
66
87
|
|
|
67
88
|
def poll_events_raw
|
|
68
|
-
@backend.poll_events_raw
|
|
89
|
+
events = @backend.poll_events_raw
|
|
90
|
+
Array(events).each do |event|
|
|
91
|
+
next unless event.respond_to?(:[]) && event[:type] == :resize
|
|
92
|
+
|
|
93
|
+
apply_resize(Event.new(:resize, width: event[:width], height: event[:height]))
|
|
94
|
+
end
|
|
95
|
+
events
|
|
69
96
|
end
|
|
70
97
|
|
|
71
98
|
def close
|
|
99
|
+
return if @closed
|
|
100
|
+
|
|
101
|
+
@render_loop.stop
|
|
72
102
|
@backend.close
|
|
103
|
+
@closed = true
|
|
73
104
|
end
|
|
74
105
|
|
|
75
106
|
def fps
|
|
@@ -91,10 +122,10 @@ module RBGL
|
|
|
91
122
|
end
|
|
92
123
|
|
|
93
124
|
def apply_resize(event)
|
|
125
|
+
@backend.resize(event.width, event.height)
|
|
126
|
+
@context.resize(width: event.width, height: event.height)
|
|
94
127
|
@width = event.width
|
|
95
128
|
@height = event.height
|
|
96
|
-
@context.resize(width: event.width, height: event.height)
|
|
97
|
-
@backend.resize(event.width, event.height)
|
|
98
129
|
end
|
|
99
130
|
|
|
100
131
|
def validate_event_handler!(event_type, block)
|
|
@@ -17,8 +17,11 @@ module RBGL
|
|
|
17
17
|
|
|
18
18
|
def fetch(name)
|
|
19
19
|
return name if name.is_a?(Integer)
|
|
20
|
+
return @cache[name] if @cache.key?(name)
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
resolved = @resolver.call(name)
|
|
23
|
+
@cache[name] = resolved
|
|
24
|
+
resolved
|
|
22
25
|
end
|
|
23
26
|
end
|
|
24
27
|
end
|
data/lib/rbgl/gui/x11/backend.rb
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "connection"
|
|
4
|
+
require_relative "pixel_encoder"
|
|
4
5
|
|
|
5
6
|
module RBGL
|
|
6
7
|
module GUI
|
|
7
8
|
module X11
|
|
8
9
|
class Backend < GUI::Backend
|
|
9
|
-
def initialize(width, height, title = "RBGL", env: ENV)
|
|
10
|
+
def initialize(width, height, title = "RBGL", env: ENV, reply_timeout: Connection::DEFAULT_REPLY_TIMEOUT)
|
|
10
11
|
super(width, height, title)
|
|
11
|
-
@display = Connection.new(env["DISPLAY"] || ":0")
|
|
12
|
-
@
|
|
12
|
+
@display = Connection.new(env["DISPLAY"] || ":0", env: env, reply_timeout: reply_timeout)
|
|
13
|
+
@window = nil
|
|
13
14
|
setup_window(width, height, title)
|
|
15
|
+
rescue StandardError => error
|
|
16
|
+
begin
|
|
17
|
+
close
|
|
18
|
+
rescue StandardError
|
|
19
|
+
nil
|
|
20
|
+
end
|
|
21
|
+
raise error
|
|
14
22
|
end
|
|
15
23
|
|
|
16
24
|
private def setup_window(w, h, t)
|
|
@@ -42,33 +50,43 @@ module RBGL
|
|
|
42
50
|
gc_id = @display.generate_id
|
|
43
51
|
@display.create_gc(gc_id, wid)
|
|
44
52
|
|
|
45
|
-
@
|
|
53
|
+
@window = {
|
|
54
|
+
id: wid,
|
|
46
55
|
width: w,
|
|
47
56
|
height: h,
|
|
48
57
|
gc: gc_id,
|
|
49
58
|
should_close: false
|
|
50
59
|
}
|
|
51
|
-
|
|
52
|
-
@handle = wid
|
|
53
60
|
end
|
|
54
61
|
|
|
55
62
|
def present(framebuffer)
|
|
56
|
-
return false unless @
|
|
57
|
-
|
|
58
|
-
window = @windows[@handle]
|
|
59
|
-
return false unless window
|
|
63
|
+
return false unless @window
|
|
60
64
|
|
|
61
65
|
buffer = convert_to_x11_format(framebuffer)
|
|
66
|
+
present_bytes(buffer, framebuffer.width, framebuffer.height)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def set_pixels(buffer, width, height)
|
|
70
|
+
return false unless @window
|
|
71
|
+
|
|
72
|
+
bytes = validate_rgba_buffer(buffer, width, height)
|
|
73
|
+
present_bytes(pixel_encoder.encode_rgba(bytes, width, height), width, height)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private def present_bytes(buffer, width, height)
|
|
77
|
+
window = @window
|
|
78
|
+
return false unless window
|
|
62
79
|
|
|
63
80
|
@display.put_image(
|
|
64
81
|
format: :z_pixmap,
|
|
65
|
-
drawable:
|
|
82
|
+
drawable: window[:id],
|
|
66
83
|
gc: window[:gc],
|
|
67
|
-
width:
|
|
68
|
-
height:
|
|
84
|
+
width: width,
|
|
85
|
+
height: height,
|
|
69
86
|
dst_x: 0, dst_y: 0,
|
|
70
87
|
depth: @display.root_depth,
|
|
71
|
-
data: buffer
|
|
88
|
+
data: buffer,
|
|
89
|
+
bytes_per_line: pixel_encoder.bytes_per_line(width)
|
|
72
90
|
)
|
|
73
91
|
|
|
74
92
|
@display.flush
|
|
@@ -90,33 +108,53 @@ module RBGL
|
|
|
90
108
|
end
|
|
91
109
|
|
|
92
110
|
def should_close?
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@windows[@handle]&.[](:should_close) || false
|
|
111
|
+
@window.nil? || @window[:should_close]
|
|
96
112
|
end
|
|
97
113
|
|
|
98
114
|
def close
|
|
99
|
-
|
|
115
|
+
window = @window
|
|
116
|
+
operations = []
|
|
117
|
+
if window
|
|
118
|
+
window[:should_close] = true
|
|
119
|
+
operations << -> { @display.destroy_window(window[:id]) }
|
|
120
|
+
operations << -> { @display.flush }
|
|
121
|
+
end
|
|
122
|
+
operations << -> { @display&.close }
|
|
100
123
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
124
|
+
error = nil
|
|
125
|
+
operations.each do |operation|
|
|
126
|
+
operation.call
|
|
127
|
+
rescue StandardError => cleanup_error
|
|
128
|
+
error ||= cleanup_error
|
|
129
|
+
end
|
|
130
|
+
@window = nil
|
|
131
|
+
raise error if error
|
|
105
132
|
end
|
|
106
133
|
|
|
107
134
|
private
|
|
108
135
|
|
|
109
136
|
def convert_to_x11_format(framebuffer)
|
|
110
|
-
|
|
111
|
-
|
|
137
|
+
pixel_encoder.encode(framebuffer)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def pixel_encoder
|
|
141
|
+
@pixel_encoder ||= PixelEncoder.new(
|
|
142
|
+
bits_per_pixel: @display.bits_per_pixel,
|
|
143
|
+
scanline_pad: @display.scanline_pad,
|
|
144
|
+
visual_class: @display.visual_class,
|
|
145
|
+
red_mask: @display.red_mask,
|
|
146
|
+
green_mask: @display.green_mask,
|
|
147
|
+
blue_mask: @display.blue_mask,
|
|
148
|
+
image_byte_order: @display.image_byte_order
|
|
149
|
+
)
|
|
112
150
|
end
|
|
113
151
|
|
|
114
152
|
def convert_event(raw)
|
|
115
153
|
case raw[:type]
|
|
116
154
|
when :key_press
|
|
117
|
-
Event.new(:key_press, key: raw[:keycode])
|
|
155
|
+
Event.new(:key_press, key: @display.key_for_keycode(raw[:keycode]), keycode: raw[:keycode])
|
|
118
156
|
when :key_release
|
|
119
|
-
Event.new(:key_release, key: raw[:keycode])
|
|
157
|
+
Event.new(:key_release, key: @display.key_for_keycode(raw[:keycode]), keycode: raw[:keycode])
|
|
120
158
|
when :button_press
|
|
121
159
|
Event.new(:mouse_press, x: raw[:x], y: raw[:y], button: raw[:button])
|
|
122
160
|
when :button_release
|
|
@@ -126,11 +164,11 @@ module RBGL
|
|
|
126
164
|
when :configure_notify
|
|
127
165
|
Event.new(:resize, width: raw[:width], height: raw[:height])
|
|
128
166
|
when :client_message
|
|
129
|
-
return nil unless raw[:window] == @
|
|
167
|
+
return nil unless @window && raw[:window] == @window[:id]
|
|
130
168
|
return nil unless raw[:message_type] == @display.wm_protocols_atom
|
|
131
169
|
return nil unless raw[:data32]&.first == @display.wm_delete_window_atom
|
|
132
170
|
|
|
133
|
-
@
|
|
171
|
+
@window[:should_close] = true
|
|
134
172
|
Event.new(:close)
|
|
135
173
|
else
|
|
136
174
|
nil
|