source: liacs/mms/project/xdo.c@ 240

Last change on this file since 240 was 17, checked in by Rick van der Zwet, 15 years ago

Bit hacky, but uinput did not do the trick. Using the X11Test does the trick

File size: 27.4 KB
Line 
1/* xdo library
2 *
3 * $Id: xdo.c 2172 2009-03-30 02:38:06Z jordansissel $
4 *
5 * - getwindowfocus contributed by Lee Pumphret
6 * - keysequence_{up,down} contributed by Magnus Boman
7 *
8 */
9
10#define _XOPEN_SOURCE 500
11#include <sys/select.h>
12#include <stdlib.h>
13#include <stdio.h>
14#include <string.h>
15#include <strings.h>
16#include <unistd.h>
17#include <regex.h>
18
19#include <X11/Xlib.h>
20#include <X11/Xresource.h>
21#include <X11/Xutil.h>
22#include <X11/extensions/XTest.h>
23
24#include "xdo.h"
25#include "xdo_util.h"
26
27static void _xdo_populate_charcode_map(xdo_t *xdo);
28static int _xdo_has_xtest(xdo_t *xdo);
29
30static int _xdo_keycode_from_char(xdo_t *xdo, char key);
31static int _xdo_get_shiftcode_if_needed(xdo_t *xdo, char key);
32
33static void _xdo_get_child_windows(xdo_t *xdo, Window window,
34 Window **total_window_list,
35 int *ntotal_windows,
36 int *window_list_size);
37
38static int _xdo_keysequence_to_keycode_list(xdo_t *xdo, char *keyseq,
39 charcodemap_t **keys, int *nkeys);
40static int _xdo_keysequence_do(xdo_t *xdo, char *keyseq, int pressed);
41static int _xdo_regex_match_window(xdo_t *xdo, Window window, int flags, regex_t *re);
42static int _xdo_is_window_visible(xdo_t *xdo, Window wid);
43static unsigned char * _xdo_getwinprop(xdo_t *xdo, Window window, Atom atom,
44 long *nitems, Atom *type, int *size);
45static int _xdo_ewmh_is_supported(xdo_t *xdo, const char *feature);
46
47static int _is_success(const char *funcname, int code);
48
49/* context-free functions */
50static char _keysym_to_char(const char *keysym);
51
52xdo_t* xdo_new(char *display_name) {
53 Display *xdpy;
54
55 if ((xdpy = XOpenDisplay(display_name)) == NULL) {
56 fprintf(stderr, "Error: Can't open display: %s\n", display_name);
57 return NULL;
58 }
59
60 return xdo_new_with_opened_display(xdpy, display_name, 1);
61}
62
63xdo_t* xdo_new_with_opened_display(Display *xdpy, const char *display,
64 int close_display_when_freed) {
65 xdo_t *xdo = NULL;
66
67 if (xdpy == NULL) {
68 fprintf(stderr, "xdo_new: xdisplay I was given is a null pointer\n");
69 return NULL;
70 }
71
72 /* XXX: Check for NULL here */
73 xdo = malloc(sizeof(xdo_t));
74 memset(xdo, 0, sizeof(xdo_t));
75
76 xdo->xdpy = xdpy;
77 xdo->close_display_when_freed = close_display_when_freed;
78
79 if (display == NULL)
80 display = "unknown";
81
82 if (!_xdo_has_xtest(xdo)) {
83 fprintf(stderr, "Error: XTEST extension unavailable on '%s'.",
84 xdo->display_name);
85 xdo_free(xdo);
86 return NULL;
87 }
88
89 /* populate the character map? */
90 _xdo_populate_charcode_map(xdo);
91
92 return xdo;
93}
94
95void xdo_free(xdo_t *xdo) {
96 if (xdo->display_name)
97 free(xdo->display_name);
98 if (xdo->charcodes)
99 free(xdo->charcodes);
100 if (xdo->xdpy && xdo->close_display_when_freed)
101 XCloseDisplay(xdo->xdpy);
102 free(xdo);
103}
104
105int xdo_window_map(xdo_t *xdo, Window wid) {
106 int ret = 0;
107 ret = XMapWindow(xdo->xdpy, wid);
108 XFlush(xdo->xdpy);
109 return _is_success("XMapWindow", ret == 0);
110}
111
112int xdo_window_unmap(xdo_t *xdo, Window wid) {
113 int ret = 0;
114 ret = XUnmapWindow(xdo->xdpy, wid);
115 XFlush(xdo->xdpy);
116 return _is_success("XUnmapWindow", ret == 0);
117}
118
119int xdo_window_list_by_regex(xdo_t *xdo, char *regex, int flags,
120 Window **windowlist, int *nwindows) {
121 regex_t re;
122 Window *total_window_list = NULL;
123 int ntotal_windows = 0;
124 int window_list_size = 0;
125 int matched_window_list_size = 100;
126
127 int ret = 0;
128 int i = 0;
129
130 ret = regcomp(&re, regex, REG_EXTENDED | REG_ICASE);
131 if (ret != 0) {
132 fprintf(stderr, "Failed to compile regex: '%s'\n", regex);
133 return 1;
134 }
135
136 /* Default search settings:
137 * All windows (visible and hidden) and search all text pieces
138 */
139 if ((flags & (SEARCH_TITLE | SEARCH_CLASS | SEARCH_NAME)) == 0) {
140 fprintf(stderr, "No text fields specified for regex search. \nDefaulting to"
141 " window title, class, and name searching\n");
142 flags |= SEARCH_TITLE | SEARCH_CLASS | SEARCH_NAME;
143 }
144
145 *nwindows = 0;
146 *windowlist = malloc(matched_window_list_size * sizeof(Window));
147
148 _xdo_get_child_windows(xdo, RootWindow(xdo->xdpy, 0),
149 &total_window_list, &ntotal_windows,
150 &window_list_size);
151 for (i = 0; i < ntotal_windows; i++) {
152 Window wid = total_window_list[i];
153 if (flags & SEARCH_VISIBLEONLY && !_xdo_is_window_visible(xdo, wid))
154 continue;
155 if (!_xdo_regex_match_window(xdo, wid, flags, &re))
156 continue;
157
158 (*windowlist)[*nwindows] = wid;
159 (*nwindows)++;
160
161 if (matched_window_list_size == *nwindows) {
162 matched_window_list_size *= 2;
163 *windowlist = realloc(*windowlist,
164 matched_window_list_size * sizeof(Window));
165 }
166 }
167
168 regfree(&re);
169 return 0;
170}
171
172int xdo_window_move(xdo_t *xdo, Window wid, int x, int y) {
173 XWindowChanges wc;
174 int ret = 0;
175 wc.x = x;
176 wc.y = y;
177
178 ret = XConfigureWindow(xdo->xdpy, wid, CWX | CWY, &wc);
179 return _is_success("XConfigureWindow", ret == 0);
180}
181
182int xdo_window_setsize(xdo_t *xdo, Window wid, int width, int height, int flags) {
183 XWindowChanges wc;
184 int ret = 0;
185 int cw_flags = 0;
186
187 wc.width = width;
188 wc.height = height;
189
190 if (flags & SIZE_USEHINTS) {
191 XSizeHints hints;
192 long supplied_return;
193 memset(&hints, 0, sizeof(hints));
194 XGetWMNormalHints(xdo->xdpy, wid, &hints, &supplied_return);
195 if (supplied_return & PResizeInc) {
196 wc.width *= hints.width_inc;
197 wc.height *= hints.height_inc;
198 } else {
199 fprintf(stderr, "No size hints found for this window\n");
200 }
201
202 if (supplied_return & PBaseSize) {
203 wc.width += hints.base_width;
204 wc.height += hints.base_height;
205 }
206
207 }
208
209 if (width > 0)
210 cw_flags |= CWWidth;
211 if (height > 0)
212 cw_flags |= CWHeight;
213
214 ret = XConfigureWindow(xdo->xdpy, wid, cw_flags, &wc);
215 XFlush(xdo->xdpy);
216 return _is_success("XConfigureWindow", ret == 0);
217}
218
219int xdo_window_focus(xdo_t *xdo, Window wid) {
220 int ret = 0;
221 ret = XSetInputFocus(xdo->xdpy, wid, RevertToParent, CurrentTime);
222 XFlush(xdo->xdpy);
223 return _is_success("XSetInputFocus", ret == 0);
224}
225
226int xdo_window_activate(xdo_t *xdo, Window wid) {
227 int ret = 0;
228 long desktop = 0;
229 XEvent xev;
230 XWindowAttributes wattr;
231
232 if (_xdo_ewmh_is_supported(xdo, "_NET_ACTIVE_WINDOW") == False) {
233 fprintf(stderr,
234 "Your windowmanager claims not to support _NET_ACTIVE_WINDOW, "
235 "so the attempt to activate the window was aborted.\n");
236 return 1;
237 }
238
239 /* If this window is on another desktop, let's go to that desktop first */
240
241 if (_xdo_ewmh_is_supported(xdo, "_NET_WM_DESKTOP") == True
242 && _xdo_ewmh_is_supported(xdo, "_NET_CURRENT_DESKTOP") == True) {
243 xdo_get_desktop_for_window(xdo, wid, &desktop);
244 xdo_set_current_desktop(xdo, desktop);
245 }
246
247 memset(&xev, 0, sizeof(xev));
248 xev.type = ClientMessage;
249 xev.xclient.display = xdo->xdpy;
250 xev.xclient.window = wid;
251 xev.xclient.message_type = XInternAtom(xdo->xdpy, "_NET_ACTIVE_WINDOW", False);
252 xev.xclient.format = 32;
253 xev.xclient.data.l[0] = 2L; /* 2 == Message from a window pager */
254 xev.xclient.data.l[1] = CurrentTime;
255
256 XGetWindowAttributes(xdo->xdpy, wid, &wattr);
257 ret = XSendEvent(xdo->xdpy, wattr.screen->root, False,
258 SubstructureNotifyMask | SubstructureRedirectMask,
259 &xev);
260
261 /* XXX: XSendEvent returns 0 on conversion failure, nonzero otherwise.
262 * Manpage says it will only generate BadWindow or BadValue errors */
263 return _is_success("XSendEvent[EWMH:_NET_ACTIVE_WINDOW]", ret == 0);
264}
265
266int xdo_set_number_of_desktops(xdo_t *xdo, long ndesktops) {
267 /* XXX: This should support passing a screen number */
268 XEvent xev;
269 Window root;
270 int ret = 0;
271
272 if (_xdo_ewmh_is_supported(xdo, "_NET_NUMBER_OF_DESKTOPS") == False) {
273 fprintf(stderr,
274 "Your windowmanager claims not to support _NET_NUMBER_OF_DESKTOPS, "
275 "so the attempt to change the number of desktops was aborted.\n");
276 return 1;
277 }
278
279 root = RootWindow(xdo->xdpy, 0);
280
281 memset(&xev, 0, sizeof(xev));
282 xev.type = ClientMessage;
283 xev.xclient.display = xdo->xdpy;
284 xev.xclient.window = root;
285 xev.xclient.message_type = XInternAtom(xdo->xdpy, "_NET_NUMBER_OF_DESKTOPS",
286 False);
287 xev.xclient.format = 32;
288 xev.xclient.data.l[0] = ndesktops;
289
290 ret = XSendEvent(xdo->xdpy, root, False,
291 SubstructureNotifyMask | SubstructureRedirectMask,
292 &xev);
293
294 return _is_success("XSendEvent[EWMH:_NET_NUMBER_OF_DESKTOPS]", ret == 0);
295}
296
297int xdo_get_number_of_desktops(xdo_t *xdo, long *ndesktops) {
298 Atom type;
299 int size;
300 long nitems;
301 unsigned char *data;
302 Window root;
303 Atom request;
304
305 if (_xdo_ewmh_is_supported(xdo, "_NET_NUMBER_OF_DESKTOPS") == False) {
306 fprintf(stderr,
307 "Your windowmanager claims not to support _NET_NUMBER_OF_DESKTOPS, "
308 "so the attempt to query the number of desktops was aborted.\n");
309 return 1;
310 }
311
312 request = XInternAtom(xdo->xdpy, "_NET_NUMBER_OF_DESKTOPS", False);
313 root = XDefaultRootWindow(xdo->xdpy);
314
315 data = _xdo_getwinprop(xdo, root, request, &nitems, &type, &size);
316
317 if (nitems > 0)
318 *ndesktops = *((long*)data);
319 else
320 *ndesktops = 0;
321
322 return _is_success("XGetWindowProperty[_NET_NUMBER_OF_DESKTOPS]",
323 *ndesktops == 0);
324
325}
326
327int xdo_set_current_desktop(xdo_t *xdo, long desktop) {
328 /* XXX: This should support passing a screen number */
329 XEvent xev;
330 Window root;
331 int ret = 0;
332
333 root = RootWindow(xdo->xdpy, 0);
334
335 if (_xdo_ewmh_is_supported(xdo, "_NET_CURRENT_DESKTOP") == False) {
336 fprintf(stderr,
337 "Your windowmanager claims not to support _NET_CURRENT_DESKTOP, "
338 "so the attempt to change desktops was aborted.\n");
339 return 1;
340 }
341
342 memset(&xev, 0, sizeof(xev));
343 xev.type = ClientMessage;
344 xev.xclient.display = xdo->xdpy;
345 xev.xclient.window = root;
346 xev.xclient.message_type = XInternAtom(xdo->xdpy, "_NET_CURRENT_DESKTOP",
347 False);
348 xev.xclient.format = 32;
349 xev.xclient.data.l[0] = desktop;
350 xev.xclient.data.l[1] = CurrentTime;
351
352 ret = XSendEvent(xdo->xdpy, root, False,
353 SubstructureNotifyMask | SubstructureRedirectMask,
354 &xev);
355
356 return _is_success("XSendEvent[EWMH:_NET_CURRENT_DESKTOP]", ret == 0);
357}
358
359int xdo_get_current_desktop(xdo_t *xdo, long *desktop) {
360 Atom type;
361 int size;
362 long nitems;
363 unsigned char *data;
364 Window root;
365
366 Atom request;
367
368 if (_xdo_ewmh_is_supported(xdo, "_NET_CURRENT_DESKTOP") == False) {
369 fprintf(stderr,
370 "Your windowmanager claims not to support _NET_CURRENT_DESKTOP, "
371 "so the query for the current desktop was aborted.\n");
372 return 1;
373 }
374
375 request = XInternAtom(xdo->xdpy, "_NET_CURRENT_DESKTOP", False);
376 root = XDefaultRootWindow(xdo->xdpy);
377
378 data = _xdo_getwinprop(xdo, root, request, &nitems, &type, &size);
379
380 if (nitems > 0)
381 *desktop = *((long*)data);
382 else
383 *desktop = -1;
384
385 return _is_success("XGetWindowProperty[_NET_CURRENT_DESKTOP]",
386 *desktop == -1);
387}
388
389int xdo_set_desktop_for_window(xdo_t *xdo, Window wid, long desktop) {
390 XEvent xev;
391 int ret = 0;
392 XWindowAttributes wattr;
393 XGetWindowAttributes(xdo->xdpy, wid, &wattr);
394
395 if (_xdo_ewmh_is_supported(xdo, "_NET_WM_DESKTOP") == False) {
396 fprintf(stderr,
397 "Your windowmanager claims not to support _NET_WM_DESKTOP, "
398 "so the attempt to change a window's desktop location was "
399 "aborted.\n");
400 return 1;
401 }
402
403 memset(&xev, 0, sizeof(xev));
404 xev.type = ClientMessage;
405 xev.xclient.display = xdo->xdpy;
406 xev.xclient.window = wid;
407 xev.xclient.message_type = XInternAtom(xdo->xdpy, "_NET_WM_DESKTOP",
408 False);
409 xev.xclient.format = 32;
410 xev.xclient.data.l[0] = desktop;
411 xev.xclient.data.l[1] = 2; /* indicate we are messaging from a pager */
412
413 ret = XSendEvent(xdo->xdpy, wattr.screen->root, False,
414 SubstructureNotifyMask | SubstructureRedirectMask,
415 &xev);
416
417 return _is_success("XSendEvent[EWMH:_NET_WM_DESKTOP]", ret == 0);
418}
419
420int xdo_get_desktop_for_window(xdo_t *xdo, Window wid, long *desktop) {
421 Atom type;
422 int size;
423 long nitems;
424 unsigned char *data;
425 Atom request;
426
427 if (_xdo_ewmh_is_supported(xdo, "_NET_WM_DESKTOP") == False) {
428 fprintf(stderr,
429 "Your windowmanager claims not to support _NET_WM_DESKTOP, "
430 "so the attempt to query a window's desktop location was "
431 "aborted.\n");
432 return 1;
433 }
434
435 request = XInternAtom(xdo->xdpy, "_NET_WM_DESKTOP", False);
436
437 data = _xdo_getwinprop(xdo, wid, request, &nitems, &type, &size);
438
439 if (nitems > 0)
440 *desktop = *((long*)data);
441 else
442 *desktop = -1;
443
444 return _is_success("XGetWindowProperty[_NET_WM_DESKTOP]",
445 *desktop == -1);
446}
447
448int xdo_window_get_active(xdo_t *xdo, Window *window_ret) {
449 Atom type;
450 int size;
451 long nitems;
452 unsigned char *data;
453 Atom request;
454 Window root;
455
456 if (_xdo_ewmh_is_supported(xdo, "_NET_ACTIVE_WINDOW") == False) {
457 fprintf(stderr,
458 "Your windowmanager claims not to support _NET_ACTIVE_WINDOW, "
459 "so the attempt to query the active window aborted.\n");
460 return 1;
461 }
462
463 request = XInternAtom(xdo->xdpy, "_NET_ACTIVE_WINDOW", False);
464 root = XDefaultRootWindow(xdo->xdpy);
465 data = _xdo_getwinprop(xdo, root, request, &nitems, &type, &size);
466
467 if (nitems > 0)
468 *window_ret = *((Window*)data);
469 else
470 *window_ret = 0;
471
472 return _is_success("XGetWindowProperty[_NET_ACTIVE_WINDOW]",
473 *window_ret == 0);
474}
475
476/* XRaiseWindow is ignored in ion3 and Gnome2. Is it even useful? */
477int xdo_window_raise(xdo_t *xdo, Window wid) {
478 int ret = 0;
479 ret = XRaiseWindow(xdo->xdpy, wid);
480 XFlush(xdo->xdpy);
481 return _is_success("XRaiseWindow", ret == 0);
482}
483
484/* XXX: Include 'screen number' support? */
485int xdo_mousemove(xdo_t *xdo, int x, int y) {
486 int ret = 0;
487 ret = XTestFakeMotionEvent(xdo->xdpy, -1, x, y, CurrentTime);
488 XFlush(xdo->xdpy);
489 return _is_success("XTestFakeMotionEvent", ret == 0);
490}
491
492int xdo_mousemove_relative(xdo_t *xdo, int x, int y) {
493 int ret = 0;
494 ret = XTestFakeRelativeMotionEvent(xdo->xdpy, x, y, CurrentTime);
495 XFlush(xdo->xdpy);
496 return _is_success("XTestFakeRelativeMotionEvent", ret == 0);
497}
498
499int xdo_mousedown(xdo_t *xdo, int button) {
500 int ret = 0;
501 ret = XTestFakeButtonEvent(xdo->xdpy, button, True, CurrentTime);
502 XFlush(xdo->xdpy);
503 return _is_success("XTestFakeButtonEvent(down)", ret == 0);
504}
505
506int xdo_mouseup(xdo_t *xdo, int button) {
507 int ret = 0;
508 ret = XTestFakeButtonEvent(xdo->xdpy, button, False, CurrentTime);
509 XFlush(xdo->xdpy);
510 return _is_success("XTestFakeButtonEvent(up)", ret == 0);
511}
512
513int xdo_mouselocation(xdo_t *xdo, int *x_ret, int *y_ret, int *screen_num_ret) {
514 int ret = False;
515 int x = 0, y = 0, screen_num = 0;
516 int i = 0;
517 Window dummy_win = 0;
518 int dummy_int = 0;
519 unsigned int dummy_uint = 0;
520 int screencount = ScreenCount(xdo->xdpy);
521
522 for (i = 0; i < screencount; i++) {
523 Screen *screen = ScreenOfDisplay(xdo->xdpy, i);
524 ret = XQueryPointer(xdo->xdpy, RootWindowOfScreen(screen),
525 &dummy_win, &dummy_win,
526 &x, &y, &dummy_int, &dummy_int, &dummy_uint);
527 if (ret == True) {
528 screen_num = i;
529 break;
530 }
531 }
532
533 if (ret == True) {
534 if (x_ret != NULL) *x_ret = x;
535 if (y_ret != NULL) *y_ret = y;
536 if (screen_num_ret != NULL) *screen_num_ret = screen_num;
537 }
538
539 return _is_success("XQueryPointer", ret == False);
540
541}
542
543int xdo_click(xdo_t *xdo, int button) {
544 int ret = 0;
545 ret = xdo_mousedown(xdo, button);
546 if (ret)
547 return ret;
548 ret = xdo_mouseup(xdo, button);
549 return ret;
550}
551
552/* XXX: Return proper code if errors found */
553int xdo_type(xdo_t *xdo, char *string) {
554 int i = 0;
555 char key = '0';
556 int keycode = 0;
557 int shiftcode = 0;
558
559 /* XXX: Add error handling */
560 for (i = 0; string[i] != '\0'; i++) {
561 key = string[i];
562 keycode = _xdo_keycode_from_char(xdo, key);
563 shiftcode = _xdo_get_shiftcode_if_needed(xdo, key);
564
565 if (shiftcode > 0)
566 XTestFakeKeyEvent(xdo->xdpy, shiftcode, True, CurrentTime);
567 XTestFakeKeyEvent(xdo->xdpy, keycode, True, CurrentTime);
568 XTestFakeKeyEvent(xdo->xdpy, keycode, False, CurrentTime);
569 if (shiftcode > 0)
570 XTestFakeKeyEvent(xdo->xdpy, shiftcode, False, CurrentTime);
571
572 /* XXX: Flush here or at the end? */
573 XFlush(xdo->xdpy);
574 }
575
576 return 0;
577}
578
579int _xdo_keysequence_do(xdo_t *xdo, char *keyseq, int pressed) {
580 int ret = 0;
581 charcodemap_t *keys = NULL;
582 int nkeys = 0;
583 int i;
584 KeyCode shiftcode;
585
586 if (_xdo_keysequence_to_keycode_list(xdo, keyseq, &keys, &nkeys) == False) {
587 fprintf(stderr, "Failure converting key sequence '%s' to keycodes\n", keyseq);
588 return False;
589 }
590
591 /* If shiftcode is necessary, send shift before the key if pressed is true,
592 * otherwise release the shift key after the key is released */
593 for (i = 0; i < nkeys; i++) {
594 shiftcode = keys[i].shift;
595
596 if (pressed > 0 && shiftcode)
597 ret += !XTestFakeKeyEvent(xdo->xdpy, shiftcode, pressed, CurrentTime);
598
599 ret += !XTestFakeKeyEvent(xdo->xdpy, keys[i].code, pressed, CurrentTime);
600
601 if (pressed == 0 && shiftcode)
602 ret += !XTestFakeKeyEvent(xdo->xdpy, shiftcode, pressed, CurrentTime);
603 }
604
605 if (keys != NULL) {
606 free(keys);
607 }
608 XFlush(xdo->xdpy);
609 return ret;
610}
611
612int xdo_keysequence_down(xdo_t *xdo, char *keyseq) {
613 return _xdo_keysequence_do(xdo, keyseq, True);
614}
615
616int xdo_keysequence_up(xdo_t *xdo, char *keyseq) {
617 return _xdo_keysequence_do(xdo, keyseq, False);
618}
619
620int xdo_keysequence(xdo_t *xdo, char *keyseq) {
621 int ret = 0;
622 ret += _xdo_keysequence_do(xdo, keyseq, True);
623 ret += _xdo_keysequence_do(xdo, keyseq, False);
624 return ret;
625}
626
627/* Add by Lee Pumphret 2007-07-28
628 * Modified slightly by Jordan Sissel */
629int xdo_window_get_focus(xdo_t *xdo, Window *window_ret) {
630 int ret = 0;
631 int unused_revert_ret;
632
633 ret = XGetInputFocus(xdo->xdpy, window_ret, &unused_revert_ret);
634 return _is_success("XGetInputFocus", ret == 0);
635}
636
637/* Like xdo_window_get_focus, but return the first ancestor-or-self window
638 * having a property of WM_CLASS. This allows you to get the "real" or
639 * top-level-ish window having focus rather than something you may
640 * not expect to be the window having focused. */
641int xdo_window_sane_get_focus(xdo_t *xdo, Window *window_ret) {
642 int done = 0;
643 Window w;
644 XClassHint classhint;
645
646 /* for XQueryTree */
647 Window dummy, parent, *children = NULL;
648 unsigned int nchildren;
649
650 xdo_window_get_focus(xdo, &w);
651
652 while (!done) {
653 Status s;
654 s = XGetClassHint(xdo->xdpy, w, &classhint);
655 //fprintf(stderr, "%d\n", s);
656
657 if (s == 0) {
658 /* Error. This window doesn't have a class hint */
659 //fprintf(stderr, "no class on: %d\n", w);
660 XQueryTree(xdo->xdpy, w, &dummy, &parent, &children, &nchildren);
661
662 /* Don't care about the children, but we still need to free them */
663 if (children != NULL)
664 XFree(children);
665 //fprintf(stderr, "parent: %d\n", parent);
666 w = parent;
667 } else {
668 /* Must XFree the class and name items. */
669 XFree(classhint.res_class);
670 XFree(classhint.res_name);
671
672 done = 1;
673 }
674 }
675
676 *window_ret = w;
677 return _is_success("xdo_window_sane_get_focus", w == 0);
678}
679
680/* Helper functions */
681static int _xdo_keycode_from_char(xdo_t *xdo, char key) {
682 int i = 0;
683 int len = xdo->keycode_high - xdo->keycode_low;
684
685 for (i = 0; i < len; i++)
686 if (xdo->charcodes[i].key == key)
687 return xdo->charcodes[i].code;
688
689 return -1;
690}
691
692static int _xdo_get_shiftcode_if_needed(xdo_t *xdo, char key) {
693 int i = 0;
694 int len = xdo->keycode_high - xdo->keycode_low;
695
696 for (i = 0; i < len; i++)
697 if (xdo->charcodes[i].key == key)
698 return xdo->charcodes[i].shift;
699
700 return -1;
701}
702
703static int _xdo_has_xtest(xdo_t *xdo) {
704 int dummy;
705 return (XTestQueryExtension(xdo->xdpy, &dummy, &dummy, &dummy, &dummy) == True);
706}
707
708static void _xdo_populate_charcode_map(xdo_t *xdo) {
709 /* assert xdo->display is valid */
710 int keycodes_length = 0;
711 int shift_keycode = 0;
712 int i, j;
713
714 XDisplayKeycodes(xdo->xdpy, &(xdo->keycode_low), &(xdo->keycode_high));
715
716 /* Double size of keycode range because some
717 * keys have "shift" values. ie; 'a' and 'A', '2' and '@' */
718 /* Add 2 to the size because the range [low, high] is inclusive */
719 keycodes_length = (xdo->keycode_high - xdo->keycode_low) * 2 + 2;
720 xdo->charcodes = malloc(keycodes_length * sizeof(charcodemap_t));
721 memset(xdo->charcodes, 0, keycodes_length * sizeof(charcodemap_t));
722
723 /* Fetch the keycode for Shift_L */
724 /* XXX: Make 'Shift_L' configurable? */
725 shift_keycode = XKeysymToKeycode(xdo->xdpy, XStringToKeysym("Shift_L"));
726
727 for (i = xdo->keycode_low; i <= xdo->keycode_high; i++) {
728 char *keybuf = 0;
729
730 /* Index '0' in KeycodeToKeysym == no shift key
731 * Index '1' in ... == shift key held
732 * hence this little loop. */
733 for (j = 0; j <= 1; j++) {
734 int idx = (i - xdo->keycode_low) * 2 + j;
735 keybuf = XKeysymToString(XKeycodeToKeysym(xdo->xdpy, i, j));
736
737 xdo->charcodes[idx].key = _keysym_to_char(keybuf);
738 xdo->charcodes[idx].code = i;
739 xdo->charcodes[idx].shift = j ? shift_keycode : 0;
740 }
741 }
742}
743
744/* context-free functions */
745char _keysym_to_char(const char *keysym) {
746 int i;
747
748 if (keysym == NULL)
749 return -1;
750
751 /* keysymcharmap comes from xdo_util.h */
752 for (i = 0; keysymcharmap[i].keysym; i++) {
753 if (!strcmp(keysymcharmap[i].keysym, keysym))
754 return keysymcharmap[i].key;
755 }
756
757 if (strlen(keysym) == 1)
758 return keysym[0];
759
760 return -1;
761}
762
763 /* regexec(&re, string, 0, NULL, 0) == 0 means MATCH */
764
765static void _xdo_get_child_windows(xdo_t *xdo, Window window,
766 Window **total_window_list,
767 int *ntotal_windows,
768 int *window_list_size) {
769 Window dummy;
770 Window *children;
771 unsigned int i, nchildren;
772
773 if (*window_list_size == 0) {
774 *ntotal_windows = 0;
775 *window_list_size = 100;
776 *total_window_list = malloc(*window_list_size * sizeof(Window));
777 }
778
779 if (!XQueryTree(xdo->xdpy, window, &dummy, &dummy, &children, &nchildren))
780 return;
781
782 for (i = 0; i < nchildren; i++) {
783 Window w = children[i];
784 (*total_window_list)[*ntotal_windows] = w;
785 *ntotal_windows += 1;
786 if (*ntotal_windows == *window_list_size) {
787 *window_list_size *= 2;
788 *total_window_list = realloc(*total_window_list,
789 *window_list_size * sizeof(Window));
790 }
791 _xdo_get_child_windows(xdo, w, total_window_list,
792 ntotal_windows, window_list_size);
793 }
794
795 XFree(children);
796}
797
798int _xdo_keysequence_to_keycode_list(xdo_t *xdo, char *keyseq,
799 charcodemap_t **keys, int *nkeys) {
800 char *tokctx = NULL;
801 const char *tok = NULL;
802 char *keyseq_copy = NULL, *strptr = NULL;
803 int i;
804 KeyCode shift_keycode;
805
806 /* Array of keys to press, in order given by keyseq */
807 int keys_size = 10;
808 *nkeys = 0;
809
810 if (strcspn(keyseq, " \t\n.-[]{}\\|") != strlen(keyseq)) {
811 fprintf(stderr, "Error: Invalid key sequence '%s'\n", keyseq);
812 return False;
813 }
814
815 shift_keycode = XKeysymToKeycode(xdo->xdpy, XStringToKeysym("Shift_L"));
816
817 *keys = malloc(keys_size * sizeof(charcodemap_t));
818 keyseq_copy = strptr = strdup(keyseq);
819 while ((tok = strtok_r(strptr, "+", &tokctx)) != NULL) {
820 KeySym sym;
821 KeyCode key;
822
823 if (strptr != NULL)
824 strptr = NULL;
825
826 /* Check if 'tok' (string keysym) is an alias to another key */
827 /* symbol_map comes from xdo.util */
828 for (i = 0; symbol_map[i] != NULL; i+=2)
829 if (!strcasecmp(tok, symbol_map[i]))
830 tok = symbol_map[i + 1];
831
832 sym = XStringToKeysym(tok);
833 if (sym == NoSymbol) {
834 fprintf(stderr, "(symbol) No such key name '%s'. Ignoring it.\n", tok);
835 continue;
836 }
837
838 key = XKeysymToKeycode(xdo->xdpy, sym);
839 (*keys)[*nkeys].code = key;
840 if (XKeycodeToKeysym(xdo->xdpy, key, 0) == sym) {
841 (*keys)[*nkeys].shift = 0;
842 } else {
843 (*keys)[*nkeys].shift = shift_keycode;
844 }
845
846 if ((*keys)[*nkeys].code == 0) {
847 fprintf(stderr, "No such key '%s'. Ignoring it.\n", tok);
848 continue;
849 }
850
851 (*nkeys)++;
852 if (*nkeys == keys_size) {
853 keys_size *= 2;
854 *keys = realloc(*keys, keys_size * sizeof(KeyCode));
855 }
856 }
857
858 free(keyseq_copy);
859
860 return True;
861}
862
863int _xdo_regex_match_window(xdo_t *xdo, Window window, int flags, regex_t *re) {
864 XWindowAttributes attr;
865 XTextProperty tp;
866 XClassHint classhint;
867 int i;
868
869 XGetWindowAttributes(xdo->xdpy, window, &attr);
870
871 /* XXX: Memory leak here according to valgrind? */
872 XGetWMName(xdo->xdpy, window, &tp);
873
874 if (flags & SEARCH_TITLE) {
875 if (tp.nitems > 0) {
876 int count = 0;
877 char **list = NULL;
878 XmbTextPropertyToTextList(xdo->xdpy, &tp, &list, &count);
879 for (i = 0; i < count; i++) {
880 if (regexec(re, list[i], 0, NULL, 0) == 0) {
881 XFreeStringList(list);
882 return True;
883 }
884 XFreeStringList(list);
885 }
886 }
887 }
888
889 if (XGetClassHint(xdo->xdpy, window, &classhint)) {
890 if ((flags & SEARCH_NAME) && classhint.res_name) {
891 if (regexec(re, classhint.res_name, 0, NULL, 0) == 0) {
892 XFree(classhint.res_name);
893 XFree(classhint.res_class);
894 return True;
895 }
896 XFree(classhint.res_name);
897 }
898 if ((flags & SEARCH_CLASS) && classhint.res_class) {
899 if (regexec(re, classhint.res_class, 0, NULL, 0) == 0) {
900 XFree(classhint.res_class);
901 return True;
902 }
903 XFree(classhint.res_class);
904 }
905 }
906 return False;
907}
908
909int _is_success(const char *funcname, int code) {
910 if (code != 0)
911 fprintf(stderr, "%s failed (code=%d)\n", funcname, code);
912 return code;
913}
914
915int _xdo_is_window_visible(xdo_t *xdo, Window wid) {
916 XWindowAttributes wattr;
917
918 XGetWindowAttributes(xdo->xdpy, wid, &wattr);
919 if (wattr.map_state != IsViewable)
920 return False;
921
922 return True;
923}
924
925/* Arbitrary window property retrieval
926 * slightly modified version from xprop.c from Xorg */
927unsigned char * _xdo_getwinprop(xdo_t *xdo, Window window, Atom atom,
928 long *nitems, Atom *type, int *size) {
929 Atom actual_type;
930 int actual_format;
931 unsigned long _nitems;
932 unsigned long nbytes;
933 unsigned long bytes_after; /* unused */
934 unsigned char *prop;
935 int status;
936
937 status = XGetWindowProperty(xdo->xdpy, window, atom, 0, (~0L),
938 False, AnyPropertyType, &actual_type,
939 &actual_format, &_nitems, &bytes_after,
940 &prop);
941 if (status == BadWindow) {
942 fprintf(stderr, "window id # 0x%lx does not exists!", window);
943 return NULL;
944 } if (status != Success) {
945 fprintf(stderr, "XGetWindowProperty failed!");
946 return NULL;
947 }
948
949 if (actual_format == 32)
950 nbytes = sizeof(long);
951 else if (actual_format == 16)
952 nbytes = sizeof(short);
953 else if (actual_format == 8)
954 nbytes = 1;
955 else if (actual_format == 0)
956 nbytes = 0;
957
958 *nitems = _nitems;
959 *type = actual_type;
960 *size = actual_format;
961 return prop;
962}
963
964int _xdo_ewmh_is_supported(xdo_t *xdo, const char *feature) {
965 Atom type = 0;
966 long nitems = 0L;
967 int size = 0;
968 Atom *results = NULL;
969 long i = 0;
970
971 Window root;
972 Atom request;
973 Atom feature_atom;
974
975 request = XInternAtom(xdo->xdpy, "_NET_SUPPORTED", False);
976 feature_atom = XInternAtom(xdo->xdpy, feature, False);
977 root = RootWindow(xdo->xdpy, 0);
978
979 results = (Atom *) _xdo_getwinprop(xdo, root, request, &nitems, &type, &size);
980 for (i = 0L; i < nitems; i++) {
981 if (results[i] == feature_atom)
982 return True;
983 }
984
985 return False;
986}
Note: See TracBrowser for help on using the repository browser.