pure-x11 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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