rbgl 0.1.0 → 1.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 +4 -4
- data/CHANGELOG.md +13 -1
- data/lib/rbgl/engine/buffer.rb +102 -33
- data/lib/rbgl/engine/clip_space_clipper.rb +143 -0
- data/lib/rbgl/engine/context.rb +117 -64
- data/lib/rbgl/engine/framebuffer.rb +41 -32
- data/lib/rbgl/engine/pipeline.rb +38 -7
- data/lib/rbgl/engine/rasterizer/attribute_interpolator.rb +105 -0
- data/lib/rbgl/engine/rasterizer/line_renderer.rb +72 -0
- data/lib/rbgl/engine/rasterizer/point_renderer.rb +35 -0
- data/lib/rbgl/engine/rasterizer/triangle_renderer.rb +87 -0
- data/lib/rbgl/engine/rasterizer.rb +73 -162
- data/lib/rbgl/engine/shader/base_shader.rb +55 -0
- data/lib/rbgl/engine/shader/builtins.rb +254 -0
- data/lib/rbgl/engine/shader/dynamic_data.rb +65 -0
- data/lib/rbgl/engine/shader.rb +5 -318
- data/lib/rbgl/engine/texture.rb +227 -38
- data/lib/rbgl/engine.rb +1 -0
- data/lib/rbgl/gui/backend.rb +12 -30
- data/lib/rbgl/gui/backend_factory.rb +85 -0
- data/lib/rbgl/gui/cocoa/backend.rb +15 -35
- data/lib/rbgl/gui/file_backend/bmp_writer.rb +63 -0
- data/lib/rbgl/gui/file_backend/frame_writer.rb +28 -0
- data/lib/rbgl/gui/file_backend/ppm_writer.rb +25 -0
- data/lib/rbgl/gui/file_backend.rb +25 -48
- data/lib/rbgl/gui/wayland/backend.rb +108 -26
- data/lib/rbgl/gui/wayland/codec.rb +54 -0
- data/lib/rbgl/gui/wayland/connection.rb +80 -240
- data/lib/rbgl/gui/wayland/event_dispatcher.rb +74 -0
- data/lib/rbgl/gui/wayland/global_binder.rb +44 -0
- data/lib/rbgl/gui/wayland/protocol_objects/arguments.rb +51 -0
- data/lib/rbgl/gui/wayland/protocol_objects/display.rb +54 -0
- data/lib/rbgl/gui/wayland/protocol_objects/shm.rb +146 -0
- data/lib/rbgl/gui/wayland/protocol_objects/surface.rb +33 -0
- data/lib/rbgl/gui/wayland/protocol_objects/xdg_shell.rb +45 -0
- data/lib/rbgl/gui/wayland/protocol_objects.rb +7 -0
- data/lib/rbgl/gui/window/event_dispatcher.rb +30 -0
- data/lib/rbgl/gui/window/render_loop.rb +58 -0
- data/lib/rbgl/gui/window.rb +41 -82
- data/lib/rbgl/gui/x11/atom_cache.rb +26 -0
- data/lib/rbgl/gui/x11/backend.rb +14 -28
- data/lib/rbgl/gui/x11/connection.rb +104 -169
- data/lib/rbgl/gui/x11/event_parser.rb +60 -0
- data/lib/rbgl/gui/x11/request_encoder.rb +154 -0
- data/lib/rbgl/gui/x11/transport.rb +31 -0
- data/lib/rbgl/gui.rb +1 -0
- data/lib/rbgl/version.rb +1 -1
- metadata +31 -4
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "socket"
|
|
4
|
+
require_relative "../backend"
|
|
5
|
+
require_relative "atom_cache"
|
|
6
|
+
require_relative "event_parser"
|
|
7
|
+
require_relative "request_encoder"
|
|
8
|
+
require_relative "transport"
|
|
4
9
|
|
|
5
10
|
module RBGL
|
|
6
11
|
module GUI
|
|
@@ -12,7 +17,7 @@ module RBGL
|
|
|
12
17
|
def initialize(display_name)
|
|
13
18
|
host, display_num, _screen_num = parse_display_name(display_name)
|
|
14
19
|
@socket = connect(host, display_num)
|
|
15
|
-
@
|
|
20
|
+
@transport = Transport.new(@socket)
|
|
16
21
|
@resource_id_counter = 0
|
|
17
22
|
@pending_events = []
|
|
18
23
|
|
|
@@ -20,66 +25,54 @@ module RBGL
|
|
|
20
25
|
end
|
|
21
26
|
|
|
22
27
|
def generate_id
|
|
23
|
-
|
|
28
|
+
masked_counter = @resource_id_counter & @resource_id_mask
|
|
29
|
+
raise GUI::BackendUnavailable, "X11 resource ID space exhausted" if masked_counter != @resource_id_counter
|
|
30
|
+
|
|
31
|
+
id = @resource_id_base | masked_counter
|
|
24
32
|
@resource_id_counter += 1
|
|
25
33
|
id
|
|
26
34
|
end
|
|
27
35
|
|
|
28
36
|
def flush
|
|
29
|
-
|
|
37
|
+
transport.flush
|
|
30
38
|
end
|
|
31
39
|
|
|
32
|
-
def
|
|
33
|
-
|
|
34
|
-
ready ? 1 : 0
|
|
40
|
+
def atom(name)
|
|
41
|
+
atom_cache.fetch(name)
|
|
35
42
|
end
|
|
36
43
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
when :input_output then 1
|
|
41
|
-
when :input_only then 2
|
|
42
|
-
else 0
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
mask = 0
|
|
46
|
-
value_list = []
|
|
47
|
-
|
|
48
|
-
if value_mask.include?(:back_pixel)
|
|
49
|
-
mask |= 0x0002
|
|
50
|
-
value_list << values[:back_pixel]
|
|
51
|
-
end
|
|
44
|
+
def wm_delete_window_atom
|
|
45
|
+
atom(:wm_delete_window)
|
|
46
|
+
end
|
|
52
47
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
values[:event_mask].each do |ev|
|
|
57
|
-
event_mask |= case ev
|
|
58
|
-
when :exposure then 0x8000
|
|
59
|
-
when :key_press then 0x0001
|
|
60
|
-
when :key_release then 0x0002
|
|
61
|
-
when :button_press then 0x0004
|
|
62
|
-
when :button_release then 0x0008
|
|
63
|
-
when :pointer_motion then 0x0040
|
|
64
|
-
when :structure_notify then 0x020000
|
|
65
|
-
else 0
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
value_list << event_mask
|
|
69
|
-
end
|
|
48
|
+
def wm_protocols_atom
|
|
49
|
+
atom(:wm_protocols)
|
|
50
|
+
end
|
|
70
51
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
parent,
|
|
75
|
-
x, y,
|
|
76
|
-
width, height,
|
|
77
|
-
border_width,
|
|
78
|
-
class_val,
|
|
79
|
-
visual,
|
|
80
|
-
mask
|
|
81
|
-
].pack("CVVSSSSSVV") + value_list.pack("V*")
|
|
52
|
+
def enable_wm_delete_window(window)
|
|
53
|
+
change_property(window, :wm_protocols, :atom, [wm_delete_window_atom], format: 32)
|
|
54
|
+
end
|
|
82
55
|
|
|
56
|
+
def pending
|
|
57
|
+
transport.pending
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def create_window(depth:, wid:, parent:, x:, y:, width:, height:,
|
|
61
|
+
border_width:, window_class:, visual:, value_mask:, values:)
|
|
62
|
+
request = request_encoder.create_window_data(
|
|
63
|
+
depth: depth,
|
|
64
|
+
wid: wid,
|
|
65
|
+
parent: parent,
|
|
66
|
+
x: x,
|
|
67
|
+
y: y,
|
|
68
|
+
width: width,
|
|
69
|
+
height: height,
|
|
70
|
+
border_width: border_width,
|
|
71
|
+
window_class: window_class,
|
|
72
|
+
visual: visual,
|
|
73
|
+
value_mask: value_mask,
|
|
74
|
+
values: values
|
|
75
|
+
)
|
|
83
76
|
send_request(1, request)
|
|
84
77
|
end
|
|
85
78
|
|
|
@@ -92,76 +85,41 @@ module RBGL
|
|
|
92
85
|
end
|
|
93
86
|
|
|
94
87
|
def create_gc(gc_id, drawable, values = {})
|
|
95
|
-
|
|
96
|
-
value_list = []
|
|
97
|
-
|
|
98
|
-
if values[:foreground]
|
|
99
|
-
mask |= 0x0004
|
|
100
|
-
value_list << values[:foreground]
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
if values[:background]
|
|
104
|
-
mask |= 0x0008
|
|
105
|
-
value_list << values[:background]
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
request = [gc_id, drawable, mask].pack("VVV") + value_list.pack("V*")
|
|
88
|
+
request = request_encoder.create_gc_data(gc_id, drawable, values)
|
|
109
89
|
send_request(55, request)
|
|
110
90
|
end
|
|
111
91
|
|
|
112
92
|
def put_image(format:, drawable:, gc:, width:, height:, dst_x:, dst_y:, depth:, data:)
|
|
113
|
-
format_byte =
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
send_request_with_data(72, format_byte, header, data)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def change_property(window, property, type, data, mode: :replace)
|
|
135
|
-
mode_val = case mode
|
|
136
|
-
when :replace then 0
|
|
137
|
-
when :prepend then 1
|
|
138
|
-
when :append then 2
|
|
139
|
-
else 0
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
property_atom = get_atom(property)
|
|
143
|
-
type_atom = get_atom(type)
|
|
144
|
-
|
|
145
|
-
format = 8
|
|
146
|
-
data_bytes = data.to_s
|
|
147
|
-
|
|
148
|
-
request = [
|
|
93
|
+
format_byte, header, image_data = request_encoder.put_image_data(
|
|
94
|
+
format: format,
|
|
95
|
+
drawable: drawable,
|
|
96
|
+
gc: gc,
|
|
97
|
+
width: width,
|
|
98
|
+
height: height,
|
|
99
|
+
dst_x: dst_x,
|
|
100
|
+
dst_y: dst_y,
|
|
101
|
+
depth: depth,
|
|
102
|
+
data: data
|
|
103
|
+
)
|
|
104
|
+
send_request_with_data(72, format_byte, header, image_data)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def change_property(window, property, type, data, mode: :replace, format: 8)
|
|
108
|
+
property_atom = atom(property)
|
|
109
|
+
type_atom = atom(type)
|
|
110
|
+
mode_val, request = request_encoder.change_property_data(
|
|
149
111
|
window,
|
|
150
112
|
property_atom,
|
|
151
113
|
type_atom,
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
114
|
+
data,
|
|
115
|
+
mode: mode,
|
|
116
|
+
format: format
|
|
117
|
+
)
|
|
156
118
|
send_request(18, request, mode_val)
|
|
157
119
|
end
|
|
158
120
|
|
|
159
121
|
def intern_atom(name, only_if_exists: false)
|
|
160
|
-
request =
|
|
161
|
-
name.bytesize,
|
|
162
|
-
0
|
|
163
|
-
].pack("vv") + pad_to_4(name)
|
|
164
|
-
|
|
122
|
+
request = request_encoder.intern_atom_data(name)
|
|
165
123
|
send_request(16, request, only_if_exists ? 1 : 0)
|
|
166
124
|
flush
|
|
167
125
|
|
|
@@ -188,10 +146,12 @@ module RBGL
|
|
|
188
146
|
private
|
|
189
147
|
|
|
190
148
|
def parse_display_name(name)
|
|
191
|
-
if name =~ /^(?:(
|
|
192
|
-
|
|
149
|
+
if name =~ /^(?:(.*):)?(\d+)(?:\.(\d+))?$/
|
|
150
|
+
host = ::Regexp.last_match(1)
|
|
151
|
+
host = nil if host&.empty?
|
|
152
|
+
[host, ::Regexp.last_match(2).to_i, (::Regexp.last_match(3) || 0).to_i]
|
|
193
153
|
else
|
|
194
|
-
raise "Invalid display name: #{name}"
|
|
154
|
+
raise GUI::BackendUnavailable, "Invalid X11 display name: #{name}"
|
|
195
155
|
end
|
|
196
156
|
end
|
|
197
157
|
|
|
@@ -217,9 +177,11 @@ module RBGL
|
|
|
217
177
|
@socket.flush
|
|
218
178
|
|
|
219
179
|
header = @socket.read(8)
|
|
180
|
+
raise GUI::BackendUnavailable, "X11 connection closed during handshake" unless header&.bytesize == 8
|
|
181
|
+
|
|
220
182
|
status = header.unpack1("C")
|
|
221
183
|
|
|
222
|
-
raise "X11 connection failed" unless status == 1
|
|
184
|
+
raise GUI::BackendUnavailable, "X11 connection failed" unless status == 1
|
|
223
185
|
|
|
224
186
|
additional_length = header[6, 2].unpack1("v")
|
|
225
187
|
data = @socket.read(additional_length * 4)
|
|
@@ -247,82 +209,34 @@ module RBGL
|
|
|
247
209
|
end
|
|
248
210
|
|
|
249
211
|
def send_request(opcode, data, extra = 0)
|
|
250
|
-
|
|
251
|
-
header = [opcode, extra, length].pack("CCv")
|
|
252
|
-
|
|
253
|
-
padding_size = length * 4 - 4 - data.bytesize
|
|
254
|
-
padded_data = data + ("\x00" * padding_size)
|
|
255
|
-
|
|
256
|
-
@socket.write(header + padded_data)
|
|
257
|
-
@next_seq += 1
|
|
212
|
+
transport.write(request_encoder.request_packet(opcode, data, extra))
|
|
258
213
|
end
|
|
259
214
|
|
|
260
215
|
def send_request_with_data(opcode, extra, header_data, bulk_data)
|
|
261
|
-
|
|
262
|
-
length = (4 + total_data.bytesize + 3) / 4
|
|
263
|
-
|
|
264
|
-
header = [opcode, extra, length].pack("CCv")
|
|
265
|
-
padding_size = length * 4 - 4 - total_data.bytesize
|
|
266
|
-
padded = total_data + ("\x00" * padding_size)
|
|
267
|
-
|
|
268
|
-
@socket.write(header + padded)
|
|
269
|
-
@next_seq += 1
|
|
216
|
+
transport.write(request_encoder.request_packet_with_data(opcode, extra, header_data, bulk_data))
|
|
270
217
|
end
|
|
271
218
|
|
|
272
219
|
def read_reply
|
|
273
|
-
header =
|
|
220
|
+
header = transport.read(32)
|
|
274
221
|
return nil unless header && header.bytesize == 32
|
|
275
222
|
|
|
276
223
|
additional = header[4, 4].unpack1("V")
|
|
277
224
|
if additional > 0
|
|
278
|
-
header +
|
|
225
|
+
header + transport.read(additional * 4)
|
|
279
226
|
else
|
|
280
227
|
header
|
|
281
228
|
end
|
|
282
229
|
end
|
|
283
230
|
|
|
284
231
|
def read_event
|
|
285
|
-
|
|
286
|
-
return nil unless data && data.bytesize == 32
|
|
287
|
-
|
|
288
|
-
event_type = data.unpack1("C") & 0x7F
|
|
289
|
-
|
|
290
|
-
case event_type
|
|
291
|
-
when 2
|
|
292
|
-
keycode = data[1, 1].unpack1("C")
|
|
293
|
-
{ type: :key_press, keycode: keycode }
|
|
294
|
-
when 3
|
|
295
|
-
keycode = data[1, 1].unpack1("C")
|
|
296
|
-
{ type: :key_release, keycode: keycode }
|
|
297
|
-
when 4
|
|
298
|
-
x, y = data[24, 4].unpack("ss")
|
|
299
|
-
button = data[1, 1].unpack1("C")
|
|
300
|
-
{ type: :button_press, x: x, y: y, button: button }
|
|
301
|
-
when 5
|
|
302
|
-
x, y = data[24, 4].unpack("ss")
|
|
303
|
-
button = data[1, 1].unpack1("C")
|
|
304
|
-
{ type: :button_release, x: x, y: y, button: button }
|
|
305
|
-
when 6
|
|
306
|
-
x, y = data[24, 4].unpack("ss")
|
|
307
|
-
{ type: :motion_notify, x: x, y: y }
|
|
308
|
-
when 12
|
|
309
|
-
{ type: :exposure }
|
|
310
|
-
when 22
|
|
311
|
-
width, height = data[20, 4].unpack("vv")
|
|
312
|
-
{ type: :configure_notify, width: width, height: height }
|
|
313
|
-
when 33
|
|
314
|
-
window = data[4, 4].unpack1("V")
|
|
315
|
-
atom = data[8, 4].unpack1("V")
|
|
316
|
-
{ type: :client_message, window: window, data: atom }
|
|
317
|
-
else
|
|
318
|
-
{ type: :unknown, code: event_type }
|
|
319
|
-
end
|
|
232
|
+
event_parser.parse(transport.read(32))
|
|
320
233
|
end
|
|
321
234
|
|
|
322
|
-
def
|
|
235
|
+
def resolve_atom(name)
|
|
323
236
|
case name
|
|
324
237
|
when :wm_name then 39
|
|
325
238
|
when :string then 31
|
|
239
|
+
when :atom then 4
|
|
326
240
|
when :wm_protocols then intern_atom("WM_PROTOCOLS")
|
|
327
241
|
when :wm_delete_window then intern_atom("WM_DELETE_WINDOW")
|
|
328
242
|
else
|
|
@@ -330,14 +244,35 @@ module RBGL
|
|
|
330
244
|
end
|
|
331
245
|
end
|
|
332
246
|
|
|
247
|
+
def pack_property_data(data, format)
|
|
248
|
+
request_encoder.pack_property_data(data, format)
|
|
249
|
+
end
|
|
250
|
+
|
|
333
251
|
def pad_to_4(str)
|
|
334
|
-
|
|
335
|
-
str + ("\x00" * padding)
|
|
252
|
+
request_encoder.pad_to_4(str)
|
|
336
253
|
end
|
|
337
254
|
|
|
338
255
|
def pad_length(len)
|
|
339
256
|
((len + 3) / 4) * 4
|
|
340
257
|
end
|
|
258
|
+
|
|
259
|
+
def transport
|
|
260
|
+
return @transport if @transport&.socket.equal?(@socket)
|
|
261
|
+
|
|
262
|
+
@transport = Transport.new(@socket)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def request_encoder
|
|
266
|
+
@request_encoder ||= RequestEncoder.new
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def event_parser
|
|
270
|
+
@event_parser ||= EventParser.new
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def atom_cache
|
|
274
|
+
@atom_cache ||= AtomCache.new { |name| resolve_atom(name) }
|
|
275
|
+
end
|
|
341
276
|
end
|
|
342
277
|
end
|
|
343
278
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBGL
|
|
4
|
+
module GUI
|
|
5
|
+
module X11
|
|
6
|
+
class EventParser
|
|
7
|
+
def parse(data)
|
|
8
|
+
return nil unless data && data.bytesize == 32
|
|
9
|
+
|
|
10
|
+
event_type = data.unpack1("C") & 0x7F
|
|
11
|
+
|
|
12
|
+
case event_type
|
|
13
|
+
when 2
|
|
14
|
+
key_event(:key_press, data)
|
|
15
|
+
when 3
|
|
16
|
+
key_event(:key_release, data)
|
|
17
|
+
when 4
|
|
18
|
+
button_event(:button_press, data)
|
|
19
|
+
when 5
|
|
20
|
+
button_event(:button_release, data)
|
|
21
|
+
when 6
|
|
22
|
+
x, y = data[24, 4].unpack("ss")
|
|
23
|
+
{ type: :motion_notify, x: x, y: y }
|
|
24
|
+
when 12
|
|
25
|
+
{ type: :exposure }
|
|
26
|
+
when 22
|
|
27
|
+
width, height = data[20, 4].unpack("vv")
|
|
28
|
+
{ type: :configure_notify, width: width, height: height }
|
|
29
|
+
when 33
|
|
30
|
+
{
|
|
31
|
+
type: :client_message,
|
|
32
|
+
format: data[1, 1].unpack1("C"),
|
|
33
|
+
window: data[4, 4].unpack1("V"),
|
|
34
|
+
message_type: data[8, 4].unpack1("V"),
|
|
35
|
+
data32: data[12, 20].unpack("V5")
|
|
36
|
+
}
|
|
37
|
+
else
|
|
38
|
+
{ type: :unknown, code: event_type }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def key_event(type, data)
|
|
45
|
+
{ type: type, keycode: data[1, 1].unpack1("C") }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def button_event(type, data)
|
|
49
|
+
x, y = data[24, 4].unpack("ss")
|
|
50
|
+
{
|
|
51
|
+
type: type,
|
|
52
|
+
x: x,
|
|
53
|
+
y: y,
|
|
54
|
+
button: data[1, 1].unpack1("C")
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBGL
|
|
4
|
+
module GUI
|
|
5
|
+
module X11
|
|
6
|
+
class RequestEncoder
|
|
7
|
+
EVENT_MASKS = {
|
|
8
|
+
exposure: 0x8000,
|
|
9
|
+
key_press: 0x0001,
|
|
10
|
+
key_release: 0x0002,
|
|
11
|
+
button_press: 0x0004,
|
|
12
|
+
button_release: 0x0008,
|
|
13
|
+
pointer_motion: 0x0040,
|
|
14
|
+
structure_notify: 0x020000
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
WINDOW_CLASSES = {
|
|
18
|
+
input_output: 1,
|
|
19
|
+
input_only: 2
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
PROPERTY_MODES = {
|
|
23
|
+
replace: 0,
|
|
24
|
+
prepend: 1,
|
|
25
|
+
append: 2
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
IMAGE_FORMATS = {
|
|
29
|
+
bitmap: 0,
|
|
30
|
+
xy_pixmap: 1,
|
|
31
|
+
z_pixmap: 2
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
def create_window_data(depth:, wid:, parent:, x:, y:, width:, height:,
|
|
35
|
+
border_width:, window_class:, visual:, value_mask:, values:)
|
|
36
|
+
mask = 0
|
|
37
|
+
value_list = []
|
|
38
|
+
|
|
39
|
+
if value_mask.include?(:back_pixel)
|
|
40
|
+
mask |= 0x0002
|
|
41
|
+
value_list << values[:back_pixel]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if value_mask.include?(:event_mask)
|
|
45
|
+
mask |= 0x0800
|
|
46
|
+
value_list << encode_event_mask(values[:event_mask])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
[
|
|
50
|
+
depth,
|
|
51
|
+
wid,
|
|
52
|
+
parent,
|
|
53
|
+
x, y,
|
|
54
|
+
width, height,
|
|
55
|
+
border_width,
|
|
56
|
+
WINDOW_CLASSES.fetch(window_class, 0),
|
|
57
|
+
visual,
|
|
58
|
+
mask
|
|
59
|
+
].pack("CVVSSSSSVV") + value_list.pack("V*")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def create_gc_data(gc_id, drawable, values = {})
|
|
63
|
+
mask = 0
|
|
64
|
+
value_list = []
|
|
65
|
+
|
|
66
|
+
if values[:foreground]
|
|
67
|
+
mask |= 0x0004
|
|
68
|
+
value_list << values[:foreground]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if values[:background]
|
|
72
|
+
mask |= 0x0008
|
|
73
|
+
value_list << values[:background]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
[gc_id, drawable, mask].pack("VVV") + value_list.pack("V*")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def put_image_data(format:, drawable:, gc:, width:, height:, dst_x:, dst_y:, depth:, data:)
|
|
80
|
+
format_byte = IMAGE_FORMATS.fetch(format, IMAGE_FORMATS[:z_pixmap])
|
|
81
|
+
header = [
|
|
82
|
+
drawable,
|
|
83
|
+
gc,
|
|
84
|
+
width, height,
|
|
85
|
+
dst_x, dst_y,
|
|
86
|
+
0,
|
|
87
|
+
depth
|
|
88
|
+
].pack("VVvvvvCC") + "\x00\x00"
|
|
89
|
+
|
|
90
|
+
[format_byte, header, data]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def change_property_data(window, property_atom, type_atom, data, mode: :replace, format: 8)
|
|
94
|
+
data_bytes, value_count = pack_property_data(data, format)
|
|
95
|
+
request = [
|
|
96
|
+
window,
|
|
97
|
+
property_atom,
|
|
98
|
+
type_atom,
|
|
99
|
+
format,
|
|
100
|
+
value_count
|
|
101
|
+
].pack("VVVCV") + "\x00\x00\x00" + pad_to_4(data_bytes)
|
|
102
|
+
|
|
103
|
+
[PROPERTY_MODES.fetch(mode, 0), request]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def intern_atom_data(name)
|
|
107
|
+
[name.bytesize, 0].pack("vv") + pad_to_4(name)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def request_packet(opcode, data, extra = 0)
|
|
111
|
+
length = (4 + data.bytesize + 3) / 4
|
|
112
|
+
header = [opcode, extra, length].pack("CCv")
|
|
113
|
+
padding = "\x00" * (length * 4 - 4 - data.bytesize)
|
|
114
|
+
header + data + padding
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def request_packet_with_data(opcode, extra, header_data, bulk_data)
|
|
118
|
+
total_data = header_data + bulk_data
|
|
119
|
+
length = (4 + total_data.bytesize + 3) / 4
|
|
120
|
+
header = [opcode, extra, length].pack("CCv")
|
|
121
|
+
padding = "\x00" * (length * 4 - 4 - total_data.bytesize)
|
|
122
|
+
header + total_data + padding
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def pack_property_data(data, format)
|
|
126
|
+
case format
|
|
127
|
+
when 8
|
|
128
|
+
bytes = data.to_s
|
|
129
|
+
[bytes, bytes.bytesize]
|
|
130
|
+
when 16
|
|
131
|
+
values = Array(data)
|
|
132
|
+
[values.pack("v*"), values.size]
|
|
133
|
+
when 32
|
|
134
|
+
values = Array(data)
|
|
135
|
+
[values.pack("V*"), values.size]
|
|
136
|
+
else
|
|
137
|
+
raise ArgumentError, "Unsupported property format: #{format}"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def pad_to_4(str)
|
|
142
|
+
padding = (4 - str.bytesize % 4) % 4
|
|
143
|
+
str + ("\x00" * padding)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
|
|
148
|
+
def encode_event_mask(events)
|
|
149
|
+
Array(events).sum { |event| EVENT_MASKS.fetch(event, 0) }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBGL
|
|
4
|
+
module GUI
|
|
5
|
+
module X11
|
|
6
|
+
class Transport
|
|
7
|
+
attr_reader :socket
|
|
8
|
+
|
|
9
|
+
def initialize(socket)
|
|
10
|
+
@socket = socket
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def write(data)
|
|
14
|
+
@socket.write(data)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def read(length)
|
|
18
|
+
@socket.read(length)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def flush
|
|
22
|
+
@socket.flush
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def pending
|
|
26
|
+
IO.select([@socket], nil, nil, 0) ? 1 : 0
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/rbgl/gui.rb
CHANGED
data/lib/rbgl/version.rb
CHANGED