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