1 /* xdotool
2 *
3 * command line interface to the xdo library
4 *
5 * getwindowfocus contributed by Lee Pumphret
6 * keyup/down contributed by Lee Pumphret
7 *
8 * vim:expandtab shiftwidth=2 softtabstop=2
9 */
10
11 #define _GNU_SOURCE 1
12 #ifndef __USE_BSD
13 #define __USE_BSD /* for strdup on linux/glibc */
14 #endif /* __USE_BSD */
15
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <getopt.h>
22 #include <string.h>
23 #include <strings.h>
24 #include <errno.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27
28 #include "xdo.h"
29 #include "xdotool.h"
30
31 static int script_main(int argc, char **argv);
32 static int args_main(int argc, char **argv);
33 int context_execute(context_t *context);
34 void consume_args(context_t *context, int argc);
35 void window_save(context_t *context, Window window);
36 void window_list(context_t *context, const char *window_arg,
37 Window **windowlist_ret, int *nwindows_ret,
38 const int add_to_list);
39 int window_get_arg(context_t *context, int min_arg, int window_arg_pos,
40 const char **window_arg);
41 int window_is_valid(context_t *context, const char *window_arg);
42 int is_command(char* cmd);
43 void xdotool_debug(context_t *context, const char *format, ...);
44 void xdotool_output(context_t *context, const char *format, ...);
45
consume_args(context_t * context,int argc)46 void consume_args(context_t *context, int argc) {
47 if (argc > context->argc) {
48 fprintf(stderr,
49 "Can't consume %d args; are only %d available. This is a bug.\n",
50 argc, context->argc);
51 context->argv += context->argc;
52 context->argc = 0;
53 return;
54 }
55
56 context->argv += argc;
57 context->argc -= argc;
58 } /* void consume_args(context_t *, int) */
59
window_save(context_t * context,Window window)60 void window_save(context_t *context, Window window) {
61 if (context->windows != NULL) {
62 free(context->windows);
63 }
64
65 context->windows = calloc(1, sizeof(Window));
66 context->nwindows = 1;
67 context->windows[0] = window;
68 } /* void window_save(context_t *, Window) */
69
window_is_valid(context_t * context,const char * window_arg)70 int window_is_valid(context_t *context, const char *window_arg) {
71 if (window_arg == NULL) {
72 return True;
73 }
74
75 if (window_arg[0] != '%') {
76 return True;
77 }
78
79 /* Selected a window with %N or %@, but are there windows on the stack? */
80 if (context->nwindows == 0) {
81 fprintf(stderr, "There are no windows in the stack\n");
82 return False;
83 }
84
85 if (window_arg[1] == '\0') {
86 fprintf(stderr, "Invalid window stack selection '%s'\n", window_arg);
87 return False;
88 }
89
90 if (window_arg[1] == '@') {
91 return True;
92 }
93
94 int window_index = atoi(window_arg + 1);
95 if (abs(window_index - 1) >= context->nwindows || (window_index == 0)) {
96 fprintf(stderr, "Invalid window stack selection '%s' (out of range)\n", window_arg);
97 return False;
98 }
99
100 return True;
101 } /* int window_is_valid(context_t *, const char *) */
102
window_get_arg(context_t * context,int min_arg,int window_arg_pos,const char ** window_arg)103 int window_get_arg(context_t *context, int min_arg, int window_arg_pos,
104 const char **window_arg) {
105 if (context->argc < min_arg) {
106 fprintf(stderr, "Too few arguments (got %d, minimum is %d)\n",
107 context->argc, min_arg);
108 return False;
109 } else if (context->argc == min_arg) {
110 /* nothing, keep default */
111 } else if (context->argc > min_arg) {
112 if (is_command(context->argv[min_arg])) {
113 /* keep default */
114 } else {
115 /* got enough args, let's use the window you asked for */
116 *window_arg = context->argv[window_arg_pos];
117 consume_args(context, 1);
118 }
119 }
120
121 if (!window_is_valid(context, *window_arg)) {
122 fprintf(stderr, "Invalid window '%s'\n", *window_arg);
123 return False;
124 }
125
126 return True;
127 } /* int window_get_arg(context_t *, int, int, char **, int *) */
128
window_list(context_t * context,const char * window_arg,Window ** windowlist_ret,int * nwindows_ret,const int add_to_list)129 void window_list(context_t *context, const char *window_arg,
130 Window **windowlist_ret, int *nwindows_ret,
131 const int add_to_list) {
132 /* If window_arg is NULL and we have windows in the list, use the list.
133 * If window_arg is "%@" and we have windows in the list, use the list.
134 * If window_arg is "%N" and we have windows in the list, use Nth window.
135 * 'N' above must be a positive number.
136 * Otherwise, assume it's a window id.
137 *
138 * TODO(sissel): Not implemented yet:
139 * If window_arg is "%r" it means the root window of the current screen.
140 * If window_arg is "%q" it means we will wait for you to select a window
141 * by clicking on it. (May not be necessary since we have 'selectwindow')
142 * If window_arg is "%c" it means the currently-active window.
143 */
144
145 *nwindows_ret = 0;
146 *windowlist_ret = NULL;
147
148 if (window_arg != NULL && window_arg[0] == '%') {
149 if (context->nwindows == 0) {
150 fprintf(stderr, "There are no windows on the stack, Can't continue.\n");
151 return;
152 }
153
154 if (strlen(window_arg) < 2) {
155 fprintf(stderr, "Invalid window selection '%s'\n", window_arg);
156 return;
157 }
158
159 /* options.
160 * %N selects the Nth window. %1, %2, %-1 (last), %-2, etc.
161 * %@ selects all
162 */
163 if (window_arg[1] == '@') {
164 *windowlist_ret = context->windows;
165 *nwindows_ret = context->nwindows;
166 } else if (window_arg[1] == 'q') {
167 /* TODO(sissel): Wait for you to click on the window. */
168 } else if (window_arg[1] == 'r') {
169 /* TODO(sissel): Get the root window of the current screen */
170 } else if (window_arg[1] == 'c') {
171 /* TODO(sissel): Get the current window */
172 } else {
173 /* Otherwise assume %N */
174 int window_index = atoi(window_arg + 1);
175 if (window_index < 0) {
176 /* negative offset */
177 window_index = context->nwindows + window_index;
178 }
179
180 if (window_index > context->nwindows || window_index <= 0) {
181 fprintf(stderr, "%d is out of range (only %d windows in list)\n",
182 window_index, context->nwindows);
183 return;
184 }
185
186 /* Subtract 1 since %1 is the first window in the list */
187 context->window_placeholder[0] = context->windows[window_index - 1];
188 *windowlist_ret = context->window_placeholder;
189 *nwindows_ret = 1;
190 }
191 } else {
192 /* Otherwise, window_arg is either invalid or null. Default to CURRENTWINDOW
193 */
194
195 /* We can't return a pointer to a piece of the stack in this function,
196 * so we'll store the window in the context_t and return a pointer
197 * to that.
198 */
199 Window window = CURRENTWINDOW;
200 if (window_arg != NULL) {
201 window = (Window)strtol(window_arg, NULL, 0);
202 }
203
204 context->window_placeholder[0] = window;
205 *nwindows_ret = 1;
206 *windowlist_ret = context->window_placeholder;
207 }
208
209 if (add_to_list) {
210 /* save the window to the windowlist */
211 }
212 }
213
214
215 struct dispatch {
216 const char *name;
217 int (*func)(context_t *context);
218 } dispatch[] = {
219 /* Query functions */
220 { "getactivewindow", cmd_getactivewindow, },
221 { "getwindowfocus", cmd_getwindowfocus, },
222 { "getwindowname", cmd_getwindowname, },
223 { "getwindowclassname", cmd_getwindowclassname},
224 { "getwindowpid", cmd_getwindowpid, },
225 { "getwindowgeometry", cmd_getwindowgeometry, },
226 { "getdisplaygeometry", cmd_get_display_geometry, },
227 { "search", cmd_search, },
228 { "selectwindow", cmd_window_select, },
229
230 /* Help me! */
231 { "help", cmd_help, },
232 { "version", cmd_version, },
233
234 /* Action functions */
235 { "behave", cmd_behave, },
236 { "behave_screen_edge", cmd_behave_screen_edge, },
237 { "click", cmd_click, },
238 { "getmouselocation", cmd_getmouselocation, },
239 { "key", cmd_key, },
240 { "keydown", cmd_key, },
241 { "keyup", cmd_key, },
242 { "mousedown", cmd_mousedown, },
243 { "mousemove", cmd_mousemove, },
244 { "mousemove_relative", cmd_mousemove_relative, },
245 { "mouseup", cmd_mouseup, },
246 { "set_window", cmd_set_window, },
247 { "type", cmd_type, },
248 { "windowactivate", cmd_windowactivate, },
249 { "windowfocus", cmd_windowfocus, },
250 { "windowkill", cmd_windowkill, },
251 { "windowclose", cmd_windowclose, },
252 { "windowquit", cmd_windowquit, },
253 { "windowmap", cmd_windowmap, },
254 { "windowminimize", cmd_windowminimize, },
255 { "windowmove", cmd_windowmove, },
256 { "windowraise", cmd_windowraise, },
257 { "windowreparent", cmd_windowreparent, },
258 { "windowsize", cmd_windowsize, },
259 { "windowstate", cmd_windowstate, },
260 { "windowunmap", cmd_windowunmap, },
261
262 { "set_num_desktops", cmd_set_num_desktops, },
263 { "get_num_desktops", cmd_get_num_desktops, },
264 { "set_desktop", cmd_set_desktop, },
265 { "get_desktop", cmd_get_desktop, },
266 { "set_desktop_for_window", cmd_set_desktop_for_window, },
267 { "get_desktop_for_window", cmd_get_desktop_for_window, },
268 { "get_desktop_viewport", cmd_get_desktop_viewport, },
269 { "set_desktop_viewport", cmd_set_desktop_viewport, },
270
271 { "exec", cmd_exec, },
272 { "sleep", cmd_sleep, },
273
274 { NULL, NULL, },
275 };
276
is_command(char * cmd)277 int is_command(char* cmd) {
278 int i;
279 for (i = 0; dispatch[i].name != NULL; i++) {
280 if (!strcasecmp(dispatch[i].name, cmd)) {
281 return 1;
282 }
283 }
284 return 0;
285 }
286
main(int argc,char ** argv)287 int main(int argc, char **argv) {
288 return xdotool_main(argc, argv);
289 }
290
xdotool_main(int argc,char ** argv)291 int xdotool_main(int argc, char **argv) {
292
293 /* If argv[1] is a file or "-", read commands from file or stdin,
294 * else use commands from argv.
295 */
296
297 struct stat data;
298 int stat_ret;
299
300 if (argc >= 2) {
301 /* See if the first argument is an existing file */
302 stat_ret = stat(argv[1], &data);
303 int i = 0;
304 int argv1_is_command= 0;
305
306 for (i = 0; dispatch[i].name != NULL; i++) {
307 if (!strcasecmp(dispatch[i].name, argv[1])) {
308 argv1_is_command = 1;
309 break;
310 }
311 }
312
313 if (!argv1_is_command && (strcmp(argv[1], "-") == 0 || stat_ret == 0)) {
314 return script_main(argc, argv);
315 }
316 }
317 return args_main(argc, argv);
318 }
319
script_main(int argc,char ** argv)320 int script_main(int argc, char **argv) {
321 /* Tokenize the input file while expanding positional parameters and
322 * environment variables. Pass the resulting argument list to
323 * args_main().
324 */
325
326 FILE *input = NULL;
327 const char *path = argv[1];
328 char buffer[4096];
329
330 char **script_argv = (char **) calloc(1, sizeof(char *));
331 int script_argc = 0;
332 int script_argc_max = 0;
333
334 /* determine whether reading from a file or from stdin */
335 if (!strcmp(path, "-")) {
336 input = fdopen(0, "r");
337 } else {
338 input = fopen(path, "r");
339 if (input == NULL) {
340 fprintf(stderr, "Failure opening '%s': %s\n", path, strerror(errno));
341 return EXIT_FAILURE;
342 }
343 }
344
345 context_t context;
346 context.xdo = xdo_new(NULL);
347 context.prog = *argv;
348 context.windows = NULL;
349 context.nwindows = 0;
350 context.have_last_mouse = False;
351 context.debug = (getenv("DEBUG") != NULL);
352
353 if (context.xdo == NULL) {
354 fprintf(stderr, "Failed creating new xdo instance\n");
355 return 1;
356 }
357 context.xdo->debug = context.debug;
358
359 /* read input... */
360 int pos;
361 char *token;
362 int result = XDO_SUCCESS;
363
364 while (fgets(buffer, 4096, input) != NULL) {
365 char *line = buffer;
366 token = NULL;
367
368 /* Ignore leading whitespace */
369 line += strspn(line, " \t");
370
371 /* blanklines or line comment are ignored, too */
372 if (line[0] == '\n' || line[0] == '#') {
373 continue;
374 }
375
376 /* replace newline with null */
377 if (line[strlen(line)-1] == '\n')
378 line[strlen(line)-1] = '\0';
379
380 /* tokenize line into script_argv... */
381 while (strlen(line)) {
382 token = NULL;
383
384 /* modify line to contain the current token. Tokens are
385 * separated by whitespace, or quoted with single/double quotes.
386 */
387 if (line[0] == '"') {
388 line++;
389 line[strcspn(line, "\"")] = '\0';
390 }
391 else if (line[0] == '\'') {
392 line++;
393 line[strcspn(line, "\'")] = '\0';
394 }
395 else {
396 line[strcspn(line, " \t")] = '\0';
397 }
398
399 /* if a token begins with "$", append the corresponding
400 * positional parameter or environment variable to
401 * script_argv...
402 */
403 if (line[0] == '$') {
404 /* ignore dollar sign */
405 line++;
406
407 if (isdigit(line[0])) {
408 /* get the position of this parameter in argv */
409 pos = atoi(line) + 1; /* $1 is actually index 2 in the argv array */
410
411 /* bail if no argument was given for this parameter */
412 if (pos >= argc) {
413 fprintf (stderr, "%s: error: `%s' needs at least %d %s; only %d given\n",
414 argv[0], argv[1], pos - 1, pos == 2 ? "argument" : "arguments",
415 argc - 2);
416 return EXIT_FAILURE;
417 }
418 /* use command line argument */
419 token = argv[pos];
420 }
421 else {
422 /* use environment variable */
423 token = getenv(line);
424 if (token == NULL) {
425 /* since it's not clear what we should do if this env var is not
426 * present, let's abort */
427 fprintf(stderr, "%s: error: environment variable $%s is not set.\n",
428 argv[0], line);
429 return EXIT_FAILURE;
430 }
431 }
432 }
433 else {
434 /* use the verbatim token */
435 token = line;
436 }
437
438 /* append token */
439 if (token != NULL) {
440
441 if(script_argc + 1 > script_argc_max){
442 script_argv = realloc(script_argv, (script_argc + 1) * sizeof(char *));
443 script_argc_max++;
444 }
445
446 if (script_argv == NULL) {
447 fprintf(stderr, "%s: error: failed to allocate memory while parsing `%s'.\n",
448 argv[0], argv[1]);
449 exit(EXIT_FAILURE);
450 }
451 script_argv[script_argc] = (char *) calloc(strlen(token) + 1, sizeof(char));
452
453 //printf("arg %d: %s\n", script_argc, token);
454 strcpy(script_argv[script_argc], token);
455 script_argc++;
456 }
457
458 /* advance line to the next token */
459 line += strlen(line) + 1;
460 line += strspn(line, " \t");
461 } /* while line being tokenized */
462
463 /*
464 * Add NULL at the end and reallocate memory if necessary.
465 */
466 if(script_argc_max <= script_argc){
467 script_argv = realloc(script_argv, (script_argc+1) * sizeof(char *));
468 /* TODO(sissel): STOPPED HERE */
469 script_argc_max++;
470 }
471 *(script_argv + script_argc) = NULL;
472
473 if(script_argc > 0){
474 context.argc = script_argc;
475 context.argv = script_argv;
476 result = context_execute(&context);
477
478 /*
479 * Free the allocated memory for tokens.
480 */
481 for(int j = 0; j < script_argc; j++){
482 if(*(script_argv + j) != NULL){
483 free(*(script_argv + j));
484 }
485 }
486
487 script_argc = 0;
488 *script_argv = NULL;
489 }
490 }
491 fclose(input);
492
493
494 xdo_free(context.xdo);
495 if (context.windows != NULL) {
496 free(context.windows);
497 }
498
499 for(int i=0; i<script_argc+1; ++i) {
500 free(script_argv[i]);
501 }
502 free(script_argv);
503 return result;
504 }
505
args_main(int argc,char ** argv)506 int args_main(int argc, char **argv) {
507 int ret = 0;
508 int opt;
509 int option_index;
510
511 const char *usage = "Usage: %s <cmd> <args>\n";
512 static struct option long_options[] = {
513 { "help", no_argument, NULL, 'h' },
514 { "version", no_argument, NULL, 'v' },
515 { 0, 0, 0, 0 }
516 };
517
518 if (argc < 2) {
519 fprintf(stderr, usage, argv[0]);
520 cmd_help(NULL);
521 exit(1);
522 }
523
524 if (!strcasecmp(argv[1], "help")) {
525 cmd_help(NULL);
526 exit(EXIT_SUCCESS);
527 } else if (!strcasecmp(argv[1], "version")) {
528 cmd_version(NULL);
529 exit(EXIT_SUCCESS);
530 }
531
532 while ((opt = getopt_long_only(argc, argv, "++hv", long_options, &option_index)) != -1) {
533 switch (opt) {
534 case 'h':
535 cmd_help(NULL);
536 exit(EXIT_SUCCESS);
537 case 'v':
538 cmd_version(NULL);
539 exit(EXIT_SUCCESS);
540 default:
541 fprintf(stderr, usage, argv[0]);
542 exit(EXIT_FAILURE);
543 }
544 }
545
546 context_t context;
547 context.xdo = xdo_new(NULL);
548 context.prog = *argv;
549 argv++; argc--;
550 context.argc = argc;
551 context.argv = argv;
552 context.windows = NULL;
553 context.nwindows = 0;
554 context.have_last_mouse = False;
555 context.debug = (getenv("DEBUG") != NULL);
556
557 if (context.xdo == NULL) {
558 fprintf(stderr, "Failed creating new xdo instance.\n");
559 return 1;
560 }
561 context.xdo->debug = context.debug;
562
563 ret = context_execute(&context);
564
565 xdo_free(context.xdo);
566 if (context.windows != NULL) {
567 free(context.windows);
568 }
569
570 return ret;
571 } /* int args_main(int, char **) */
572
context_execute(context_t * context)573 int context_execute(context_t *context) {
574 int cmd_found = 0;
575 int i = 0;
576 char *cmd = NULL;
577 int ret = XDO_SUCCESS;
578
579 /* Loop until all argv is consumed. */
580 while (context->argc > 0 && ret == XDO_SUCCESS) {
581 cmd = context->argv[0];
582 cmd_found = 0;
583 for (i = 0; dispatch[i].name != NULL && !cmd_found; i++) {
584 if (!strcasecmp(dispatch[i].name, cmd)) {
585 cmd_found = 1;
586 optind = 0;
587 if (context->debug) {
588 fprintf(stderr, "command: %s\n", cmd);
589 }
590 ret = dispatch[i].func(context);
591 }
592 }
593
594 if (!cmd_found) {
595 fprintf(stderr, "%s: Unknown command: %s\n", context->prog, cmd);
596 fprintf(stderr, "Run '%s help' if you want a command list\n", context->prog);
597 ret = 1;
598 }
599 } /* while ... */
600 return ret;
601 } /* int args_main(int, char **) */
602
cmd_help(context_t * context)603 int cmd_help(context_t *context) {
604 int i;
605 printf("Available commands:\n");
606 for (i = 0; dispatch[i].name != NULL; i++)
607 printf(" %s\n", dispatch[i].name);
608
609 /* "help" can be invoked on errors, like when xdotool is given no arguments,
610 * so let's make sure we only consume if we have a context */
611 if (context != NULL) {
612 consume_args(context, 1);
613 }
614
615 return 0;
616 }
617
cmd_version(context_t * context)618 int cmd_version(context_t *context) {
619 xdotool_output(context, "xdotool version %s", xdo_version());
620 if (context != NULL) {
621 consume_args(context, 1);
622 }
623
624 return 0;
625 }
626
xdotool_debug(context_t * context,const char * format,...)627 void xdotool_debug(context_t *context, const char *format, ...) {
628 va_list args;
629
630 va_start(args, format);
631 if (context->debug) {
632 vfprintf(stderr, format, args);
633 fprintf(stderr, "\n");
634 }
635 } /* xdotool_debug */
636
xdotool_output(context_t * context,const char * format,...)637 void xdotool_output(context_t *context, const char *format, ...) {
638 context = context; /* Do something with context to avoid warnings */
639 va_list args;
640
641 va_start(args, format);
642 vfprintf(stdout, format, args);
643 fprintf(stdout, "\n");
644 fflush(stdout);
645 } /* xdotool_output */
646