pure-x11 0.0.6 → 0.0.9
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/Gemfile.lock +1 -1
- data/example/client_message.rb +13 -0
- data/example/test.rb +24 -23
- data/lib/X11/auth.rb +10 -13
- data/lib/X11/display.rb +156 -63
- data/lib/X11/form.rb +51 -8
- data/lib/X11/protocol.rb +4 -6
- data/lib/X11/screen.rb +5 -19
- data/lib/X11/type.rb +16 -13
- data/lib/X11/version.rb +1 -1
- data/lib/X11/window.rb +39 -0
- data/lib/X11.rb +2 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d50a871d744420a3fc90c8ca3cff7defb5d4917bce297469502c94e64ae12790
|
4
|
+
data.tar.gz: e17c0019823b08847fca5b33b35f2d618bde521fd3e86bf5a5168eb68807383a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36892209167f3aea6ccc68309e64f0e45f56e036ce8a547ffab37460f2169902429d6d1b4f11b7156d64a45151a155a7a46df693b2db86203ba75c9b99fe41ff
|
7
|
+
data.tar.gz: 6c7d7c83efef12e9e9303526cf2b92b5d40ad4e7d726af5a5fe59117f1396a171d8d79e452b7a5ede941d2d1bf0fcef05b7e60fc0cfd180b7f7dce95b5c0289c
|
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
@@ -14,7 +14,7 @@ dpy = display = X11::Display.new
|
|
14
14
|
screen = dpy.screens.first
|
15
15
|
root = screen.root
|
16
16
|
|
17
|
-
|
17
|
+
window = X11::Window.create(dpy,
|
18
18
|
0, 0, # x,y
|
19
19
|
1000, 600, # w,h
|
20
20
|
# FIXME: WTH isn't depth: 32 working here?
|
@@ -32,17 +32,18 @@ wid = dpy.create_window(
|
|
32
32
|
#dpy.next_packet
|
33
33
|
#exit(0)
|
34
34
|
|
35
|
-
|
36
|
-
|
35
|
+
|
36
|
+
def set_window_opacity(window, opacity)
|
37
|
+
window.change_property(
|
37
38
|
:replace,
|
38
|
-
|
39
|
+
"_NET_WM_WINDOW_OPACITY",
|
39
40
|
:cardinal, 32,
|
40
41
|
[(0xffffffff * opacity).to_i].pack("V").unpack("C*")
|
41
42
|
)
|
42
43
|
end
|
43
44
|
|
44
45
|
|
45
|
-
set_window_opacity(
|
46
|
+
set_window_opacity(window, 0.8)
|
46
47
|
|
47
48
|
#p dpy.display_info
|
48
49
|
|
@@ -87,11 +88,11 @@ def lookup_keysym(dpy, event)
|
|
87
88
|
end
|
88
89
|
|
89
90
|
puts "Mapping"
|
90
|
-
|
91
|
+
window.map
|
91
92
|
|
92
|
-
$gc = gc =
|
93
|
-
$gc2 =
|
94
|
-
$gc3 =
|
93
|
+
$gc = gc = window.create_gc(foreground: 0xff0000)
|
94
|
+
$gc2 = window.create_gc(foreground: 0xffffff, background: 0x444444)
|
95
|
+
$gc3 = window.create_gc
|
95
96
|
|
96
97
|
|
97
98
|
puts "Main loop"
|
@@ -123,7 +124,7 @@ $sft = SFT.new($f)
|
|
123
124
|
$sft.x_scale = 15
|
124
125
|
$sft.y_scale = 15
|
125
126
|
$glyphcache = {}
|
126
|
-
def render_glyph(
|
127
|
+
def render_glyph(window, x,y, ch)
|
127
128
|
gid = $sft.lookup(ch.ord)
|
128
129
|
mtx = $sft.gmetrics(gid)
|
129
130
|
data = $glyphcache[gid]
|
@@ -135,45 +136,45 @@ def render_glyph(display, wid, x,y, ch)
|
|
135
136
|
# p img
|
136
137
|
data = img.pixels.map {|px|
|
137
138
|
"\0\0"+px.chr+"\0" #+ "\0\0\0"
|
138
|
-
}.join.
|
139
|
+
}.join.b
|
139
140
|
$glyphcache[gid] = data
|
140
141
|
end
|
141
142
|
depth = 24
|
142
143
|
# p data
|
143
144
|
#p img
|
144
145
|
# p ch
|
145
|
-
|
146
|
-
:ZPixmap,
|
146
|
+
window.put_image(
|
147
|
+
:ZPixmap, $gc2,
|
147
148
|
mtx.min_width,mtx.min_height,
|
148
149
|
x, y - mtx.y_offset, 0, depth, data
|
149
150
|
)
|
150
151
|
mtx.advance_width
|
151
152
|
end
|
152
153
|
|
153
|
-
def render_str(
|
154
|
+
def render_str(window, x,y, str)
|
154
155
|
str.each_byte do |ch|
|
155
|
-
off = render_glyph(
|
156
|
+
off = render_glyph(window, x, y, ch.chr)
|
156
157
|
x+= off
|
157
158
|
end
|
158
159
|
end
|
159
160
|
|
160
|
-
def redraw(
|
161
|
-
|
162
|
-
|
163
|
-
|
161
|
+
def redraw(window, gc)
|
162
|
+
window.poly_fill_rectangle(gc, [20,20, 60, 80])
|
163
|
+
window.clear_area(false, 30, 30, 5, 5)
|
164
|
+
window.image_text16($gc2, 30, 70, "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ")
|
164
165
|
#"\u25f0\u25ef Hello World")
|
165
|
-
|
166
|
-
:ZPixmap,
|
166
|
+
window.put_image(
|
167
|
+
:ZPixmap, $gc2,
|
167
168
|
$png.width, $png.height, 80, 120, 0, 24, $data
|
168
169
|
)
|
169
|
-
render_str(
|
170
|
+
render_str(window, 30,90, 'HelloWorld')
|
170
171
|
end
|
171
172
|
|
172
173
|
loop do
|
173
174
|
pkt = display.next_packet
|
174
175
|
if pkt
|
175
176
|
p pkt
|
176
|
-
redraw(
|
177
|
+
redraw(window, gc) if pkt.is_a?(X11::Form::Expose)
|
177
178
|
|
178
179
|
if pkt.is_a?(X11::Form::KeyPress)
|
179
180
|
lookup_keysym(dpy,pkt)
|
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
@@ -3,10 +3,16 @@ require 'stringio'
|
|
3
3
|
|
4
4
|
module X11
|
5
5
|
|
6
|
-
class DisplayError <
|
7
|
-
class ConnectionError <
|
8
|
-
class AuthorizationError <
|
9
|
-
class ProtocolError <
|
6
|
+
class DisplayError < X11::BasicError; end
|
7
|
+
class ConnectionError < X11::BasicError; end
|
8
|
+
class AuthorizationError < X11::BasicError; end
|
9
|
+
class ProtocolError < X11::BasicError; end
|
10
|
+
class Error < X11::BasicError
|
11
|
+
def initialize(pkt)
|
12
|
+
super("Error: #{pkt.error}, code=#{pkt.code}, seq=#{pkt.sequence_number}, resource=#{pkt.bad_resource_id}, major=#{pkt.major_opcode}, minor=#{pkt.minor_opcode}")
|
13
|
+
@error = pkt
|
14
|
+
end
|
15
|
+
end
|
10
16
|
|
11
17
|
class Display
|
12
18
|
attr_accessor :socket
|
@@ -17,6 +23,8 @@ module X11
|
|
17
23
|
host, display_id, screen_id = $1, $2, $3
|
18
24
|
family = nil
|
19
25
|
|
26
|
+
@debug = ENV["PUREX_DEBUG"].to_s.strip == "true"
|
27
|
+
|
20
28
|
if host.empty?
|
21
29
|
@socket = UNIXSocket.new("/tmp/.X11-unix/X#{display_id}")
|
22
30
|
family = :Local
|
@@ -26,22 +34,27 @@ module X11
|
|
26
34
|
family = :Internet
|
27
35
|
end
|
28
36
|
|
29
|
-
|
30
|
-
authorize(host, family, display_id) rescue nil
|
37
|
+
authorize(host, family, display_id)
|
31
38
|
|
32
39
|
@requestseq = 1
|
33
|
-
@
|
40
|
+
@rqueue = Queue.new # Read but not returned events
|
41
|
+
@wqueue = Queue.new
|
42
|
+
@extensions = {} # Known extensions
|
43
|
+
@atoms = {} # Interned atoms
|
34
44
|
|
35
|
-
|
36
|
-
|
37
|
-
# Interned atoms
|
38
|
-
@atoms = {}
|
45
|
+
start_io
|
39
46
|
end
|
40
47
|
|
41
48
|
def event_handler= block
|
42
49
|
@event_handler= block
|
43
50
|
end
|
44
51
|
|
52
|
+
def flush
|
53
|
+
while !@wqueue.empty?
|
54
|
+
sleep(0.01)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
45
58
|
def display_info
|
46
59
|
@internal
|
47
60
|
end
|
@@ -67,8 +80,15 @@ module X11
|
|
67
80
|
|
68
81
|
def read_error data
|
69
82
|
error = Form::Error.from_packet(StringIO.new(data))
|
83
|
+
# FIXME: Maybe make this configurable, as it means potentially
|
84
|
+
# keeping along really heavy requests. or alternative purge them
|
85
|
+
# more aggressively also when there are no errors, as otherwise
|
86
|
+
# the growth might be unbounded
|
87
|
+
error.request = @requests[error.sequence_number]
|
88
|
+
@requests.keys.find_all{|s| s <= error.sequence_number}.each do |s|
|
89
|
+
@requests.delete(s)
|
90
|
+
end
|
70
91
|
STDERR.puts "ERROR: #{error.inspect}"
|
71
|
-
raise error.inspect
|
72
92
|
error
|
73
93
|
end
|
74
94
|
|
@@ -126,22 +146,24 @@ module X11
|
|
126
146
|
end
|
127
147
|
|
128
148
|
def read_full_packet(len = 32)
|
129
|
-
data = @socket.
|
149
|
+
data = @socket.read(32)
|
130
150
|
return nil if data.nil?
|
131
151
|
while data.length < 32
|
132
152
|
IO.select([@socket],nil,nil,0.001)
|
133
153
|
data.concat(@socket.read_nonblock(32 - data.length))
|
134
154
|
end
|
135
155
|
return data
|
136
|
-
rescue IO::WaitReadable
|
137
|
-
return nil
|
138
156
|
end
|
139
157
|
|
140
|
-
def read_packet
|
141
|
-
IO.select([@socket],nil,nil, timeout)
|
158
|
+
def read_packet
|
142
159
|
data = read_full_packet(32)
|
143
160
|
return nil if data.nil?
|
144
161
|
|
162
|
+
# FIXME: Make it configurable.
|
163
|
+
@requests.keys.find_all{|s| s <= @requestseq - 50}.each do |s|
|
164
|
+
@requests.delete(s)
|
165
|
+
end
|
166
|
+
|
145
167
|
# FIXME: What is bit 8 for? Synthentic?
|
146
168
|
type = data.unpack("C").first & 0x7f
|
147
169
|
case type
|
@@ -153,57 +175,100 @@ module X11
|
|
153
175
|
end
|
154
176
|
end
|
155
177
|
|
156
|
-
def write_raw_packet(pkt)
|
157
|
-
@requestseq += 1
|
158
|
-
@socket.write(pkt)
|
159
|
-
end
|
160
|
-
|
161
178
|
def write_packet(*args)
|
162
179
|
pkt = args.join
|
163
180
|
pkt[2..3] = u16(pkt.length/4)
|
164
|
-
|
181
|
+
@wqueue << [nil,nil,pkt]
|
165
182
|
end
|
166
183
|
|
167
184
|
def write_request ob
|
168
|
-
data = ob.to_packet if ob.respond_to?(:to_packet)
|
185
|
+
data = ob.to_packet(self) if ob.respond_to?(:to_packet)
|
169
186
|
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
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
187
|
+
STDERR.puts "write_req: #{ob.inspect}" if @debug
|
188
|
+
@wqueue << [ob,nil,data]
|
189
|
+
end
|
190
|
+
|
191
|
+
def write_sync(ob, reply=nil)
|
192
|
+
data = ob.to_packet(self) if ob.respond_to?(:to_packet)
|
193
|
+
q = Queue.new
|
194
|
+
@wqueue << [ob,q,data]
|
195
|
+
STDERR.puts "write_sync_req: #{ob.inspect}" if @debug
|
196
|
+
pkt = q.shift
|
197
|
+
STDERR.puts "write_sync_rep: #{pkt.inspect}" if @debug
|
198
|
+
raise(X11::Error.new(pkt)) if pkt.is_a?(X11::Form::Error)
|
199
|
+
return pkt if !pkt.is_a?(String)
|
180
200
|
reply ? reply.from_packet(StringIO.new(pkt)) : pkt
|
181
201
|
end
|
182
202
|
|
183
|
-
def peek_packet
|
184
|
-
|
185
|
-
end
|
203
|
+
def peek_packet = !@rqueue.empty?
|
204
|
+
def next_packet = @rqueue.shift
|
186
205
|
|
187
|
-
def
|
188
|
-
|
189
|
-
|
206
|
+
def close = @rqueue.close
|
207
|
+
|
208
|
+
def start_io
|
209
|
+
@replies ||= {}
|
210
|
+
@requests ||= {}
|
211
|
+
# Read thread.
|
212
|
+
# FIXME: Drop the select.
|
213
|
+
rt = Thread.new do
|
214
|
+
while pkt = read_packet
|
215
|
+
#STDERR.puts "read: #{pkt.inspect}"
|
216
|
+
if !pkt
|
217
|
+
sleep 0.1
|
218
|
+
elsif pkt.is_a?(String)
|
219
|
+
# This is a reply. We need the sequence number.
|
220
|
+
#
|
221
|
+
seq = pkt.unpack1("@2S")
|
222
|
+
STDERR.puts " - seq= #{seq}" if @debug
|
223
|
+
STDERR.puts @replies.inspect if @debug
|
224
|
+
if @replies[seq]
|
225
|
+
q = @replies.delete(seq)
|
226
|
+
STDERR.puts " - reply to #{q}" if @debug
|
227
|
+
q << pkt
|
228
|
+
end
|
229
|
+
elsif pkt.is_a?(X11::Form::Error)
|
230
|
+
if @replies[pkt.sequence_number]
|
231
|
+
q = @replies.delete(pkt.sequence_number)
|
232
|
+
q << pkt
|
233
|
+
else
|
234
|
+
@rqueue << pkt
|
235
|
+
end
|
236
|
+
else
|
237
|
+
@rqueue << pkt
|
238
|
+
end
|
239
|
+
end
|
240
|
+
@rqueue.close
|
241
|
+
@replies.values.each(&:close)
|
242
|
+
end
|
190
243
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
@queue.push(pkt)
|
244
|
+
# Write thread
|
245
|
+
wt = Thread.new do
|
246
|
+
while msg = @wqueue.shift
|
247
|
+
ob, q, data = *msg
|
248
|
+
@requests[@requestseq] = ob
|
249
|
+
@replies[@requestseq] = q if q
|
250
|
+
@requestseq = (@requestseq + 1) % 65536
|
251
|
+
@socket.write(data)
|
200
252
|
end
|
201
253
|
end
|
254
|
+
|
255
|
+
at_exit do
|
256
|
+
flush
|
257
|
+
@rqueue.close
|
258
|
+
@wqueue.close
|
259
|
+
# We kill this because it may be stuck in a read
|
260
|
+
# we'll never care about
|
261
|
+
Thread.kill(rt)
|
262
|
+
|
263
|
+
# We wait for this to finish because otherwise we may
|
264
|
+
# lose side-effects
|
265
|
+
wt.join
|
266
|
+
end
|
202
267
|
end
|
203
|
-
|
268
|
+
|
204
269
|
def run
|
205
270
|
loop do
|
206
|
-
pkt =
|
271
|
+
pkt = next_packet
|
207
272
|
return if !pkt
|
208
273
|
yield(pkt)
|
209
274
|
end
|
@@ -214,13 +279,15 @@ module X11
|
|
214
279
|
d.depth == depth }.visuals.find{|v| v.qlass = qlass }
|
215
280
|
end
|
216
281
|
|
282
|
+
def default_root = screens.first.root
|
283
|
+
|
217
284
|
# Requests
|
218
285
|
def create_window(x,y,w,h,
|
219
286
|
values: {},
|
220
287
|
depth: 32, parent: nil, border_width: 0, wclass: X11::Form::InputOutput, visual: nil
|
221
288
|
)
|
222
289
|
wid = new_id
|
223
|
-
parent ||=
|
290
|
+
parent ||= default_root
|
224
291
|
|
225
292
|
if visual.nil?
|
226
293
|
visual = find_visual(0, depth).visual_id
|
@@ -255,6 +322,12 @@ module X11
|
|
255
322
|
|
256
323
|
def atom(name)
|
257
324
|
return name if name.is_a?(Integer) # Allow atom(atom_integer_or_symbol)
|
325
|
+
begin
|
326
|
+
return Form::Atoms.const_get(name.to_sym) if Form::Atoms.const_defined?(name.to_sym)
|
327
|
+
rescue
|
328
|
+
# const_defined? will throw if name isn't a valid constant name, but
|
329
|
+
# that's fine
|
330
|
+
end
|
258
331
|
name = name.to_sym
|
259
332
|
intern_atom(false, name) if !@atoms[name]
|
260
333
|
@atoms[name]
|
@@ -343,7 +416,10 @@ module X11
|
|
343
416
|
def u8(*args) = args.pack("c*")
|
344
417
|
def u16(*args) = args.pack("v*")
|
345
418
|
def u32(*args) = args.pack("V*")
|
346
|
-
def atom_enum(val)
|
419
|
+
def atom_enum(val)
|
420
|
+
open_enum(val, {cardinal: Form::CardinalAtom, atom: Form::AtomAtom, window: Form::WindowAtom}) || atom(val)
|
421
|
+
end
|
422
|
+
|
347
423
|
def window(*args)
|
348
424
|
args.each {|a| raise "Window expected" if a.nil? }
|
349
425
|
u32(*args)
|
@@ -354,7 +430,7 @@ module X11
|
|
354
430
|
def set_input_focus(revert_to, focus, time=:now)
|
355
431
|
# FIXME: This is an experiment.
|
356
432
|
# Upside: Simpler. Downside: Doesn't work server-side.
|
357
|
-
#
|
433
|
+
# Probably a bad idea.
|
358
434
|
revert_to = open_enum(revert_to, {none: 0, pointer_root: 1, parent: 2})
|
359
435
|
focus = open_enum(focus, {none: 0, pointer_root: 1 })
|
360
436
|
time = open_enum(time, {current_time: 0, now: 0})
|
@@ -394,9 +470,8 @@ module X11
|
|
394
470
|
def configure_window(window, x: nil, y: nil, width: nil, height: nil,
|
395
471
|
border_width: nil, sibling: nil, stack_mode: nil)
|
396
472
|
|
397
|
-
mask = 0
|
398
473
|
values = []
|
399
|
-
|
474
|
+
mask = 0
|
400
475
|
mask |= set_value(values, 0x001, x)
|
401
476
|
mask |= set_value(values, 0x002, y)
|
402
477
|
mask |= set_value(values, 0x004, width)
|
@@ -419,7 +494,9 @@ module X11
|
|
419
494
|
end
|
420
495
|
|
421
496
|
|
422
|
-
def create_gc(window, foreground: nil, background: nil
|
497
|
+
def create_gc(window, foreground: nil, background: nil,
|
498
|
+
graphics_exposures: nil
|
499
|
+
)
|
423
500
|
mask = 0
|
424
501
|
args = []
|
425
502
|
|
@@ -428,12 +505,29 @@ module X11
|
|
428
505
|
# https://tronche.com/gui/x/xlib/GC/manipulating.html#XGCValues
|
429
506
|
mask |= set_value(args, 0x04, foreground)
|
430
507
|
mask |= set_value(args, 0x08, background)
|
508
|
+
mask |= set_value(args, 0x10000, graphics_exposures)
|
431
509
|
|
432
510
|
gc = new_id
|
433
511
|
write_request(X11::Form::CreateGC.new(gc, window, mask, args))
|
434
512
|
gc
|
435
513
|
end
|
436
514
|
|
515
|
+
def send_event(...) = write_request(Form::SendEvent.new(...))
|
516
|
+
def client_message(window: default_root, type: :ClientMessage, format: 32, destination: default_root, mask: 0, data: [], propagate: true)
|
517
|
+
f = {8 => "C20", 16 => "S10", 32 => "L5"}[format]
|
518
|
+
p f
|
519
|
+
data = (Array(data).map{|item|atom(item)} +[0]*20).pack(f)
|
520
|
+
event = Form::ClientMessage.new(
|
521
|
+
format, 0, window, atom(type), data
|
522
|
+
)
|
523
|
+
event.code =33
|
524
|
+
pp event
|
525
|
+
|
526
|
+
send_event(propagate, destination, mask, event)
|
527
|
+
end
|
528
|
+
|
529
|
+
def query_tree(...) = write_sync(X11::Form::QueryTree.new(...), X11::Form::QueryTreeReply)
|
530
|
+
|
437
531
|
def put_image(*args) = write_request(X11::Form::PutImage.new(*args))
|
438
532
|
def clear_area(*args) = write_request(X11::Form::ClearArea.new(*args))
|
439
533
|
def copy_area(*args) = write_request(X11::Form::CopyArea.new(*args))
|
@@ -499,19 +593,19 @@ module X11
|
|
499
593
|
end
|
500
594
|
when :rgb24
|
501
595
|
@rgb24 ||= formats.formats.find do |f|
|
502
|
-
f.type == 1
|
596
|
+
f.type == 1 &&
|
503
597
|
f.depth == 24 &&
|
504
598
|
f.direct.red == 16 &&
|
505
|
-
f.direct.green == 8
|
599
|
+
f.direct.green == 8 &&
|
506
600
|
f.direct.blue == 0
|
507
601
|
end
|
508
602
|
when :argb24
|
509
603
|
@argb24 ||= formats.formats.find do |f|
|
510
|
-
f.type == 1
|
604
|
+
f.type == 1 &&
|
511
605
|
f.depth == 32 &&
|
512
606
|
f.direct.alpha == 24 &&
|
513
607
|
f.direct.red == 16 &&
|
514
|
-
f.direct.green == 8
|
608
|
+
f.direct.green == 8 &&
|
515
609
|
f.direct.blue == 0
|
516
610
|
end
|
517
611
|
else
|
@@ -570,7 +664,6 @@ module X11
|
|
570
664
|
auth_name = ""
|
571
665
|
auth_data = ""
|
572
666
|
end
|
573
|
-
p [auth_name, auth_data]
|
574
667
|
|
575
668
|
handshake = Form::ClientHandshake.new(
|
576
669
|
Protocol::BYTE_ORDER,
|
@@ -580,7 +673,7 @@ module X11
|
|
580
673
|
auth_data
|
581
674
|
)
|
582
675
|
|
583
|
-
@socket.write(handshake.to_packet)
|
676
|
+
@socket.write(handshake.to_packet(self))
|
584
677
|
|
585
678
|
data = @socket.read(1)
|
586
679
|
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,10 +64,10 @@ module X11
|
|
64
64
|
#p [s,value]
|
65
65
|
|
66
66
|
if value.is_a?(BaseForm)
|
67
|
-
v = value.to_packet
|
67
|
+
v = value.to_packet(dpy)
|
68
68
|
else
|
69
69
|
#p [s,value]
|
70
|
-
v = s.type_klass.pack(value)
|
70
|
+
v = s.type_klass.pack(value, dpy)
|
71
71
|
end
|
72
72
|
#p v
|
73
73
|
v
|
@@ -77,15 +77,15 @@ module X11
|
|
77
77
|
when :length, :format_length
|
78
78
|
#p [s,value]
|
79
79
|
#p [value.size]
|
80
|
-
s.type_klass.pack(value.size)
|
80
|
+
s.type_klass.pack(value.size, dpy)
|
81
81
|
when :string
|
82
|
-
s.type_klass.pack(value)
|
82
|
+
s.type_klass.pack(value, dpy)
|
83
83
|
when :list
|
84
84
|
Array(value).collect do |obj|
|
85
85
|
if obj.is_a?(BaseForm)
|
86
|
-
obj.to_packet
|
86
|
+
obj.to_packet(dpy)
|
87
87
|
else
|
88
|
-
s.type_klass.pack(obj)
|
88
|
+
s.type_klass.pack(obj, dpy)
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
@@ -184,6 +184,29 @@ module X11
|
|
184
184
|
end
|
185
185
|
end
|
186
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
|
187
210
|
AtomAtom=4
|
188
211
|
CardinalAtom=6
|
189
212
|
WindowAtom=33
|
@@ -286,6 +309,9 @@ module X11
|
|
286
309
|
field :minor_opcode, Uint16
|
287
310
|
field :major_opcode, Uint8
|
288
311
|
unused 21
|
312
|
+
|
313
|
+
# The original request
|
314
|
+
attr_accessor :request
|
289
315
|
end
|
290
316
|
|
291
317
|
# XRender structures
|
@@ -329,8 +355,9 @@ module X11
|
|
329
355
|
field :colormap, Colormap
|
330
356
|
end
|
331
357
|
|
332
|
-
# Requests
|
358
|
+
# # Requests
|
333
359
|
|
360
|
+
# Constants, p112 onwards
|
334
361
|
CopyFromParent = 0
|
335
362
|
InputOutput = 1
|
336
363
|
InputOnly = 2
|
@@ -358,12 +385,17 @@ module X11
|
|
358
385
|
PointerMotionMask = 0x000040
|
359
386
|
PointerMotionHintMask = 0x000080
|
360
387
|
Button1MotionMask = 0x000100
|
388
|
+
# 0x200 .. 0x40000; page 113
|
361
389
|
ExposureMask = 0x008000
|
390
|
+
VisibilityChangeMask = 0x010000
|
362
391
|
StructureNotifyMask = 0x020000
|
392
|
+
ResizeRedirectMask = 0x040000
|
363
393
|
SubstructureNotifyMask = 0x080000
|
364
394
|
SubstructureRedirectMask=0x100000
|
365
395
|
FocusChangeMask = 0x200000
|
366
396
|
PropertyChangeMask = 0x400000
|
397
|
+
ColormapChangeMask = 0x800000
|
398
|
+
OwnerGrabButtonMask = 0x100000
|
367
399
|
|
368
400
|
class CreateWindow < BaseForm
|
369
401
|
field :opcode, Uint8, value: 1
|
@@ -592,6 +624,15 @@ module X11
|
|
592
624
|
field :value, String8, :string
|
593
625
|
end
|
594
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
|
+
|
595
636
|
class GrabButton < BaseForm
|
596
637
|
field :opcode, Uint8, value: 28
|
597
638
|
field :owner_events, Bool
|
@@ -678,6 +719,7 @@ module X11
|
|
678
719
|
ForegroundMask = 0x04
|
679
720
|
BackgroundMask = 0x08
|
680
721
|
FontMask = 0x4000
|
722
|
+
GraphicsExposures = 0x10000
|
681
723
|
|
682
724
|
class CreateGC < BaseForm
|
683
725
|
field :opcode, Uint8, value: 55
|
@@ -1002,6 +1044,7 @@ module X11
|
|
1002
1044
|
unused 1
|
1003
1045
|
field :sequence_number, Uint16
|
1004
1046
|
field :event, Window
|
1047
|
+
field :window, Window
|
1005
1048
|
field :above_sibling, Window
|
1006
1049
|
field :x, Int16
|
1007
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
@@ -11,7 +11,7 @@ module X11
|
|
11
11
|
|
12
12
|
def self.config(d,b) = (@directive, @bytesize = d,b)
|
13
13
|
|
14
|
-
def self.pack(x)
|
14
|
+
def self.pack(x, dpy)
|
15
15
|
if x.is_a?(Symbol)
|
16
16
|
if (t = X11::Form.const_get(x)) && t.is_a?(Numeric)
|
17
17
|
x = t
|
@@ -27,7 +27,6 @@ module X11
|
|
27
27
|
def self.from_packet(sock) = unpack(sock.read(size))
|
28
28
|
end
|
29
29
|
|
30
|
-
# Primitive Types
|
31
30
|
class Int8 < BaseType; config("c",1); end
|
32
31
|
class Int16 < BaseType; config("s",2); end
|
33
32
|
class Int32 < BaseType; config("l",4); end
|
@@ -36,16 +35,14 @@ module X11
|
|
36
35
|
class Uint32 < BaseType; config("L",4); end
|
37
36
|
|
38
37
|
class Message
|
39
|
-
def self.pack(x)
|
40
|
-
def self.unpack(x)
|
41
|
-
def self.size
|
38
|
+
def self.pack(x,dpy) = x.b
|
39
|
+
def self.unpack(x) = x.b
|
40
|
+
def self.size = 20
|
42
41
|
def self.from_packet(sock) = sock.read(2).b
|
43
42
|
end
|
44
43
|
|
45
44
|
class String8
|
46
|
-
def self.pack(x)
|
47
|
-
x.b + "\x00"*(-x.length & 3)
|
48
|
-
end
|
45
|
+
def self.pack(x, dpy) = (x.b + "\x00"*(-x.length & 3))
|
49
46
|
|
50
47
|
def self.unpack(socket, size)
|
51
48
|
raise "Expected size for String8" if size.nil?
|
@@ -57,7 +54,7 @@ module X11
|
|
57
54
|
end
|
58
55
|
|
59
56
|
class String16
|
60
|
-
def self.pack(x)
|
57
|
+
def self.pack(x, dpy)
|
61
58
|
x.encode("UTF-16BE").b + "\x00\x00"*(-x.length & 1)
|
62
59
|
end
|
63
60
|
|
@@ -71,13 +68,13 @@ module X11
|
|
71
68
|
|
72
69
|
|
73
70
|
class String8Unpadded
|
74
|
-
def self.pack(x) = x
|
71
|
+
def self.pack(x,dpy) = x
|
75
72
|
def self.unpack(socket, size) = socket.read(size)
|
76
73
|
end
|
77
74
|
|
78
75
|
class Bool
|
79
|
-
def self.pack(x)
|
80
|
-
def self.unpack(str)
|
76
|
+
def self.pack(x, dpy) = (x ? "\x01" : "\x00")
|
77
|
+
def self.unpack(str) = (str[0] == "\x01")
|
81
78
|
def self.size = 1
|
82
79
|
end
|
83
80
|
|
@@ -96,10 +93,16 @@ module X11
|
|
96
93
|
Colormap = Uint32
|
97
94
|
Drawable = Uint32
|
98
95
|
Fontable = Uint32
|
99
|
-
Atom = Uint32
|
100
96
|
VisualID = Uint32
|
101
97
|
Mask = Uint32
|
102
98
|
Timestamp = Uint32
|
103
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
|
104
107
|
end
|
105
108
|
end
|
data/lib/X11/version.rb
CHANGED
data/lib/X11/window.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
module X11
|
3
|
+
|
4
|
+
# A simple helper class that makes requests where the
|
5
|
+
# target object is a specific window a bit more convenient
|
6
|
+
class Window
|
7
|
+
attr_reader :dpy, :wid
|
8
|
+
|
9
|
+
def initialize(dpy,wid)
|
10
|
+
@dpy, @wid = dpy,wid
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.create(dpy, ...)
|
14
|
+
wid = dpy.create_window(...)
|
15
|
+
Window.new(dpy,wid)
|
16
|
+
end
|
17
|
+
|
18
|
+
def query_tree = dpy.query_tree(@wid)
|
19
|
+
def map = dpy.map_window(@wid)
|
20
|
+
def unmap = dpy.unmap_window(@wid)
|
21
|
+
def destroy = dpy.destroy_window(@wid)
|
22
|
+
def get_geometry = dpy.get_geometry(@wid)
|
23
|
+
def configure(...) = dpy.configure_window(@wid, ...)
|
24
|
+
def get_property(...) = dpy.get_property(@wid,...)
|
25
|
+
def grab_key(arg, ...) = dpy.grab_key(arg, @wid, ...)
|
26
|
+
def grab_button(arg,...) = dpy.grab_button(arg, @wid, ...)
|
27
|
+
def change_property(mode, ...) = dpy.change_property(mode, @wid, ...)
|
28
|
+
def set_input_focus(mode) = dpy.set_input_focus(mode, @wid)
|
29
|
+
def select_input(...) = dpy.select_input(@wid,...)
|
30
|
+
def get_window_attributes(...) = dpy.get_window_attributes(@wid,...)
|
31
|
+
def change_attributes(...) = dpy.change_window_attributes(@wid,...)
|
32
|
+
|
33
|
+
def image_text16(...) = dpy.image_text16(@wid, ...)
|
34
|
+
def clear_area(arg, ...) = dpy.clear_area(arg, @wid, ...)
|
35
|
+
def poly_fill_rectangle(...) = dpy.poly_fill_rectangle(@wid, ...)
|
36
|
+
def put_image(type, ...) = dpy.put_image(type, @wid, ...)
|
37
|
+
def create_gc(...) = dpy.create_gc(@wid, ...)
|
38
|
+
end
|
39
|
+
end
|
data/lib/X11.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module X11
|
2
|
-
class
|
2
|
+
class BasicError < StandardError; end
|
3
3
|
end
|
4
4
|
|
5
5
|
require 'socket'
|
@@ -9,4 +9,5 @@ require_relative './X11/display'
|
|
9
9
|
require_relative './X11/screen'
|
10
10
|
require_relative './X11/type'
|
11
11
|
require_relative './X11/form'
|
12
|
+
require_relative './X11/window'
|
12
13
|
require_relative './X11/keysyms'
|
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.9
|
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-26 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
|
@@ -37,6 +38,7 @@ files:
|
|
37
38
|
- lib/X11/screen.rb
|
38
39
|
- lib/X11/type.rb
|
39
40
|
- lib/X11/version.rb
|
41
|
+
- lib/X11/window.rb
|
40
42
|
- ruby-x11.gemspec
|
41
43
|
- test/core_test.rb
|
42
44
|
- test/form_test.rb
|