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,329 +1,286 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "socket"
|
|
4
|
+
require "io/wait"
|
|
5
|
+
require_relative "protocol_objects"
|
|
6
|
+
require_relative "codec"
|
|
7
|
+
require_relative "event_dispatcher"
|
|
8
|
+
require_relative "global_binder"
|
|
4
9
|
|
|
5
10
|
module RBGL
|
|
6
11
|
module GUI
|
|
7
12
|
module Wayland
|
|
8
|
-
class
|
|
9
|
-
|
|
13
|
+
class Connection
|
|
14
|
+
DEFAULT_ROUNDTRIP_TIMEOUT = 1.0
|
|
15
|
+
ROUNDTRIP_POLL_INTERVAL = 0.01
|
|
16
|
+
READ_CHUNK_SIZE = 16_384
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
@connection = connection
|
|
13
|
-
@id = id
|
|
14
|
-
end
|
|
18
|
+
attr_reader :compositor, :shm, :xdg_wm_base, :seat, :registry, :globals
|
|
15
19
|
|
|
16
|
-
def
|
|
17
|
-
@
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
def initialize(env: ENV, socket_path: nil, roundtrip_timeout: DEFAULT_ROUNDTRIP_TIMEOUT)
|
|
21
|
+
@env = env
|
|
22
|
+
@roundtrip_timeout = roundtrip_timeout
|
|
23
|
+
@socket = UNIXSocket.new(resolve_socket_path(socket_path))
|
|
24
|
+
@objects = {}
|
|
25
|
+
@next_id = 2
|
|
26
|
+
@globals = {}
|
|
27
|
+
@pending_events = []
|
|
28
|
+
@received_fds = []
|
|
29
|
+
@read_buffer = String.new(encoding: Encoding::BINARY)
|
|
30
|
+
@closed = false
|
|
20
31
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
super(connection, 1)
|
|
24
|
-
end
|
|
32
|
+
@display = Display.new(self)
|
|
33
|
+
register_object(@display)
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Callback.new(@connection, callback_id)
|
|
30
|
-
end
|
|
35
|
+
@registry = @display.get_registry
|
|
36
|
+
flush
|
|
37
|
+
roundtrip
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
bind_globals
|
|
40
|
+
rescue StandardError => error
|
|
41
|
+
begin
|
|
42
|
+
close
|
|
43
|
+
rescue StandardError
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
raise error
|
|
36
47
|
end
|
|
37
|
-
end
|
|
38
48
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
new_id
|
|
49
|
+
def allocate_id
|
|
50
|
+
id = @next_id
|
|
51
|
+
@next_id += 1
|
|
52
|
+
id
|
|
44
53
|
end
|
|
45
|
-
end
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@done = false
|
|
55
|
+
def register_object(object)
|
|
56
|
+
@objects[object.id] = object
|
|
57
|
+
object
|
|
51
58
|
end
|
|
52
59
|
|
|
53
|
-
def
|
|
54
|
-
@
|
|
60
|
+
def object_for(object_id)
|
|
61
|
+
@objects[object_id]
|
|
55
62
|
end
|
|
56
63
|
|
|
57
|
-
def
|
|
58
|
-
@
|
|
64
|
+
def unregister_object(object_id)
|
|
65
|
+
@objects.delete(object_id)
|
|
59
66
|
end
|
|
60
|
-
end
|
|
61
67
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
surface_id = @connection.allocate_id
|
|
65
|
-
send_request(0, surface_id)
|
|
66
|
-
Surface.new(@connection, surface_id)
|
|
68
|
+
def store_global(interface, name:, version:)
|
|
69
|
+
@globals[interface] = { name: name, version: version }
|
|
67
70
|
end
|
|
68
|
-
end
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
send_request(1, buffer.id, x, y)
|
|
72
|
+
def remove_global(name)
|
|
73
|
+
@globals.delete_if { |_interface, global| global[:name] == name }
|
|
73
74
|
end
|
|
74
75
|
|
|
75
|
-
def
|
|
76
|
-
|
|
76
|
+
def consume_received_fd
|
|
77
|
+
@received_fds.shift
|
|
77
78
|
end
|
|
78
79
|
|
|
79
|
-
def
|
|
80
|
-
|
|
80
|
+
def queue_event(event)
|
|
81
|
+
@pending_events << event
|
|
81
82
|
end
|
|
82
83
|
|
|
83
|
-
def
|
|
84
|
-
|
|
84
|
+
def send_request(object_id, opcode, *args)
|
|
85
|
+
payload = pack_args(args)
|
|
86
|
+
header = [object_id, ((payload.bytesize + 8) << 16) | opcode].pack("VV")
|
|
87
|
+
@socket.write(header + payload)
|
|
85
88
|
end
|
|
86
|
-
end
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
end
|
|
94
|
-
end
|
|
90
|
+
def send_request_with_fd(object_id, opcode, *args, fd)
|
|
91
|
+
io = fd.respond_to?(:to_io) ? fd.to_io : fd
|
|
92
|
+
unless io.is_a?(IO)
|
|
93
|
+
raise ArgumentError, "Wayland file descriptor arguments must be IO objects"
|
|
94
|
+
end
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
buffer_id = @connection.allocate_id
|
|
99
|
-
format_val = case format
|
|
100
|
-
when :argb8888 then 0
|
|
101
|
-
when :xrgb8888 then 1
|
|
102
|
-
else 0
|
|
103
|
-
end
|
|
104
|
-
send_request(0, buffer_id, offset, width, height, stride, format_val)
|
|
105
|
-
WlBuffer.new(@connection, buffer_id)
|
|
106
|
-
end
|
|
96
|
+
payload = pack_args(args)
|
|
97
|
+
header = [object_id, ((payload.bytesize + 8) << 16) | opcode].pack("VV")
|
|
107
98
|
|
|
108
|
-
|
|
109
|
-
send_request(1)
|
|
99
|
+
@socket.sendmsg(header + payload, 0, nil, Socket::AncillaryData.unix_rights(io))
|
|
110
100
|
end
|
|
111
|
-
end
|
|
112
101
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
send_request(0)
|
|
102
|
+
def flush
|
|
103
|
+
@socket.flush
|
|
116
104
|
end
|
|
117
|
-
end
|
|
118
105
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
xdg_surface_id = @connection.allocate_id
|
|
122
|
-
send_request(2, xdg_surface_id, surface.id)
|
|
123
|
-
XdgSurface.new(@connection, xdg_surface_id)
|
|
124
|
-
end
|
|
106
|
+
def dispatch_pending
|
|
107
|
+
pump_events(timeout: 0)
|
|
125
108
|
|
|
126
|
-
|
|
127
|
-
|
|
109
|
+
events = @pending_events
|
|
110
|
+
@pending_events = []
|
|
111
|
+
events
|
|
128
112
|
end
|
|
129
|
-
end
|
|
130
113
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
end
|
|
114
|
+
def pump_events(timeout: nil)
|
|
115
|
+
read_from_socket(timeout) unless complete_message?
|
|
116
|
+
drain_messages
|
|
117
|
+
read_from_socket(0) if @socket.wait_readable(0)
|
|
118
|
+
drain_messages
|
|
137
119
|
|
|
138
|
-
|
|
139
|
-
send_request(4, serial)
|
|
120
|
+
@pending_events.length
|
|
140
121
|
end
|
|
141
122
|
|
|
142
|
-
def
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
123
|
+
def roundtrip
|
|
124
|
+
callback = @display.sync
|
|
125
|
+
flush
|
|
126
|
+
deadline = monotonic_time + @roundtrip_timeout
|
|
146
127
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
send_request(2, title)
|
|
150
|
-
end
|
|
128
|
+
until callback.done?
|
|
129
|
+
raise BackendUnavailable, "Wayland roundtrip timed out" if monotonic_time >= deadline
|
|
151
130
|
|
|
152
|
-
|
|
153
|
-
|
|
131
|
+
pump_events(timeout: roundtrip_poll_interval(deadline))
|
|
132
|
+
end
|
|
154
133
|
end
|
|
155
|
-
end
|
|
156
134
|
|
|
157
|
-
|
|
158
|
-
|
|
135
|
+
def wait_until(timeout: @roundtrip_timeout)
|
|
136
|
+
deadline = monotonic_time + timeout
|
|
137
|
+
|
|
138
|
+
until yield
|
|
139
|
+
return false if monotonic_time >= deadline
|
|
159
140
|
|
|
160
|
-
|
|
161
|
-
socket_path = ENV["WAYLAND_DISPLAY"] || "wayland-0"
|
|
162
|
-
unless socket_path.start_with?("/")
|
|
163
|
-
runtime_dir = ENV["XDG_RUNTIME_DIR"] || "/run/user/#{Process.uid}"
|
|
164
|
-
socket_path = File.join(runtime_dir, socket_path)
|
|
141
|
+
pump_events(timeout: roundtrip_poll_interval(deadline))
|
|
165
142
|
end
|
|
166
143
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
@next_id = 2
|
|
170
|
-
@globals = {}
|
|
144
|
+
true
|
|
145
|
+
end
|
|
171
146
|
|
|
172
|
-
|
|
173
|
-
|
|
147
|
+
def close
|
|
148
|
+
return if @closed
|
|
174
149
|
|
|
175
|
-
|
|
176
|
-
@
|
|
177
|
-
|
|
178
|
-
|
|
150
|
+
error = nil
|
|
151
|
+
@received_fds&.each do |fd|
|
|
152
|
+
fd.close unless fd.closed?
|
|
153
|
+
rescue StandardError => close_error
|
|
154
|
+
error ||= close_error
|
|
155
|
+
end
|
|
156
|
+
@received_fds&.reject!(&:closed?)
|
|
179
157
|
|
|
180
|
-
|
|
158
|
+
begin
|
|
159
|
+
@socket&.close unless @socket&.closed?
|
|
160
|
+
rescue StandardError => close_error
|
|
161
|
+
error ||= close_error
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
@closed = @received_fds.to_a.empty? && (!@socket || @socket.closed?)
|
|
165
|
+
raise error if error
|
|
181
166
|
end
|
|
182
167
|
|
|
183
|
-
def
|
|
184
|
-
|
|
185
|
-
@next_id += 1
|
|
186
|
-
id
|
|
168
|
+
def closed?
|
|
169
|
+
@closed || !@socket || @socket.closed?
|
|
187
170
|
end
|
|
188
171
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
@
|
|
172
|
+
private
|
|
173
|
+
|
|
174
|
+
def resolve_socket_path(socket_path = nil)
|
|
175
|
+
socket_path ||= @env["WAYLAND_DISPLAY"] || "wayland-0"
|
|
176
|
+
return socket_path if socket_path.start_with?("/")
|
|
177
|
+
|
|
178
|
+
runtime_dir = @env["XDG_RUNTIME_DIR"] || "/run/user/#{Process.uid}"
|
|
179
|
+
File.join(runtime_dir, socket_path)
|
|
193
180
|
end
|
|
194
181
|
|
|
195
|
-
def
|
|
196
|
-
|
|
197
|
-
|
|
182
|
+
def roundtrip_poll_interval(deadline)
|
|
183
|
+
[deadline - monotonic_time, ROUNDTRIP_POLL_INTERVAL].min.clamp(0.0, ROUNDTRIP_POLL_INTERVAL)
|
|
184
|
+
end
|
|
198
185
|
|
|
199
|
-
|
|
186
|
+
def monotonic_time
|
|
187
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
200
188
|
end
|
|
201
189
|
|
|
202
|
-
def
|
|
203
|
-
|
|
190
|
+
def handle_event(object_id, opcode, payload)
|
|
191
|
+
event_dispatcher.handle_event(object_id, opcode, payload)
|
|
204
192
|
end
|
|
205
193
|
|
|
206
|
-
def
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
194
|
+
def complete_message?
|
|
195
|
+
return false if @read_buffer.bytesize < 8
|
|
196
|
+
|
|
197
|
+
size = @read_buffer.byteslice(4, 4).unpack1("V") >> 16
|
|
198
|
+
raise GUI::BackendUnavailable, "Invalid Wayland message size: #{size}" if size < 8
|
|
199
|
+
|
|
200
|
+
@read_buffer.bytesize >= size
|
|
201
|
+
end
|
|
210
202
|
|
|
211
|
-
|
|
203
|
+
def drain_messages
|
|
204
|
+
while complete_message?
|
|
205
|
+
object_id, size_and_opcode = @read_buffer.byteslice(0, 8).unpack("VV")
|
|
212
206
|
size = size_and_opcode >> 16
|
|
213
207
|
opcode = size_and_opcode & 0xFFFF
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
handle_event(object_id, opcode, payload)
|
|
208
|
+
message = @read_buffer.slice!(0, size)
|
|
209
|
+
handle_event(object_id, opcode, message.byteslice(8, size - 8))
|
|
217
210
|
end
|
|
218
211
|
end
|
|
219
212
|
|
|
220
|
-
def
|
|
221
|
-
|
|
222
|
-
@objects[callback.id] = callback
|
|
223
|
-
flush
|
|
213
|
+
def read_from_socket(timeout)
|
|
214
|
+
return false unless @socket.wait_readable(timeout)
|
|
224
215
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
end
|
|
216
|
+
read_any = false
|
|
217
|
+
loop do
|
|
218
|
+
message = receive_message
|
|
219
|
+
break if message == :wait_readable
|
|
230
220
|
|
|
231
|
-
|
|
221
|
+
chunk, ancillary_data = message
|
|
222
|
+
raise GUI::BackendUnavailable, "Wayland compositor closed the connection" if chunk.nil?
|
|
232
223
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
interface_len = payload[4, 4].unpack1("V")
|
|
239
|
-
interface = payload[8, interface_len - 1]
|
|
240
|
-
version = payload[8 + pad_length(interface_len), 4].unpack1("V")
|
|
241
|
-
@globals[interface] = { name: name, version: version }
|
|
242
|
-
end
|
|
243
|
-
else
|
|
244
|
-
obj = @objects[object_id]
|
|
245
|
-
if obj.is_a?(Callback) && opcode == 0
|
|
246
|
-
obj.handle_done
|
|
224
|
+
@read_buffer << chunk
|
|
225
|
+
ancillary_data.each do |control|
|
|
226
|
+
@received_fds.concat(control.unix_rights)
|
|
227
|
+
rescue TypeError
|
|
228
|
+
next
|
|
247
229
|
end
|
|
230
|
+
read_any = true
|
|
248
231
|
end
|
|
232
|
+
read_any
|
|
249
233
|
end
|
|
250
234
|
|
|
251
|
-
def
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
@objects[2].bind(g[:name], "wl_compositor", [g[:version], 4].min)
|
|
256
|
-
@compositor = Compositor.new(self, id)
|
|
257
|
-
@objects[id] = @compositor
|
|
258
|
-
end
|
|
235
|
+
def receive_message
|
|
236
|
+
unless @socket.respond_to?(:recvmsg_nonblock)
|
|
237
|
+
chunk = @socket.read_nonblock(READ_CHUNK_SIZE, exception: false)
|
|
238
|
+
return :wait_readable if chunk == :wait_readable
|
|
259
239
|
|
|
260
|
-
|
|
261
|
-
id = allocate_id
|
|
262
|
-
g = @globals["wl_shm"]
|
|
263
|
-
@objects[2].bind(g[:name], "wl_shm", [g[:version], 1].min)
|
|
264
|
-
@shm = Shm.new(self, id)
|
|
265
|
-
@objects[id] = @shm
|
|
240
|
+
return [chunk, []]
|
|
266
241
|
end
|
|
267
242
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
243
|
+
result = @socket.recvmsg_nonblock(
|
|
244
|
+
READ_CHUNK_SIZE,
|
|
245
|
+
0,
|
|
246
|
+
nil,
|
|
247
|
+
scm_rights: true,
|
|
248
|
+
exception: false
|
|
249
|
+
)
|
|
250
|
+
return :wait_readable if result == :wait_readable
|
|
251
|
+
return [nil, []] if !result || result[0].empty?
|
|
275
252
|
|
|
253
|
+
[result[0], result.drop(3)]
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def bind_globals
|
|
257
|
+
bound = global_binder.bind!(@registry, @globals)
|
|
258
|
+
@compositor = bound[:compositor]
|
|
259
|
+
@shm = bound[:shm]
|
|
260
|
+
@xdg_wm_base = bound[:xdg_wm_base]
|
|
261
|
+
@seat = bound[:seat]
|
|
276
262
|
flush
|
|
277
263
|
roundtrip
|
|
278
264
|
end
|
|
279
265
|
|
|
280
266
|
def pack_args(args)
|
|
281
|
-
|
|
282
|
-
args.each do |arg|
|
|
283
|
-
case arg
|
|
284
|
-
when Integer
|
|
285
|
-
result << [arg].pack("V")
|
|
286
|
-
when String
|
|
287
|
-
len = arg.bytesize + 1
|
|
288
|
-
result << [len].pack("V")
|
|
289
|
-
result << arg << "\x00"
|
|
290
|
-
result << "\x00" * ((4 - len % 4) % 4)
|
|
291
|
-
when Float
|
|
292
|
-
result << [(arg * 256).to_i].pack("V")
|
|
293
|
-
end
|
|
294
|
-
end
|
|
295
|
-
result
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
def pad_length(len)
|
|
299
|
-
((len + 3) / 4) * 4
|
|
267
|
+
codec.pack_args(args)
|
|
300
268
|
end
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
class ShmBuffer
|
|
304
|
-
attr_reader :wl_buffer
|
|
305
269
|
|
|
306
|
-
def
|
|
307
|
-
|
|
308
|
-
@size = size
|
|
309
|
-
@wl_buffer = wl_buffer
|
|
310
|
-
@file = File.open("/proc/self/fd/#{fd}", "r+b")
|
|
311
|
-
@file.seek(0)
|
|
270
|
+
def pad_length(length)
|
|
271
|
+
codec.pad_length(length)
|
|
312
272
|
end
|
|
313
273
|
|
|
314
|
-
def
|
|
315
|
-
@
|
|
316
|
-
@file.write(data)
|
|
317
|
-
@file.flush
|
|
274
|
+
def codec
|
|
275
|
+
@codec ||= Codec.new
|
|
318
276
|
end
|
|
319
277
|
|
|
320
|
-
def
|
|
321
|
-
@
|
|
278
|
+
def event_dispatcher
|
|
279
|
+
@event_dispatcher ||= EventDispatcher.new(self, codec: codec)
|
|
322
280
|
end
|
|
323
281
|
|
|
324
|
-
def
|
|
325
|
-
@
|
|
326
|
-
@file.close
|
|
282
|
+
def global_binder
|
|
283
|
+
@global_binder ||= GlobalBinder.new(self)
|
|
327
284
|
end
|
|
328
285
|
end
|
|
329
286
|
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "input_mapper"
|
|
4
|
+
|
|
5
|
+
module RBGL
|
|
6
|
+
module GUI
|
|
7
|
+
module Wayland
|
|
8
|
+
class EventDispatcher
|
|
9
|
+
def initialize(connection, codec:)
|
|
10
|
+
@connection = connection
|
|
11
|
+
@codec = codec
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def handle_event(object_id, opcode, payload)
|
|
15
|
+
object = @connection.object_for(object_id)
|
|
16
|
+
|
|
17
|
+
case object
|
|
18
|
+
when Display
|
|
19
|
+
handle_display_event(opcode, payload)
|
|
20
|
+
when Registry
|
|
21
|
+
handle_registry_event(opcode, payload)
|
|
22
|
+
when Callback
|
|
23
|
+
object.handle_done if opcode == 0
|
|
24
|
+
when WlBuffer
|
|
25
|
+
object.handle_release if opcode == 0
|
|
26
|
+
when Seat
|
|
27
|
+
object.handle_capabilities(payload.unpack1("V")) if opcode == 0
|
|
28
|
+
when Keyboard
|
|
29
|
+
handle_keyboard_event(object, opcode, payload)
|
|
30
|
+
when Pointer
|
|
31
|
+
handle_pointer_event(object, opcode, payload)
|
|
32
|
+
when XdgWmBase
|
|
33
|
+
handle_xdg_wm_base_event(object, opcode, payload)
|
|
34
|
+
when XdgSurface
|
|
35
|
+
object.handle_configure(payload.unpack1("V")) if opcode == 0
|
|
36
|
+
when XdgToplevel
|
|
37
|
+
handle_xdg_toplevel_event(object_id, opcode, payload)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def handle_display_event(opcode, payload)
|
|
44
|
+
case opcode
|
|
45
|
+
when 0
|
|
46
|
+
failed_object, code, length = payload.byteslice(0, 12).unpack("VVV")
|
|
47
|
+
message = payload.byteslice(12, length - 1)
|
|
48
|
+
raise GUI::BackendUnavailable,
|
|
49
|
+
"Wayland protocol error #{code} on object #{failed_object}: #{message}"
|
|
50
|
+
when 1
|
|
51
|
+
@connection.unregister_object(payload.unpack1("V"))
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def handle_registry_event(opcode, payload)
|
|
56
|
+
case opcode
|
|
57
|
+
when 0
|
|
58
|
+
name = payload[0, 4].unpack1("V")
|
|
59
|
+
interface_length = payload[4, 4].unpack1("V")
|
|
60
|
+
interface = payload[8, interface_length - 1]
|
|
61
|
+
version = payload[8 + @codec.pad_length(interface_length), 4].unpack1("V")
|
|
62
|
+
@connection.store_global(interface, name: name, version: version)
|
|
63
|
+
when 1
|
|
64
|
+
@connection.remove_global(payload.unpack1("V"))
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def handle_xdg_wm_base_event(object, opcode, payload)
|
|
69
|
+
return unless opcode == 0
|
|
70
|
+
|
|
71
|
+
object.pong(payload.unpack1("V"))
|
|
72
|
+
@connection.flush
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def handle_keyboard_event(keyboard, opcode, payload)
|
|
76
|
+
case opcode
|
|
77
|
+
when 0
|
|
78
|
+
format, size = payload.unpack("V2")
|
|
79
|
+
keyboard.handle_keymap(format, size, @connection.consume_received_fd)
|
|
80
|
+
when 1
|
|
81
|
+
keyboard.focus(payload.byteslice(4, 4).unpack1("V"))
|
|
82
|
+
when 2
|
|
83
|
+
keyboard.blur
|
|
84
|
+
when 3
|
|
85
|
+
_serial, _time, keycode, state = payload.unpack("V4")
|
|
86
|
+
event = {
|
|
87
|
+
type: state == 1 ? :key_press : :key_release,
|
|
88
|
+
surface_id: keyboard.focused_surface_id,
|
|
89
|
+
key: InputMapper.key(keycode),
|
|
90
|
+
keycode: keycode
|
|
91
|
+
}
|
|
92
|
+
event[:modifiers] = keyboard.modifiers.dup unless keyboard.modifiers.empty?
|
|
93
|
+
@connection.queue_event(event)
|
|
94
|
+
when 4
|
|
95
|
+
_serial, depressed, latched, locked, group = payload.unpack("V5")
|
|
96
|
+
keyboard.handle_modifiers(
|
|
97
|
+
depressed: depressed,
|
|
98
|
+
latched: latched,
|
|
99
|
+
locked: locked,
|
|
100
|
+
group: group
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def handle_pointer_event(pointer, opcode, payload)
|
|
106
|
+
case opcode
|
|
107
|
+
when 0
|
|
108
|
+
_serial, surface_id, x, y = payload.unpack("VVl<l<")
|
|
109
|
+
pointer.focus(surface_id)
|
|
110
|
+
queue_pointer_motion(pointer, x, y)
|
|
111
|
+
when 1
|
|
112
|
+
pointer.blur
|
|
113
|
+
when 2
|
|
114
|
+
_time, x, y = payload.unpack("Vl<l<")
|
|
115
|
+
queue_pointer_motion(pointer, x, y)
|
|
116
|
+
when 3
|
|
117
|
+
_serial, _time, button, state = payload.unpack("V4")
|
|
118
|
+
@connection.queue_event(
|
|
119
|
+
type: state == 1 ? :pointer_button_press : :pointer_button_release,
|
|
120
|
+
surface_id: pointer.focused_surface_id,
|
|
121
|
+
button: InputMapper.button(button),
|
|
122
|
+
x: pointer.x,
|
|
123
|
+
y: pointer.y
|
|
124
|
+
)
|
|
125
|
+
when 4
|
|
126
|
+
_time, axis, value = payload.unpack("VVl<")
|
|
127
|
+
@connection.queue_event(
|
|
128
|
+
type: :pointer_axis,
|
|
129
|
+
surface_id: pointer.focused_surface_id,
|
|
130
|
+
axis: { 0 => :vertical, 1 => :horizontal }.fetch(axis, axis),
|
|
131
|
+
value: value / 256.0,
|
|
132
|
+
x: pointer.x,
|
|
133
|
+
y: pointer.y
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def queue_pointer_motion(pointer, fixed_x, fixed_y)
|
|
139
|
+
pointer.move(fixed_x / 256.0, fixed_y / 256.0)
|
|
140
|
+
@connection.queue_event(
|
|
141
|
+
type: :pointer_motion,
|
|
142
|
+
surface_id: pointer.focused_surface_id,
|
|
143
|
+
x: pointer.x,
|
|
144
|
+
y: pointer.y
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def handle_xdg_toplevel_event(object_id, opcode, payload)
|
|
149
|
+
case opcode
|
|
150
|
+
when 0
|
|
151
|
+
width, height = payload[0, 8].unpack("l<l<")
|
|
152
|
+
@connection.queue_event(
|
|
153
|
+
type: :xdg_toplevel_configure,
|
|
154
|
+
object_id: object_id,
|
|
155
|
+
width: width,
|
|
156
|
+
height: height
|
|
157
|
+
)
|
|
158
|
+
when 1
|
|
159
|
+
@connection.queue_event(type: :xdg_toplevel_close, object_id: object_id)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|