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 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