pure-x11 0.0.5 → 0.0.7
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/.gitignore +2 -0
- data/Gemfile.lock +1 -1
- data/example/client_message.rb +13 -0
- data/example/test.rb +3 -3
- data/lib/X11/auth.rb +10 -13
- data/lib/X11/display.rb +201 -151
- data/lib/X11/form.rb +51 -15
- data/lib/X11/protocol.rb +4 -6
- data/lib/X11/screen.rb +5 -19
- data/lib/X11/type.rb +44 -59
- data/lib/X11/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37b5a813f478de65ecb12e89bafd332ef219baabac729a1baf29cf016d055347
|
4
|
+
data.tar.gz: 357ef8cd392bad30e9c2d8a7b6771c5c4fd1dd68f3e77da1544cf93f22df14f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81d50a93f9df23fe424f4dd62fe10fa09f840ab16eccb5de4d83b0d2f681b3cdb8fc9416e2b818f230399276974cac7455f38bfa1fe66c55968f70fc5c1661bb
|
7
|
+
data.tar.gz: 00bc7b7851c843a92988c23feacba461e6b9e1c2ef1f27b025c363f452f759492a30d4c1ff5d8117ba6bb4940eaebb19234c5fef9012d6cf8339a2ee143bf875
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require 'X11'
|
5
|
+
|
6
|
+
display = X11::Display.new
|
7
|
+
|
8
|
+
display.client_message(
|
9
|
+
mask: X11::Form::SubstructureNotifyMask | X11::Form::SubstructureRedirectMask,
|
10
|
+
type: :_NET_CURRENT_DESKTOP,
|
11
|
+
data: ARGV.shift.to_i
|
12
|
+
)
|
13
|
+
|
data/example/test.rb
CHANGED
@@ -143,7 +143,7 @@ def render_glyph(display, wid, x,y, ch)
|
|
143
143
|
#p img
|
144
144
|
# p ch
|
145
145
|
display.put_image(
|
146
|
-
|
146
|
+
:ZPixmap, wid, $gc2,
|
147
147
|
mtx.min_width,mtx.min_height,
|
148
148
|
x, y - mtx.y_offset, 0, depth, data
|
149
149
|
)
|
@@ -158,12 +158,12 @@ def render_str(display, wid, x,y, str)
|
|
158
158
|
end
|
159
159
|
|
160
160
|
def redraw(dpy, wid, gc)
|
161
|
-
dpy.poly_fill_rectangle(wid, gc, [
|
161
|
+
dpy.poly_fill_rectangle(wid, gc, [20,20, 60, 80])
|
162
162
|
dpy.clear_area(false, wid, 30, 30, 5, 5)
|
163
163
|
dpy.image_text16(wid, $gc2, 30, 70, "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ")
|
164
164
|
#"\u25f0\u25ef Hello World")
|
165
165
|
dpy.put_image(
|
166
|
-
|
166
|
+
:ZPixmap, wid, $gc2,
|
167
167
|
$png.width, $png.height, 80, 120, 0, 24, $data
|
168
168
|
)
|
169
169
|
render_str(dpy, wid, 30,90, 'HelloWorld')
|
data/lib/X11/auth.rb
CHANGED
@@ -11,14 +11,14 @@ module X11
|
|
11
11
|
AUTHENTICATE = 2
|
12
12
|
|
13
13
|
ADDRESS_TYPES = {
|
14
|
-
|
14
|
+
0 => :Internet,
|
15
|
+
1 => :DECnet,
|
16
|
+
2 => :Chaos,
|
17
|
+
252 => :LocalHost,
|
18
|
+
253 => :Krb5Principal,
|
19
|
+
254 => :Netname,
|
20
|
+
256 => :Local,
|
15
21
|
65535 => :Wild,
|
16
|
-
254 => :Netname,
|
17
|
-
253 => :Krb5Principal,
|
18
|
-
252 => :LocalHost,
|
19
|
-
0 => :Internet,
|
20
|
-
1 => :DECnet,
|
21
|
-
2 => :Chaos
|
22
22
|
}
|
23
23
|
|
24
24
|
AuthInfo = Struct.new :family, :address, :display, :auth_name, :auth_data
|
@@ -52,18 +52,15 @@ module X11
|
|
52
52
|
|
53
53
|
# returns one entry from Xauthority file
|
54
54
|
def read
|
55
|
-
auth_info = [] << ADDRESS_TYPES[ @file.read(2).
|
55
|
+
auth_info = [] << ADDRESS_TYPES[ @file.read(2).unpack1('n') ]
|
56
56
|
|
57
57
|
4.times do
|
58
|
-
length = @file.read(2).
|
58
|
+
length = @file.read(2).unpack1('n')
|
59
59
|
auth_info << @file.read(length)
|
60
60
|
end
|
61
61
|
AuthInfo[*auth_info]
|
62
62
|
end
|
63
63
|
|
64
|
-
def reset
|
65
|
-
@file.seek(0, IO::SEEK_SET)
|
66
|
-
end
|
67
|
-
|
64
|
+
def reset = @file.seek(0, IO::SEEK_SET)
|
68
65
|
end
|
69
66
|
end
|
data/lib/X11/display.rb
CHANGED
@@ -17,6 +17,8 @@ module X11
|
|
17
17
|
host, display_id, screen_id = $1, $2, $3
|
18
18
|
family = nil
|
19
19
|
|
20
|
+
@debug = ENV["PUREX_DEBUG"].to_s.strip == "true"
|
21
|
+
|
20
22
|
if host.empty?
|
21
23
|
@socket = UNIXSocket.new("/tmp/.X11-unix/X#{display_id}")
|
22
24
|
family = :Local
|
@@ -26,22 +28,27 @@ module X11
|
|
26
28
|
family = :Internet
|
27
29
|
end
|
28
30
|
|
29
|
-
|
30
|
-
authorize(host, family, display_id) rescue nil
|
31
|
+
authorize(host, family, display_id)
|
31
32
|
|
32
33
|
@requestseq = 1
|
33
|
-
@
|
34
|
-
|
35
|
-
@extensions = {}
|
34
|
+
@rqueue = Queue.new # Read but not returned events
|
35
|
+
@wqueue = Queue.new
|
36
|
+
@extensions = {} # Known extensions
|
37
|
+
@atoms = {} # Interned atoms
|
36
38
|
|
37
|
-
|
38
|
-
@atoms = {}
|
39
|
+
start_io
|
39
40
|
end
|
40
41
|
|
41
42
|
def event_handler= block
|
42
43
|
@event_handler= block
|
43
44
|
end
|
44
45
|
|
46
|
+
def flush
|
47
|
+
while !@wqueue.empty?
|
48
|
+
sleep(0.01)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
45
52
|
def display_info
|
46
53
|
@internal
|
47
54
|
end
|
@@ -67,8 +74,15 @@ module X11
|
|
67
74
|
|
68
75
|
def read_error data
|
69
76
|
error = Form::Error.from_packet(StringIO.new(data))
|
77
|
+
# FIXME: Maybe make this configurable, as it means potentially
|
78
|
+
# keeping along really heavy requests. or alternative purge them
|
79
|
+
# more aggressively also when there are no errors, as otherwise
|
80
|
+
# the growth might be unbounded
|
81
|
+
error.request = @requests[error.sequence_number]
|
82
|
+
@requests.keys.find_all{|s| s <= error.sequence_number}.each do |s|
|
83
|
+
@requests.delete(s)
|
84
|
+
end
|
70
85
|
STDERR.puts "ERROR: #{error.inspect}"
|
71
|
-
raise error.inspect
|
72
86
|
error
|
73
87
|
end
|
74
88
|
|
@@ -116,7 +130,7 @@ module X11
|
|
116
130
|
# FIXME: 30: SelectionRequest
|
117
131
|
# FIXME: 31: SelectionNotify
|
118
132
|
# FIXME: 32: ColormapNotify
|
119
|
-
when 33 then return Form::ClientMessage.from_packet(
|
133
|
+
when 33 then return Form::ClientMessage.from_packet(io)
|
120
134
|
# FIXME: 34: MappingNotify
|
121
135
|
else
|
122
136
|
STDERR.puts "FIXME: Event: #{type}"
|
@@ -126,22 +140,24 @@ module X11
|
|
126
140
|
end
|
127
141
|
|
128
142
|
def read_full_packet(len = 32)
|
129
|
-
data = @socket.
|
143
|
+
data = @socket.read(32)
|
130
144
|
return nil if data.nil?
|
131
145
|
while data.length < 32
|
132
146
|
IO.select([@socket],nil,nil,0.001)
|
133
147
|
data.concat(@socket.read_nonblock(32 - data.length))
|
134
148
|
end
|
135
149
|
return data
|
136
|
-
rescue IO::WaitReadable
|
137
|
-
return nil
|
138
150
|
end
|
139
151
|
|
140
|
-
def read_packet
|
141
|
-
IO.select([@socket],nil,nil, timeout)
|
152
|
+
def read_packet
|
142
153
|
data = read_full_packet(32)
|
143
154
|
return nil if data.nil?
|
144
155
|
|
156
|
+
# FIXME: Make it configurable.
|
157
|
+
@requests.keys.find_all{|s| s <= @requestseq - 50}.each do |s|
|
158
|
+
@requests.delete(s)
|
159
|
+
end
|
160
|
+
|
145
161
|
# FIXME: What is bit 8 for? Synthentic?
|
146
162
|
type = data.unpack("C").first & 0x7f
|
147
163
|
case type
|
@@ -153,61 +169,100 @@ module X11
|
|
153
169
|
end
|
154
170
|
end
|
155
171
|
|
156
|
-
def write_raw_packet(pkt)
|
157
|
-
@requestseq += 1
|
158
|
-
@socket.write(pkt)
|
159
|
-
end
|
160
|
-
|
161
172
|
def write_packet(*args)
|
162
173
|
pkt = args.join
|
163
174
|
pkt[2..3] = u16(pkt.length/4)
|
164
|
-
|
175
|
+
@wqueue << [nil,nil,pkt]
|
165
176
|
end
|
166
177
|
|
167
178
|
def write_request ob
|
168
|
-
|
169
|
-
#p [:write_request, @requestseq, ob.class]
|
170
|
-
data = ob.to_packet if ob.respond_to?(:to_packet)
|
171
|
-
#p [:AddGlyph,data] if ob.is_a?(X11::Form::XRenderAddGlyphs)
|
172
|
-
#p [ob.request_length.to_i*4, data.size]
|
179
|
+
data = ob.to_packet(self) if ob.respond_to?(:to_packet)
|
173
180
|
raise "BAD LENGTH for #{ob.inspect} (#{ob.request_length.to_i*4} ! #{data.size} " if ob.request_length && ob.request_length.to_i*4 != data.size
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
181
|
+
STDERR.puts "write_req: #{ob.inspect}" if @debug
|
182
|
+
@wqueue << [ob,nil,data]
|
183
|
+
end
|
184
|
+
|
185
|
+
def write_sync(ob, reply=nil)
|
186
|
+
data = ob.to_packet(self) if ob.respond_to?(:to_packet)
|
187
|
+
q = Queue.new
|
188
|
+
@wqueue << [ob,q,data]
|
189
|
+
STDERR.puts "write_sync_req: #{ob.inspect}" if @debug
|
190
|
+
pkt = q.shift
|
191
|
+
STDERR.puts "write_sync_rep: #{pkt.inspect}" if @debug
|
192
|
+
raise(pkt) if pkt.is_a?(X11::Form::Error)
|
193
|
+
return pkt if !pkt.is_a?(String)
|
184
194
|
reply ? reply.from_packet(StringIO.new(pkt)) : pkt
|
185
195
|
end
|
186
196
|
|
187
|
-
def peek_packet
|
188
|
-
|
189
|
-
end
|
197
|
+
def peek_packet = !@rqueue.empty?
|
198
|
+
def next_packet = @rqueue.shift
|
190
199
|
|
191
|
-
def
|
192
|
-
|
193
|
-
|
200
|
+
def close = @rqueue.close
|
201
|
+
|
202
|
+
def start_io
|
203
|
+
@replies ||= {}
|
204
|
+
@requests ||= {}
|
205
|
+
# Read thread.
|
206
|
+
# FIXME: Drop the select.
|
207
|
+
rt = Thread.new do
|
208
|
+
while pkt = read_packet
|
209
|
+
#STDERR.puts "read: #{pkt.inspect}"
|
210
|
+
if !pkt
|
211
|
+
sleep 0.1
|
212
|
+
elsif pkt.is_a?(String)
|
213
|
+
# This is a reply. We need the sequence number.
|
214
|
+
#
|
215
|
+
seq = pkt.unpack1("@2S")
|
216
|
+
STDERR.puts " - seq= #{seq}" if @debug
|
217
|
+
STDERR.puts @replies.inspect if @debug
|
218
|
+
if @replies[seq]
|
219
|
+
q = @replies.delete(seq)
|
220
|
+
STDERR.puts " - reply to #{q}" if @debug
|
221
|
+
q << pkt
|
222
|
+
end
|
223
|
+
elsif pkt.is_a?(X11::Form::Error)
|
224
|
+
if @replies[pkt.sequence_number]
|
225
|
+
q = @replies.delete(pkt.sequence_number)
|
226
|
+
q << pkt
|
227
|
+
else
|
228
|
+
@rqueue << pkt
|
229
|
+
end
|
230
|
+
else
|
231
|
+
@rqueue << pkt
|
232
|
+
end
|
233
|
+
end
|
234
|
+
@rqueue.close
|
235
|
+
@replies.values.each(&:close)
|
236
|
+
end
|
194
237
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
@queue.push(pkt)
|
238
|
+
# Write thread
|
239
|
+
wt = Thread.new do
|
240
|
+
while msg = @wqueue.shift
|
241
|
+
ob, q, data = *msg
|
242
|
+
@requests[@requestseq] = ob
|
243
|
+
@replies[@requestseq] = q if q
|
244
|
+
@requestseq = (@requestseq + 1) % 65536
|
245
|
+
@socket.write(data)
|
204
246
|
end
|
205
247
|
end
|
248
|
+
|
249
|
+
at_exit do
|
250
|
+
flush
|
251
|
+
@rqueue.close
|
252
|
+
@wqueue.close
|
253
|
+
# We kill this because it may be stuck in a read
|
254
|
+
# we'll never care about
|
255
|
+
Thread.kill(rt)
|
256
|
+
|
257
|
+
# We wait for this to finish because otherwise we may
|
258
|
+
# lose side-effects
|
259
|
+
wt.join
|
260
|
+
end
|
206
261
|
end
|
207
|
-
|
262
|
+
|
208
263
|
def run
|
209
264
|
loop do
|
210
|
-
pkt =
|
265
|
+
pkt = next_packet
|
211
266
|
return if !pkt
|
212
267
|
yield(pkt)
|
213
268
|
end
|
@@ -218,13 +273,15 @@ module X11
|
|
218
273
|
d.depth == depth }.visuals.find{|v| v.qlass = qlass }
|
219
274
|
end
|
220
275
|
|
276
|
+
def default_root = screens.first.root
|
277
|
+
|
221
278
|
# Requests
|
222
279
|
def create_window(x,y,w,h,
|
223
280
|
values: {},
|
224
281
|
depth: 32, parent: nil, border_width: 0, wclass: X11::Form::InputOutput, visual: nil
|
225
282
|
)
|
226
283
|
wid = new_id
|
227
|
-
parent ||=
|
284
|
+
parent ||= default_root
|
228
285
|
|
229
286
|
if visual.nil?
|
230
287
|
visual = find_visual(0, depth).visual_id
|
@@ -252,24 +309,27 @@ module X11
|
|
252
309
|
values = values.sort_by{_1[0]}
|
253
310
|
mask = values.inject(0) {|acc,v| (acc | v[0]) }
|
254
311
|
values = values.map{_1[1]}
|
255
|
-
write_request(
|
256
|
-
X11::Form::ChangeWindowAttributes.new(wid, mask, values)
|
257
|
-
)
|
312
|
+
write_request(Form::ChangeWindowAttributes.new(wid, mask, values))
|
258
313
|
end
|
259
314
|
|
260
315
|
def select_input(w, events) = change_window_attributes(w, values: {Form::CWEventMask => events})
|
261
316
|
|
262
317
|
def atom(name)
|
318
|
+
return name if name.is_a?(Integer) # Allow atom(atom_integer_or_symbol)
|
319
|
+
begin
|
320
|
+
return Form::Atoms.const_get(name.to_sym) if Form::Atoms.const_defined?(name.to_sym)
|
321
|
+
rescue
|
322
|
+
# const_defined? will throw if name isn't a valid constant name, but
|
323
|
+
# that's fine
|
324
|
+
end
|
263
325
|
name = name.to_sym
|
264
326
|
intern_atom(false, name) if !@atoms[name]
|
265
327
|
@atoms[name]
|
266
328
|
end
|
267
329
|
|
268
330
|
def query_extension(name)
|
269
|
-
r = write_sync(
|
270
|
-
@extensions[name] = {
|
271
|
-
major: r.major_opcode
|
272
|
-
}
|
331
|
+
r = write_sync(Form::QueryExtension.new(name), Form::QueryExtensionReply)
|
332
|
+
@extensions[name] = { major: r.major_opcode }
|
273
333
|
r
|
274
334
|
end
|
275
335
|
|
@@ -282,38 +342,33 @@ module X11
|
|
282
342
|
end
|
283
343
|
|
284
344
|
def intern_atom(flag, name)
|
285
|
-
reply = write_sync(
|
286
|
-
X11::Form::InternAtomReply)
|
345
|
+
reply = write_sync(Form::InternAtom.new(flag, name.to_s),Form::InternAtomReply)
|
287
346
|
if reply
|
288
347
|
@atoms[name.to_sym] = reply.atom
|
289
348
|
end
|
290
349
|
end
|
291
350
|
|
292
|
-
def get_atom_name(atom)
|
293
|
-
|
294
|
-
|
295
|
-
end
|
296
|
-
|
297
|
-
def destroy_window(window) = write_request(X11::Form::DestroyWindow.new(window))
|
298
|
-
def get_geometry(drawable) = write_sync(X11::Form::GetGeometry.new(drawable), X11::Form::Geometry)
|
351
|
+
def get_atom_name(atom) = write_sync(Form::GetAtomName.new(atom), Form::AtomName)&.name
|
352
|
+
def destroy_window(window) = write_request(Form::DestroyWindow.new(window))
|
353
|
+
def get_geometry(drawable) = write_sync(Form::GetGeometry.new(drawable), Form::Geometry)
|
299
354
|
|
300
355
|
def get_keyboard_mapping(min_keycode=display_info.min_keycode, count= display_info.max_keycode - min_keycode)
|
301
|
-
write_sync(
|
356
|
+
write_sync(Form::GetKeyboardMapping.new(min_keycode, count), Form::GetKeyboardMappingReply)
|
302
357
|
end
|
303
358
|
|
304
359
|
def create_colormap(alloc, window, visual)
|
305
360
|
mid = new_id
|
306
|
-
write_request(
|
361
|
+
write_request(Form::CreateColormap.new(alloc, mid, window, visual))
|
307
362
|
mid
|
308
363
|
end
|
309
364
|
|
310
365
|
def get_property(window, property, type, offset: 0, length: 4, delete: false)
|
311
|
-
property = atom(property)
|
366
|
+
property = atom(property)
|
312
367
|
type = atom_enum(type)
|
313
368
|
|
314
|
-
result = write_sync(
|
369
|
+
result = write_sync(Form::GetProperty.new(
|
315
370
|
delete, window, property, type, offset, length
|
316
|
-
),
|
371
|
+
), Form::Property)
|
317
372
|
|
318
373
|
if result && result.format != 0
|
319
374
|
case result.format
|
@@ -335,30 +390,30 @@ module X11
|
|
335
390
|
|
336
391
|
mode = open_enum(mode, {replace: 0, prepend: 1, append: 2})
|
337
392
|
type = atom_enum(type)
|
338
|
-
write_request(
|
339
|
-
end
|
340
|
-
|
341
|
-
def list_fonts(*args)
|
342
|
-
write_sync(X11::Form::ListFonts.new(*args),
|
343
|
-
X11::Form::ListFontsReply)
|
393
|
+
write_request(Form::ChangeProperty.new(mode, window, property, type, format, data))
|
344
394
|
end
|
345
395
|
|
346
|
-
def
|
347
|
-
def
|
348
|
-
def
|
396
|
+
def list_fonts(...) = write_sync(Form::ListFonts.new(...), Form::ListFontsReply)
|
397
|
+
def open_font(...) = write_request(Form::OpenFont.new(...))
|
398
|
+
def change_gc(...) = write_request(Form::ChangeGC.new(...))
|
399
|
+
def change_save_set(...)= write_request(Form::ChangeSaveSet.new(...))
|
400
|
+
|
349
401
|
def reparent_window(window, parent, x, y, save: true)
|
350
402
|
# You so almost always want this that it should've been a single request
|
351
403
|
change_save_set(0, window) if save
|
352
|
-
write_request(
|
404
|
+
write_request(Form::ReparentWindow.new(window, parent, x,y))
|
353
405
|
end
|
354
406
|
|
355
|
-
def map_window(
|
356
|
-
def unmap_window(
|
407
|
+
def map_window(...) = write_request(Form::MapWindow.new(...))
|
408
|
+
def unmap_window(...) = write_request(Form::UnmapWindow.new(...))
|
357
409
|
|
358
410
|
def u8(*args) = args.pack("c*")
|
359
411
|
def u16(*args) = args.pack("v*")
|
360
412
|
def u32(*args) = args.pack("V*")
|
361
|
-
def atom_enum(val)
|
413
|
+
def atom_enum(val)
|
414
|
+
open_enum(val, {cardinal: Form::CardinalAtom, atom: Form::AtomAtom, window: Form::WindowAtom}) || atom(val)
|
415
|
+
end
|
416
|
+
|
362
417
|
def window(*args)
|
363
418
|
args.each {|a| raise "Window expected" if a.nil? }
|
364
419
|
u32(*args)
|
@@ -369,15 +424,15 @@ module X11
|
|
369
424
|
def set_input_focus(revert_to, focus, time=:now)
|
370
425
|
# FIXME: This is an experiment.
|
371
426
|
# Upside: Simpler. Downside: Doesn't work server-side.
|
372
|
-
#
|
427
|
+
# Probably a bad idea.
|
373
428
|
revert_to = open_enum(revert_to, {none: 0, pointer_root: 1, parent: 2})
|
374
429
|
focus = open_enum(focus, {none: 0, pointer_root: 1 })
|
375
430
|
time = open_enum(time, {current_time: 0, now: 0})
|
376
431
|
write_packet(u8(42,revert_to), u16(3), window(focus), u32(time))
|
377
432
|
end
|
378
|
-
|
433
|
+
|
379
434
|
def grab_key(owner_events, grab_window, modifiers, keycode, pointer_mode, keyboard_mode)
|
380
|
-
write_request(
|
435
|
+
write_request(Form::GrabKey.new(
|
381
436
|
owner_events,
|
382
437
|
grab_window,
|
383
438
|
modifiers,
|
@@ -389,7 +444,7 @@ module X11
|
|
389
444
|
|
390
445
|
def grab_button(owner_events, grab_window, event_mask, pointer_mode,
|
391
446
|
keyboard_mode, confine_to, cursor, button, modifiers)
|
392
|
-
write_request(
|
447
|
+
write_request(Form::GrabButton.new(
|
393
448
|
owner_events, grab_window, event_mask,
|
394
449
|
pointer_mode == :async ? 1 : 0,
|
395
450
|
keyboard_mode == :async ? 1 : 0,
|
@@ -397,41 +452,26 @@ module X11
|
|
397
452
|
)
|
398
453
|
end
|
399
454
|
|
400
|
-
def
|
401
|
-
border_width: nil, sibling: nil, stack_mode: nil)
|
402
|
-
|
403
|
-
mask = 0
|
404
|
-
values = []
|
405
|
-
|
455
|
+
def set_value(values, mask, x)
|
406
456
|
if x
|
407
|
-
mask |= 0x001
|
408
457
|
values << x
|
458
|
+
mask
|
459
|
+
else
|
460
|
+
0
|
409
461
|
end
|
462
|
+
end
|
463
|
+
|
464
|
+
def configure_window(window, x: nil, y: nil, width: nil, height: nil,
|
465
|
+
border_width: nil, sibling: nil, stack_mode: nil)
|
410
466
|
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
end
|
420
|
-
|
421
|
-
if height
|
422
|
-
mask |= 0x008
|
423
|
-
values << height
|
424
|
-
end
|
425
|
-
|
426
|
-
if border_width
|
427
|
-
mask |= 0x010
|
428
|
-
values << border_width
|
429
|
-
end
|
430
|
-
|
431
|
-
if sibling
|
432
|
-
mask |= 0x020
|
433
|
-
values << sibling
|
434
|
-
end
|
467
|
+
values = []
|
468
|
+
mask = 0
|
469
|
+
mask |= set_value(values, 0x001, x)
|
470
|
+
mask |= set_value(values, 0x002, y)
|
471
|
+
mask |= set_value(values, 0x004, width)
|
472
|
+
mask |= set_value(values, 0x008, height)
|
473
|
+
mask |= set_value(values, 0x010, border_width)
|
474
|
+
mask |= set_value(values, 0x020, sibling)
|
435
475
|
|
436
476
|
if stack_mode
|
437
477
|
mask |= 0x040
|
@@ -448,39 +488,50 @@ module X11
|
|
448
488
|
end
|
449
489
|
|
450
490
|
|
451
|
-
def create_gc(window, foreground: nil, background: nil
|
491
|
+
def create_gc(window, foreground: nil, background: nil,
|
492
|
+
graphics_exposures: nil
|
493
|
+
)
|
452
494
|
mask = 0
|
453
495
|
args = []
|
454
496
|
|
455
497
|
# FIXME:
|
456
498
|
# The rest can be found here:
|
457
499
|
# https://tronche.com/gui/x/xlib/GC/manipulating.html#XGCValues
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
end
|
462
|
-
if background
|
463
|
-
mask |= 0x08
|
464
|
-
args << background
|
465
|
-
end
|
466
|
-
|
500
|
+
mask |= set_value(args, 0x04, foreground)
|
501
|
+
mask |= set_value(args, 0x08, background)
|
502
|
+
mask |= set_value(args, 0x10000, graphics_exposures)
|
467
503
|
|
468
504
|
gc = new_id
|
469
505
|
write_request(X11::Form::CreateGC.new(gc, window, mask, args))
|
470
506
|
gc
|
471
507
|
end
|
472
508
|
|
509
|
+
def send_event(...) = write_request(Form::SendEvent.new(...))
|
510
|
+
def client_message(window: default_root, type: :ClientMessage, format: 32, destination: default_root, mask: 0, data: [], propagate: true)
|
511
|
+
f = {8 => "C20", 16 => "S10", 32 => "L5"}[format]
|
512
|
+
p f
|
513
|
+
data = (Array(data).map{|item|atom(item)} +[0]*20).pack(f)
|
514
|
+
event = Form::ClientMessage.new(
|
515
|
+
format, 0, window, atom(type), data
|
516
|
+
)
|
517
|
+
event.code =33
|
518
|
+
pp event
|
519
|
+
|
520
|
+
send_event(propagate, destination, mask, event)
|
521
|
+
end
|
522
|
+
|
473
523
|
def put_image(*args) = write_request(X11::Form::PutImage.new(*args))
|
474
524
|
def clear_area(*args) = write_request(X11::Form::ClearArea.new(*args))
|
475
525
|
def copy_area(*args) = write_request(X11::Form::CopyArea.new(*args))
|
476
526
|
def image_text8(*args) = write_request(X11::Form::ImageText8.new(*args))
|
477
527
|
def image_text16(*args)= write_request(X11::Form::ImageText16.new(*args))
|
478
|
-
def poly_fill_rectangle(
|
528
|
+
def poly_fill_rectangle(wid, gc, *rects)
|
529
|
+
rects = rects.map{|r| r.is_a?(Array) ? Form::Rectangle.new(*r) : r}
|
530
|
+
write_request(X11::Form::PolyFillRectangle.new(wid, gc, rects))
|
531
|
+
end
|
479
532
|
|
480
533
|
def create_pixmap(depth, drawable, w,h)
|
481
|
-
pid
|
482
|
-
write_request(X11::Form::CreatePixmap.new(depth, pid, drawable, w,h))
|
483
|
-
pid
|
534
|
+
new_id.tap{|pid| write_request(Form::CreatePixmap.new(depth, pid, drawable, w,h)) }
|
484
535
|
end
|
485
536
|
|
486
537
|
# XRender
|
@@ -506,8 +557,8 @@ module X11
|
|
506
557
|
|
507
558
|
def render_query_pict_formats
|
508
559
|
@render_formats ||= write_sync(
|
509
|
-
|
510
|
-
|
560
|
+
Form::XRenderQueryPictFormats.new(render_opcode),
|
561
|
+
Form::XRenderQueryPictFormatsReply
|
511
562
|
)
|
512
563
|
end
|
513
564
|
|
@@ -528,26 +579,26 @@ module X11
|
|
528
579
|
case sym
|
529
580
|
when :a8
|
530
581
|
@a8 ||= formats.formats.find do |f|
|
531
|
-
f.type
|
582
|
+
f.type == 1 &&
|
532
583
|
f.depth == 8 &&
|
533
584
|
f.direct.alpha_mask == 255
|
534
585
|
end
|
535
586
|
when :rgb24
|
536
587
|
@rgb24 ||= formats.formats.find do |f|
|
537
|
-
f.type
|
538
|
-
f.depth
|
539
|
-
f.direct.red
|
540
|
-
f.direct.green == 8
|
541
|
-
f.direct.blue
|
588
|
+
f.type == 1 &&
|
589
|
+
f.depth == 24 &&
|
590
|
+
f.direct.red == 16 &&
|
591
|
+
f.direct.green == 8 &&
|
592
|
+
f.direct.blue == 0
|
542
593
|
end
|
543
594
|
when :argb24
|
544
595
|
@argb24 ||= formats.formats.find do |f|
|
545
|
-
f.type
|
546
|
-
f.depth
|
596
|
+
f.type == 1 &&
|
597
|
+
f.depth == 32 &&
|
547
598
|
f.direct.alpha == 24 &&
|
548
|
-
f.direct.red
|
549
|
-
f.direct.green == 8
|
550
|
-
f.direct.blue
|
599
|
+
f.direct.red == 16 &&
|
600
|
+
f.direct.green == 8 &&
|
601
|
+
f.direct.blue == 0
|
551
602
|
end
|
552
603
|
else
|
553
604
|
raise "Unsupported format (a4/a1 by omission)"
|
@@ -605,7 +656,6 @@ module X11
|
|
605
656
|
auth_name = ""
|
606
657
|
auth_data = ""
|
607
658
|
end
|
608
|
-
p [auth_name, auth_data]
|
609
659
|
|
610
660
|
handshake = Form::ClientHandshake.new(
|
611
661
|
Protocol::BYTE_ORDER,
|
@@ -615,7 +665,7 @@ module X11
|
|
615
665
|
auth_data
|
616
666
|
)
|
617
667
|
|
618
|
-
@socket.write(handshake.to_packet)
|
668
|
+
@socket.write(handshake.to_packet(self))
|
619
669
|
|
620
670
|
data = @socket.read(1)
|
621
671
|
raise AuthorizationError, "Failed to read response from server" if !data
|
data/lib/X11/form.rb
CHANGED
@@ -44,7 +44,7 @@ module X11
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
def to_packet
|
47
|
+
def to_packet(dpy)
|
48
48
|
# fetch class level instance variable holding defined fields
|
49
49
|
structs = self.class.structs
|
50
50
|
|
@@ -64,17 +64,10 @@ module X11
|
|
64
64
|
#p [s,value]
|
65
65
|
|
66
66
|
if value.is_a?(BaseForm)
|
67
|
-
v = value.to_packet
|
68
|
-
elsif value.is_a?(Symbol)
|
69
|
-
#if !@atoms[value]
|
70
|
-
# reply = write_sync(X11::Forms::InternAtom.new(false, value.to_s), X11::Forms::InternAtomReply)
|
71
|
-
# @
|
72
|
-
#end
|
73
|
-
#value = @atoms[value]
|
74
|
-
raise "FIXME"
|
67
|
+
v = value.to_packet(dpy)
|
75
68
|
else
|
76
69
|
#p [s,value]
|
77
|
-
v = s.type_klass.pack(value)
|
70
|
+
v = s.type_klass.pack(value, dpy)
|
78
71
|
end
|
79
72
|
#p v
|
80
73
|
v
|
@@ -84,15 +77,15 @@ module X11
|
|
84
77
|
when :length, :format_length
|
85
78
|
#p [s,value]
|
86
79
|
#p [value.size]
|
87
|
-
s.type_klass.pack(value.size)
|
80
|
+
s.type_klass.pack(value.size, dpy)
|
88
81
|
when :string
|
89
|
-
s.type_klass.pack(value)
|
82
|
+
s.type_klass.pack(value, dpy)
|
90
83
|
when :list
|
91
84
|
Array(value).collect do |obj|
|
92
85
|
if obj.is_a?(BaseForm)
|
93
|
-
obj.to_packet
|
86
|
+
obj.to_packet(dpy)
|
94
87
|
else
|
95
|
-
s.type_klass.pack(obj)
|
88
|
+
s.type_klass.pack(obj, dpy)
|
96
89
|
end
|
97
90
|
end
|
98
91
|
end
|
@@ -191,6 +184,29 @@ module X11
|
|
191
184
|
end
|
192
185
|
end
|
193
186
|
|
187
|
+
# # Predefined constants, that can be used in the form of symbols
|
188
|
+
|
189
|
+
module Atoms
|
190
|
+
PRIMARY = 1
|
191
|
+
SECONDARY = 2
|
192
|
+
ARC = 3
|
193
|
+
ATOM = 4
|
194
|
+
BITMAP = 5
|
195
|
+
CARDINAL = 6
|
196
|
+
COLORMAP = 7
|
197
|
+
CURSOR = 8
|
198
|
+
#...
|
199
|
+
STRING = 31
|
200
|
+
VISUALID = 32
|
201
|
+
WINDOW = 33
|
202
|
+
WM_COMMAND = 34
|
203
|
+
WM_HINTS = 35
|
204
|
+
end
|
205
|
+
|
206
|
+
PointerWindow = 0
|
207
|
+
InputFocus = 1
|
208
|
+
|
209
|
+
# FIXME: Deprecated in favour of the Constants module
|
194
210
|
AtomAtom=4
|
195
211
|
CardinalAtom=6
|
196
212
|
WindowAtom=33
|
@@ -293,6 +309,9 @@ module X11
|
|
293
309
|
field :minor_opcode, Uint16
|
294
310
|
field :major_opcode, Uint8
|
295
311
|
unused 21
|
312
|
+
|
313
|
+
# The original request
|
314
|
+
attr_accessor :request
|
296
315
|
end
|
297
316
|
|
298
317
|
# XRender structures
|
@@ -336,8 +355,9 @@ module X11
|
|
336
355
|
field :colormap, Colormap
|
337
356
|
end
|
338
357
|
|
339
|
-
# Requests
|
358
|
+
# # Requests
|
340
359
|
|
360
|
+
# Constants, p112 onwards
|
341
361
|
CopyFromParent = 0
|
342
362
|
InputOutput = 1
|
343
363
|
InputOnly = 2
|
@@ -365,12 +385,17 @@ module X11
|
|
365
385
|
PointerMotionMask = 0x000040
|
366
386
|
PointerMotionHintMask = 0x000080
|
367
387
|
Button1MotionMask = 0x000100
|
388
|
+
# 0x200 .. 0x40000; page 113
|
368
389
|
ExposureMask = 0x008000
|
390
|
+
VisibilityChangeMask = 0x010000
|
369
391
|
StructureNotifyMask = 0x020000
|
392
|
+
ResizeRedirectMask = 0x040000
|
370
393
|
SubstructureNotifyMask = 0x080000
|
371
394
|
SubstructureRedirectMask=0x100000
|
372
395
|
FocusChangeMask = 0x200000
|
373
396
|
PropertyChangeMask = 0x400000
|
397
|
+
ColormapChangeMask = 0x800000
|
398
|
+
OwnerGrabButtonMask = 0x100000
|
374
399
|
|
375
400
|
class CreateWindow < BaseForm
|
376
401
|
field :opcode, Uint8, value: 1
|
@@ -599,6 +624,15 @@ module X11
|
|
599
624
|
field :value, String8, :string
|
600
625
|
end
|
601
626
|
|
627
|
+
class SendEvent < BaseForm
|
628
|
+
field :opcode, Uint8, value: 25
|
629
|
+
field :propagate, Bool
|
630
|
+
field :request_length, Uint16, value: 11
|
631
|
+
field :destination, Window
|
632
|
+
field :event_mask, Uint32
|
633
|
+
field :event, Uint32 # FIXME: This is wrong, and will break on parsing.
|
634
|
+
end
|
635
|
+
|
602
636
|
class GrabButton < BaseForm
|
603
637
|
field :opcode, Uint8, value: 28
|
604
638
|
field :owner_events, Bool
|
@@ -685,6 +719,7 @@ module X11
|
|
685
719
|
ForegroundMask = 0x04
|
686
720
|
BackgroundMask = 0x08
|
687
721
|
FontMask = 0x4000
|
722
|
+
GraphicsExposures = 0x10000
|
688
723
|
|
689
724
|
class CreateGC < BaseForm
|
690
725
|
field :opcode, Uint8, value: 55
|
@@ -1009,6 +1044,7 @@ module X11
|
|
1009
1044
|
unused 1
|
1010
1045
|
field :sequence_number, Uint16
|
1011
1046
|
field :event, Window
|
1047
|
+
field :window, Window
|
1012
1048
|
field :above_sibling, Window
|
1013
1049
|
field :x, Int16
|
1014
1050
|
field :y, Int16
|
data/lib/X11/protocol.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
module X11
|
2
2
|
module Protocol
|
3
|
-
#
|
3
|
+
# endianess of your machine
|
4
4
|
BYTE_ORDER = case [1].pack("L")
|
5
|
-
when "\0\0\0\1"
|
6
|
-
|
7
|
-
when "\1\0\0\0"
|
8
|
-
"l".ord
|
5
|
+
when "\0\0\0\1" then "B".ord
|
6
|
+
when "\1\0\0\0" then "l".ord
|
9
7
|
else
|
10
8
|
raise ByteOrderError.new "Cannot determine byte order"
|
11
|
-
|
9
|
+
end
|
12
10
|
|
13
11
|
MAJOR = 11
|
14
12
|
MINOR = 0
|
data/lib/X11/screen.rb
CHANGED
@@ -7,25 +7,11 @@ module X11
|
|
7
7
|
@internal = data
|
8
8
|
end
|
9
9
|
|
10
|
-
def root
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def
|
15
|
-
@internal.root_depth
|
16
|
-
end
|
17
|
-
|
18
|
-
def root_visual
|
19
|
-
@internal.root_visual
|
20
|
-
end
|
21
|
-
|
22
|
-
def width
|
23
|
-
@internal.width_in_pixels
|
24
|
-
end
|
25
|
-
|
26
|
-
def height
|
27
|
-
@internal.height_in_pixels
|
28
|
-
end
|
10
|
+
def root = @internal.root
|
11
|
+
def root_depth = @internal.root_depth
|
12
|
+
def root_visual = @internal.root_visual
|
13
|
+
def width = @internal.width_in_pixels
|
14
|
+
def height = @internal.height_in_pixels
|
29
15
|
|
30
16
|
def to_s
|
31
17
|
"#<X11::Screen(#{id}) width=#{width} height=#{height}>"
|
data/lib/X11/type.rb
CHANGED
@@ -1,55 +1,48 @@
|
|
1
1
|
# This module is used for encoding Ruby Objects to binary
|
2
2
|
# data. The types Int8, Int16, etc. are data-types defined
|
3
|
-
# in the X11 protocol.
|
4
|
-
# which gets evaluated when a packet is created.
|
3
|
+
# in the X11 protocol.
|
5
4
|
|
6
5
|
module X11
|
7
6
|
module Type
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def self.pack(x)
|
13
|
-
[x].pack(\"#{directive}\")
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.unpack(x)
|
17
|
-
x.unpack(\"#{directive}\").first
|
18
|
-
end
|
8
|
+
class BaseType
|
9
|
+
@directive = nil
|
10
|
+
@bytesize = nil
|
19
11
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
r ? unpack(r) : nil
|
12
|
+
def self.config(d,b) = (@directive, @bytesize = d,b)
|
13
|
+
|
14
|
+
def self.pack(x, dpy)
|
15
|
+
if x.is_a?(Symbol)
|
16
|
+
if (t = X11::Form.const_get(x)) && t.is_a?(Numeric)
|
17
|
+
x = t
|
27
18
|
end
|
28
19
|
end
|
29
|
-
|
30
|
-
|
20
|
+
[x].pack(@directive)
|
21
|
+
rescue TypeError => e
|
22
|
+
raise "Expected #{self.name}, got #{x.class} (value: #{x})"
|
23
|
+
end
|
31
24
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
25
|
+
def self.unpack(x) = x.nil? ? nil : x.unpack1(@directive)
|
26
|
+
def self.size = @bytesize
|
27
|
+
def self.from_packet(sock) = unpack(sock.read(size))
|
28
|
+
end
|
29
|
+
|
30
|
+
class Int8 < BaseType; config("c",1); end
|
31
|
+
class Int16 < BaseType; config("s",2); end
|
32
|
+
class Int32 < BaseType; config("l",4); end
|
33
|
+
class Uint8 < BaseType; config("C",1); end
|
34
|
+
class Uint16 < BaseType; config("S",2); end
|
35
|
+
class Uint32 < BaseType; config("L",4); end
|
39
36
|
|
40
|
-
define "Message", "c*", 20
|
41
|
-
|
42
37
|
class Message
|
43
|
-
def self.pack(x) = x.b
|
44
|
-
def self.unpack(x)
|
45
|
-
def self.size
|
38
|
+
def self.pack(x,dpy) = x.b
|
39
|
+
def self.unpack(x) = x.b
|
40
|
+
def self.size = 20
|
46
41
|
def self.from_packet(sock) = sock.read(2).b
|
47
42
|
end
|
48
43
|
|
49
44
|
class String8
|
50
|
-
def self.pack(x)
|
51
|
-
x.force_encoding("ASCII-8BIT") + "\x00"*(-x.length & 3)
|
52
|
-
end
|
45
|
+
def self.pack(x, dpy) = (x.b + "\x00"*(-x.length & 3))
|
53
46
|
|
54
47
|
def self.unpack(socket, size)
|
55
48
|
raise "Expected size for String8" if size.nil?
|
@@ -61,8 +54,8 @@ module X11
|
|
61
54
|
end
|
62
55
|
|
63
56
|
class String16
|
64
|
-
def self.pack(x)
|
65
|
-
x.encode("UTF-16BE").
|
57
|
+
def self.pack(x, dpy)
|
58
|
+
x.encode("UTF-16BE").b + "\x00\x00"*(-x.length & 1)
|
66
59
|
end
|
67
60
|
|
68
61
|
def self.unpack(socket, size)
|
@@ -75,28 +68,14 @@ module X11
|
|
75
68
|
|
76
69
|
|
77
70
|
class String8Unpadded
|
78
|
-
def self.pack(x)
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
|
83
|
-
def self.unpack(socket, size)
|
84
|
-
val = socket.read(size)
|
85
|
-
end
|
71
|
+
def self.pack(x,dpy) = x
|
72
|
+
def self.unpack(socket, size) = socket.read(size)
|
86
73
|
end
|
87
|
-
|
74
|
+
|
88
75
|
class Bool
|
89
|
-
def self.pack(x)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
def self.unpack(str)
|
94
|
-
str[0] == "\x01"
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.size
|
98
|
-
1
|
99
|
-
end
|
76
|
+
def self.pack(x, dpy) = (x ? "\x01" : "\x00")
|
77
|
+
def self.unpack(str) = (str[0] == "\x01")
|
78
|
+
def self.size = 1
|
100
79
|
end
|
101
80
|
|
102
81
|
KeyCode = Uint8
|
@@ -114,10 +93,16 @@ module X11
|
|
114
93
|
Colormap = Uint32
|
115
94
|
Drawable = Uint32
|
116
95
|
Fontable = Uint32
|
117
|
-
Atom = Uint32
|
118
96
|
VisualID = Uint32
|
119
97
|
Mask = Uint32
|
120
98
|
Timestamp = Uint32
|
121
99
|
Keysym = Uint32
|
100
|
+
|
101
|
+
class Atom
|
102
|
+
def self.pack(x,dpy) = [dpy.atom(x)].pack("L")
|
103
|
+
def self.unpack(x) = x.nil? ? nil : x.unpack1("L")
|
104
|
+
def self.size = 4
|
105
|
+
def self.from_packet(sock) = unpack(sock.read(size))
|
106
|
+
end
|
122
107
|
end
|
123
108
|
end
|
data/lib/X11/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pure-x11
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vidar Hokstad
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-01-11 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Pure Ruby X11 bindings
|
15
15
|
email:
|
@@ -25,6 +25,7 @@ files:
|
|
25
25
|
- README.md
|
26
26
|
- Rakefile
|
27
27
|
- docs/protocol.pdf
|
28
|
+
- example/client_message.rb
|
28
29
|
- example/genie.png
|
29
30
|
- example/test.rb
|
30
31
|
- lib/X11.rb
|