pure-x11 0.0.2

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.
@@ -0,0 +1,450 @@
1
+ # FIXME: Temp workaround
2
+ require 'stringio'
3
+
4
+ module X11
5
+
6
+ class DisplayError < X11Error; end
7
+ class ConnectionError < X11Error; end
8
+ class AuthorizationError < X11Error; end
9
+ class ProtocolError < X11Error; end
10
+
11
+ class Display
12
+ attr_accessor :socket
13
+
14
+ # Open a connection to the specified display (numbered from 0) on the specified host
15
+ def initialize(target = ENV['DISPLAY'])
16
+ target =~ /^([\w.-]*):(\d+)(?:.(\d+))?$/
17
+ host, display_id, screen_id = $1, $2, $3
18
+ family = nil
19
+
20
+ if host.empty?
21
+ @socket = UNIXSocket.new("/tmp/.X11-unix/X#{display_id}")
22
+ family = :Local
23
+ host = nil
24
+ else
25
+ @socket = TCPSocket.new(host,6000+display_id)
26
+ family = :Internet
27
+ end
28
+
29
+ authorize(host, family, display_id)
30
+
31
+ @requestseq = 1
32
+ @queue = []
33
+
34
+ @extensions = {}
35
+
36
+ # Interned atoms
37
+ @atoms = {}
38
+ end
39
+
40
+ def event_handler= block
41
+ @event_handler= block
42
+ end
43
+
44
+ def display_info
45
+ @internal
46
+ end
47
+
48
+ def screens
49
+ @internal.screens.map do |s|
50
+ Screen.new(self, s)
51
+ end
52
+ end
53
+
54
+ ##
55
+ # The resource-id-mask contains a single contiguous set of bits (at least 18).
56
+ # The client allocates resource IDs for types WINDOW, PIXMAP, CURSOR, FONT,
57
+ # GCONTEXT, and COLORMAP by choosing a value with only some subset of these
58
+ # bits set and ORing it with resource-id-base.
59
+
60
+ def new_id
61
+ id = (@xid_next ||= 0)
62
+ @xid_next += 1
63
+
64
+ (id & @internal.resource_id_mask) | @internal.resource_id_base
65
+ end
66
+
67
+ def read_error data
68
+ error = Form::Error.from_packet(StringIO.new(data))
69
+ STDERR.puts "ERROR: #{error.inspect}"
70
+ error
71
+ end
72
+
73
+ def read_reply data
74
+ len = data.unpack("@4L")[0]
75
+ extra = len > 0 ? @socket.read(len*4) : ""
76
+ #STDERR.puts "REPLY: #{data.inspect}"
77
+ #STDERR.puts "EXTRA: #{extra.inspect}"
78
+ data + extra
79
+ end
80
+
81
+ def read_event type, data, event_class
82
+ case type
83
+ when 2
84
+ return Form::KeyPress.from_packet(StringIO.new(data))
85
+ when 3
86
+ return Form::KeyRelease.from_packet(StringIO.new(data))
87
+ when 4
88
+ return Form::ButtonPress.from_packet(StringIO.new(data))
89
+ when 6
90
+ return Form::MotionNotify.from_packet(StringIO.new(data))
91
+ when 12
92
+ return Form::Expose.from_packet(StringIO.new(data))
93
+ when 14
94
+ return Form::NoExposure.from_packet(StringIO.new(data))
95
+ when 19
96
+ return Form::MapNotify.from_packet(StringIO.new(data))
97
+ when 22
98
+ return Form::ConfigureNotify.from_packet(StringIO.new(data))
99
+ else
100
+ STDERR.puts "FIXME: Event: #{type}"
101
+ STDERR.puts "EVENT: #{data.inspect}"
102
+ end
103
+ end
104
+
105
+ def read_full_packet(len = 32)
106
+ data = @socket.read_nonblock(32)
107
+ return nil if data.nil?
108
+ while data.length < 32
109
+ IO.select([@socket],nil,nil,0.001)
110
+ data.concat(@socket.read_nonblock(32 - data.length))
111
+ end
112
+ return data
113
+ rescue IO::WaitReadable
114
+ return nil
115
+ end
116
+
117
+ def read_packet timeout=5.0
118
+ IO.select([@socket],nil,nil, timeout)
119
+ data = read_full_packet(32)
120
+ return nil if data.nil?
121
+
122
+ type = data.unpack("C").first
123
+ case type
124
+ when 0
125
+ read_error(data)
126
+ when 1
127
+ read_reply(data)
128
+ when 2..34
129
+ read_event(type, data, nil)
130
+ else
131
+ raise ProtocolError, "Unsupported reply type: #{type}"
132
+ end
133
+ end
134
+
135
+ def write_request ob
136
+ #p data
137
+ #p [:write_request, @requestseq, ob.class]
138
+ data = ob.to_packet if ob.respond_to?(:to_packet)
139
+ #p [:AddGlyph,data] if ob.is_a?(X11::Form::XRenderAddGlyphs)
140
+ #p [ob.request_length.to_i*4, data.size]
141
+ 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
142
+ @requestseq += 1
143
+ @socket.write(data)
144
+ end
145
+
146
+ def write_sync(data, reply=nil)
147
+ write_request(data)
148
+ pkt = next_reply
149
+ return nil if !pkt
150
+ reply ? reply.from_packet(StringIO.new(pkt)) : pkt
151
+ end
152
+
153
+ def peek_packet
154
+ !@queue.empty?
155
+ end
156
+
157
+ def next_packet
158
+ @queue.shift || read_packet
159
+ end
160
+
161
+ def next_reply
162
+ # FIXME: This is totally broken
163
+ while pkt = read_packet
164
+ if pkt.is_a?(String)
165
+ return pkt
166
+ else
167
+ @queue.push(pkt)
168
+ end
169
+ end
170
+ end
171
+
172
+ def run
173
+ loop do
174
+ pkt = read_packet
175
+ return if !pkt
176
+ yield(pkt)
177
+ end
178
+ end
179
+
180
+ # Requests
181
+ def create_window(*args)
182
+ write_request(X11::Form::CreateWindow.new(*args))
183
+ end
184
+
185
+ def atom(name)
186
+ intern_atom(false, name) if !@atoms[name]
187
+ @atoms[name]
188
+ end
189
+
190
+ def query_extension(name)
191
+ r = write_sync(X11::Form::QueryExtension.new(name), X11::Form::QueryExtensionReply)
192
+ @extensions[name] = {
193
+ major: r.major_opcode
194
+ }
195
+ r
196
+ end
197
+
198
+ def major_opcode(name)
199
+ if !@extensions[name]
200
+ query_extension(name)
201
+ end
202
+ raise "No such extension '#{name}'" if !@extensions[name]
203
+ @extensions[name][:major]
204
+ end
205
+
206
+ def intern_atom(flag, name)
207
+ reply = write_sync(X11::Form::InternAtom.new(flag, name.to_s),
208
+ X11::Form::InternAtomReply)
209
+ if reply
210
+ @atoms[name.to_sym] = reply.atom
211
+ end
212
+ end
213
+
214
+ def get_keyboard_mapping(min_keycode=display_info.min_keycode, count= display_info.max_keycode - min_keycode)
215
+ write_sync(X11::Form::GetKeyboardMapping.new(min_keycode, count), X11::Form::GetKeyboardMappingReply)
216
+ end
217
+
218
+ def create_colormap(alloc, window, visual)
219
+ mid = new_id
220
+ write_request(X11::Form::CreateColormap.new(alloc, mid, window, visual))
221
+ mid
222
+ end
223
+
224
+ def change_property(*args)
225
+ write_request(X11::Form::ChangeProperty.new(*args))
226
+ end
227
+
228
+ def list_fonts(*args)
229
+ write_sync(X11::Form::ListFonts.new(*args),
230
+ X11::Form::ListFontsReply)
231
+ end
232
+
233
+ def open_font(*args)
234
+ write_request(X11::Form::OpenFont.new(*args))
235
+ end
236
+
237
+ def change_gc(*args)
238
+ write_request(X11::Form::ChangeGC.new(*args))
239
+ end
240
+
241
+ def map_window(*args)
242
+ write_request(X11::Form::MapWindow.new(*args))
243
+ end
244
+
245
+
246
+ def create_gc(window, foreground: nil, background: nil)
247
+ mask = 0
248
+ args = []
249
+
250
+ # FIXME:
251
+ # The rest can be found here:
252
+ # https://tronche.com/gui/x/xlib/GC/manipulating.html#XGCValues
253
+ if foreground
254
+ mask |= 0x04
255
+ args << foreground
256
+ end
257
+ if background
258
+ mask |= 0x08
259
+ args << background
260
+ end
261
+
262
+
263
+ gc = new_id
264
+ write_request(X11::Form::CreateGC.new(gc, window, mask, args))
265
+ gc
266
+ end
267
+
268
+ def put_image(*args)
269
+ write_request(X11::Form::PutImage.new(*args))
270
+ end
271
+
272
+ def clear_area(*args)
273
+ write_request(X11::Form::ClearArea.new(*args))
274
+ end
275
+
276
+ def copy_area(*args)
277
+ write_request(X11::Form::CopyArea.new(*args))
278
+ end
279
+
280
+ def image_text8(*args)
281
+ write_request(X11::Form::ImageText8.new(*args))
282
+ end
283
+
284
+ def image_text16(*args)
285
+ write_request(X11::Form::ImageText16.new(*args))
286
+ end
287
+
288
+ def poly_fill_rectangle(*args)
289
+ write_request(X11::Form::PolyFillRectangle.new(*args))
290
+ end
291
+
292
+ def create_pixmap(depth, drawable, w,h)
293
+ pid = new_id
294
+ write_request(X11::Form::CreatePixmap.new(depth, pid, drawable, w,h))
295
+ pid
296
+ end
297
+
298
+ # XRender
299
+
300
+ def render_opcode
301
+ return @render_opcode if @render_opcode
302
+ @render_opcode = major_opcode("RENDER")
303
+ if @render_opcode
304
+ @render_version = write_sync(X11::Form::XRenderQueryVersion.new(
305
+ @render_opcode,0,11),
306
+ X11::Form::XRenderQueryVersionReply
307
+ )
308
+ end
309
+ @render_opcode
310
+ end
311
+
312
+ def render_create_picture(drawable, format, vmask=0, vlist=[])
313
+ pid = new_id
314
+ write_request(X11::Form::XRenderCreatePicture.new(
315
+ render_opcode, pid, drawable, format, vmask, vlist))
316
+ pid
317
+ end
318
+
319
+ def render_query_pict_formats
320
+ @render_formats ||= write_sync(
321
+ X11::Form::XRenderQueryPictFormats.new(render_opcode),
322
+ X11::Form::XRenderQueryPictFormatsReply
323
+ )
324
+ end
325
+
326
+ def render_find_visual_format(visual)
327
+ # FIXME.
328
+ render_query_pict_formats.screens.map do |s|
329
+ s.depths.map do |d|
330
+ d.visuals.map {|v| v.visual == visual ? v : nil }
331
+ end
332
+ end.flatten.compact.first.format
333
+ end
334
+
335
+ def render_find_standard_format(sym)
336
+ # A pox be on the people who made this necessary
337
+
338
+ formats = render_query_pict_formats
339
+
340
+ case sym
341
+ when :a8
342
+ @a8 ||= formats.formats.find do |f|
343
+ f.type == 1 &&
344
+ f.depth == 8 &&
345
+ f.direct.alpha_mask == 255
346
+ end
347
+ when :rgb24
348
+ @rgb24 ||= formats.formats.find do |f|
349
+ f.type == 1 &&
350
+ f.depth == 24 &&
351
+ f.direct.red == 16 &&
352
+ f.direct.green == 8 &&
353
+ f.direct.blue == 0
354
+ end
355
+ when :argb24
356
+ @argb24 ||= formats.formats.find do |f|
357
+ f.type == 1 &&
358
+ f.depth == 32 &&
359
+ f.direct.alpha == 24 &&
360
+ f.direct.red == 16 &&
361
+ f.direct.green == 8 &&
362
+ f.direct.blue == 0
363
+ end
364
+ else
365
+ raise "Unsupported format (a4/a1 by omission)"
366
+ end
367
+ end
368
+
369
+ def render_create_glyph_set(format)
370
+ glyphset = new_id
371
+ write_request(X11::Form::XRenderCreateGlyphSet.new(
372
+ major_opcode("RENDER"),glyphset, format))
373
+ glyphset
374
+ end
375
+
376
+ def render_add_glyphs(glyphset, glyphids, glyphinfos, data)
377
+ write_request(X11::Form::XRenderAddGlyphs.new(render_opcode,
378
+ glyphset, Array(glyphids), Array(glyphinfos), data))
379
+ end
380
+
381
+ def render_fill_rectangles(op, dst, color, rects)
382
+ color = Form::XRenderColor.new(*color) if color.is_a?(Array)
383
+ rects = rects.map{|r| r.is_a?(Array) ? Form::Rectangle.new(*r) : r}
384
+ write_request(Form::XRenderFillRectangles.new(render_opcode, op, dst, color, rects))
385
+ end
386
+
387
+ def render_composite_glyphs32(op, src, dst, fmt, glyphset, srcx,srcy, *elts)
388
+ write_request(X11::Form::XRenderCompositeGlyphs32.new(
389
+ render_opcode,
390
+ op, src, dst, fmt,
391
+ glyphset,
392
+ srcx, srcy,
393
+ elts.map {|e| e.is_a?(Array) ? Form::GlyphElt32.new(*e) : e }
394
+ ))
395
+ end
396
+
397
+ def render_create_solid_fill(*color)
398
+ if color.length == 1 && color.is_a?(Form::XRenderColor)
399
+ color = color[0]
400
+ else
401
+ color = Form::XRenderColor.new(*color)
402
+ end
403
+ fill = new_id
404
+ write_request(Form::XRenderCreateSolidFill.new(render_opcode,fill,color))
405
+ fill
406
+ end
407
+
408
+ private
409
+
410
+ def authorize(host, family, display_id)
411
+ auth = Auth.new
412
+ auth_info = auth.get_by_hostname(host||"localhost", family, display_id)
413
+
414
+ auth_name, auth_data = auth_info.auth_name, auth_info.auth_data
415
+ p [auth_name, auth_data]
416
+
417
+ handshake = Form::ClientHandshake.new(
418
+ Protocol::BYTE_ORDER,
419
+ Protocol::MAJOR,
420
+ Protocol::MINOR,
421
+ auth_name,
422
+ auth_data
423
+ )
424
+
425
+ @socket.write(handshake.to_packet)
426
+
427
+ data = @socket.read(1)
428
+ raise AuthorizationError, "Failed to read response from server" if !data
429
+
430
+ case data.unpack("w").first
431
+ when X11::Auth::FAILED
432
+ len, major, minor, xlen = @socket.read(7).unpack("CSSS")
433
+ reason = @socket.read(xlen * 4)
434
+ reason = reason[0..len]
435
+ raise AuthorizationError, "Connection to server failed -- (version #{major}.#{minor}) #{reason}"
436
+ when X11::Auth::AUTHENTICATE
437
+ raise AuthorizationError, "Connection requires authentication"
438
+ when X11::Auth::SUCCESS
439
+ @socket.read(7) # skip unused bytes
440
+ @internal = Form::DisplayInfo.from_packet(@socket)
441
+ else
442
+ raise AuthorizationError, "Received unknown opcode #{type}"
443
+ end
444
+ end
445
+
446
+ def to_s
447
+ "#<X11::Display:0x#{object_id.to_s(16)} screens=#{@internal.screens.size}>"
448
+ end
449
+ end
450
+ end
data/lib/X11/event.rb ADDED
File without changes