imitator_x 0.0.1

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