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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 927c52f35913e7d8bc466563bb5d238919c4a43d6a9f9fb1be8a6df4735cc5fb
4
- data.tar.gz: 3b92ed556881c32486ed189c9de27fdd373988379118fefdbd6f40ff6268c0e7
3
+ metadata.gz: d50a871d744420a3fc90c8ca3cff7defb5d4917bce297469502c94e64ae12790
4
+ data.tar.gz: e17c0019823b08847fca5b33b35f2d618bde521fd3e86bf5a5168eb68807383a
5
5
  SHA512:
6
- metadata.gz: 580ab719f597acaecfa467389f26526dc50c1d25cacb14dbda9d63f61e3cb63f3665fd010408fc093501893a229a4b6a6f314734f6cf35786c58d965d0ec51cd
7
- data.tar.gz: 211b56d642b773c29769e9620c746a657596b6314e1d5a89487ace79bf29c6f01f4675ec2456a8668b8723912b2c81f2c95309dcdaf9eac459682150b884f726
6
+ metadata.gz: 36892209167f3aea6ccc68309e64f0e45f56e036ce8a547ffab37460f2169902429d6d1b4f11b7156d64a45151a155a7a46df693b2db86203ba75c9b99fe41ff
7
+ data.tar.gz: 6c7d7c83efef12e9e9303526cf2b92b5d40ad4e7d726af5a5fe59117f1396a171d8d79e452b7a5ede941d2d1bf0fcef05b7e60fc0cfd180b7f7dce95b5c0289c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pure-x11 (0.0.6)
4
+ pure-x11 (0.0.8)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -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
- wid = dpy.create_window(
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
- def set_window_opacity(dpy, wid, opacity)
36
- dpy.change_property(
35
+
36
+ def set_window_opacity(window, opacity)
37
+ window.change_property(
37
38
  :replace,
38
- wid, "_NET_WM_WINDOW_OPACITY",
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(dpy, wid, 0.8)
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
- dpy.map_window(wid)
91
+ window.map
91
92
 
92
- $gc = gc = dpy.create_gc(wid, foreground: 0xff0000)
93
- $gc2 = dpy.create_gc(wid,foreground: 0xffffff, background: 0x444444)
94
- $gc3 = dpy.create_gc(wid)
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(display, wid, x,y, ch)
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.force_encoding("ASCII-8BIT")
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
- display.put_image(
146
- :ZPixmap, wid, $gc2,
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(display, wid, x,y, str)
154
+ def render_str(window, x,y, str)
154
155
  str.each_byte do |ch|
155
- off = render_glyph(display, wid, x, y, ch.chr)
156
+ off = render_glyph(window, x, y, ch.chr)
156
157
  x+= off
157
158
  end
158
159
  end
159
160
 
160
- def redraw(dpy, wid, gc)
161
- dpy.poly_fill_rectangle(wid, gc, [20,20, 60, 80])
162
- dpy.clear_area(false, wid, 30, 30, 5, 5)
163
- dpy.image_text16(wid, $gc2, 30, 70, "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ")
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
- dpy.put_image(
166
- :ZPixmap, wid, $gc2,
166
+ window.put_image(
167
+ :ZPixmap, $gc2,
167
168
  $png.width, $png.height, 80, 120, 0, 24, $data
168
169
  )
169
- render_str(dpy, wid, 30,90, 'HelloWorld')
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(display, wid, gc) if pkt.is_a?(X11::Form::Expose)
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
- 256 => :Local,
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).unpack('n').first ]
55
+ auth_info = [] << ADDRESS_TYPES[ @file.read(2).unpack1('n') ]
56
56
 
57
57
  4.times do
58
- length = @file.read(2).unpack('n').first
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 < X11Error; end
7
- class ConnectionError < X11Error; end
8
- class AuthorizationError < X11Error; end
9
- class ProtocolError < X11Error; end
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
- # FIXME
30
- authorize(host, family, display_id) rescue nil
37
+ authorize(host, family, display_id)
31
38
 
32
39
  @requestseq = 1
33
- @queue = []
40
+ @rqueue = Queue.new # Read but not returned events
41
+ @wqueue = Queue.new
42
+ @extensions = {} # Known extensions
43
+ @atoms = {} # Interned atoms
34
44
 
35
- @extensions = {}
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.read_nonblock(32)
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 timeout=5.0
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
- write_raw_packet(pkt)
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
- write_raw_packet(data)
171
- end
172
-
173
- def write_sync(data, reply=nil)
174
- seq = @requestseq
175
- write_request(data)
176
- pkt = next_reply(seq)
177
- return nil if !pkt
178
- return pkt if pkt.is_a?(X11::Form::Error)
179
- pp reply
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
- !@queue.empty?
185
- end
203
+ def peek_packet = !@rqueue.empty?
204
+ def next_packet = @rqueue.shift
186
205
 
187
- def next_packet
188
- @queue.shift || read_packet
189
- end
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
- def next_reply(errseq)
192
- # FIXME: This is totally broken
193
- while pkt = read_packet
194
- if pkt.is_a?(String)
195
- return pkt
196
- elsif pkt.is_a?(X11::Form::Error) && pkt.sequence_number == errseq
197
- return pkt
198
- else
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 = read_packet
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 ||= screens.first.root
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) = open_enum(val, {cardinal: Form::CardinalAtom, atom: Form::AtomAtom, window: Form::WindowAtom})
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
- # endiness of your machine
3
+ # endianess of your machine
4
4
  BYTE_ORDER = case [1].pack("L")
5
- when "\0\0\0\1"
6
- "B".ord
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
- end
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
- @internal.root
12
- end
13
-
14
- def root_depth
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) = x.b
40
- def self.unpack(x) = x.b
41
- def self.size = 20
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) = (x ? "\x01" : "\x00")
80
- def self.unpack(str) = (str[0] == "\x01")
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
@@ -1,3 +1,3 @@
1
1
  module X11
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.9"
3
3
  end
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 X11Error < StandardError; end
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.6
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: 2023-12-28 00:00:00.000000000 Z
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