source: trunk/xdotool/xdo.c @ 1068

Last change on this file since 1068 was 1068, checked in by dkg, 5 years ago

xdotool: changes to appease gcc and reduce warnings.

File size: 16.0 KB
Line 
1/* xdo library
2 *
3 * $Id: xdo.c 1538 2007-09-04 06:00:24Z psionic $
4 *
5 * - getwindowfocus contributed by Lee Pumphret
6 * - keysequence_{up,down} contributed by Magnus Boman
7 *
8 */
9
10#include <sys/select.h>
11#include <stdlib.h>
12#include <stdio.h>
13#include <string.h>
14#include <unistd.h>
15#include <regex.h>
16
17#include <X11/Xlib.h>
18#include <X11/Xresource.h>
19#include <X11/Xutil.h>
20#include <X11/extensions/XTest.h>
21
22#include "xdo.h"
23#include "xdo_util.h"
24
25static void _xdo_populate_charcode_map(xdo_t *xdo);
26static int _xdo_has_xtest(xdo_t *xdo);
27
28static int _xdo_keycode_from_char(xdo_t *xdo, char key);
29static int _xdo_get_shiftcode_if_needed(xdo_t *xdo, char key);
30
31static void _xdo_get_child_windows(xdo_t *xdo, Window window,
32                                   Window **total_window_list, 
33                                   int *ntotal_windows, 
34                                   int *window_list_size);
35
36static int _xdo_keysequence_to_keycode_list(xdo_t *xdo, char *keyseq, uint **keys, uint *nkeys);
37static int _xdo_keysequence_do(xdo_t *xdo, char *keyseq, int pressed);
38static int _xdo_regex_match_window(xdo_t *xdo, Window window, int flags, regex_t *re);
39static int _xdo_is_window_visible(xdo_t *xdo, Window wid);
40
41static int _is_success(const char *funcname, int code);
42
43/* context-free functions */
44char _keysym_to_char(char *keysym);
45
46xdo_t* xdo_new(char *display_name) {
47  Display *xdpy;
48
49  if ((xdpy = XOpenDisplay(display_name)) == NULL) {
50    fprintf(stderr, "Error: Can't open display: %s\n", display_name);
51    return NULL;
52  }
53
54  return xdo_new_with_opened_display(xdpy, display_name, 1);
55}
56
57xdo_t* xdo_new_with_opened_display(Display *xdpy, const char *display, 
58                                   int close_display_when_freed) {
59  xdo_t *xdo = NULL;
60
61  if (xdpy == NULL) {
62    fprintf(stderr, "xdo_new: xdisplay I was given is a null pointer\n");
63    return NULL;
64  }
65
66  /* XXX: Check for NULL here */
67  xdo = malloc(sizeof(xdo_t));
68  memset(xdo, 0, sizeof(xdo_t));
69
70  xdo->xdpy = xdpy;
71  xdo->close_display_when_freed = close_display_when_freed;
72
73  if (display == NULL)
74    display = "unknown";
75
76  if (!_xdo_has_xtest(xdo)) {
77    fprintf(stderr, "Error: XTEST extension unavailable on '%s'.", 
78            xdo->display_name);
79    xdo_free(xdo);
80    return NULL;
81  }
82
83  /* populate the character map? */
84  _xdo_populate_charcode_map(xdo);
85
86  return xdo;
87}
88
89void xdo_free(xdo_t *xdo) {
90  if (xdo->display_name)
91    free(xdo->display_name);
92  if (xdo->charcodes)
93    free(xdo->charcodes);
94  if (xdo->xdpy && xdo->close_display_when_freed)
95    XCloseDisplay(xdo->xdpy);
96  free(xdo);
97}
98
99int xdo_window_map(xdo_t *xdo, Window wid) {
100  int ret;
101  ret = XMapWindow(xdo->xdpy, wid);
102  XFlush(xdo->xdpy);
103  return _is_success("XMapWindow", ret);
104}
105
106int xdo_window_unmap(xdo_t *xdo, Window wid) {
107  int ret;
108  ret = XUnmapWindow(xdo->xdpy, wid);
109  XFlush(xdo->xdpy);
110  return _is_success("XMapWindow", ret);
111}
112
113void xdo_window_list_by_regex(xdo_t *xdo, char *regex, int flags,
114                              Window **windowlist, int *nwindows) {
115  regex_t re;
116  Window *total_window_list = NULL;
117  int ntotal_windows = 0;
118  int window_list_size = 0;
119  int matched_window_list_size = 100;
120
121  int ret, i;
122
123  ret = regcomp(&re, regex, REG_EXTENDED | REG_ICASE);
124  if (ret != 0) {
125    fprintf(stderr, "Failed to compile regex: '%s'\n", regex);
126    return;
127  }
128
129  /* Default search settings:
130   * All windows (visible and hidden) and search all text pieces
131   */
132  if ((flags & (SEARCH_TITLE | SEARCH_CLASS | SEARCH_NAME)) == 0) {
133    fprintf(stderr, "No text fields specified for regex search. \nDefaulting to"
134            " window title, class, and name searching\n");
135    flags = SEARCH_TITLE | SEARCH_CLASS | SEARCH_NAME;
136  }
137
138  *nwindows = 0;
139  *windowlist = malloc(matched_window_list_size * sizeof(Window));
140
141  _xdo_get_child_windows(xdo, XDefaultRootWindow(xdo->xdpy), 
142                         &total_window_list, &ntotal_windows,
143                         &window_list_size);
144  for (i = 0; i < ntotal_windows; i++) {
145    Window wid = total_window_list[i];
146    if (flags & SEARCH_VISIBLEONLY && !_xdo_is_window_visible(xdo, wid))
147      continue;
148
149    if (!_xdo_regex_match_window(xdo, wid, flags, &re))
150      continue;
151
152    (*windowlist)[*nwindows] = wid;
153    (*nwindows)++;
154
155    if (matched_window_list_size == *nwindows) {
156      matched_window_list_size *= 2;
157      *windowlist = realloc(*windowlist, 
158                            matched_window_list_size * sizeof(Window));
159    }
160  }
161
162  regfree(&re);
163}
164
165int xdo_window_move(xdo_t *xdo, Window wid, int x, int y) {
166  XWindowChanges wc;
167  int ret;
168  wc.x = x;
169  wc.y = y;
170
171  ret = XConfigureWindow(xdo->xdpy, wid, CWX | CWY, &wc);
172  return _is_success("XConfigureWindow", ret);
173}
174
175int xdo_window_setsize(xdo_t *xdo, Window wid, int width, int height, int flags) {
176  XWindowChanges wc;
177  int ret;
178  uint cw_flags = 0;
179
180  wc.width = width;
181  wc.height = height;
182
183  if (flags & SIZE_USEHINTS) {
184    XSizeHints hints;
185    long supplied_return;
186    memset(&hints, 0, sizeof(hints));
187    XGetWMNormalHints(xdo->xdpy, wid, &hints, &supplied_return);
188    if (supplied_return & PResizeInc) {
189      wc.width *= hints.width_inc;
190      wc.height *= hints.height_inc;
191    } else {
192      fprintf(stderr, "No size hints found for this window\n");
193    }
194
195    if (supplied_return & PBaseSize) {
196      wc.width += hints.base_width;
197      wc.height += hints.base_height;
198    }
199
200  }
201
202  if (width > 0)
203    cw_flags |= CWWidth;
204  if (height > 0)
205    cw_flags |= CWHeight;
206
207  ret = XConfigureWindow(xdo->xdpy, wid, cw_flags, &wc);
208  XFlush(xdo->xdpy);
209  return _is_success("XConfigureWindow", ret);
210}
211
212int xdo_window_focus(xdo_t *xdo, Window wid) {
213  int ret;
214  ret = XSetInputFocus(xdo->xdpy, wid, RevertToParent, CurrentTime);
215  XFlush(xdo->xdpy);
216  return _is_success("XSetInputFocus", ret);
217}
218
219/* XRaiseWindow is ignored in ion3 and Gnome2. Is it even useful? */
220int xdo_window_raise(xdo_t *xdo, Window wid) {
221  int ret;
222  ret = XRaiseWindow(xdo->xdpy, wid);
223  XFlush(xdo->xdpy);
224  return _is_success("XRaiseWindow", ret);
225}
226
227/* XXX: Include 'screen number' support? */
228int xdo_mousemove(xdo_t *xdo, int x, int y)  {
229  int ret;
230  ret = XTestFakeMotionEvent(xdo->xdpy, -1, x, y, CurrentTime);
231  XFlush(xdo->xdpy);
232  return _is_success("XTestFakeMotionEvent", ret);
233}
234
235int xdo_mousemove_relative(xdo_t *xdo, int x, int y)  {
236  int ret;
237  ret = XTestFakeRelativeMotionEvent(xdo->xdpy, x, y, CurrentTime);
238  XFlush(xdo->xdpy);
239  return _is_success("XTestFakeRelativeMotionEvent", ret);
240}
241
242int xdo_mousedown(xdo_t *xdo, uint button) {
243  int ret;
244  ret = XTestFakeButtonEvent(xdo->xdpy, button, True, CurrentTime);
245  XFlush(xdo->xdpy);
246  return _is_success("XTestFakeButtonEvent", ret);
247}
248
249int xdo_mouseup(xdo_t *xdo, uint button) {
250  int ret;
251  ret = XTestFakeButtonEvent(xdo->xdpy, button, False, CurrentTime);
252  XFlush(xdo->xdpy);
253  return _is_success("XTestFakeKeyEvent", ret);
254}
255
256int xdo_click(xdo_t *xdo, uint button) {
257  int ret;
258  ret = xdo_mousedown(xdo, button);
259  if (!ret)
260    return ret;
261  ret = xdo_mouseup(xdo, button);
262  return ret;
263
264  /* no need to flush here */
265}
266
267/* XXX: Return proper code if errors found */
268int xdo_type(xdo_t *xdo, char *string) {
269  int i = 0;
270  char key = '0';
271  uint keycode = 0;
272  uint shiftcode = 0;
273
274  for (i = 0; string[i] != '\0'; i++) {
275    key = string[i];
276    keycode = _xdo_keycode_from_char(xdo, key);
277    shiftcode = _xdo_get_shiftcode_if_needed(xdo, key);
278
279    if (shiftcode)
280      XTestFakeKeyEvent(xdo->xdpy, shiftcode, True, CurrentTime);
281    XTestFakeKeyEvent(xdo->xdpy, keycode, True, CurrentTime);
282    XTestFakeKeyEvent(xdo->xdpy, keycode, False, CurrentTime);
283    if (shiftcode)
284      XTestFakeKeyEvent(xdo->xdpy, shiftcode, False, CurrentTime);
285
286    /* XXX: Flush here or at the end? */
287    XFlush(xdo->xdpy);
288  }
289
290  return True;
291}
292
293int _xdo_keysequence_do(xdo_t *xdo, char *keyseq, int pressed) {
294  uint *keys = NULL;
295  uint nkeys;
296  uint i;
297
298  if (_xdo_keysequence_to_keycode_list(xdo, keyseq, &keys, &nkeys) == False) {
299    fprintf(stderr, "Failure converting key sequence '%s' to keycodes\n", keyseq);
300    return False;
301  }
302
303  for (i = 0; i < nkeys; i++) {
304    /* fprintf(stderr, "Typing %d (%d)\n", keys[i], pressed); */
305    XTestFakeKeyEvent(xdo->xdpy, keys[i], pressed, CurrentTime);
306  }
307
308  free(keys);
309  XFlush(xdo->xdpy);
310  return True;
311}
312 
313int xdo_keysequence_down(xdo_t *xdo, char *keyseq) {
314  return _xdo_keysequence_do(xdo, keyseq, True);
315}
316
317int xdo_keysequence_up(xdo_t *xdo, char *keyseq) {
318  return _xdo_keysequence_do(xdo, keyseq, False);
319}
320
321int xdo_keysequence(xdo_t *xdo, char *keyseq) {
322  _xdo_keysequence_do(xdo, keyseq, True);
323  _xdo_keysequence_do(xdo, keyseq, False);
324  return True;
325}
326
327
328/* Add by Lee Pumphret 2007-07-28
329 * Modified slightly by Jordan Sissel */
330int xdo_window_get_focus(xdo_t *xdo, Window *window_ret) {
331  int ret;
332  int unused_revert_ret;
333  ret = XGetInputFocus(xdo->xdpy, window_ret, &unused_revert_ret);
334  return _is_success("XGetInputFocus", ret);
335}
336
337
338/* Helper functions */
339static int _xdo_keycode_from_char(xdo_t *xdo, char key) {
340  int i = 0;
341  int len = xdo->keycode_high - xdo->keycode_low;
342
343  for (i = 0; i < len; i++)
344    if (xdo->charcodes[i].key == key)
345      return xdo->charcodes[i].code;
346
347  return -1;
348}
349
350static int _xdo_get_shiftcode_if_needed(xdo_t *xdo, char key) {
351  int i = 0;
352  int len = xdo->keycode_high - xdo->keycode_low;
353
354  for (i = 0; i < len; i++)
355    if (xdo->charcodes[i].key == key)
356      return xdo->charcodes[i].shift;
357
358  return -1;
359}
360
361static int _xdo_has_xtest(xdo_t *xdo) {
362  int dummy;
363  return (XTestQueryExtension(xdo->xdpy, &dummy, &dummy, &dummy, &dummy) == True);
364}
365
366static void _xdo_populate_charcode_map(xdo_t *xdo) {
367  /* assert xdo->display is valid */
368  int keycodes_length = 0;
369  int shift_keycode = 0;
370  KeyCode i;
371  int j;
372
373  XDisplayKeycodes(xdo->xdpy, &(xdo->keycode_low), &(xdo->keycode_high));
374
375  /* Double size of keycode range because some
376   * keys have "shift" values. ie; 'a' and 'A', '2' and '@' */
377  /* Add 2 to the size because the range [low, high] is inclusive */
378  keycodes_length = (xdo->keycode_high - xdo->keycode_low) * 2 + 2;
379  xdo->charcodes = malloc(keycodes_length * sizeof(charcodemap_t));
380  memset(xdo->charcodes, 0, keycodes_length * sizeof(charcodemap_t));
381
382  /* Fetch the keycode for Shift_L */
383  /* XXX: Make 'Shift_L' configurable? */
384  shift_keycode = XKeysymToKeycode(xdo->xdpy, XStringToKeysym("Shift_L"));
385
386  for (i = xdo->keycode_low; i <= xdo->keycode_high; i++) {
387    char *keybuf = 0;
388
389    /* Index '0' in KeycodeToKeysym == no shift key
390     * Index '1' in ... == shift key held
391     * hence this little loop. */
392    for (j = 0; j <= 1; j++) { 
393     int ix = (i - xdo->keycode_low) * 2 + j;
394     keybuf = XKeysymToString(XKeycodeToKeysym(xdo->xdpy, i, j));
395
396     xdo->charcodes[ix].key = _keysym_to_char(keybuf);
397     xdo->charcodes[ix].code = i;
398     xdo->charcodes[ix].shift = j ? shift_keycode : 0;
399    }
400  }
401}
402
403/* context-free functions */
404char _keysym_to_char(char *keysym) {
405  int i;
406
407  if (keysym == NULL)
408    return -1;
409
410  /* keysymcharmap comes from xdo_util.h */
411  for (i = 0; keysymcharmap[i].keysym; i++) {
412    if (!strcmp(keysymcharmap[i].keysym, keysym))
413      return keysymcharmap[i].key;
414  }
415
416  if (strlen(keysym) == 1)
417    return keysym[0];
418
419  return -1;
420}
421
422  /* regexec(&re, string, 0, NULL, 0) == 0 means MATCH */
423
424static void _xdo_get_child_windows(xdo_t *xdo, Window window,
425                                   Window **total_window_list, 
426                                   int *ntotal_windows,
427                                   int *window_list_size) {
428  Window dummy;
429  uint i;
430  Window *children;
431  unsigned int nchildren;
432
433  if (*window_list_size == 0) {
434    *ntotal_windows = 0;
435    *window_list_size = 100;
436    *total_window_list = malloc(*window_list_size * sizeof(Window));
437  }
438
439  /* foo */
440  if (!XQueryTree(xdo->xdpy, window, &dummy, &dummy, &children, &nchildren))
441    return;
442
443  for (i = 0; i < nchildren; i++) {
444    Window w = children[i];
445    (*total_window_list)[*ntotal_windows] = w;
446    *ntotal_windows += 1;
447    if (*ntotal_windows == *window_list_size) {
448      *window_list_size *= 2;
449      *total_window_list = realloc(*total_window_list,
450                                   *window_list_size * sizeof(Window));
451    }
452    _xdo_get_child_windows(xdo, w, total_window_list,
453                           ntotal_windows, window_list_size);
454  }
455
456  XFree(children);
457}
458
459int _xdo_keysequence_to_keycode_list(xdo_t *xdo, char *keyseq, uint **keys, uint *nkeys) {
460  char *tokctx = NULL;
461  const char *tok = NULL;
462  char *strptr = NULL;
463  int i;
464 
465  /* Array of keys to press, in order given by keyseq */
466  uint keys_size = 10;
467  *nkeys = 0;
468
469  if (strcspn(keyseq, " \t\n.-[]{}\\|") != strlen(keyseq)) {
470    fprintf(stderr, "Error: Invalid key sequence '%s'\n", keyseq);
471    return False;
472  }
473
474  *keys = malloc(keys_size * sizeof(int));
475  strptr = strdup(keyseq);
476  while ((tok = strtok_r(strptr, "+", &tokctx)) != NULL) {
477    KeySym keysym;
478    if (strptr != NULL)
479      strptr = NULL;
480
481    /* Check if 'tok' (string keysym) is an alias to another key */
482    /* symbol_map comes from xdo.util */
483    for (i = 0; symbol_map[i] != NULL; i+=2)
484      if (!strcasecmp(tok, symbol_map[i]))
485        tok = symbol_map[i + 1];
486
487    keysym = XStringToKeysym(tok);
488    if (keysym == NoSymbol) {
489      fprintf(stderr, "(symbol) No such key name '%s'. Ignoring it.\n", tok);
490      continue;
491    }
492
493    (*keys)[*nkeys] = XKeysymToKeycode(xdo->xdpy, keysym);
494
495    if ((*keys)[*nkeys] == 0) {
496      fprintf(stderr, "No such key '%s'. Ignoring it.\n", tok);
497      continue;
498    }
499
500    (*nkeys)++;
501    if (*nkeys == keys_size) {
502      keys_size *= 2;
503      *keys = realloc(*keys, keys_size);
504    }
505  }
506
507  free(strptr);
508
509  return True;
510}
511
512int _xdo_regex_match_window(xdo_t *xdo, Window window, int flags, regex_t *re) {
513  XWindowAttributes attr;
514  XTextProperty tp;
515  XClassHint classhint;
516  int i;
517
518  XGetWindowAttributes(xdo->xdpy, window, &attr);
519
520  /* XXX: Memory leak here according to valgrind? */
521  XGetWMName(xdo->xdpy, window, &tp);
522
523  if (flags & SEARCH_TITLE) {
524    if (tp.nitems > 0) {
525      int count = 0;
526      char **list = NULL;
527      XmbTextPropertyToTextList(xdo->xdpy, &tp, &list, &count);
528      for (i = 0; i < count; i++) {
529        if (regexec(re, list[i], 0, NULL, 0) == 0) {
530          XFreeStringList(list);
531          return True;
532        }
533        XFreeStringList(list);
534      }
535    }
536  }
537
538  if (XGetClassHint(xdo->xdpy, window, &classhint)) {
539    if ((flags & SEARCH_NAME) && classhint.res_name) {
540      if (regexec(re, classhint.res_name, 0, NULL, 0) == 0) {
541        XFree(classhint.res_name);
542        XFree(classhint.res_class);
543        return True;
544      }
545      XFree(classhint.res_name);
546    }
547    if ((flags & SEARCH_CLASS) && classhint.res_class) {
548      if (regexec(re, classhint.res_class, 0, NULL, 0) == 0) {
549        XFree(classhint.res_class);
550        return True;
551      }
552      XFree(classhint.res_class);
553    }
554  }
555  return False;
556}
557
558int _is_success(const char *funcname, int code) {
559  if (code == BadMatch) {
560    fprintf(stderr, "%s failed: got bad match\n", funcname);
561    return False;
562  } else if (code == BadValue) {
563    fprintf(stderr, "%s failed: got bad value\n", funcname);
564    return False;
565  } else if (code == BadWindow) {
566    fprintf(stderr, "%s failed: got bad window\n", funcname);
567    return False;
568  }
569
570  return True;
571}
572
573int _xdo_is_window_visible(xdo_t *xdo, Window wid) {
574  XWindowAttributes wattr;
575
576  XGetWindowAttributes(xdo->xdpy, wid, &wattr);
577  if (wattr.map_state != IsViewable)
578    return False;
579
580  return True;
581}
582
583/* main test */
584#ifdef BUILDMAIN
585int main(int argc, char **argv) {
586  char *display_name;
587  xdo_t *xdo;
588
589  char *yay;
590
591  if ( (display_name = getenv("DISPLAY")) == (void *)NULL) {
592    fprintf(stderr, "Error: DISPLAY environment variable not set\n");
593    exit(1);
594  }
595
596  /* yay = strdup("ctrl+l"); */
597
598  xdo = xdo_new(display_name);
599/*   xdo_mousemove(xdo, 100, 100); */
600/*   usleep(100 * 1000); */
601/*   xdo_keysequence(xdo, strdup("ctrl+l")); */
602/*   xdo_type(xdo, strdup("ls")); */
603/*   xdo_keysequence(xdo, strdup("Return")); */
604
605 
606  Window *list;
607  int nwindows;
608  char *query = "xterm";
609  int i;
610  if (argc > 1)
611    query = argv[1];
612
613  xdo_window_list_by_regex(xdo, query, &list, &nwindows);
614  for (i = 0; i < nwindows; i++) {
615    printf("%d\n", list[i]);
616  }
617  xdo_free(xdo);
618
619  return 0;
620}
621#endif
622
Note: See TracBrowser for help on using the repository browser.