imitator_x 0.0.1

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/ext/xwindow.c ADDED
@@ -0,0 +1,1434 @@
1
+ /*********************************************************************************
2
+ Imitator for X is a library allowing you to fake input to systems using X11.
3
+ Copyright � 2010 Marvin G�lker
4
+
5
+ This file is part of Imitator for X.
6
+
7
+ Imitator for X is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Imitator for X is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with Imitator for X. If not, see <http://www.gnu.org/licenses/>.
19
+ *********************************************************************************/
20
+ #include "x.h"
21
+ #include "xwindow.h"
22
+ #include "keyboard.h"
23
+
24
+ /*Always remember: The Window type is just a long containing the window handle.*/
25
+ /*Heavy use of the GET_WINDOW macro is made here*/
26
+
27
+ /*Document-class: Imitator::X::XWindow
28
+ *This class allows interaction with the windows of an X server. There are some important terms
29
+ *that have to be made clear:
30
+ *[window] This means a window on the X server.
31
+ *[screen] Each window resides on a screen. A screen is a physical monitor. Numbering starts with 0.
32
+ *[display] A display is a collection of screens. If you have for example 4 monitors connected, you get 4 screens on 1 display. Numbering starts with 0.
33
+ *[display_string] This is a string describing a screen on a display, of form <tt>":display.screen"</tt> (yes, it starts with a colon).
34
+ *[root window] Every display has a root window, which is the parent of all windows visible on that screen (including the desktop window).
35
+ *
36
+ *Every method in this class will raise XProtocolErrors if you try to operate on non-existant windows, e.g. trying to
37
+ *retrieve a killed window's position.
38
+ *
39
+ *All methods of this class that return strings return them encoded in UTF-8. However, it's assumed that your
40
+ *X Server's locale is ISO-8859-1 (that's Latin-1), since I couldn't figure out how to query X for that information.
41
+ *Change the value of XSTR_TO_RSTR (in xwindow.h) to your X Server's locale, than recompile this library if
42
+ *it's incorrect. If you know how to obtain X's locale, please tell me at sutniuq$gmx:net. A patch would be nice, too.
43
+ *
44
+ *The methods covering EWMH standard may be not available on every system. If such a method is called on a
45
+ *system that doesn't support that part of EWMH or doesn't support EWMH at all, a NotImplementedError is raised.
46
+ *The corresponding EWMH standard of a method is mentioned in it's _Remarks_ section.
47
+ */
48
+
49
+ /*******************Helper functions**************************/
50
+
51
+ /*
52
+ *This function retrieves the display of the calling
53
+ *window. It's just shorthand for the two lines included in it.
54
+ */
55
+ static Display * get_win_display(VALUE self)
56
+ {
57
+ Display * p_display;
58
+ VALUE rstr;
59
+
60
+ rstr = rb_ivar_get(self, rb_intern("@display_string"));
61
+ p_display = XOpenDisplay(StringValuePtr(rstr));
62
+ return p_display;
63
+ }
64
+
65
+ /*
66
+ *This function checks whather the specified EWMH standard is supported
67
+ *by the system's window manager. If not, it raises a NotImplementedError
68
+ *exception.
69
+ */
70
+ static void check_for_ewmh(Display * p_display, const char * ewmh)
71
+ {
72
+ Window root = XRootWindow(p_display, 0);
73
+ Atom atom, actual_type, support_atom;
74
+ int actual_format;
75
+ unsigned long nitems, bytes;
76
+ unsigned char * props;
77
+ Atom * props2;
78
+ int ret, i, result = 0;
79
+ /*We don't use actual_type, actual_format and bytes. */
80
+
81
+ /*To check wheather any EWMH is supported*/
82
+ atom = XInternAtom(p_display, "_NET_SUPPORTED", False);
83
+ /*To check if the specified EWMH is supported*/
84
+ support_atom = XInternAtom(p_display, ewmh, False);
85
+
86
+ /*GetWindowProperty(_NET_SUPPORTED) gives us an array of supported EWMH standards
87
+ *or fails if no EWMH is supported.
88
+ *Many great thanks to Jordan Sissel whose xdotool code
89
+ *showed me how this function works. */
90
+ ret = XGetWindowProperty(p_display, root, atom, 0, (~0L), False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes, &props);
91
+ if (ret != Success)
92
+ {
93
+ XSetErrorHandler(NULL); /*Ensure the connection...*/
94
+ XCloseDisplay(p_display); /*...is properly closed. */
95
+ rb_raise(rb_eNotImpError, "EWMH is not supported by this window manager!");
96
+ }
97
+
98
+ /*Cast props to an Atom array, otherwise we get BadAtom errors. */
99
+ props2 = (Atom *) props;
100
+ for(i = 0L; ((i < nitems) && (result == 0)); i++)
101
+ {
102
+ if(props2[i] == support_atom)
103
+ result = 1; /*Found the atom.*/
104
+ }
105
+ XFree(props);
106
+
107
+ if (result == 0)
108
+ {
109
+ XSetErrorHandler(NULL);
110
+ XCloseDisplay(p_display);
111
+ rb_raise(rb_eNotImpError, "EWMH '%s' is not supported by this window manager!", ewmh);
112
+ }
113
+ }
114
+
115
+ /*************************Class methods***********************************/
116
+
117
+ /*
118
+ *call-seq:
119
+ * XWindow.xquery(display_string, window_id) ==> true
120
+ *
121
+ *<b>Don't call this method, it's used internally. </b>
122
+ *
123
+ *Raises a XProtocolError if the specified window doesn't exist on the specified +display_string+.
124
+ */
125
+ static VALUE cm_xquery(VALUE self, VALUE display_string, VALUE window_id)
126
+ {
127
+ Display * p_display = XOpenDisplay(StringValuePtr(display_string));
128
+ Window win = (Window)NUM2LONG(window_id);
129
+ Window dummy, dummy2, *children = NULL;
130
+ unsigned int child_num;
131
+
132
+ XSetErrorHandler(handle_x_errors); /*Let Ruby handle the errors*/
133
+ XQueryTree(p_display, win, &dummy, &dummy2, &children, &child_num);
134
+
135
+ XSetErrorHandler(NULL); /*Let X handle the errors again*/
136
+
137
+ XFree(children);
138
+ XCloseDisplay(p_display);
139
+ return Qtrue;
140
+ }
141
+
142
+ /*
143
+ *call-seq:
144
+ * XWindow.default_root_window() ==> aXWindow
145
+ *
146
+ *Returns the default root window of the default screen.
147
+ *===Return value
148
+ *The default root window of the default screen as an XWindow object.
149
+ *===Example
150
+ * puts Imitator::X::XWindow.default_root_window.title #=> (null)
151
+ */
152
+ static VALUE cm_default_root_window(VALUE self)
153
+ {
154
+ Display * p_display = XOpenDisplay(NULL);
155
+ Window root_win;
156
+ VALUE args[1];
157
+
158
+ root_win = XDefaultRootWindow(p_display);
159
+ args[0] = LONG2NUM(root_win);
160
+
161
+ XCloseDisplay(p_display);
162
+ return rb_class_new_instance(1, args, XWindow);
163
+ }
164
+
165
+ /*
166
+ *call-seq:
167
+ * XWindow.exists?(window_id, screen = 0, display = 0) ==> true or false
168
+ *
169
+ *Checks if the given window ID exists on the given display and screen.
170
+ *===Parameters
171
+ *[+window_id+] The window ID to check.
172
+ *[+screen+] (0) The screen to check.
173
+ *[+display+] (0) The display to check.
174
+ *===Return value
175
+ *true or false.
176
+ *===Example
177
+ * root_win = Imitator::X::XWindow.default_root_window
178
+ * puts Imitator::X::XWindow.exists?(root_win.window_id) #=> true
179
+ * puts Imitator::X::XWindow.exists?(12345) #=> false
180
+ *===Remarks
181
+ *This method rescues a XProtocolError exception you'll see when running
182
+ *under $DEBUG and get a +false+ result.
183
+ */
184
+ static VALUE cm_exists(int argc, VALUE argv[], VALUE self)
185
+ {
186
+ VALUE window_id;
187
+ VALUE screen;
188
+ VALUE display;
189
+ VALUE result;
190
+ char display_string[100];
191
+ char eval_str[1000];
192
+
193
+ rb_scan_args(argc, argv, "12", &window_id, &screen, &display);
194
+
195
+ /*Assign the display or default to 0*/
196
+ if (NIL_P(display))
197
+ display = INT2FIX(0);
198
+ /*Assign the screen or default to 0*/
199
+ if (NIL_P(screen))
200
+ screen = INT2FIX(0);
201
+
202
+ /*Get the display string, form ":display.screen"*/
203
+ sprintf(display_string, ":%i.%i", FIX2INT(display), FIX2INT(screen));
204
+ /*
205
+ *eval is awful. rb_rescue too. I evaluate this string due to rb_rescue's inusability.
206
+ *This eval is quite safe, because before I put the two parts together in display_string, I convert
207
+ *screen and display into integers.
208
+ */
209
+ sprintf(eval_str, "begin; Imitator::X::XWindow.xquery('%s', %i);true;rescue Imitator::X::XProtocolError;false;end", display_string, NUM2INT(window_id));
210
+ result = rb_eval_string(eval_str);
211
+
212
+ return result;
213
+ }
214
+
215
+ /*
216
+ *call-seq:
217
+ * XWindow.search(str , screen = 0 , display = 0) ==> anArray
218
+ * XWindow.search(regexp , screen = 0 , display = 0 ) ==> anArray
219
+ *
220
+ *Searches for a special window title.
221
+ *===Parameters
222
+ *[+str+] The title to look for. This will only match *exactly*.
223
+ *[+regexp+] The title to look for, as a Regular Expression to match.
224
+ *[+screen+] (0) The screen to look for the window.
225
+ *[+display+] (0) The display to look for the screen.
226
+ *===Return value
227
+ *An array containing the window IDs of all windows whose titles matched the string
228
+ *or Regular Expression. This may be empty if nothing matches.
229
+ *===Example
230
+ * #Search for a window whose name is exactly "x-nautilus-desktop"
231
+ * Imitator::X::XWindow.search("x-nautilus-desktop") #=> [33554464]
232
+ * #Search for a window whose name contains the string "imitator".
233
+ * Imitator::X::XWindow.search(/imitator/) #=> [...]
234
+ * #If a window isn't found, you get an empty array.
235
+ * Imitator::X::XWindow.search("nonexistant") #=> []
236
+ *===Remarks
237
+ *This method just searches the first children layer, i.e. the child windows of the root window.
238
+ *You can't find windows in windows with this method.
239
+ */
240
+ static VALUE cm_search(int argc, VALUE argv[], VALUE self) /*title as string or regexp*/
241
+ {
242
+ VALUE title, screen, display;
243
+ char display_string[100];
244
+ Display * p_display;
245
+ Window root_win, parent_win, temp_win;
246
+ Window * p_children;
247
+ unsigned int num_children;
248
+ XTextProperty xtext;
249
+ char * p_title;
250
+ int i;
251
+ short is_regexp;
252
+ VALUE result = rb_ary_new();
253
+
254
+ rb_scan_args(argc, argv, "12", &title, &screen, &display);
255
+ /*Check wheather we're operating on a Regular Expression or a String. Strings mean exact matching later on. */
256
+ if (TYPE(title) == T_REGEXP)
257
+ is_regexp = 1;
258
+ else
259
+ is_regexp = 0;
260
+ /*Assign the display or default to 0*/
261
+ if (NIL_P(display))
262
+ display = INT2FIX(0);
263
+ /*Assign the screen or default to 0*/
264
+ if (NIL_P(screen))
265
+ screen = INT2FIX(0);
266
+ /*Get the display string, form ":display.screen"*/
267
+ sprintf(display_string, ":%i.%i", FIX2INT(display), FIX2INT(screen));
268
+
269
+ p_display = XOpenDisplay(display_string);
270
+ XSetErrorHandler(handle_x_errors); /*Let Ruby handle the errors*/
271
+ root_win = XDefaultRootWindow(p_display);
272
+
273
+ XQueryTree(p_display, root_win, &root_win, &parent_win, &p_children, &num_children);
274
+
275
+ if (p_children != NULL) /*This means there are child windows*/
276
+ {
277
+ for(i = 0; i < num_children; i++)
278
+ {
279
+ temp_win = *(p_children + i);
280
+ XGetWMName(p_display, temp_win, &xtext); /*We want to match against the title later*/
281
+ p_title = (char *) malloc(sizeof(char) * (xtext.nitems + 1)); /*Allocate one char more than neccassary, because we get an "invalid pointer" otherwise*/
282
+ sprintf(p_title, "%s", xtext.value);
283
+
284
+ if (is_regexp)
285
+ {
286
+ if (!NIL_P(rb_reg_match(title, XSTR_TO_RSTR(p_title)))) /*This performs the regexp match*/
287
+ rb_ary_push(result, LONG2NUM(temp_win));
288
+ }
289
+ else /*Not using a regular expression*/
290
+ {
291
+ if (strcmp(StringValuePtr(title), p_title) == 0)
292
+ rb_ary_push(result, LONG2NUM(temp_win));
293
+ }
294
+ XFree(xtext.value);
295
+ free(p_title);
296
+ }
297
+ XFree(p_children);
298
+ }
299
+
300
+ XSetErrorHandler(NULL); /*Let X handle it's errors again*/
301
+ XCloseDisplay(p_display);
302
+ return result;
303
+ }
304
+
305
+ /*
306
+ *call-seq:
307
+ * XWindow.from_title(str [, screen = 0 [, display = 0 ] ] ) ==> aXWindow
308
+ * XWindow.from_title(regexp [, screen = 0 [, display = 0 ] ] ) ==> aXWindow
309
+ *
310
+ *Creates a new XWindow object by passing on the given parameters to XWindow.search and
311
+ *using the first found window ID to make the XWindow object.
312
+ *===Parameters
313
+ *See the XWindow.search parameter description.
314
+ *===Return value
315
+ *The first matching window as a XWindow object.
316
+ *===Raises
317
+ *[ArgumentError] No matching window was found.
318
+ *===Example
319
+ * #Get the first found window which name includes the string "imitator".
320
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
321
+ */
322
+ static VALUE cm_from_title(int argc, VALUE argv[], VALUE self)
323
+ {
324
+ VALUE args[1];
325
+
326
+ args[0] = rb_ary_entry(cm_search(argc, argv, self), 0);
327
+ if (NIL_P(args[0]))
328
+ rb_raise(rb_eArgError, "No matching window found!");
329
+
330
+ return rb_class_new_instance(1, args, XWindow);
331
+ }
332
+
333
+ /*
334
+ *call-seq:
335
+ * XWindow.from_focused( [screen = 0 [, display = 0 ] ] ) ==> aXWindow
336
+ *
337
+ *Creates a new XWindow from the actually focused window.
338
+ *===Parameters
339
+ *[screen] (0) The screen to look for the window.
340
+ *[display] (0) The display to look for the screen.
341
+ *===Return value
342
+ *The window having the input focus.
343
+ *===Example
344
+ * xwin = Imitator::X::XWindow.from_focused
345
+ *===Remarks
346
+ *This method is not reliable, since it's likely to find one of those
347
+ *invisible "InputOnly" windows. Have a look at XWindow.from_active
348
+ *for a more reliable variant.
349
+ */
350
+ static VALUE cm_from_focused(int argc, VALUE argv[], VALUE self)
351
+ {
352
+ VALUE screen, display;
353
+ Display * p_display;
354
+ char display_string[100];
355
+ Window win;
356
+ int revert;
357
+ VALUE result;
358
+ VALUE args[1];
359
+
360
+ rb_scan_args(argc, argv, "02", &screen, &display);
361
+
362
+ /*Assign the display or default to 0*/
363
+ if (NIL_P(display))
364
+ display = INT2FIX(0);
365
+ /*Assign the screen or default to 0*/
366
+ if (NIL_P(screen))
367
+ screen = INT2FIX(0);
368
+ /*Get the display string, form ":display.screen"*/
369
+ sprintf(display_string, ":%i.%i", FIX2INT(display), FIX2INT(screen));
370
+
371
+ p_display = XOpenDisplay(display_string);
372
+ XSetErrorHandler(handle_x_errors);
373
+
374
+ XGetInputFocus(p_display, &win, &revert);
375
+ args[0] = LONG2NUM(win);
376
+ result = rb_class_new_instance(1, args, XWindow);
377
+
378
+ XSetErrorHandler(NULL);
379
+ XCloseDisplay(p_display);
380
+ return result;
381
+ }
382
+
383
+ /*
384
+ *call-seq:
385
+ * XWindow.from_active( [screen = 0 [, display = 0 ] ] ) ==> aXWindow
386
+ *
387
+ *Creates a new XWindow from the currently active window.
388
+ *===Parameters
389
+ *[screen] (0) The screen to look for the window.
390
+ *[display] (0) The display to look for the screen.
391
+ *===Return value
392
+ *The found window as a XWindow object.
393
+ *===Raises
394
+ *[NotImplementedError] The EWMH standard _NET_ACTIVE_WINDOW isn't supported.
395
+ *[XError] Failed to retrieve the active window from the X server. Did you specify a correct screen and display?
396
+ *===Example
397
+ * xwin = Imitator::X::XWindow.from_active
398
+ * #Active window of screen 3
399
+ * xwin = Imitator::X::XWindow.from_active(3)
400
+ * #Active window of screen 0 on display 2
401
+ * xwin = Imitator::X::XWindow.from_active(0, 2)
402
+ *===Remarks
403
+ *This method is part of the EWMH standard _NET_ACTIVE_WINDOW.
404
+ *
405
+ *If you can choose between this method and XWindow.from_focused, choose this one,
406
+ *because it doesn't find invisible windows.
407
+ */
408
+ static VALUE cm_from_active(int argc, VALUE argv[], VALUE self)
409
+ {
410
+ Display * p_display;
411
+ VALUE screen, display, result;
412
+ VALUE args[1];
413
+ char display_string[100];
414
+ Atom atom, actual_type;
415
+ Window root, active_win;
416
+ int actual_format;
417
+ unsigned long nitems, bytes;
418
+ unsigned char * prop;
419
+
420
+ rb_scan_args(argc, argv, "02", &screen, &display);
421
+ /*Assign the display or default to 0*/
422
+ if (NIL_P(display))
423
+ display = INT2FIX(0);
424
+ /*Assign the screen or default to 0*/
425
+ if (NIL_P(screen))
426
+ screen = INT2FIX(0);
427
+ /*Get the display string, form ":display.screen"*/
428
+ sprintf(display_string, ":%i.%i", FIX2INT(display), FIX2INT(screen));
429
+
430
+ p_display = XOpenDisplay(display_string);
431
+ XSetErrorHandler(handle_x_errors);
432
+ check_for_ewmh(p_display, "_NET_ACTIVE_WINDOW");
433
+
434
+ atom = XInternAtom(p_display, "_NET_ACTIVE_WINDOW", False);
435
+ root = XDefaultRootWindow(p_display);
436
+
437
+ XGetWindowProperty(p_display, root, atom, 0, (~0L), False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes, &prop);
438
+ if (nitems > 0)
439
+ active_win = *((Window *) prop); /*We got a Window*/
440
+ else /*Shouldn't be the case*/
441
+ rb_raise(XError, "Couldn't retrieve the active window for some reason!");
442
+ args[0] = LONG2NUM(active_win);
443
+ result = rb_class_new_instance(1, args, XWindow);
444
+
445
+ XFree(prop);
446
+ XSetErrorHandler(NULL);
447
+ XCloseDisplay(p_display);
448
+ return result;
449
+ }
450
+
451
+ /*
452
+ *call-seq:
453
+ * XWindow.wait_for_window( str [, screen = 0 [, display = 0 ] ] ) ==> aXWindow
454
+ * XWindow.wait_for_window(regexp [, screen = 0 [, display = 0 ] ] ) ==> aXWindow
455
+ *
456
+ *Pauses execution until a window matching the given criteria is found.
457
+ *===Parameters
458
+ *[+str+] The *exact* title of the window you want to to wait for.
459
+ *[+regexp+] A Regular Expression matching the window title you want to wait for.
460
+ *[+screen+] (0) The screen the window will be mapped to.
461
+ *[+display+] (0) The display the window will be mapped to.
462
+ *===Return value
463
+ *The XWindow object of the matching window.
464
+ *===Example
465
+ * #Wait until a window with "gedit" in it's title exists
466
+ * gedit_win = Imitator::X::XWindow.wait_for_window(/gedit/)
467
+ */
468
+ static VALUE cm_wait_for_window(int argc, VALUE argv[], VALUE self)
469
+ {
470
+ VALUE rstr;
471
+ VALUE rscreen;
472
+ VALUE rdisplay;
473
+ VALUE args[3];
474
+
475
+ rb_scan_args(argc, argv, "12", &rstr, &rscreen, &rdisplay);
476
+ args[0] = rstr;
477
+ args[1] = rscreen;
478
+ args[2] = rdisplay;
479
+
480
+ /*Hang around until we see a window matching the cirteria*/
481
+ while (RTEST(rb_funcall(cm_search(3, args, self), rb_intern("empty?"), 0)))
482
+ rb_thread_sleep(1);
483
+
484
+ /*Wait another second to be sure that the window can be worked with*/
485
+ rb_thread_sleep(1);
486
+
487
+ return cm_from_title(3, args, self);
488
+ }
489
+
490
+ /*
491
+ *call-seq:
492
+ * XWindow.wait_for_window_termination( str [, screen = 0 [, display = 0 ] ] ) ==> nil
493
+ * XWindow.wait_for_window_termination( regexp [, streen = 0 [, display = 0 ] ] ) ==> nil
494
+ *
495
+ *Pauses execution flow until *every* window matching the given criteria disappeared.
496
+ *===Parameters
497
+ *[+str+] The window's title. This must match *excatly*.
498
+ *[+regexp+] The window's title, as a Regular Expression to match.
499
+ *[+screen+] (0) The screen the window resides on.
500
+ *[+display+] (0) The screen's display.
501
+ *===Return value
502
+ *nil.
503
+ *===Example
504
+ * #Wait until all gedit windows are closed
505
+ * Imitator::X::XWindow.wait_for_window_termination(/gedit/)
506
+ * #Wait until Firefox on screen 1 closes
507
+ * Imitator::X::XWindow.wait_for_window_termination(/Mozilla Firefox/, 1)
508
+ *===Remarks
509
+ *If you want to wait for a specific window's termination and you already have
510
+ *a XWindow object for that one, you can also use the following code which is
511
+ *probably shorter:
512
+ * #... (Get your XWindow instance somewhere)
513
+ * sleep 0.1 while xwin.exists?
514
+ *This causes Ruby to check every tenth of a second to check if the reference
515
+ *hold by +xwin+ is still valid, and if not, the loop exits and program flow continues.
516
+ */
517
+ static VALUE cm_wait_for_window_termination(int argc, VALUE argv[], VALUE self)
518
+ {
519
+ VALUE rstr;
520
+ VALUE rscreen;
521
+ VALUE rdisplay;
522
+ VALUE args[3];
523
+
524
+ rb_scan_args(argc, argv, "12", &rstr, &rscreen, &rdisplay);
525
+ args[0] = rstr;
526
+ args[1] = rscreen;
527
+ args[2] = rdisplay;
528
+
529
+ /*Hang around until every window matching the criteria has gone*/
530
+ while (!RTEST(rb_funcall(cm_search(3, args, self), rb_intern("empty?"), 0)))
531
+ rb_thread_sleep(1);
532
+
533
+ /*Wait another second to be sure that the window can't be worked with*/
534
+ rb_thread_sleep(1);
535
+
536
+ return Qnil;
537
+ }
538
+ /****************************Instance methods*************************************/
539
+
540
+ /*
541
+ *call-seq:
542
+ * XWindow.new(window_id, screen = 0, display = 0) ==> aXWindow
543
+ *
544
+ *Creates a new XWindow object which holds a pseudo reference to a real window.
545
+ *===Parameters
546
+ *[+window_id+] The ID of the window to get a reference to.
547
+ *[+screen+] (0) The number of the screen the window is shown on.
548
+ *[+display+] (0) The number of the display that contains the screen the window is mapped to.
549
+ *===Return value
550
+ *A brand new XWindow object.
551
+ *===Raises
552
+ *[XProtocolError] The window ID wasn't found (on the specified screen and/or display).
553
+ *===Example
554
+ * #Assume 12345 is an existing window on the default screen and display
555
+ * xwin = Imitator::X::XWindow.new(12345)
556
+ * #Or on screen 2 on display 0
557
+ * xwin = Imitator::X::XWindow.new(12345, 2)
558
+ * #Or on screen 3 on display 1 (you almost never need this)
559
+ * xwin = Imitator::X::XWindow.new(12345, 3, 1)
560
+ */
561
+ static VALUE m_initialize(VALUE argc, VALUE argv[], VALUE self)
562
+ {
563
+ VALUE window_id;
564
+ VALUE screen;
565
+ VALUE display;
566
+ VALUE display_string = rb_class_new_instance(0, 0, rb_cString);
567
+
568
+ rb_scan_args(argc, argv, "12", &window_id, &screen, &display);
569
+
570
+ rb_str_concat(display_string, rb_str_new2(":")); /*Ommit hostname, we're only operating locally*/
571
+ /*Assign the display or default to 0*/
572
+ if (NIL_P(display))
573
+ rb_str_concat(display_string, rb_str_new2("0"));
574
+ else
575
+ rb_str_concat(display_string, display);
576
+ rb_str_concat(display_string, rb_str_new2(".")); /*Screen delimiter*/
577
+ /*Assign the screen or default to 0*/
578
+ if (NIL_P(screen))
579
+ rb_str_concat(display_string, rb_str_new2("0"));
580
+ else
581
+ rb_str_concat(display_string, screen);
582
+
583
+ rb_ivar_set(self, rb_intern("@window_id"), window_id);
584
+ rb_ivar_set(self, rb_intern("@display_string"), display_string);
585
+ return self;
586
+ }
587
+
588
+ /*
589
+ *Human-readable description of form <tt><Imitator::X::XWindow 'window_title' (window_id_as_hex)></tt>.
590
+ */
591
+ static VALUE m_inspect(VALUE self)
592
+ {
593
+ char str[1000];
594
+ VALUE rstr;
595
+ VALUE handle;
596
+
597
+ rstr = rb_funcall(self, rb_intern("title"), 0);
598
+ handle = rb_funcall(rb_ivar_get(self, rb_intern("@window_id")), rb_intern("to_s"), 1, INT2NUM(16));
599
+ sprintf(str, "<Imitator::X::XWindow '%s' (0x%s)>", StringValuePtr(rstr), StringValuePtr(handle));
600
+ rstr = rb_enc_str_new(str, strlen(str), rb_utf8_encoding());
601
+ return rstr;
602
+ }
603
+
604
+ /*
605
+ *Returns the window's title.
606
+ *===Return value
607
+ *The window's title.
608
+ *===Example
609
+ * #Get the root window's title. This is not very useful, it's always "(null)".
610
+ * puts Imitator::X::XWindow.default_root_window.title #=> (null)
611
+ */
612
+ static VALUE m_title(VALUE self)
613
+ {
614
+ Display * p_display;
615
+ Window win;
616
+ XTextProperty xtext;
617
+ char * cp;
618
+ VALUE rstr;
619
+
620
+ p_display = get_win_display(self);
621
+ XSetErrorHandler(handle_x_errors);
622
+
623
+ win = NUM2LONG(rb_ivar_get(self, rb_intern("@window_id")));
624
+ XGetWMName(p_display, win, &xtext);
625
+ cp = malloc(sizeof(char) * xtext.nitems);
626
+ sprintf(cp, "%s", xtext.value);
627
+ rstr = XSTR_TO_RSTR(cp);
628
+
629
+ XFree(xtext.value);
630
+ XSetErrorHandler(NULL);
631
+ XCloseDisplay(p_display);
632
+ return rstr;
633
+ }
634
+
635
+ /*
636
+ *Returns the window_id.
637
+ *===Return value
638
+ *The window ID as an integer.
639
+ *===Example
640
+ * #Get the root window's ID.
641
+ * puts Imitator::X::XWindow.default_root_window.window_id #=> 316
642
+ */
643
+ static VALUE m_window_id(VALUE self)
644
+ {
645
+ return rb_ivar_get(self, rb_intern("@window_id"));
646
+ }
647
+
648
+ /*
649
+ *Returns the root window of the screen that holds this window.
650
+ *===Return value
651
+ *The root window of the screen holding this window, as a XWindow.
652
+ *===Example
653
+ * #Get a window with "imitator" in it's title and...
654
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
655
+ * #...get the root window of the screen it's mapped to.
656
+ * xwin.root_win #=> <Imitator::X::XWindow '(null)' (0x13c)>
657
+ */
658
+ static VALUE m_root_win(VALUE self)
659
+ {
660
+ Display * p_display;
661
+ Window win = GET_WINDOW;
662
+ XWindowAttributes xattr;
663
+ VALUE args[1];
664
+ VALUE rroot_win;
665
+
666
+ p_display = get_win_display(self);
667
+ XSetErrorHandler(handle_x_errors);
668
+
669
+ XGetWindowAttributes(p_display, win, &xattr);
670
+ args[0] = LONG2NUM(xattr.root);
671
+ rroot_win = rb_class_new_instance(1, args, XWindow);
672
+
673
+ XSetErrorHandler(NULL);
674
+ XCloseDisplay(p_display);
675
+ return rroot_win;
676
+ }
677
+
678
+ /*
679
+ *Returns this window's parent window.
680
+ *===Return value
681
+ *This window's parent window as a XWindow object.
682
+ *===Example
683
+ * xwin = Imitator:X::XWindow.from_title(/imitator/)
684
+ * xwin.parent #=> <Imitator::X::XWindow '(null)' (0x13c)>
685
+ */
686
+ static VALUE m_parent(VALUE self)
687
+ {
688
+ Display * p_display;
689
+ Window win = GET_WINDOW;
690
+ Window root_win, parent;
691
+ Window * p_children;
692
+ unsigned int nchildren;
693
+ VALUE args[1];
694
+ VALUE result;
695
+
696
+ p_display = get_win_display(self);
697
+ XSetErrorHandler(handle_x_errors);
698
+
699
+ XQueryTree(p_display, win, &root_win, &parent, &p_children, &nchildren);
700
+ args[0] = LONG2NUM(parent);
701
+ result = rb_class_new_instance(1, args, XWindow);
702
+
703
+ XFree(p_children);
704
+ XSetErrorHandler(NULL);
705
+ XCloseDisplay(p_display);
706
+ return result;
707
+ }
708
+
709
+ /*
710
+ *Returns the children of this window.
711
+ *===Return value
712
+ *The window IDs of the children of this window as integers.
713
+ *===Example
714
+ * #Get a list of all windows that are searched by XWindow.search
715
+ * Imitator::X::XWindow.default_root_window.children #=> [...]
716
+ *===Remarks
717
+ *I orginally wanted to return an array of XWindow obejcts, but everytime
718
+ *I tried to #inspect the created array, I got a segfault, memory corruption or
719
+ *malloc failed assertion. When only inspecting parts of the array, everything goes fine.
720
+ *So, don't try to map the list returned by this method into an array and then inspect it.
721
+ *E-mail me at sutniuq$gmx:net if you know a solution... See also the commented-out source
722
+ *in the for loop inside this function.
723
+ */
724
+ static VALUE m_children(VALUE self)
725
+ {
726
+ Display * p_display;
727
+ Window win = GET_WINDOW;
728
+ Window root_win, parent;
729
+ Window * p_children;
730
+ unsigned int num_children;
731
+ int i;
732
+ //VALUE args[1];
733
+ VALUE result = rb_ary_new();
734
+ //VALUE rtemp;
735
+
736
+ p_display = get_win_display(self);
737
+ XSetErrorHandler(handle_x_errors);
738
+
739
+ XQueryTree(p_display, win, &root_win, &parent, &p_children, &num_children);
740
+ for(i = 0;i < num_children; i++)
741
+ {
742
+ //printf("%lu\n", *(p_children + i));
743
+ //args[0] = LONG2NUM(*(p_children + i));
744
+ //rtemp = rb_class_new_instance(1, args, XWindow);
745
+ //rb_ary_push(result, rtemp); /*Why causes this a segfault??? AND WHY ONLY WHEN INSPECTING THE WHOLE ARRAY?????*/
746
+ rb_ary_push(result, LONG2NUM(*(p_children + i)));
747
+ }
748
+
749
+ XFree(p_children);
750
+ XSetErrorHandler(NULL);
751
+ XCloseDisplay(p_display);
752
+ return result;
753
+ }
754
+
755
+ /*
756
+ *Returns true if +self+ is a root window.
757
+ *===Return value
758
+ *true or false.
759
+ *===Example
760
+ * root = Imitator::X::XWindow.default_root_window
761
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
762
+ * root.root_win? #=> true
763
+ * xwin.root_win? #=> false
764
+ */
765
+ static VALUE m_is_root_win(VALUE self)
766
+ {
767
+ Display * p_display;
768
+ Window win = GET_WINDOW;
769
+ Window root_win, parent;
770
+ Window * p_children;
771
+ unsigned int num_children;
772
+ VALUE result;
773
+
774
+ p_display = get_win_display(self);
775
+ XSetErrorHandler(handle_x_errors);
776
+
777
+ XQueryTree(p_display, win, &root_win, &parent, &p_children, &num_children);
778
+
779
+ if (parent == 0)
780
+ result = Qtrue;
781
+ else
782
+ result = Qfalse;
783
+
784
+ XFree(p_children);
785
+ XSetErrorHandler(NULL);
786
+ XCloseDisplay(p_display);
787
+ return result;
788
+ }
789
+
790
+ /*
791
+ *Returns the window's current position.
792
+ *===Return value
793
+ *The position as a two-element array of form <tt>[x, y]</tt>.
794
+ *===Example
795
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
796
+ * p xwin.position #=> [3, 47]
797
+ */
798
+ static VALUE m_position(VALUE self)
799
+ {
800
+ Display * p_display;
801
+ Window win = GET_WINDOW;
802
+ XWindowAttributes xattr;
803
+ VALUE pos = rb_ary_new();
804
+
805
+ p_display = get_win_display(self);
806
+ XSetErrorHandler(handle_x_errors);
807
+
808
+ XGetWindowAttributes(p_display, win, &xattr);
809
+ rb_ary_push(pos, INT2NUM(xattr.x));
810
+ rb_ary_push(pos, INT2NUM(xattr.y));
811
+
812
+ XSetErrorHandler(NULL);
813
+ XCloseDisplay(p_display);
814
+ return pos;
815
+ }
816
+
817
+ /*
818
+ *Return the window's current size.
819
+ *===Return value
820
+ *The window's size as a two-element array of form <tt>[width, height]</tt>.
821
+ *===Example
822
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
823
+ * p xwin.size #=> [801, 551]
824
+ */
825
+ static VALUE m_size(VALUE self)
826
+ {
827
+ Display * p_display;
828
+ Window win = GET_WINDOW;
829
+ XWindowAttributes xattr;
830
+ VALUE size = rb_ary_new();
831
+
832
+ p_display = get_win_display(self);
833
+ XSetErrorHandler(handle_x_errors);
834
+
835
+ XGetWindowAttributes(p_display, win, &xattr);
836
+ rb_ary_push(size, INT2NUM(xattr.width));
837
+ rb_ary_push(size, INT2NUM(xattr.height));
838
+
839
+ XSetErrorHandler(NULL);
840
+ XCloseDisplay(p_display);
841
+ return size;
842
+ }
843
+
844
+ /*
845
+ *Determines wheather or not +self+ is visible on the screen.
846
+ *===Return value
847
+ *true or false.
848
+ *===Example
849
+ * Imitator::X::XWindow.from_title(/imitator/).visible? #=> true
850
+ *===Remarks
851
+ *Generally invisible, so-called InputOnly-windows, are treated as
852
+ *invisible and return false. If you want to check the map state of such
853
+ *a window, use #mapped?.
854
+ *
855
+ *This method returns also false if an ancestor is unmapped.
856
+ */
857
+ static VALUE m_is_visible(VALUE self)
858
+ {
859
+ Display * p_display;
860
+ Window win = GET_WINDOW;
861
+ XWindowAttributes xattr;
862
+ VALUE result;
863
+
864
+ p_display = get_win_display(self);
865
+ XSetErrorHandler(handle_x_errors);
866
+
867
+ XGetWindowAttributes(p_display, win, &xattr);
868
+ if (xattr.class == InputOnly)
869
+ result = Qfalse; /*Input-only windows are always invisible*/
870
+ else
871
+ {
872
+ if (xattr.map_state == IsUnmapped || xattr.map_state == IsUnviewable)
873
+ result = Qfalse; /*Only mapped windows are visible (their ancestors must be mapped, too)*/
874
+ else
875
+ result = Qtrue;
876
+ }
877
+
878
+ XSetErrorHandler(NULL);
879
+ XCloseDisplay(p_display);
880
+ return result;
881
+ }
882
+
883
+ /*
884
+ *Checkes wheather or not +self+ is mapped to the screen.
885
+ *===Return value
886
+ *true or false.
887
+ *===Example
888
+ * Imitator::X::XWindow.from_title(/imitator/).mapped? #=> true
889
+ *===Remarks
890
+ *This method doesn't give reliable information about a window's
891
+ *visibility, because it returns true for InputOnly-windows which
892
+ *are generally invisible. If you don't want this behaviour, use #visible?.
893
+ *
894
+ *This method returns also false if an ancestor is unmapped.
895
+ */
896
+ static VALUE m_is_mapped(VALUE self)
897
+ {
898
+ Display * p_display;
899
+ Window win = GET_WINDOW;
900
+ XWindowAttributes xattr;
901
+ VALUE result;
902
+
903
+ p_display = get_win_display(self);
904
+ XSetErrorHandler(handle_x_errors);
905
+
906
+ XGetWindowAttributes(p_display, win, &xattr);
907
+ if (xattr.map_state == IsUnmapped || xattr.map_state == IsUnviewable)
908
+ result = Qfalse;
909
+ else
910
+ result = Qtrue;
911
+
912
+ XSetErrorHandler(NULL);
913
+ XCloseDisplay(p_display);
914
+ return result;
915
+ }
916
+
917
+ /*
918
+ *call-seq:
919
+ * move(x, y) ==> anArray
920
+ *
921
+ *Moves +self+ to the specified position.
922
+ *===Parameters
923
+ *[+x+] The goal X coordinate.
924
+ *[+y+] The goal Y coordinate.
925
+ *===Return value
926
+ *The window's new position.
927
+ *===Example
928
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
929
+ * #Move the window to (100|100)
930
+ * xwin.move(100, 100) #=> [103, 123]
931
+ * xwin.pos #=> [103, 123] #See remarks
932
+ *===Remarks
933
+ *It's impossible to set a window exactly to that coordinate you want. It seems,
934
+ *that X sets and retrieves a window's position at the upper-left coordinate of a window's client area,
935
+ *that is, beyond the border and the title bar. Therefore, to make an exact position movement, you would have
936
+ *to subtract the width of those elements. Unfortunally, aXWindowAttributes.border_width always returns 0 on my
937
+ *system. :(
938
+ *
939
+ *Also, this function can't move the window off the screen. If you try to, the window will
940
+ *be moved as near to the screen's edge as possible.
941
+ */
942
+ static VALUE m_move(VALUE self, VALUE rx, VALUE ry)
943
+ {
944
+ Display * p_display;
945
+ Window win = GET_WINDOW;
946
+ int x = NUM2INT(rx);
947
+ int y = NUM2INT(ry);
948
+
949
+ p_display = get_win_display(self);
950
+ XSetErrorHandler(handle_x_errors);
951
+
952
+ XMoveWindow(p_display, win, x, y);
953
+
954
+ XSetErrorHandler(NULL);
955
+ XCloseDisplay(p_display);
956
+ return m_position(self);
957
+ }
958
+
959
+ /*
960
+ *call-seq:
961
+ * resize(width, height) ==> anArray
962
+ *
963
+ *Resizes a window.
964
+ *===Parameters
965
+ *[+width+] The desired width, in pixels.
966
+ *[+height+] The desired height, in pixels.
967
+ *===Return value
968
+ *The new window size.
969
+ *===Example
970
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
971
+ * #Resize the window to 400x400px
972
+ * xwin.resize(400, 400) #=> [400, 400]
973
+ * xwin.size #=> [400, 400]
974
+ *===Remarks
975
+ *Some windows have a minumum width value set. If a windows has, you can't change it's size
976
+ *to a smaller value than that one. Also, it's impossible to make a window larger than the screen.
977
+ *In any of those cases, the window will be resized to the minimum/maximum acceptable value.
978
+ */
979
+ static VALUE m_resize(VALUE self, VALUE rwidth, VALUE rheight)
980
+ {
981
+ Display * p_display;
982
+ Window win = GET_WINDOW;
983
+ unsigned int width = NUM2UINT(rwidth);
984
+ unsigned int height = NUM2UINT(rheight);
985
+
986
+ p_display = get_win_display(self);
987
+ XSetErrorHandler(handle_x_errors);
988
+
989
+ XResizeWindow(p_display, win, width, height);
990
+
991
+ XSetErrorHandler(NULL);
992
+ XCloseDisplay(p_display);
993
+ return m_size(self);
994
+ }
995
+
996
+ /*
997
+ *Raises a window to the top, but doesn't give it the input focus.
998
+ *===Return value
999
+ *nil.
1000
+ *===Example
1001
+ * wxin = Imitator::X::XWindow.from_title(/imitator/)
1002
+ * xwin.raise_win
1003
+ *===Remarks
1004
+ *This method isn't named "raise", because that would conflict with the
1005
+ *Kernel module's "raise" method. You can define an alias if you want to.
1006
+ *
1007
+ *Instead of combining this method with #focus you may should have a
1008
+ *look at #activate.
1009
+ */
1010
+ static VALUE m_raise_win(VALUE self)
1011
+ {
1012
+ Display * p_display;
1013
+ Window win = GET_WINDOW;
1014
+
1015
+ p_display = get_win_display(self);
1016
+ XSetErrorHandler(handle_x_errors);
1017
+
1018
+ XRaiseWindow(p_display, win);
1019
+
1020
+ XSetErrorHandler(NULL);
1021
+ XCloseDisplay(p_display);
1022
+ return Qnil;
1023
+ }
1024
+
1025
+ /*
1026
+ *Gives +self+ the input focus, but doesn't bring it to the front.
1027
+ *===Return value
1028
+ *nil.
1029
+ *===Example
1030
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
1031
+ * xwin.focus
1032
+ *===Remarks
1033
+ *Instead of combining this method with #raise_win you may should
1034
+ *have a look at #activate.
1035
+ */
1036
+ static VALUE m_focus(VALUE self)
1037
+ {
1038
+ Display * p_display;
1039
+ Window win = GET_WINDOW;
1040
+
1041
+ p_display = get_win_display(self);
1042
+ XSetErrorHandler(handle_x_errors);
1043
+
1044
+ XSetInputFocus(p_display, win, RevertToNone, CurrentTime);
1045
+
1046
+ XSetErrorHandler(NULL);
1047
+ XCloseDisplay(p_display);
1048
+ return Qnil;
1049
+ }
1050
+
1051
+ /*
1052
+ *Makes +self+ lose the input focus. The window will continue looking focused.
1053
+ *===Return value
1054
+ *nil.
1055
+ *===Example
1056
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
1057
+ * #Try to type after this, your input won't get anywhere:
1058
+ * xwin.focus;sleep 2;xwin.unfocus
1059
+ */
1060
+ static VALUE m_unfocus(VALUE self)
1061
+ {
1062
+ Display * p_display;
1063
+
1064
+ p_display = get_win_display(self);
1065
+ XSetErrorHandler(handle_x_errors);
1066
+
1067
+ XSetInputFocus(p_display, None, RevertToNone, CurrentTime);
1068
+
1069
+ XSetErrorHandler(NULL);
1070
+ XCloseDisplay(p_display);
1071
+ return Qnil;
1072
+ }
1073
+
1074
+ /*
1075
+ *Maps a window to the screen, i.e. make it visible. Only possible after a previous
1076
+ *call to #unmap.
1077
+ *===Return value
1078
+ *nil.
1079
+ *===Example
1080
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
1081
+ * p xwin.visible? #=> true
1082
+ * p xwin.size #=> [804, 550]
1083
+ * xwin.unmap
1084
+ * p xwin.visible? #=> false
1085
+ * p xwin.size #=> [804, 550]
1086
+ * xwin.map
1087
+ * p xwin.visible? #=> true
1088
+ * p xwin.size #=> [804, 550]
1089
+ */
1090
+ static VALUE m_map(VALUE self)
1091
+ {
1092
+ Display * p_display;
1093
+ Window win = GET_WINDOW;
1094
+
1095
+ p_display = get_win_display(self);
1096
+ XSetErrorHandler(handle_x_errors);
1097
+
1098
+ XMapWindow(p_display, win);
1099
+
1100
+ XSetErrorHandler(NULL);
1101
+ XCloseDisplay(p_display);
1102
+ return Qnil;
1103
+ }
1104
+
1105
+ /*
1106
+ *Unmaps a window from the screen, i.e. make it invisible. The window <b>is not</b> deleted.
1107
+ *===Return value
1108
+ *nil.
1109
+ *===Example
1110
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
1111
+ * xwin.unmap
1112
+ *===Remarks
1113
+ *Perhaps it's not a good idea to unmap the root window...
1114
+ */
1115
+ static VALUE m_unmap(VALUE self)
1116
+ {
1117
+ Display * p_display;
1118
+ Window win = GET_WINDOW;
1119
+
1120
+ p_display = get_win_display(self);
1121
+ XSetErrorHandler(handle_x_errors);
1122
+
1123
+ XUnmapWindow(p_display, win);
1124
+
1125
+ XSetErrorHandler(NULL);
1126
+ XCloseDisplay(p_display);
1127
+ return Qnil;
1128
+ }
1129
+
1130
+ /*
1131
+ *Activates a window, that is, bring it to the front and give it the input focus.
1132
+ *===Return value
1133
+ *nil.
1134
+ *===Raises
1135
+ *[NotImplementedError] The EWMH standard _NET_ACTIVE_WINDOW is not supported.
1136
+ *===Example
1137
+ * Imitator::X::XWindow.from_title(/imitator/).activate
1138
+ *===Remarks
1139
+ *This method is part of the EWMH standard _NET_ACTIVE_WINDOW. It's usually more reliable
1140
+ *than #raise_win combined with #focus.
1141
+ */
1142
+ static VALUE m_activate(VALUE self)
1143
+ {
1144
+ Display * p_display;
1145
+ Window win = GET_WINDOW;
1146
+ XEvent xevt;
1147
+ XWindowAttributes xattr;
1148
+ Window root;
1149
+
1150
+ p_display = get_win_display(self);
1151
+ XSetErrorHandler(handle_x_errors);
1152
+
1153
+ check_for_ewmh(p_display, "_NET_ACTIVE_WINDOW");
1154
+ /*We're going to notify the root window*/
1155
+ XGetWindowAttributes(p_display, win, &xattr);
1156
+ root = xattr.root;
1157
+
1158
+ xevt.type = ClientMessage; /*It's a message for a client*/
1159
+ xevt.xclient.display = p_display;
1160
+ xevt.xclient.window = win;
1161
+ xevt.xclient.message_type = XInternAtom(p_display, "_NET_ACTIVE_WINDOW", False); /*Activate request*/
1162
+ xevt.xclient.format = 32; /*Using 32-bit messages*/
1163
+ xevt.xclient.data.l[0] = 2L;
1164
+ xevt.xclient.data.l[1] = CurrentTime;
1165
+
1166
+ /*Actually send the event; this has to happen to all child windows of the target window. */
1167
+ XSendEvent(p_display, root, False, SubstructureNotifyMask | SubstructureRedirectMask, &xevt);
1168
+
1169
+ XSetErrorHandler(NULL);
1170
+ XCloseDisplay(p_display);
1171
+ return Qnil;
1172
+ }
1173
+
1174
+ /*
1175
+ *Gets a window process' process identification number (PID).
1176
+ *===Return value
1177
+ *The PID of the window process.
1178
+ *===Raises
1179
+ *[XError] Failed to retrieve the window's PID. Either your window manager does not support EWMH at all, _NET_WM_PID in special, or your queried window hasn't set this property (see _Remarks_).
1180
+ *===Example
1181
+ * puts Imitator::X::XWindow.from_title(/imitator/).pid #=> 4478
1182
+ *===Remarks
1183
+ *This method is part of the EWMH standard _NET_WM_PID.
1184
+ *
1185
+ *This method may fail even if the window manager supports the _NET_WM_PID standard, beacuse
1186
+ *the use of _NET_WM_PID isn't enforced. Some programs don't use it, and those will cause this
1187
+ *method to fail. The most prominent example is the default root window, which doesn't have the
1188
+ *_NET_WM_PID property set (at least on my Ubuntu Karmic).
1189
+ */
1190
+ static VALUE m_pid(VALUE self)
1191
+ {
1192
+ Display * p_display;
1193
+ Window win = GET_WINDOW;
1194
+ Atom actual_type;
1195
+ int obtain_prop, actual_format, pid;
1196
+ unsigned long nitems;
1197
+ unsigned long bytes;
1198
+ unsigned char * property;
1199
+ VALUE result;
1200
+
1201
+ p_display = get_win_display(self);
1202
+ XSetErrorHandler(handle_x_errors);
1203
+
1204
+ //check_for_ewmh(p_display, "_NET_WM_PID"); /*For some unknown reason, this always fails*/
1205
+ obtain_prop = XInternAtom(p_display, "_NET_WM_PID", True);
1206
+
1207
+ XGetWindowProperty(p_display, win, obtain_prop, 0, 1000000, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes, &property);
1208
+ if (property == None)
1209
+ rb_raise(XError, "Could not get _NET_WM_PID attribute!");
1210
+
1211
+ /*OK, got the property. Compute the PID. I don't know why this works, I
1212
+ *just saw the arithmetic somewhere on the Internet. */
1213
+ pid = property[1] * 256;
1214
+ pid += property[0];
1215
+ result = INT2NUM(pid);
1216
+
1217
+ XFree(property);
1218
+ XSetErrorHandler(NULL);
1219
+ XCloseDisplay(p_display);
1220
+ return result;
1221
+ }
1222
+
1223
+ /*
1224
+ *Requests the X server to destroy +self+.
1225
+ *===Return value
1226
+ *nil.
1227
+ *===Example
1228
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
1229
+ * xwin.kill
1230
+ *===Remarks
1231
+ *This method requests the X server to send a DestroyNotify event to
1232
+ *+self+. A window may choose not to react on this (although this is quite
1233
+ *seldom).
1234
+ */
1235
+ static VALUE m_kill(VALUE self)
1236
+ {
1237
+ Display * p_display;
1238
+ Window win = GET_WINDOW;
1239
+
1240
+ p_display = get_win_display(self);
1241
+ XSetErrorHandler(handle_x_errors);
1242
+
1243
+ XDestroyWindow(p_display, win);
1244
+
1245
+ XSetErrorHandler(NULL);
1246
+ XCloseDisplay(p_display);
1247
+ return Qnil;
1248
+ }
1249
+
1250
+ /*
1251
+ *Forces the X server to close the connection to +self+, effectively
1252
+ *destroying the window.
1253
+ *===Return value
1254
+ *nil.
1255
+ *===Example
1256
+ * Imitator::X::XWindow.from_title(/imitator/).kill!
1257
+ *===Remarks
1258
+ *From all window closing methods, this should be the most reliable one.
1259
+ *If a window's connection to the X server was closed, the window's process
1260
+ *will crash in nearly all cases. Even if not, the window is not usable anymore.
1261
+ */
1262
+ static VALUE m_bang_kill(VALUE self)
1263
+ {
1264
+ Display * p_display;
1265
+ Window win = GET_WINDOW;
1266
+
1267
+ p_display = get_win_display(self);
1268
+ XSetErrorHandler(handle_x_errors);
1269
+
1270
+ XKillClient(p_display, win);
1271
+
1272
+ XSetErrorHandler(NULL);
1273
+ XCloseDisplay(p_display);
1274
+ return Qnil;
1275
+ }
1276
+
1277
+ /*
1278
+ *call-seq:
1279
+ * kill_process( [ term = true ] ) ==> nil
1280
+ *
1281
+ *Kills a window's process.
1282
+ *===Parameters
1283
+ *[+term+] (true) If true, SIGTERM is sent to the window process; if false or nil, SIGKILL is sent.
1284
+ *===Return value
1285
+ *nil.
1286
+ *===Raises
1287
+ *[XError] _NET_WM_PID is unsupported or the target window does not have the _NET_WM_PID property set.
1288
+ *===Example
1289
+ * #Send SIGTERM to a window process
1290
+ * Imitator::X::XWindow.from_title(/imitator/).kill_process
1291
+ * #Send SIGKILL to a window process
1292
+ * Imitator::X::XWindow.from_title(/imitator/).kill_process(false)
1293
+ *===Remarks
1294
+ *This method is part of the EWMH standard _NET_WM_PID.
1295
+ *
1296
+ *This method is by far the most aggressive variant to destroy a window,
1297
+ *especially if +term+ is false, but since not every window has set the _NET_WM_PID property,
1298
+ *it may fail. See also #close, #kill and #kill!, also have a look at #pid.
1299
+ */
1300
+ static VALUE m_kill_process(int argc, VALUE argv[], VALUE self)
1301
+ {
1302
+ VALUE rterm;
1303
+ int signal = SIGTERM;
1304
+
1305
+ rb_scan_args(argc, argv, "01", &rterm);
1306
+
1307
+ if (rterm == Qfalse || rterm == Qnil)
1308
+ signal = SIGKILL;
1309
+
1310
+ kill(NUM2INT(m_pid(self)), signal);
1311
+ return Qnil;
1312
+ }
1313
+
1314
+ /*
1315
+ *Closes +self+ by activating it and then sending [ALT]+[F4].
1316
+ *===Return value
1317
+ *nil.
1318
+ *===Example
1319
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
1320
+ * xwin.close
1321
+ *===Remarks
1322
+ *This method doesn't force a window to close. If you have unsaved data,
1323
+ *a program may ask you to save instead of closing. Have a look at the
1324
+ *various kill methods to achieve this.
1325
+ */
1326
+ static VALUE m_close(VALUE self)
1327
+ {
1328
+ m_activate(self);
1329
+ rb_funcall(Keyboard, rb_intern("key"), 1, rb_enc_str_new("Alt+F4", strlen("Alt+F4"), rb_utf8_encoding()));
1330
+ return Qnil;
1331
+ }
1332
+
1333
+ /*
1334
+ *Checks weather +self+ exists or not by calling XWindow.exists? with
1335
+ *the information of this object.
1336
+ *===Return value
1337
+ *true or false.
1338
+ *===Example
1339
+ * xwin = Imitator::X::XWindow.from_title(/imitator/)
1340
+ * puts xwin.exists? #=> true
1341
+ * xwin.kill
1342
+ * puts xwin.exists? #=> false
1343
+ */
1344
+ static VALUE m_exists(VALUE self)
1345
+ {
1346
+ VALUE args[3];
1347
+ VALUE rmatch_data, screen, display;
1348
+
1349
+ rmatch_data = rb_funcall(rb_ivar_get(self, rb_intern("@display_string")), rb_intern("match"), 1, rb_reg_new("\\:(\\d+?)\\.(\\d+?)", strlen("\\:(\\d+?)\\.(\\d+?)"), 0));
1350
+ display = rb_funcall(rb_reg_nth_match(1, rmatch_data), rb_intern("to_i"), 0);
1351
+ screen = rb_funcall(rb_reg_nth_match(2, rmatch_data), rb_intern("to_i"), 0);
1352
+ args[0] = rb_ivar_get(self, rb_intern("@window_id"));
1353
+ args[1] = screen;
1354
+ args[2] = display;
1355
+ return cm_exists(3, args, XWindow);
1356
+ }
1357
+
1358
+ /*
1359
+ *call-seq:
1360
+ * xwin.eql?( other_xwin ) ==> true or false
1361
+ * xwin == other_xwin ==> true or false
1362
+ *
1363
+ *Checks wheather or not two XWindow objects are the same. Two
1364
+ *Xwindow objects are the same if they refer to the same window id.
1365
+ *===Parameters
1366
+ *[+other_xwin+] The window to compare with.
1367
+ *===Return value
1368
+ *true or false.
1369
+ *===Example
1370
+ * xwin1 = Imitator::X::XWindow.from_title(/imitator/)
1371
+ * xwin2 = Imitator::X::XWindow.from_title(/imitat/)
1372
+ * xwin3 = Imitator::X::XWindow.from_active
1373
+ * p xwin.window_id #=> 69206019
1374
+ * p xwin2.window_id #=> 69206019
1375
+ * p xwin3.window_id #=> 73401172
1376
+ * p(xwin1 == xwin2) #=> true
1377
+ * p(xwin1 == xwin3) #=> false
1378
+ */
1379
+ static VALUE m_is_equal_to(VALUE self, VALUE other)
1380
+ {
1381
+ VALUE self_id = rb_ivar_get(self, rb_intern("@window_id"));
1382
+ VALUE other_id = rb_ivar_get(other, rb_intern("@window_id"));
1383
+
1384
+ return rb_funcall(self_id, rb_intern("=="), 1, other_id);
1385
+ }
1386
+ /***********************Init-Function*******************************/
1387
+
1388
+ void Init_xwindow(void)
1389
+ {
1390
+ XWindow = rb_define_class_under(X, "XWindow", rb_cObject);
1391
+
1392
+ rb_define_singleton_method(XWindow, "default_root_window", cm_default_root_window, 0);
1393
+ rb_define_singleton_method(XWindow, "exists?", cm_exists, -1);
1394
+ rb_define_singleton_method(XWindow, "xquery", cm_xquery, 2); /* :nodoc: */
1395
+ rb_define_singleton_method(XWindow, "search", cm_search, -1);
1396
+ rb_define_singleton_method(XWindow, "from_title", cm_from_title, -1);
1397
+ rb_define_singleton_method(XWindow, "from_focused", cm_from_focused, -1);
1398
+ rb_define_singleton_method(XWindow, "from_active", cm_from_active, -1);
1399
+ rb_define_singleton_method(XWindow, "wait_for_window", cm_wait_for_window, -1);
1400
+ rb_define_singleton_method(XWindow, "wait_for_window_termination", cm_wait_for_window_termination, -1);
1401
+
1402
+ rb_define_method(XWindow, "initialize", m_initialize, -1);
1403
+ rb_define_method(XWindow, "inspect", m_inspect, 0);
1404
+ rb_define_method(XWindow, "title", m_title, 0);
1405
+ rb_define_method(XWindow, "window_id", m_window_id, 0);
1406
+ rb_define_method(XWindow, "root_win", m_root_win, 0);
1407
+ rb_define_method(XWindow, "parent", m_parent, 0);
1408
+ rb_define_method(XWindow, "children", m_children, 0);
1409
+ rb_define_method(XWindow, "root_win?", m_is_root_win, 0);
1410
+ rb_define_method(XWindow, "position", m_position, 0);
1411
+ rb_define_method(XWindow, "size", m_size, 0);
1412
+ rb_define_method(XWindow, "visible?", m_is_visible, 0);
1413
+ rb_define_method(XWindow, "mapped?", m_is_mapped, 0);
1414
+ rb_define_method(XWindow, "move", m_move, 2);
1415
+ rb_define_method(XWindow, "resize", m_resize, 2);
1416
+ rb_define_method(XWindow, "raise_win", m_raise_win, 0);
1417
+ rb_define_method(XWindow, "focus", m_focus, 0);
1418
+ rb_define_method(XWindow, "unfocus", m_unfocus, 0);
1419
+ rb_define_method(XWindow, "map", m_map, 0);
1420
+ rb_define_method(XWindow, "unmap", m_unmap, 0);
1421
+ rb_define_method(XWindow, "activate", m_activate, 0);
1422
+ rb_define_method(XWindow, "pid", m_pid, 0);
1423
+ rb_define_method(XWindow, "kill", m_kill, 0);
1424
+ rb_define_method(XWindow, "kill!", m_bang_kill, 0);
1425
+ rb_define_method(XWindow, "kill_process", m_kill_process, -1);
1426
+ rb_define_method(XWindow, "close", m_close, 0);
1427
+ rb_define_method(XWindow, "exists?", m_exists, 0);
1428
+ rb_define_method(XWindow, "eql?", m_is_equal_to, 1);
1429
+
1430
+ rb_define_alias(XWindow, "to_s", "title");
1431
+ rb_define_alias(XWindow, "to_i", "window_id");
1432
+ rb_define_alias(XWindow, "pos", "position");
1433
+ rb_define_alias(XWindow, "==", "eql?");
1434
+ }