wmadd 0.1.0

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.
data/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # Wmadd
2
+
3
+ X Window で、
4
+
5
+ - ウィンドウがリサイズされるときにウィンドウの位置と大きさを表示する
6
+ - アクティブウィンドウの上にあるウィンドウを半透明化する
7
+
8
+ ## Installation
9
+
10
+ $ gem install wmadd
11
+
12
+ ## Usage
13
+
14
+ $ wmadd
15
+
16
+ ## LICENSE
17
+
18
+ GPLv3
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/exe/wmadd ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'wmadd'
4
+
5
+ Wmadd.start
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Wmadd
4
+ VERSION = "0.1.0"
5
+ end
data/lib/wmadd/x.rb ADDED
@@ -0,0 +1,658 @@
1
+ require 'fiddle/import'
2
+
3
+ class Wmadd
4
+ module X11
5
+ extend Fiddle::Importer
6
+ dlload 'libX11.so.6'
7
+ typealias 'XID', 'unsigned long'
8
+ typealias 'Window', 'XID'
9
+ typealias 'Pixmap', 'XID'
10
+ typealias 'Colormap', 'XID'
11
+ typealias 'Drawable', 'XID'
12
+ typealias 'Font', 'XID'
13
+ typealias 'Status', 'int'
14
+ typealias 'Atom', 'unsigned long'
15
+ typealias 'Bool', 'int'
16
+ typealias 'GC', 'void*'
17
+ typealias 'Time', 'unsigned long'
18
+
19
+ def self.struct(members)
20
+ klass = super
21
+
22
+ h = members.map.to_h do |member|
23
+ if member =~ /\A(.*\*\s*)(\w+)\z/
24
+ name = $2
25
+ pointer = true
26
+ elsif member =~ /(\w+)\z/
27
+ name = $1
28
+ pointer = false
29
+ end
30
+ [name.intern, pointer]
31
+ end
32
+
33
+ klass.define_singleton_method :attributes do
34
+ h.keys
35
+ end
36
+
37
+ klass.define_method :to_h do
38
+ super().transform_values{|v| v.is_a?(Fiddle::Pointer) && v.null? ? nil : v}
39
+ end
40
+
41
+ klass.define_method :inspect do
42
+ h = to_h.transform_values{|v| v.is_a?(Fiddle::Pointer) ? "(pointer)" : v}
43
+ "#<#{klass.name}: #{h.inspect}>"
44
+ end
45
+
46
+ klass
47
+ end
48
+
49
+ Screen = struct [
50
+ 'XExtData *ext_data',
51
+ 'struct _XDisplay *display',
52
+ 'Window root',
53
+ 'int width',
54
+ 'int height',
55
+ 'int mwidth',
56
+ 'int mheight',
57
+ 'int ndepths',
58
+ 'Depth *depths',
59
+ 'int root_depth',
60
+ 'Visual *root_visual',
61
+ 'GC default_gc',
62
+ 'Colormap cmap',
63
+ 'unsigned long white_pixel',
64
+ 'unsigned long black_pixel',
65
+ 'int max_maps',
66
+ 'int min_maps',
67
+ 'int backing_store',
68
+ 'Bool save_unders',
69
+ 'long root_input_mask',
70
+ ]
71
+
72
+ Window = struct ['Window window']
73
+ Pointer = struct ['void *ptr']
74
+ XClassHint = struct ['char *name', 'char *class_name']
75
+ XSizeHints = struct [
76
+ 'long flags', 'int x', 'int y', 'int width', 'int height',
77
+ 'int min_width', 'int min_height', 'int max_width', 'int max_height',
78
+ 'int width_inc', 'int height_inc',
79
+ 'int min_aspect_x', 'int min_aspect_y', 'int max_aspect_x', 'int max_aspect_y',
80
+ 'int base_width', 'int base_height',
81
+ 'int win_gravity',
82
+ ]
83
+ XTextProperty = struct ['unsigned char *value', 'Atom encoding', 'int format', 'unsigned long nitems']
84
+ XCreateWindowEvent = struct [
85
+ 'int type',
86
+ 'unsigned long serial',
87
+ 'Bool send_event',
88
+ 'Display *display',
89
+ 'Window parent',
90
+ 'Window window',
91
+ 'int x',
92
+ 'int y',
93
+ 'int width',
94
+ 'int height',
95
+ 'int border_width',
96
+ 'Bool override_redirect',
97
+ ]
98
+ XConfigureEvent = struct [
99
+ 'int type',
100
+ 'unsigned long serial',
101
+ 'Bool send_event',
102
+ 'Display *display',
103
+ 'Window event',
104
+ 'Window window',
105
+ 'int x',
106
+ 'int y',
107
+ 'int width',
108
+ 'int height',
109
+ 'int border_width',
110
+ 'Window above',
111
+ 'Bool override_redirect',
112
+ ]
113
+ XExposeEvent = struct [
114
+ 'int type',
115
+ 'unsigned long serial',
116
+ 'Bool send_event',
117
+ 'Display *display',
118
+ 'Window window',
119
+ 'int x',
120
+ 'int y',
121
+ 'int width',
122
+ 'int height',
123
+ 'int count',
124
+ ]
125
+ XPropertyEvent = struct [
126
+ 'int type',
127
+ 'unsigned long serial',
128
+ 'Bool send_event',
129
+ 'Display *display',
130
+ 'Window window',
131
+ 'Atom atom',
132
+ 'Time time',
133
+ 'int state',
134
+ ]
135
+ XWMHints = struct [
136
+ 'long flags',
137
+ 'Bool input',
138
+ 'int initial_state',
139
+ 'Pixmap icon_pixmap',
140
+ 'Window icon_window',
141
+ 'int icon_x',
142
+ 'int icon_y',
143
+ 'Pixmap icon_mask',
144
+ 'XID window_group',
145
+ ]
146
+ XWindowAttributes = struct [
147
+ 'int x',
148
+ 'int y',
149
+ 'int width',
150
+ 'int height',
151
+ 'int border_width',
152
+ 'int depth',
153
+ 'Visual *visual',
154
+ 'Window root',
155
+ 'int c_class',
156
+ 'int bit_gravity',
157
+ 'int win_gravity',
158
+ 'int backing_store',
159
+ 'unsigned long backing_planes',
160
+ 'unsigned long backing_pixel',
161
+ 'Bool save_under',
162
+ 'Colormap colormap',
163
+ 'Bool map_installed',
164
+ 'int map_state',
165
+ 'long all_event_masks',
166
+ 'long your_event_mask',
167
+ 'long do_not_propagate_mask',
168
+ 'Bool override_redirect',
169
+ 'Screen *screen',
170
+ ]
171
+
172
+ Int = struct ['int n']
173
+ UnsignedLong = struct ['unsigned long n']
174
+
175
+ extern 'Display* XOpenDisplay(char*)'
176
+ extern 'Window XDefaultRootWindow(Display*)'
177
+ extern 'int XGetInputFocus(Display*, Window*, int*)'
178
+ extern 'int XGetClassHint(Display*, Window, XClassHint*)'
179
+ extern 'Status XQueryTree(Display*, Window, Window*, Window*, Window**, unsigned int*)'
180
+ extern 'Status XGetWMName(Display*, Window, XTextProperty*)'
181
+ extern 'Status XGetWMNormalHints(Display*, Window, XSizeHints*, long*)'
182
+ extern 'XWMHints* XGetWMHints(Display*, Window)'
183
+ extern 'int Xutf8TextPropertyToTextList(Display*, XTextProperty*, char***, int*)'
184
+ extern 'int XFree(void*)'
185
+ extern 'void XFreeStringList(char**)'
186
+ extern 'int XSelectInput(Display*, Window, unsigned long)'
187
+ extern 'int XNextEvent(Display*, XEvent*)'
188
+ extern 'int XGetGeometry(Display*, Window, Window*, int*, int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*)'
189
+ extern 'void* XSetErrorHandler(int (*)(Display*, XErrorEvent*))'
190
+ extern 'Window XCreateSimpleWindow(Display*, Window, int, int, unsigned int, unsigned int, unsigned int, unsigned long, unsigned long)'
191
+ extern 'int XMoveWindow(Display*, Window, int, int)'
192
+ extern 'int XMapWindow(Display*, Window)'
193
+ extern 'int XUnmapWindow(Display*, Window)'
194
+ extern 'int XUnmapSubwindows(Display*, Window)'
195
+ extern 'int XChangeProperty(Display*, Window, Atom, Atom, int, int, unsigned char*, int)'
196
+ extern 'Atom XInternAtom(Display*, char*, char)'
197
+ extern 'void XFlush(Display*)'
198
+ extern 'Status XGetWindowAttributes(Display*, Window, XWindowAttributes*)'
199
+ extern 'GC XCreateGC(Display*, Drawable, unsigned long, XGCValues*)'
200
+ extern 'int XSetForeground(Display*, GC, unsigned long)'
201
+ extern 'int XSetBackground(Display*, GC, unsigned long)'
202
+ extern 'int XSetFont(Display*, GC, Font)'
203
+ extern 'Font XLoadFont(Display*, char*)'
204
+ extern 'int XDrawString(Display*, Drawable, GC, int, int, char*, int)'
205
+ extern 'int XFillRectangle(Display*, Drawable, GC, int, int, unsigned int, unsigned int)'
206
+ extern 'int XClearWindow(Display*, Window)'
207
+ extern 'int XGetWindowProperty(Display*, Window, Atom, long, long, Bool, Atom, Atom*, int*, unsigned long*, unsigned long*, unsigned char**)'
208
+
209
+ ErrorHandler = bind("int error_handler(Display*, XErrorEvent*)") { |*| return 0 }
210
+ X11::XSetErrorHandler(X11::ErrorHandler)
211
+ end
212
+
213
+ module X
214
+ # event mask
215
+ NO_EVENT_MASK = 0
216
+ KEY_PRESS_MASK = 1 << 0
217
+ KEY_RELEASE_MASK = 1 << 1
218
+ BUTTON_PRESS_MASK = 1 << 2
219
+ BUTTON_RELEASE_MASK = 1 << 3
220
+ ENTER_WINDOW_MASK = 1 << 4
221
+ LEAVE_WINDOW_MASK = 1 << 5
222
+ POINTER_MOTION_MASK = 1 << 6
223
+ POINTER_MOTION_HINT_MASK = 1 << 7
224
+ BUTTON1_MOTION_MASK = 1 << 8
225
+ BUTTON2_MOTION_MASK = 1 << 9
226
+ BUTTON3_MOTION_MASK = 1 << 10
227
+ BUTTON4_MOTION_MASK = 1 << 11
228
+ BUTTON5_MOTION_MASK = 1 << 12
229
+ BUTTON_MOTION_MASK = 1 << 13
230
+ KEYMAP_STATE_MASK = 1 << 14
231
+ EXPOSURE_MASK = 1 << 15
232
+ VISIBILITY_CHANGE_MASK = 1 << 16
233
+ STRUCTURE_NOTIFY_MASK = 1 << 17
234
+ RESIZE_REDIRECT_MASK = 1 << 18
235
+ SUBSTRUCTURE_NOTIFY_MASK = 1 << 19
236
+ SUBSTRUCTURE_REDIRECT_MASK = 1 << 20
237
+ FOCUS_CHANGE_MASK = 1 << 21
238
+ PROPERTY_CHANGE_MASK = 1 << 22
239
+ COLORMAP_CHANGE_MASK = 1 << 23
240
+ OWNER_GRAB_BUTTON_MASK = 1 << 24
241
+
242
+ # event type
243
+ KEY_PRESS = 2
244
+ KEY_RELEASE = 3
245
+ BUTTON_PRESS = 4
246
+ BUTTON_RELEASE = 5
247
+ MOTION_NOTIFY = 6
248
+ ENTER_NOTIFY = 7
249
+ LEAVE_NOTIFY = 8
250
+ FOCUS_IN = 9
251
+ FOCUS_OUT = 10
252
+ KEYMAP_NOTIFY = 11
253
+ EXPOSE = 12
254
+ GRAPHICS_EXPOSE = 13
255
+ NO_EXPOSE = 14
256
+ VISIBILITY_NOTIFY = 15
257
+ CREATE_NOTIFY = 16
258
+ DESTROY_NOTIFY = 17
259
+ UNMAP_NOTIFY = 18
260
+ MAP_NOTIFY = 19
261
+ MAP_REQUEST = 20
262
+ REPARENT_NOTIFY = 21
263
+ CONFIGURE_NOTIFY = 22
264
+ CONFIGURE_REQUEST = 23
265
+ GRAVITY_NOTIFY = 24
266
+ RESIZE_REQUEST = 25
267
+ CIRCULATE_NOTIFY = 26
268
+ CIRCULATE_REQUEST = 27
269
+ PROPERTY_NOTIFY = 28
270
+ SELECTION_CLEAR = 29
271
+ SELECTION_REQUEST = 30
272
+ SELECTION_NOTIFY = 31
273
+ COLORMAP_NOTIFY = 32
274
+ CLIENT_MESSAGE = 33
275
+ MAPPING_NOTIFY = 34
276
+ GENERIC_EVENT = 35
277
+
278
+
279
+ # property mode
280
+ PROP_MODE_REPLACE = 0
281
+ PROP_MODE_PREPEND = 1
282
+ PROP_MODE_APPEND = 2
283
+
284
+ # map state
285
+ IS_UNMAPPED = 0
286
+ IS_UNVIEWABLE = 1
287
+ IS_VIEWABLE = 2
288
+
289
+ # atom
290
+ ANY_PROPERTY_TYPE = 0
291
+ XA_PRIMARY = 1
292
+ XA_SECONDARY = 2
293
+ XA_ARC = 3
294
+ XA_ATOM = 4
295
+ XA_BITMAP = 5
296
+ XA_CARDINAL = 6
297
+ XA_COLORMAP = 7
298
+ XA_CURSOR = 8
299
+ XA_CUT_BUFFER0 = 9
300
+ XA_CUT_BUFFER1 = 10
301
+ XA_CUT_BUFFER2 = 11
302
+ XA_CUT_BUFFER3 = 12
303
+ XA_CUT_BUFFER4 = 13
304
+ XA_CUT_BUFFER5 = 14
305
+ XA_CUT_BUFFER6 = 15
306
+ XA_CUT_BUFFER7 = 16
307
+ XA_DRAWABLE = 17
308
+ XA_FONT = 18
309
+ XA_INTEGER = 19
310
+ XA_PIXMAP = 20
311
+ XA_POINT = 21
312
+ XA_RECTANGLE = 22
313
+ XA_RESOURCE_MANAGER = 23
314
+ XA_RGB_COLOR_MAP = 24
315
+ XA_RGB_BEST_MAP = 25
316
+ XA_RGB_BLUE_MAP = 26
317
+ XA_RGB_DEFAULT_MAP = 27
318
+ XA_RGB_GRAY_MAP = 28
319
+ XA_RGB_GREEN_MAP = 29
320
+ XA_RGB_RED_MAP = 30
321
+ XA_STRING = 31
322
+ XA_VISUALID = 32
323
+ XA_WINDOW = 33
324
+ XA_WM_COMMAND = 34
325
+ XA_WM_HINTS = 35
326
+ XA_WM_CLIENT_MACHINE = 36
327
+ XA_WM_ICON_NAME = 37
328
+ XA_WM_ICON_SIZE = 38
329
+ XA_WM_NAME = 39
330
+ XA_WM_NORMAL_HINTS = 40
331
+ XA_WM_SIZE_HINTS = 41
332
+ XA_WM_ZOOM_HINTS = 42
333
+ XA_MIN_SPACE = 43
334
+ XA_NORM_SPACE = 44
335
+ XA_MAX_SPACE = 45
336
+ XA_END_SPACE = 46
337
+ XA_SUPERSCRIPT_X = 47
338
+ XA_SUPERSCRIPT_Y = 48
339
+ XA_SUBSCRIPT_X = 49
340
+ XA_SUBSCRIPT_Y = 50
341
+ XA_UNDERLINE_POSITION = 51
342
+ XA_UNDERLINE_THICKNESS = 52
343
+ XA_STRIKEOUT_ASCENT = 53
344
+ XA_STRIKEOUT_DESCENT = 54
345
+ XA_ITALIC_ANGLE = 55
346
+ XA_X_HEIGHT = 56
347
+ XA_QUAD_WIDTH = 57
348
+ XA_WEIGHT = 58
349
+ XA_POINT_SIZE = 59
350
+ XA_RESOLUTION = 60
351
+ XA_COPYRIGHT = 61
352
+ XA_NOTICE = 62
353
+ XA_FONT_NAME = 63
354
+ XA_FAMILY_NAME = 64
355
+ XA_FULL_NAME = 65
356
+ XA_CAP_HEIGHT = 66
357
+ XA_WM_CLASS = 67
358
+ XA_WM_TRANSIENT_FOR = 68
359
+
360
+ class Display
361
+ attr_reader :display
362
+
363
+ # @param display [String]
364
+ def initialize(display=nil)
365
+ display ||= ENV['DISPLAY']
366
+ @display = X11::XOpenDisplay(display) or raise "Cannot open display: #{display}"
367
+ end
368
+
369
+ def default_screen
370
+ X11::DefaultScreen(@display)
371
+ end
372
+
373
+ def width(screen)
374
+ X11::DisplayWidth(@display, screen)
375
+ end
376
+
377
+ def height(screen)
378
+ X11::DisplayHeight(@display, screen)
379
+ end
380
+
381
+ def default_root_window
382
+ Window.new(self, X11::XDefaultRootWindow(@display))
383
+ end
384
+
385
+ def flush
386
+ X11::XFlush(@display)
387
+ end
388
+
389
+ def load_font(font)
390
+ X11::XLoadFont(@display, font)
391
+ end
392
+
393
+ def next_event
394
+ ev_buf = ' ' * 8 * 24
395
+ ev_ptr = Fiddle::Pointer[ev_buf]
396
+ while true
397
+ X11::XNextEvent(@display, ev_ptr)
398
+ ev_type = ev_buf.unpack1('i')
399
+ case ev_type
400
+ when CREATE_NOTIFY
401
+ return CreateWindowEvent.new(**X11::XCreateWindowEvent.new(ev_ptr).to_h)
402
+ when CONFIGURE_NOTIFY
403
+ return ConfigureEvent.new(**X11::XConfigureEvent.new(ev_ptr).to_h)
404
+ when EXPOSE
405
+ return ExposeEvent.new(**X11::XExposeEvent.new(ev_ptr).to_h)
406
+ when PROPERTY_NOTIFY
407
+ return PropertyEvent.new(**X11::XPropertyEvent.new(ev_ptr).to_h)
408
+ end
409
+ end
410
+ end
411
+
412
+ def wm_window_types
413
+ @wm_window_types ||= {
414
+ X11::XInternAtom(@display, '_NET_WM_WINDOW_TYPE_DESKTOP', 0) => :desktop,
415
+ X11::XInternAtom(@display, '_NET_WM_WINDOW_TYPE_DOCK', 0) => :dock,
416
+ X11::XInternAtom(@display, '_NET_WM_WINDOW_TYPE_TOOLBAR', 0) => :toolbar,
417
+ X11::XInternAtom(@display, '_NET_WM_WINDOW_TYPE_MENU', 0) => :menu,
418
+ X11::XInternAtom(@display, '_NET_WM_WINDOW_TYPE_UTILITY', 0) => :utility,
419
+ X11::XInternAtom(@display, '_NET_WM_WINDOW_TYPE_SPLASH', 0) => :splash,
420
+ X11::XInternAtom(@display, '_NET_WM_WINDOW_TYPE_DIALOG', 0) => :dialog,
421
+ X11::XInternAtom(@display, '_NET_WM_WINDOW_TYPE_NORMAL', 0) => :normal,
422
+ }
423
+ end
424
+
425
+ def intern_atom(sym, f=0)
426
+ X11::XInternAtom(@display, sym.to_s, f)
427
+ end
428
+ end
429
+
430
+ ClassHint = Data.define(*X11::XClassHint.attributes)
431
+ WMHints = Data.define(*X11::XWMHints.attributes)
432
+ SizeHints = Data.define(*X11::XSizeHints.attributes)
433
+ WindowAttributes = Data.define(*X11::XWindowAttributes.attributes)
434
+ CreateWindowEvent = Data.define(*X11::XCreateWindowEvent.attributes)
435
+ ConfigureEvent = Data.define(*X11::XConfigureEvent.attributes)
436
+ ExposeEvent = Data.define(*X11::XExposeEvent.attributes)
437
+ PropertyEvent = Data.define(*X11::XPropertyEvent.attributes)
438
+ Geometry = Data.define(:x, :y, :width, :height, :border_width, :depth)
439
+
440
+ class Window
441
+ def initialize(display_obj, id)
442
+ @display_obj = display_obj
443
+ @display = display_obj.display
444
+ @window = id
445
+ end
446
+
447
+ def inspect
448
+ "#<#{self.class.name}: 0x#{id.to_s(16)}: #{class_hint&.class_name}: #{wm_name}>"
449
+ end
450
+
451
+ def id
452
+ @window
453
+ end
454
+
455
+ def hash
456
+ id.hash
457
+ end
458
+
459
+ def eql?(other)
460
+ id == other.id
461
+ end
462
+
463
+ def class_hint
464
+ class_hint = X11::XClassHint.malloc(Fiddle::RUBY_FREE)
465
+ ret = X11::XGetClassHint(@display, @window, class_hint)
466
+ ret == 0 ? nil : ClassHint.new(name: class_hint.name.to_s, class_name: class_hint.class_name.to_s)
467
+ end
468
+
469
+ def geometry
470
+ root = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
471
+ x = X11::Int.malloc(Fiddle::RUBY_FREE)
472
+ y = X11::Int.malloc(Fiddle::RUBY_FREE)
473
+ width = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
474
+ height = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
475
+ border_width = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
476
+ depth = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
477
+
478
+ X11::XGetGeometry(@display, @window, root, x, y, width, height, border_width, depth)
479
+ Geometry.new(x: x.n, y: y.n, width: width.n, height: height.n, border_width: border_width.n, depth: depth.n)
480
+ end
481
+
482
+ def wm_hints
483
+ hints = X11::XGetWMHints(@display, @window)
484
+ return nil if hints.null?
485
+ WMHints.new(**X11::XWMHints.new(hints).to_h)
486
+ ensure
487
+ X11::XFree(hints) unless hints.null?
488
+ end
489
+
490
+ def wm_normal_hints
491
+ hints = X11::XSizeHints.malloc(Fiddle::RUBY_FREE)
492
+ ret = X11::XGetWMNormalHints(@display, @window, hints, ' '*8)
493
+ ret == 0 ? nil : SizeHints.new(**hints.to_h)
494
+ end
495
+
496
+ def wm_name
497
+ prop = X11::XTextProperty.malloc(Fiddle::RUBY_FREE)
498
+ text_list = X11::Pointer.malloc(Fiddle::RUBY_FREE)
499
+ X11::XGetWMName(@display, @window, prop)
500
+ X11::Xutf8TextPropertyToTextList(@display, prop, text_list, ' '*8)
501
+ ptr = text_list.ptr
502
+ if ptr.null?
503
+ nil
504
+ else
505
+ ptr.ptr.to_s.force_encoding('utf-8')
506
+ end
507
+ end
508
+
509
+ def select_input(mask)
510
+ X11::XSelectInput(@display, @window, mask)
511
+ end
512
+
513
+ def parent
514
+ root = ' '*8
515
+ parent = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
516
+ nchildren = X11::Int.malloc(Fiddle::RUBY_FREE)
517
+ children_p = X11::Pointer.malloc(Fiddle::RUBY_FREE)
518
+ ret = X11::XQueryTree(@display, @window, root, parent, children_p, nchildren)
519
+ return if ret == 0
520
+
521
+ Window.new(@display_obj, parent.n)
522
+ end
523
+
524
+ def query_tree
525
+ root = ' '*8
526
+ parent = ' '*8
527
+ nchildren = X11::Int.malloc(Fiddle::RUBY_FREE)
528
+ children_p = X11::Pointer.malloc(Fiddle::RUBY_FREE)
529
+ ret = X11::XQueryTree(@display, @window, root, parent, children_p, nchildren)
530
+ return if ret == 0
531
+
532
+ nchildren.n.times.map do |i|
533
+ win = children_p.ptr[X11::Window.size * i, X11::Window.size].unpack1('L!')
534
+ Window.new(@display_obj, win)
535
+ end
536
+ end
537
+
538
+ def create_simple_window(x, y, width, height, border_width, border, background)
539
+ win = X11::XCreateSimpleWindow(@display, @window, x, y, width, height, border_width, border, background)
540
+ Window.new(@display_obj, win)
541
+ end
542
+
543
+ def move_window(x, y)
544
+ X11::XMoveWindow(@display, @window, x, y)
545
+ end
546
+
547
+ def map_window
548
+ X11::XMapWindow(@display, @window)
549
+ end
550
+
551
+ def unmap_window
552
+ X11::XUnmapWindow(@display, @window)
553
+ end
554
+
555
+ def unmap_subwindows
556
+ X11::XUnmapSubwindows(@display, @window)
557
+ end
558
+
559
+ def change_property_atom(property, mode, data)
560
+ prop_ = X11::XInternAtom(@display, property.to_s, 0)
561
+ data_ = [X11::XInternAtom(@display, data.to_s, 0)].pack('L!')
562
+ X11::XChangeProperty(@display, @window, prop_, X::XA_ATOM, 32, mode, data_, 1)
563
+ end
564
+
565
+ def change_property_cardinal(property, mode, data)
566
+ prop_ = X11::XInternAtom(@display, property.to_s, 0)
567
+ data_ = X11::UnsignedLong.malloc
568
+ data_.n = data
569
+ X11::XChangeProperty(@display, @window, prop_, X::XA_CARDINAL, 32, mode, data_, 1)
570
+ end
571
+
572
+ def window_attributes
573
+ attr = X11::XWindowAttributes.malloc(Fiddle::RUBY_FREE)
574
+ X11::XGetWindowAttributes(@display, @window, attr)
575
+ WindowAttributes.new(**attr.to_h)
576
+ end
577
+
578
+ def screen
579
+ X11::Screen.new(window_attributes.screen)
580
+ end
581
+
582
+ def create_gc
583
+ gc = X11::XCreateGC(@display, @window, 0, nil)
584
+ GC.new(@display, @window, gc)
585
+ end
586
+
587
+ def clear
588
+ X11::XClearWindow(@display, @window)
589
+ end
590
+
591
+ def window_type
592
+ net_wm_window_type = X11::XInternAtom(@display, '_NET_WM_WINDOW_TYPE', 0)
593
+
594
+ property = net_wm_window_type
595
+ actual_type = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
596
+ actual_format = X11::Int.malloc(Fiddle::RUBY_FREE)
597
+ nitems = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
598
+ bytes = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
599
+ prop = X11::Pointer.malloc(Fiddle::RUBY_FREE)
600
+
601
+ X11::XGetWindowProperty(@display, @window, property, 0, ~0, 0, ANY_PROPERTY_TYPE, actual_type, actual_format, nitems, bytes, prop)
602
+ return nil if actual_type.n != XA_ATOM
603
+ raise "invalid format: #{actual_format.n}" unless actual_format.n == 32
604
+ type = X11::UnsignedLong.new(prop.ptr)
605
+ @display_obj.wm_window_types[type.n]
606
+ ensure
607
+ X11::XFree(prop.ptr) unless prop.ptr.null?
608
+ end
609
+
610
+ def active_window
611
+ net_active_window = X11::XInternAtom(@display, '_NET_ACTIVE_WINDOW', 1)
612
+
613
+ actual_type = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
614
+ actual_format = X11::Int.malloc(Fiddle::RUBY_FREE)
615
+ nitems = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
616
+ bytes = X11::UnsignedLong.malloc(Fiddle::RUBY_FREE)
617
+ prop = X11::Pointer.malloc(Fiddle::RUBY_FREE)
618
+ X11::XGetWindowProperty(@display, @window, net_active_window, 0, ~0, 0, ANY_PROPERTY_TYPE, actual_type, actual_format, nitems, bytes, prop)
619
+ raise "invalid format: #{actual_format.n}" unless actual_format.n == 32
620
+ raise "invalid nitems: #{nitems.n}" unless nitems.n > 0
621
+
622
+ win = X11::UnsignedLong.new(prop.ptr).n
623
+ return nil if win == 0
624
+ Window.new(@display_obj, win)
625
+ ensure
626
+ X11::XFree(prop.ptr) unless prop.ptr.null?
627
+ end
628
+ end
629
+
630
+ class GC
631
+ def initialize(display, window, gc)
632
+ @display = display
633
+ @window = window
634
+ @gc = gc
635
+ end
636
+
637
+ def set_background(color)
638
+ X11::XSetBackground(@display, @gc, color)
639
+ end
640
+
641
+ def set_foreground(color)
642
+ X11::XSetForeground(@display, @gc, color)
643
+ end
644
+
645
+ def set_font(font)
646
+ X11::XSetFont(@display, @gc, font)
647
+ end
648
+
649
+ def draw_string(x, y, string)
650
+ X11::XDrawString(@display, @window, @gc, x, y, string, string.length)
651
+ end
652
+
653
+ def fill_rectangle(x, y, width, height)
654
+ X11::XFillRectangle(@display, @window, @gc, x, y, width, height)
655
+ end
656
+ end
657
+ end
658
+ end