rbgl 0.1.0 → 1.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +20 -2
- 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 +150 -40
- data/lib/rbgl/engine/clip_space_clipper.rb +163 -0
- data/lib/rbgl/engine/context.rb +133 -63
- data/lib/rbgl/engine/framebuffer.rb +153 -48
- data/lib/rbgl/engine/immutable_color.rb +32 -0
- data/lib/rbgl/engine/pipeline.rb +38 -7
- data/lib/rbgl/engine/rasterizer/attribute_interpolator.rb +195 -0
- data/lib/rbgl/engine/rasterizer/line_renderer.rb +88 -0
- data/lib/rbgl/engine/rasterizer/point_renderer.rb +35 -0
- data/lib/rbgl/engine/rasterizer/triangle_renderer.rb +136 -0
- data/lib/rbgl/engine/rasterizer.rb +65 -171
- data/lib/rbgl/engine/shader/base_shader.rb +51 -0
- data/lib/rbgl/engine/shader/builtins.rb +257 -0
- data/lib/rbgl/engine/shader/dynamic_data.rb +73 -0
- data/lib/rbgl/engine/shader.rb +5 -318
- data/lib/rbgl/engine/texture.rb +264 -51
- data/lib/rbgl/engine.rb +4 -1
- data/lib/rbgl/gui/backend.rb +21 -34
- data/lib/rbgl/gui/backend_factory.rb +89 -0
- data/lib/rbgl/gui/cocoa/backend.rb +59 -46
- 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 +63 -0
- data/lib/rbgl/gui/file_backend/frame_writer.rb +35 -0
- data/lib/rbgl/gui/file_backend/png_writer.rb +18 -0
- data/lib/rbgl/gui/file_backend/ppm_writer.rb +26 -0
- data/lib/rbgl/gui/file_backend.rb +50 -55
- data/lib/rbgl/gui/wayland/backend.rb +214 -60
- data/lib/rbgl/gui/wayland/codec.rb +50 -0
- data/lib/rbgl/gui/wayland/connection.rb +188 -231
- data/lib/rbgl/gui/wayland/event_dispatcher.rb +165 -0
- data/lib/rbgl/gui/wayland/global_binder.rb +46 -0
- data/lib/rbgl/gui/wayland/input_mapper.rb +61 -0
- data/lib/rbgl/gui/wayland/protocol_objects/arguments.rb +63 -0
- data/lib/rbgl/gui/wayland/protocol_objects/display.rb +54 -0
- data/lib/rbgl/gui/wayland/protocol_objects/seat.rb +115 -0
- data/lib/rbgl/gui/wayland/protocol_objects/shm.rb +157 -0
- data/lib/rbgl/gui/wayland/protocol_objects/surface.rb +33 -0
- data/lib/rbgl/gui/wayland/protocol_objects/xdg_shell.rb +60 -0
- data/lib/rbgl/gui/wayland/protocol_objects.rb +8 -0
- data/lib/rbgl/gui/window/event_dispatcher.rb +30 -0
- data/lib/rbgl/gui/window/render_loop.rb +111 -0
- data/lib/rbgl/gui/window.rb +74 -84
- data/lib/rbgl/gui/x11/atom_cache.rb +29 -0
- data/lib/rbgl/gui/x11/backend.rb +73 -49
- data/lib/rbgl/gui/x11/connection.rb +284 -219
- data/lib/rbgl/gui/x11/event_parser.rb +71 -0
- 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 +155 -0
- data/lib/rbgl/gui/x11/setup_parser.rb +138 -0
- data/lib/rbgl/gui/x11/transport.rb +81 -0
- data/lib/rbgl/gui/x11/x_authority.rb +107 -0
- data/lib/rbgl/gui.rb +3 -5
- data/lib/rbgl/version.rb +1 -1
- metadata +63 -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
|
@@ -1,35 +1,43 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require_relative "file_backend/frame_writer"
|
|
5
|
+
require_relative "file_backend/ppm_writer"
|
|
6
|
+
require_relative "file_backend/bmp_writer"
|
|
7
|
+
require_relative "file_backend/png_writer"
|
|
8
|
+
|
|
3
9
|
module RBGL
|
|
4
10
|
module GUI
|
|
5
11
|
class FileBackend < Backend
|
|
6
|
-
|
|
12
|
+
SUPPORTED_FORMATS = %i[ppm bmp png].freeze
|
|
13
|
+
PPM_MODES = %i[ascii binary].freeze
|
|
14
|
+
|
|
15
|
+
attr_reader :max_frames, :output_dir
|
|
16
|
+
|
|
17
|
+
def initialize(width, height, title = "RBGL", format: :ppm, ppm_mode: :ascii, output_dir: ".", max_frames: 1)
|
|
7
18
|
super(width, height, title)
|
|
8
|
-
@format = format
|
|
9
|
-
@
|
|
19
|
+
@format = normalize_format(format)
|
|
20
|
+
@ppm_mode = normalize_ppm_mode(ppm_mode, @format)
|
|
21
|
+
@writer = FrameWriter.build(@format, ppm_mode: @ppm_mode)
|
|
22
|
+
@output_dir = File.expand_path(output_dir)
|
|
23
|
+
FileUtils.mkdir_p(@output_dir)
|
|
10
24
|
@frame_count = 0
|
|
11
25
|
@should_close = false
|
|
12
|
-
|
|
26
|
+
self.max_frames = max_frames
|
|
13
27
|
end
|
|
14
28
|
|
|
15
29
|
def present(framebuffer)
|
|
16
|
-
filename = File.join(@output_dir, format("frame_%05d.#{@
|
|
17
|
-
|
|
18
|
-
case @format
|
|
19
|
-
when :ppm
|
|
20
|
-
File.write(filename, framebuffer.to_ppm)
|
|
21
|
-
when :ppm_binary
|
|
22
|
-
File.binwrite(filename, framebuffer.to_ppm_binary)
|
|
23
|
-
when :bmp
|
|
24
|
-
File.binwrite(filename, to_bmp(framebuffer))
|
|
25
|
-
end
|
|
30
|
+
filename = File.join(@output_dir, format("frame_%05d.#{@writer.extension}", @frame_count))
|
|
31
|
+
@writer.write(filename, framebuffer)
|
|
26
32
|
|
|
27
33
|
@frame_count += 1
|
|
28
34
|
|
|
29
35
|
@should_close = true if @max_frames && @frame_count >= @max_frames
|
|
36
|
+
true
|
|
30
37
|
end
|
|
31
38
|
|
|
32
39
|
def poll_events
|
|
40
|
+
[]
|
|
33
41
|
end
|
|
34
42
|
|
|
35
43
|
def should_close?
|
|
@@ -40,51 +48,38 @@ module RBGL
|
|
|
40
48
|
@should_close = true
|
|
41
49
|
end
|
|
42
50
|
|
|
43
|
-
def
|
|
44
|
-
@max_frames = count
|
|
51
|
+
def max_frames=(count)
|
|
52
|
+
@max_frames = normalize_max_frames(count)
|
|
45
53
|
end
|
|
46
54
|
|
|
55
|
+
alias set_max_frames max_frames=
|
|
56
|
+
|
|
47
57
|
private
|
|
48
58
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
pixels = +""
|
|
75
|
-
(h - 1).downto(0) do |y|
|
|
76
|
-
row = +""
|
|
77
|
-
w.times do |x|
|
|
78
|
-
color = framebuffer.get_pixel(x, y)
|
|
79
|
-
bytes = color.to_bytes
|
|
80
|
-
row << [bytes[2], bytes[1], bytes[0]].pack("CCC")
|
|
81
|
-
end
|
|
82
|
-
padding = row_size - w * 3
|
|
83
|
-
row << "\x00" * padding
|
|
84
|
-
pixels << row
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
header + dib + pixels
|
|
59
|
+
def normalize_format(format)
|
|
60
|
+
normalized = format.to_sym
|
|
61
|
+
return normalized if SUPPORTED_FORMATS.include?(normalized)
|
|
62
|
+
|
|
63
|
+
raise ArgumentError, "Unsupported file backend format: #{format}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def normalize_ppm_mode(ppm_mode, format)
|
|
67
|
+
normalized = ppm_mode.to_sym
|
|
68
|
+
raise ArgumentError, "Unsupported PPM mode: #{ppm_mode}" unless PPM_MODES.include?(normalized)
|
|
69
|
+
return normalized if format == :ppm || normalized == :ascii
|
|
70
|
+
|
|
71
|
+
raise ArgumentError, "PPM mode is only supported with :ppm format"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def normalize_max_frames(count)
|
|
75
|
+
return nil if count.nil?
|
|
76
|
+
|
|
77
|
+
normalized = Integer(count)
|
|
78
|
+
return normalized if normalized.positive?
|
|
79
|
+
|
|
80
|
+
raise ArgumentError, "max_frames must be a positive integer or nil"
|
|
81
|
+
rescue TypeError
|
|
82
|
+
raise ArgumentError, "max_frames must be a positive integer or nil"
|
|
88
83
|
end
|
|
89
84
|
end
|
|
90
85
|
end
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "connection"
|
|
4
|
+
require "tempfile"
|
|
4
5
|
|
|
5
6
|
module RBGL
|
|
6
7
|
module GUI
|
|
7
8
|
module Wayland
|
|
8
9
|
class Backend < GUI::Backend
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
BUFFER_WAIT_TIMEOUT = 0.25
|
|
11
|
+
BUFFER_POLL_INTERVAL = 0.016
|
|
12
|
+
|
|
13
|
+
def initialize(width, height, title = "RBGL", env: ENV, roundtrip_timeout: Connection::DEFAULT_ROUNDTRIP_TIMEOUT)
|
|
14
|
+
super(width, height, title)
|
|
15
|
+
@window = nil
|
|
16
|
+
@retired_buffers = []
|
|
17
|
+
@connection = Connection.new(env: env, roundtrip_timeout: roundtrip_timeout)
|
|
13
18
|
setup_window(width, height, title)
|
|
19
|
+
rescue StandardError => error
|
|
20
|
+
begin
|
|
21
|
+
close
|
|
22
|
+
rescue StandardError
|
|
23
|
+
nil
|
|
24
|
+
end
|
|
25
|
+
raise error
|
|
14
26
|
end
|
|
15
27
|
|
|
16
28
|
private def setup_window(w, h, t)
|
|
@@ -19,72 +31,108 @@ module RBGL
|
|
|
19
31
|
toplevel = xdg_surface.get_toplevel
|
|
20
32
|
toplevel.set_title(t)
|
|
21
33
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
surface.attach(shm_buffer, 0, 0)
|
|
25
|
-
surface.commit
|
|
26
|
-
@connection.flush
|
|
27
|
-
|
|
28
|
-
handle = surface.id
|
|
29
|
-
@windows[handle] = {
|
|
34
|
+
@window = {
|
|
30
35
|
surface: surface,
|
|
31
36
|
xdg_surface: xdg_surface,
|
|
32
37
|
toplevel: toplevel,
|
|
33
|
-
|
|
38
|
+
buffers: [],
|
|
39
|
+
shm_buffer: nil,
|
|
34
40
|
width: w,
|
|
35
41
|
height: h,
|
|
36
|
-
should_close: false
|
|
37
|
-
pending_events: []
|
|
42
|
+
should_close: false
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
surface.commit
|
|
46
|
+
@connection.flush
|
|
47
|
+
wait_for_initial_configure(xdg_surface)
|
|
48
|
+
|
|
49
|
+
buffers = create_shm_buffers(w, h)
|
|
50
|
+
@window[:buffers] = buffers
|
|
51
|
+
@window[:shm_buffer] = buffers.first
|
|
41
52
|
end
|
|
42
53
|
|
|
43
54
|
def present(framebuffer)
|
|
44
|
-
return unless @
|
|
55
|
+
return false unless @window
|
|
56
|
+
|
|
57
|
+
present_bytes(convert_to_wayland_format(framebuffer), framebuffer.width, framebuffer.height)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def set_pixels(buffer, width, height)
|
|
61
|
+
return false unless @window
|
|
62
|
+
|
|
63
|
+
bytes = validate_rgba_buffer(buffer, width, height)
|
|
64
|
+
present_bytes(rgba_to_wayland_bytes(bytes), width, height)
|
|
65
|
+
end
|
|
45
66
|
|
|
46
|
-
|
|
47
|
-
|
|
67
|
+
private def present_bytes(buffer, width, height)
|
|
68
|
+
window = @window
|
|
69
|
+
return false unless window
|
|
70
|
+
unless width == window[:width] && height == window[:height]
|
|
71
|
+
raise ArgumentError, "Framebuffer dimensions must match the Wayland window"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
buffer_object = wait_for_available_buffer(window)
|
|
75
|
+
return false unless buffer_object
|
|
48
76
|
|
|
49
|
-
buffer
|
|
50
|
-
window[:shm_buffer].write(buffer)
|
|
77
|
+
buffer_object.write(buffer)
|
|
51
78
|
|
|
52
|
-
window[:surface].damage(0, 0,
|
|
53
|
-
window[:surface].attach(
|
|
79
|
+
window[:surface].damage(0, 0, width, height)
|
|
80
|
+
window[:surface].attach(buffer_object, 0, 0)
|
|
81
|
+
buffer_object.mark_in_use
|
|
82
|
+
window[:shm_buffer] = buffer_object
|
|
54
83
|
window[:surface].commit
|
|
55
84
|
@connection.flush
|
|
85
|
+
true
|
|
56
86
|
end
|
|
57
87
|
|
|
58
88
|
def poll_events
|
|
59
|
-
|
|
60
|
-
|
|
89
|
+
@connection.dispatch_pending.filter_map { |event| convert_event(event) }
|
|
90
|
+
end
|
|
61
91
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
92
|
+
def resize(width, height)
|
|
93
|
+
window = @window
|
|
94
|
+
return super unless window
|
|
95
|
+
return super if window[:width] == width && window[:height] == height
|
|
66
96
|
|
|
67
|
-
|
|
97
|
+
old_buffers = window.fetch(:buffers, [window[:shm_buffer]].compact)
|
|
98
|
+
new_buffers = create_shm_buffers(width, height)
|
|
99
|
+
window[:buffers] = new_buffers
|
|
100
|
+
window[:shm_buffer] = new_buffers.first
|
|
101
|
+
window[:width] = width
|
|
102
|
+
window[:height] = height
|
|
103
|
+
super
|
|
104
|
+
(@retired_buffers ||= []).concat(old_buffers)
|
|
105
|
+
old_buffers.each(&:destroy)
|
|
106
|
+
@retired_buffers.reject! { |buffer| buffer.respond_to?(:destroyed?) && buffer.destroyed? }
|
|
68
107
|
end
|
|
69
108
|
|
|
70
109
|
def should_close?
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
@windows[@handle]&.[](:should_close) || false
|
|
110
|
+
@window.nil? || @window[:should_close]
|
|
74
111
|
end
|
|
75
112
|
|
|
76
113
|
def close
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
window
|
|
80
|
-
|
|
114
|
+
window = @window
|
|
115
|
+
operations = []
|
|
116
|
+
if window
|
|
117
|
+
window[:should_close] = true
|
|
118
|
+
operations << -> { window[:toplevel].destroy }
|
|
119
|
+
operations << -> { window[:xdg_surface].destroy }
|
|
120
|
+
operations << -> { window[:surface].destroy }
|
|
121
|
+
buffers = window.fetch(:buffers, [window[:shm_buffer]].compact) + Array(@retired_buffers)
|
|
122
|
+
operations.concat(buffers.uniq.map { |buffer| -> { buffer.destroy(force: true) } })
|
|
123
|
+
operations << -> { @connection.flush }
|
|
124
|
+
end
|
|
125
|
+
operations << -> { @connection&.close }
|
|
81
126
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
127
|
+
error = nil
|
|
128
|
+
operations.each do |operation|
|
|
129
|
+
operation.call
|
|
130
|
+
rescue StandardError => cleanup_error
|
|
131
|
+
error ||= cleanup_error
|
|
132
|
+
end
|
|
133
|
+
@window = nil
|
|
134
|
+
@retired_buffers = []
|
|
135
|
+
raise error if error
|
|
88
136
|
end
|
|
89
137
|
|
|
90
138
|
private
|
|
@@ -93,33 +141,139 @@ module RBGL
|
|
|
93
141
|
framebuffer.to_bgra_bytes
|
|
94
142
|
end
|
|
95
143
|
|
|
144
|
+
def rgba_to_wayland_bytes(buffer)
|
|
145
|
+
output = String.new(capacity: buffer.bytesize, encoding: Encoding::BINARY)
|
|
146
|
+
byte_index = 0
|
|
147
|
+
while byte_index < buffer.bytesize
|
|
148
|
+
output << buffer.getbyte(byte_index + 2) << buffer.getbyte(byte_index + 1) <<
|
|
149
|
+
buffer.getbyte(byte_index) << buffer.getbyte(byte_index + 3)
|
|
150
|
+
byte_index += 4
|
|
151
|
+
end
|
|
152
|
+
output
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def convert_event(raw)
|
|
156
|
+
window = window_for_event(raw[:surface_id] || raw[:object_id])
|
|
157
|
+
return nil unless window
|
|
158
|
+
|
|
159
|
+
case raw[:type]
|
|
160
|
+
when :xdg_toplevel_close
|
|
161
|
+
window[:should_close] = true
|
|
162
|
+
Event.new(:close)
|
|
163
|
+
when :xdg_toplevel_configure
|
|
164
|
+
width = raw[:width]
|
|
165
|
+
height = raw[:height]
|
|
166
|
+
return nil unless width.positive? && height.positive?
|
|
167
|
+
|
|
168
|
+
Event.new(:resize, width: width, height: height)
|
|
169
|
+
when :key_press, :key_release
|
|
170
|
+
Event.new(raw[:type], key: raw[:key], keycode: raw[:keycode], modifiers: raw[:modifiers])
|
|
171
|
+
when :pointer_motion
|
|
172
|
+
Event.new(:mouse_move, x: raw[:x], y: raw[:y])
|
|
173
|
+
when :pointer_button_press
|
|
174
|
+
Event.new(:mouse_press, button: raw[:button], x: raw[:x], y: raw[:y])
|
|
175
|
+
when :pointer_button_release
|
|
176
|
+
Event.new(:mouse_release, button: raw[:button], x: raw[:x], y: raw[:y])
|
|
177
|
+
when :pointer_axis
|
|
178
|
+
Event.new(:mouse_scroll, x: raw[:x], y: raw[:y], axis: raw[:axis], value: raw[:value])
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def window_for_event(object_id)
|
|
183
|
+
return unless @window
|
|
184
|
+
return @window if @window[:toplevel].id == object_id || @window[:surface].id == object_id
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def next_available_buffer(window)
|
|
188
|
+
window.fetch(:buffers, [window[:shm_buffer]].compact).find(&:available?)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def wait_for_available_buffer(window)
|
|
192
|
+
deadline = monotonic_time + BUFFER_WAIT_TIMEOUT
|
|
193
|
+
|
|
194
|
+
loop do
|
|
195
|
+
buffer = next_available_buffer(window)
|
|
196
|
+
return buffer if buffer
|
|
197
|
+
return nil if abort_buffer_wait?(window, deadline)
|
|
198
|
+
|
|
199
|
+
pump_connection(timeout: remaining_buffer_wait(deadline))
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def pump_connection(timeout:)
|
|
204
|
+
if @connection.respond_to?(:pump_events)
|
|
205
|
+
@connection.pump_events(timeout: timeout)
|
|
206
|
+
else
|
|
207
|
+
sleep(timeout)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def abort_buffer_wait?(window, deadline)
|
|
212
|
+
window[:should_close] || monotonic_time >= deadline
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def remaining_buffer_wait(deadline)
|
|
216
|
+
[deadline - monotonic_time, BUFFER_POLL_INTERVAL].min.clamp(0.0, BUFFER_POLL_INTERVAL)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def monotonic_time
|
|
220
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def create_shm_buffers(width, height, count = 2)
|
|
224
|
+
buffers = []
|
|
225
|
+
count.times { buffers << create_shm_buffer(width, height) }
|
|
226
|
+
buffers
|
|
227
|
+
rescue StandardError => error
|
|
228
|
+
buffers&.each do |buffer|
|
|
229
|
+
begin
|
|
230
|
+
buffer.destroy(force: true)
|
|
231
|
+
rescue StandardError
|
|
232
|
+
nil
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
raise error
|
|
236
|
+
end
|
|
237
|
+
|
|
96
238
|
def create_shm_buffer(width, height)
|
|
97
239
|
size = width * height * 4
|
|
98
240
|
|
|
99
|
-
|
|
241
|
+
file = create_anonymous_file(size)
|
|
242
|
+
pool = @connection.shm.create_pool(file, size)
|
|
243
|
+
buffer = pool.create_buffer(0, width, height, width * 4, :xrgb8888)
|
|
100
244
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
245
|
+
ShmBuffer.new(file, pool, buffer)
|
|
246
|
+
rescue StandardError => error
|
|
247
|
+
begin
|
|
248
|
+
buffer&.destroy(force: true)
|
|
249
|
+
rescue StandardError
|
|
250
|
+
nil
|
|
251
|
+
end
|
|
252
|
+
begin
|
|
253
|
+
pool&.destroy
|
|
254
|
+
rescue StandardError
|
|
255
|
+
nil
|
|
256
|
+
end
|
|
257
|
+
file&.close unless file&.closed?
|
|
258
|
+
raise error
|
|
105
259
|
end
|
|
106
260
|
|
|
107
261
|
def create_anonymous_file(size)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
file = File.open(path, File::RDWR | File::CREAT | File::EXCL, 0o600)
|
|
262
|
+
file = Tempfile.new("rbgl-wayland")
|
|
263
|
+
file.binmode
|
|
264
|
+
file.chmod(0o600)
|
|
112
265
|
file.truncate(size)
|
|
113
|
-
|
|
114
|
-
|
|
266
|
+
file.unlink
|
|
267
|
+
file
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def wait_for_initial_configure(xdg_surface)
|
|
271
|
+
configured = @connection.wait_until { xdg_surface.configured? }
|
|
272
|
+
return if configured
|
|
115
273
|
|
|
116
|
-
|
|
117
|
-
rescue Errno::ENOENT
|
|
118
|
-
require "tempfile"
|
|
119
|
-
tmpfile = Tempfile.new("rbgl")
|
|
120
|
-
tmpfile.truncate(size)
|
|
121
|
-
tmpfile.fileno
|
|
274
|
+
raise GUI::BackendUnavailable, "Wayland compositor did not configure the surface"
|
|
122
275
|
end
|
|
276
|
+
|
|
123
277
|
end
|
|
124
278
|
end
|
|
125
279
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBGL
|
|
4
|
+
module GUI
|
|
5
|
+
module Wayland
|
|
6
|
+
class Codec
|
|
7
|
+
def pack_args(args)
|
|
8
|
+
result = String.new
|
|
9
|
+
|
|
10
|
+
args.each do |arg|
|
|
11
|
+
unless arg.is_a?(TypedArgument)
|
|
12
|
+
raise ArgumentError, "Wayland requests require typed arguments"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
result << pack_typed_argument(arg)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
result
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def pad_length(length)
|
|
22
|
+
((length + 3) / 4) * 4
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def pack_typed_argument(argument)
|
|
28
|
+
case argument.type
|
|
29
|
+
when :uint, :object, :new_id
|
|
30
|
+
[argument.value].pack("V")
|
|
31
|
+
when :int
|
|
32
|
+
[argument.value].pack("l<")
|
|
33
|
+
when :fixed
|
|
34
|
+
[(argument.value * 256).round].pack("l<")
|
|
35
|
+
when :string
|
|
36
|
+
pack_string(argument.value)
|
|
37
|
+
else
|
|
38
|
+
raise ArgumentError, "Unsupported Wayland argument type: #{argument.type}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def pack_string(value)
|
|
43
|
+
bytes = value.to_s.encode(Encoding::UTF_8).b
|
|
44
|
+
length = bytes.bytesize + 1
|
|
45
|
+
[length].pack("V") + bytes + "\x00" + ("\x00" * ((4 - (length % 4)) % 4))
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|