pure-x11 0.0.5 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|