root/trunk/xdotool/xdo.c

Revision 1105, 25.3 kB (checked in by dkg, 6 months ago)

moving to 20080716-1

Line 
1/* xdo library
2 *
3 * $Id: xdo.c 1954 2008-07-16 23:21:28Z 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, int **keys, int *nkeys);
39static int _xdo_keysequence_do(xdo_t *xdo, char *keyseq, int pressed);
40static int _xdo_regex_match_window(xdo_t *xdo, Window window, int flags, regex_t *re);
41static int _xdo_is_window_visible(xdo_t *xdo, Window wid);
42static unsigned char * _xdo_getwinprop(xdo_t *xdo, Window window, Atom atom,
43                                       long *nitems, Atom *type, int *size);
44static int _xdo_ewmh_is_supported(xdo_t *xdo, const char *feature);
45
46static int _is_success(const char *funcname, int code);
47
48/* context-free functions */
49char _keysym_to_char(char *keysym);
50
51xdo_t* xdo_new(char *display_name) {
52  Display *xdpy;
53
54  if ((xdpy = XOpenDisplay(display_name)) == NULL) {
55    fprintf(stderr, "Error: Can't open display: %s\n", display_name);
56    return NULL;
57  }
58
59  return xdo_new_with_opened_display(xdpy, display_name, 1);
60}
61
62xdo_t* xdo_new_with_opened_display(Display *xdpy, const char *display,
63                                   int close_display_when_freed) {
64  xdo_t *xdo = NULL;
65
66  if (xdpy == NULL) {
67    fprintf(stderr, "xdo_new: xdisplay I was given is a null pointer\n");
68    return NULL;
69  }
70
71  /* XXX: Check for NULL here */
72  xdo = malloc(sizeof(xdo_t));
73  memset(xdo, 0, sizeof(xdo_t));
74
75  xdo->xdpy = xdpy;
76  xdo->close_display_when_freed = close_display_when_freed;
77
78  if (display == NULL)
79    display = "unknown";
80
81  if (!_xdo_has_xtest(xdo)) {
82    fprintf(stderr, "Error: XTEST extension unavailable on '%s'.", 
83            xdo->display_name);
84    xdo_free(xdo);
85    return NULL;
86  }
87
88  /* populate the character map? */
89  _xdo_populate_charcode_map(xdo);
90
91  return xdo;
92}
93
94void xdo_free(xdo_t *xdo) {
95  if (xdo->display_name)
96    free(xdo->display_name);
97  if (xdo->charcodes)
98    free(xdo->charcodes);
99  if (xdo->xdpy && xdo->close_display_when_freed)
100    XCloseDisplay(xdo->xdpy);
101  free(xdo);
102}
103
104int xdo_window_map(xdo_t *xdo, Window wid) {
105  int ret = 0;
106  ret = XMapWindow(xdo->xdpy, wid);
107  XFlush(xdo->xdpy);
108  return _is_success("XMapWindow", ret == 0);
109}
110
111int xdo_window_unmap(xdo_t *xdo, Window wid) {
112  int ret = 0;
113  ret = XUnmapWindow(xdo->xdpy, wid);
114  XFlush(xdo->xdpy);
115  return _is_success("XUnmapWindow", ret == 0);
116}
117
118int xdo_window_list_by_regex(xdo_t *xdo, char *regex, int flags,
119                              Window **windowlist, int *nwindows) {
120  regex_t re;
121  Window *total_window_list = NULL;
122  int ntotal_windows = 0;
123  int window_list_size = 0;
124  int matched_window_list_size = 100;
125
126  int ret = 0;
127  int i = 0;
128
129  ret = regcomp(&re, regex, REG_EXTENDED | REG_ICASE);
130  if (ret != 0) {
131    fprintf(stderr, "Failed to compile regex: '%s'\n", regex);
132    return 1;
133  }
134
135  /* Default search settings:
136   * All windows (visible and hidden) and search all text pieces
137   */
138  if ((flags & (SEARCH_TITLE | SEARCH_CLASS | SEARCH_NAME)) == 0) {
139    fprintf(stderr, "No text fields specified for regex search. \nDefaulting to"
140            " window title, class, and name searching\n");
141    flags |= SEARCH_TITLE | SEARCH_CLASS | SEARCH_NAME;
142  }
143
144  *nwindows = 0;
145  *windowlist = malloc(matched_window_list_size * sizeof(Window));
146
147  _xdo_get_child_windows(xdo, RootWindow(xdo->xdpy, 0),
148                         &total_window_list, &ntotal_windows,
149                         &window_list_size);
150  for (i = 0; i < ntotal_windows; i++) {
151    Window wid = total_window_list[i];
152    if (flags & SEARCH_VISIBLEONLY && !_xdo_is_window_visible(xdo, wid))
153      continue;
154    if (!_xdo_regex_match_window(xdo, wid, flags, &re))
155      continue;
156
157    (*windowlist)[*nwindows] = wid;
158    (*nwindows)++;
159
160    if (matched_window_list_size == *nwindows) {
161      matched_window_list_size *= 2;
162      *windowlist = realloc(*windowlist, 
163                            matched_window_list_size * sizeof(Window));
164    }
165  }
166
167  regfree(&re);
168  return 0;
169}
170
171int xdo_window_move(xdo_t *xdo, Window wid, int x, int y) {
172  XWindowChanges wc;
173  int ret = 0;
174  wc.x = x;
175  wc.y = y;
176
177  ret = XConfigureWindow(xdo->xdpy, wid, CWX | CWY, &wc);
178  return _is_success("XConfigureWindow", ret == 0);
179}
180
181int xdo_window_setsize(xdo_t *xdo, Window wid, int width, int height, int flags) {
182  XWindowChanges wc;
183  int ret = 0;
184  int cw_flags = 0;
185
186  wc.width = width;
187  wc.height = height;
188
189  if (flags & SIZE_USEHINTS) {
190    XSizeHints hints;
191    long supplied_return;
192    memset(&hints, 0, sizeof(hints));
193    XGetWMNormalHints(xdo->xdpy, wid, &hints, &supplied_return);
194    if (supplied_return & PResizeInc) {
195      wc.width *= hints.width_inc;
196      wc.height *= hints.height_inc;
197    } else {
198      fprintf(stderr, "No size hints found for this window\n");
199    }
200
201    if (supplied_return & PBaseSize) {
202      wc.width += hints.base_width;
203      wc.height += hints.base_height;
204    }
205
206  }
207
208  if (width > 0)
209    cw_flags |= CWWidth;
210  if (height > 0)
211    cw_flags |= CWHeight;
212
213  ret = XConfigureWindow(xdo->xdpy, wid, cw_flags, &wc);
214  XFlush(xdo->xdpy);
215  return _is_success("XConfigureWindow", ret == 0);
216}
217
218int xdo_window_focus(xdo_t *xdo, Window wid) {
219  int ret = 0;
220  ret = XSetInputFocus(xdo->xdpy, wid, RevertToParent, CurrentTime);
221  XFlush(xdo->xdpy);
222  return _is_success("XSetInputFocus", ret == 0);
223}
224
225int xdo_window_activate(xdo_t *xdo, Window wid) {
226  int ret = 0;
227  long desktop = 0;
228  XEvent xev;
229  XWindowAttributes wattr;
230
231  if (_xdo_ewmh_is_supported(xdo, "_NET_ACTIVE_WINDOW") == False) {
232    fprintf(stderr,
233            "Your windowmanager claims not to support _NET_ACTIVE_WINDOW, "
234            "so the attempt to activate the window was aborted.\n");
235    return 1;
236  }
237
238  /* If this window is on another desktop, let's go to that desktop first */
239
240  if (_xdo_ewmh_is_supported(xdo, "_NET_WM_DESKTOP") == True
241      && _xdo_ewmh_is_supported(xdo, "_NET_CURRENT_DESKTOP") == True) {
242    xdo_get_desktop_for_window(xdo, wid, &desktop);
243    xdo_set_current_desktop(xdo, desktop);
244  }
245
246  memset(&xev, 0, sizeof(xev));
247  xev.type = ClientMessage;
248  xev.xclient.display = xdo->xdpy;
249  xev.xclient.window = wid;
250  xev.xclient.message_type = XInternAtom(xdo->xdpy, "_NET_ACTIVE_WINDOW", False);
251  xev.xclient.format = 32;
252  xev.xclient.data.l[0] = 2L; /* 2 == Message from a window pager */
253  xev.xclient.data.l[1] = CurrentTime;
254
255  XGetWindowAttributes(xdo->xdpy, wid, &wattr);
256  ret = XSendEvent(xdo->xdpy, wattr.screen->root, False,
257                   SubstructureNotifyMask | SubstructureRedirectMask,
258                   &xev);
259
260  /* XXX: XSendEvent returns 0 on conversion failure, nonzero otherwise.
261   * Manpage says it will only generate BadWindow or BadValue errors */
262  return _is_success("XSendEvent[EWMH:_NET_ACTIVE_WINDOW]", ret == 0);
263}
264
265int xdo_set_number_of_desktops(xdo_t *xdo, long ndesktops) {
266  /* XXX: This should support passing a screen number */
267  XEvent xev;
268  Window root;
269  int ret = 0;
270
271  if (_xdo_ewmh_is_supported(xdo, "_NET_NUMBER_OF_DESKTOPS") == False) {
272    fprintf(stderr,
273            "Your windowmanager claims not to support _NET_NUMBER_OF_DESKTOPS, "
274            "so the attempt to change the number of desktops was aborted.\n");
275    return 1;
276  }
277
278  root = RootWindow(xdo->xdpy, 0);
279
280  memset(&xev, 0, sizeof(xev));
281  xev.type = ClientMessage;
282  xev.xclient.display = xdo->xdpy;
283  xev.xclient.window = root;
284  xev.xclient.message_type = XInternAtom(xdo->xdpy, "_NET_NUMBER_OF_DESKTOPS", 
285                                         False);
286  xev.xclient.format = 32;
287  xev.xclient.data.l[0] = ndesktops;
288
289  ret = XSendEvent(xdo->xdpy, root, False,
290                   SubstructureNotifyMask | SubstructureRedirectMask,
291                   &xev);
292
293  return _is_success("XSendEvent[EWMH:_NET_NUMBER_OF_DESKTOPS]", ret == 0);
294}
295
296int xdo_get_number_of_desktops(xdo_t *xdo, long *ndesktops) {
297  Atom type;
298  int size;
299  long nitems;
300  unsigned char *data;
301  Window root;
302  Atom request;
303
304  if (_xdo_ewmh_is_supported(xdo, "_NET_NUMBER_OF_DESKTOPS") == False) {
305    fprintf(stderr,
306            "Your windowmanager claims not to support _NET_NUMBER_OF_DESKTOPS, "
307            "so the attempt to query the number of desktops was aborted.\n");
308    return 1;
309  }
310
311  request = XInternAtom(xdo->xdpy, "_NET_NUMBER_OF_DESKTOPS", False);
312  root = XDefaultRootWindow(xdo->xdpy);
313
314  data = _xdo_getwinprop(xdo, root, request, &nitems, &type, &size);
315
316  if (nitems > 0)
317    *ndesktops = *((long*)data);
318  else
319    *ndesktops = 0;
320
321  return _is_success("XGetWindowProperty[_NET_NUMBER_OF_DESKTOPS]",
322                     *ndesktops == 0);
323
324}
325
326int xdo_set_current_desktop(xdo_t *xdo, long desktop) {
327  /* XXX: This should support passing a screen number */
328  XEvent xev;
329  Window root;
330  int ret = 0;
331
332  root = RootWindow(xdo->xdpy, 0);
333
334  if (_xdo_ewmh_is_supported(xdo, "_NET_CURRENT_DESKTOP") == False) {
335    fprintf(stderr,
336            "Your windowmanager claims not to support _NET_CURRENT_DESKTOP, "
337            "so the attempt to change desktops was aborted.\n");
338    return 1;
339  }
340
341  memset(&xev, 0, sizeof(xev));
342  xev.type = ClientMessage;
343  xev.xclient.display = xdo->xdpy;
344  xev.xclient.window = root;
345  xev.xclient.message_type = XInternAtom(xdo->xdpy, "_NET_CURRENT_DESKTOP", 
346                                         False);
347  xev.xclient.format = 32;
348  xev.xclient.data.l[0] = desktop;
349  xev.xclient.data.l[1] = CurrentTime;
350
351  ret = XSendEvent(xdo->xdpy, root, False,
352                   SubstructureNotifyMask | SubstructureRedirectMask,
353                   &xev);
354
355  return _is_success("XSendEvent[EWMH:_NET_CURRENT_DESKTOP]", ret == 0);
356}
357
358int xdo_get_current_desktop(xdo_t *xdo, long *desktop) {
359  Atom type;
360  int size;
361  long nitems;
362  unsigned char *data;
363  Window root;
364
365  Atom request;
366
367  if (_xdo_ewmh_is_supported(xdo, "_NET_CURRENT_DESKTOP") == False) {
368    fprintf(stderr,
369            "Your windowmanager claims not to support _NET_CURRENT_DESKTOP, "
370            "so the query for the current desktop was aborted.\n");
371    return 1;
372  }
373
374  request = XInternAtom(xdo->xdpy, "_NET_CURRENT_DESKTOP", False);
375  root = XDefaultRootWindow(xdo->xdpy);
376
377  data = _xdo_getwinprop(xdo, root, request, &nitems, &type, &size);
378
379  if (nitems > 0)
380    *desktop = *((long*)data);
381  else
382    *desktop = -1;
383
384  return _is_success("XGetWindowProperty[_NET_CURRENT_DESKTOP]",
385                     *desktop == -1);
386}
387
388int xdo_set_desktop_for_window(xdo_t *xdo, Window wid, long desktop) {
389  XEvent xev;
390  int ret = 0;
391  XWindowAttributes wattr;
392  XGetWindowAttributes(xdo->xdpy, wid, &wattr);
393
394  if (_xdo_ewmh_is_supported(xdo, "_NET_WM_DESKTOP") == False) {
395    fprintf(stderr,
396            "Your windowmanager claims not to support _NET_WM_DESKTOP, "
397            "so the attempt to change a window's desktop location was "
398            "aborted.\n");
399    return 1;
400  }
401
402  memset(&xev, 0, sizeof(xev));
403  xev.type = ClientMessage;
404  xev.xclient.display = xdo->xdpy;
405  xev.xclient.window = wid;
406  xev.xclient.message_type = XInternAtom(xdo->xdpy, "_NET_WM_DESKTOP", 
407                                         False);
408  xev.xclient.format = 32;
409  xev.xclient.data.l[0] = desktop;
410  xev.xclient.data.l[1] = 2; /* indicate we are messaging from a pager */
411
412  ret = XSendEvent(xdo->xdpy, wattr.screen->root, False,
413                   SubstructureNotifyMask | SubstructureRedirectMask,
414                   &xev);
415
416  return _is_success("XSendEvent[EWMH:_NET_WM_DESKTOP]", ret == 0);
417}
418
419int xdo_get_desktop_for_window(xdo_t *xdo, Window wid, long *desktop) {
420  Atom type;
421  int size;
422  long nitems;
423  unsigned char *data;
424  Atom request;
425
426  if (_xdo_ewmh_is_supported(xdo, "_NET_WM_DESKTOP") == False) {
427    fprintf(stderr,
428            "Your windowmanager claims not to support _NET_WM_DESKTOP, "
429            "so the attempt to query a window's desktop location was "
430            "aborted.\n");
431    return 1;
432  }
433
434  request = XInternAtom(xdo->xdpy, "_NET_WM_DESKTOP", False);
435
436  data = _xdo_getwinprop(xdo, wid, request, &nitems, &type, &size);
437
438  if (nitems > 0)
439    *desktop = *((long*)data);
440  else
441    *desktop = -1;
442
443  return _is_success("XGetWindowProperty[_NET_WM_DESKTOP]",
444                     *desktop == -1);
445}
446
447int xdo_window_get_active(xdo_t *xdo, Window *window_ret) {
448  Atom type;
449  int size;
450  long nitems;
451  unsigned char *data;
452  Atom request;
453  Window root;
454
455  if (_xdo_ewmh_is_supported(xdo, "_NET_ACTIVE_WINDOW") == False) {
456    fprintf(stderr,
457            "Your windowmanager claims not to support _NET_ACTIVE_WINDOW, "
458            "so the attempt to query the active window aborted.\n");
459    return 1;
460  }
461
462  request = XInternAtom(xdo->xdpy, "_NET_ACTIVE_WINDOW", False);
463  root = XDefaultRootWindow(xdo->xdpy);
464  data = _xdo_getwinprop(xdo, root, request, &nitems, &type, &size);
465
466  if (nitems > 0)
467    *window_ret = *((Window*)data);
468  else
469    *window_ret = 0;
470
471  return _is_success("XGetWindowProperty[_NET_ACTIVE_WINDOW]",
472                     *window_ret == 0);
473}
474
475/* XRaiseWindow is ignored in ion3 and Gnome2. Is it even useful? */
476int xdo_window_raise(xdo_t *xdo, Window wid) {
477  int ret = 0;
478  ret = XRaiseWindow(xdo->xdpy, wid);
479  XFlush(xdo->xdpy);
480  return _is_success("XRaiseWindow", ret == 0);
481}
482
483/* XXX: Include 'screen number' support? */
484int xdo_mousemove(xdo_t *xdo, int x, int y)  {
485  int ret = 0;
486  ret = XTestFakeMotionEvent(xdo->xdpy, -1, x, y, CurrentTime);
487  XFlush(xdo->xdpy);
488  return _is_success("XTestFakeMotionEvent", ret == 0);
489}
490
491int xdo_mousemove_relative(xdo_t *xdo, int x, int y)  {
492  int ret = 0;
493  ret = XTestFakeRelativeMotionEvent(xdo->xdpy, x, y, CurrentTime);
494  XFlush(xdo->xdpy);
495  return _is_success("XTestFakeRelativeMotionEvent", ret == 0);
496}
497
498int xdo_mousedown(xdo_t *xdo, int button) {
499  int ret = 0;
500  ret = XTestFakeButtonEvent(xdo->xdpy, button, True, CurrentTime);
501  XFlush(xdo->xdpy);
502  return _is_success("XTestFakeButtonEvent(down)", ret == 0);
503}
504
505int xdo_mouseup(xdo_t *xdo, int button) {
506  int ret = 0;
507  ret = XTestFakeButtonEvent(xdo->xdpy, button, False, CurrentTime);
508  XFlush(xdo->xdpy);
509  return _is_success("XTestFakeButtonEvent(up)", ret == 0);
510}
511
512int xdo_mouselocation(xdo_t *xdo, int *x_ret, int *y_ret, int *screen_num_ret) {
513  int ret = False;
514  int x = 0, y = 0, screen_num = 0;
515  int i = 0;
516  Window dummy_win = 0;
517  int dummy_int = 0;
518  unsigned int dummy_uint = 0;
519  int screencount = ScreenCount(xdo->xdpy);
520
521  for (i = 0; i < screencount; i++) {
522    Screen *screen = ScreenOfDisplay(xdo->xdpy, i);
523    ret = XQueryPointer(xdo->xdpy, RootWindowOfScreen(screen),
524                        &dummy_win, &dummy_win,
525                        &x, &y, &dummy_int, &dummy_int, &dummy_uint);
526    if (ret == True) {
527      screen_num = i;
528      break;
529    }
530  }
531
532  if (ret == True) {
533    if (x_ret != NULL) *x_ret = x;
534    if (y_ret != NULL) *y_ret = y;
535    if (screen_num_ret != NULL) *screen_num_ret = screen_num;
536  }
537
538  return _is_success("XQueryPointer", ret == False);
539
540}
541
542int xdo_click(xdo_t *xdo, int button) {
543  int ret = 0;
544  ret = xdo_mousedown(xdo, button);
545  if (ret)
546    return ret;
547  ret = xdo_mouseup(xdo, button);
548  return ret;
549}
550
551/* XXX: Return proper code if errors found */
552int xdo_type(xdo_t *xdo, char *string) {
553  int i = 0;
554  char key = '0';
555  int keycode = 0;
556  int shiftcode = 0;
557
558  /* XXX: Add error handling */
559  for (i = 0; string[i] != '\0'; i++) {
560    key = string[i];
561    keycode = _xdo_keycode_from_char(xdo, key);
562    shiftcode = _xdo_get_shiftcode_if_needed(xdo, key);
563
564    if (shiftcode)
565      XTestFakeKeyEvent(xdo->xdpy, shiftcode, True, CurrentTime);
566    XTestFakeKeyEvent(xdo->xdpy, keycode, True, CurrentTime);
567    XTestFakeKeyEvent(xdo->xdpy, keycode, False, CurrentTime);
568    if (shiftcode)
569      XTestFakeKeyEvent(xdo->xdpy, shiftcode, False, CurrentTime);
570
571    /* XXX: Flush here or at the end? */
572    XFlush(xdo->xdpy);
573  }
574
575  return 0;
576}
577
578int _xdo_keysequence_do(xdo_t *xdo, char *keyseq, int pressed) {
579  int ret = 0;
580  int *keys = NULL;
581  int nkeys;
582  int i;
583
584  if (_xdo_keysequence_to_keycode_list(xdo, keyseq, &keys, &nkeys) == False) {
585    fprintf(stderr, "Failure converting key sequence '%s' to keycodes\n", keyseq);
586    return False;
587  }
588
589  for (i = 0; i < nkeys; i++) {
590    ret += !XTestFakeKeyEvent(xdo->xdpy, keys[i], pressed, CurrentTime);
591  }
592
593  free(keys);
594  XFlush(xdo->xdpy);
595  return ret;
596}
597 
598int xdo_keysequence_down(xdo_t *xdo, char *keyseq) {
599  return _xdo_keysequence_do(xdo, keyseq, True);
600}
601
602int xdo_keysequence_up(xdo_t *xdo, char *keyseq) {
603  return _xdo_keysequence_do(xdo, keyseq, False);
604}
605
606int xdo_keysequence(xdo_t *xdo, char *keyseq) {
607  int ret = 0;
608  ret += _xdo_keysequence_do(xdo, keyseq, True);
609  ret += _xdo_keysequence_do(xdo, keyseq, False);
610  return ret;
611}
612
613/* Add by Lee Pumphret 2007-07-28
614 * Modified slightly by Jordan Sissel */
615int xdo_window_get_focus(xdo_t *xdo, Window *window_ret) {
616  int ret = 0;
617  int unused_revert_ret;
618  ret = XGetInputFocus(xdo->xdpy, window_ret, &unused_revert_ret);
619  return _is_success("XGetInputFocus", ret == 0);
620}
621
622/* Helper functions */
623static int _xdo_keycode_from_char(xdo_t *xdo, char key) {
624  int i = 0;
625  int len = xdo->keycode_high - xdo->keycode_low;
626
627  for (i = 0; i < len; i++)
628    if (xdo->charcodes[i].key == key)
629      return xdo->charcodes[i].code;
630
631  return -1;
632}
633
634static int _xdo_get_shiftcode_if_needed(xdo_t *xdo, char key) {
635  int i = 0;
636  int len = xdo->keycode_high - xdo->keycode_low;
637
638  for (i = 0; i < len; i++)
639    if (xdo->charcodes[i].key == key)
640      return xdo->charcodes[i].shift;
641
642  return -1;
643}
644
645static int _xdo_has_xtest(xdo_t *xdo) {
646  int dummy;
647  return (XTestQueryExtension(xdo->xdpy, &dummy, &dummy, &dummy, &dummy) == True);
648}
649
650static void _xdo_populate_charcode_map(xdo_t *xdo) {
651  /* assert xdo->display is valid */
652  int keycodes_length = 0;
653  int shift_keycode = 0;
654  int i, j;
655
656  XDisplayKeycodes(xdo->xdpy, &(xdo->keycode_low), &(xdo->keycode_high));
657
658  /* Double size of keycode range because some
659   * keys have "shift" values. ie; 'a' and 'A', '2' and '@' */
660  /* Add 2 to the size because the range [low, high] is inclusive */
661  keycodes_length = (xdo->keycode_high - xdo->keycode_low) * 2 + 2;
662  xdo->charcodes = malloc(keycodes_length * sizeof(charcodemap_t));
663  memset(xdo->charcodes, 0, keycodes_length * sizeof(charcodemap_t));
664
665  /* Fetch the keycode for Shift_L */
666  /* XXX: Make 'Shift_L' configurable? */
667  shift_keycode = XKeysymToKeycode(xdo->xdpy, XStringToKeysym("Shift_L"));
668
669  for (i = xdo->keycode_low; i <= xdo->keycode_high; i++) {
670    char *keybuf = 0;
671
672    /* Index '0' in KeycodeToKeysym == no shift key
673     * Index '1' in ... == shift key held
674     * hence this little loop. */
675    for (j = 0; j <= 1; j++) { 
676     int idx = (i - xdo->keycode_low) * 2 + j;
677     keybuf = XKeysymToString(XKeycodeToKeysym(xdo->xdpy, i, j));
678
679     xdo->charcodes[idx].key = _keysym_to_char(keybuf);
680     xdo->charcodes[idx].code = i;
681     xdo->charcodes[idx].shift = j ? shift_keycode : 0;
682    }
683  }
684}
685
686/* context-free functions */
687char _keysym_to_char(char *keysym) {
688  int i;
689
690  if (keysym == NULL)
691    return -1;
692
693  /* keysymcharmap comes from xdo_util.h */
694  for (i = 0; keysymcharmap[i].keysym; i++) {
695    if (!strcmp(keysymcharmap[i].keysym, keysym))
696      return keysymcharmap[i].key;
697  }
698
699  if (strlen(keysym) == 1)
700    return keysym[0];
701
702  return -1;
703}
704
705  /* regexec(&re, string, 0, NULL, 0) == 0 means MATCH */
706
707static void _xdo_get_child_windows(xdo_t *xdo, Window window,
708                                   Window **total_window_list, 
709                                   int *ntotal_windows,
710                                   int *window_list_size) {
711  Window dummy;
712  Window *children;
713  unsigned int i, nchildren;
714
715  if (*window_list_size == 0) {
716    *ntotal_windows = 0;
717    *window_list_size = 100;
718    *total_window_list = malloc(*window_list_size * sizeof(Window));
719  }
720
721  /* foo */
722  if (!XQueryTree(xdo->xdpy, window, &dummy, &dummy, &children, &nchildren))
723    return;
724
725  for (i = 0; i < nchildren; i++) {
726    Window w = children[i];
727    (*total_window_list)[*ntotal_windows] = w;
728    *