Exemple #1
0
int cmd_windowreparent(context_t *context) {
  int ret = 0;
  char *cmd = *context->argv;
  const char *window_arg = "%1";

  int c;
  typedef enum {
    opt_unused, opt_help
  } optlist_t;
  static struct option longopts[] = {
    { "help", no_argument, NULL, opt_help },
    { 0, 0, 0, 0 },
  };
  static const char *usage = "Usage: %s [window_source=%1] window_destination\n";

  int option_index;
  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  if (!window_get_arg(context, 1, 0, &window_arg)) {
    fprintf(stderr, usage, cmd);
    return EXIT_FAILURE;
  }

  /* Permit using WINDOW STACK notation for the destination window, too */
  Window *destwindows = NULL;
  int ndestwindows = 0;
  window_list(context, context->argv[0], &destwindows, &ndestwindows, False); \

  if (ndestwindows > 1) {
    fprintf(stderr, "It doesn't make sense to have multiple destinations as the "
            "new parent window. Your destination selection '%s' resulted in %d "
            "windows.", context->argv[0], ndestwindows);
    return EXIT_FAILURE;
  }
  Window destination = destwindows[0];

  consume_args(context, 1);

  window_each(context, window_arg, {
    //printf("Reparenting %ld -> %ld\n", window, destination);
    ret = xdo_window_reparent(context->xdo, window, destination);
    if (ret) {
      fprintf(stderr, "xdo_window_reparent reported an error on for "
              "src=%ld, dest=%ld\n", window, destination);
    }
  }); /* window_each(...) */
int cmd_windowactivate(context_t *context) {
  int ret = 0;
  char *cmd = *context->argv;
  const char *window_arg = "%1";
  int opsync = 0;

  int c;
  typedef enum {
    opt_unused, opt_help, opt_sync
  } optlist_t;
  static struct option longopts[] = {
    { "help", no_argument, NULL, opt_help },
    { "sync", no_argument, NULL, opt_sync },
    { 0, 0, 0, 0 },
  };
  static const char *usage = 
    "Usage: %s [options] [window=%1]\n"
    "--sync    - only exit once the window is active (is visible + active)\n"
    HELP_SEE_WINDOW_STACK;

  int option_index;
  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
      case opt_help:
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      case opt_sync:
        opsync = 1;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  if (!window_get_arg(context, 0, 0, &window_arg)) {
    fprintf(stderr, usage, cmd);
    return EXIT_FAILURE;
  }

  window_each(context, window_arg, {
    ret = xdo_activate_window(context->xdo, window);
    if (ret) {
      fprintf(stderr, "xdo_activate_window on window:%ld reported an error\n",
              window);
      return ret;
    } else {
      if (opsync) {
        xdo_wait_for_window_active(context->xdo, window, 1);
      }
    }
  }); /* window_each(...) */
Exemple #3
0
int cmd_windowunmap(context_t *context) {
  int ret = 0;
  char *cmd = *context->argv;
  const char *window_arg = "%1";
  int opsync;

  int c;
  typedef enum {
    opt_unused, opt_help, opt_sync, opt_verbose
  } optlist_t;
  static struct option longopts[] = {
    { "help", no_argument, NULL, opt_help },
    { "sync", no_argument, NULL, opt_sync },
    { 0, 0, 0, 0 },
  };
  static const char *usage = 
    "Usage: %s [--sync] [window=%1]\n"
    "--sync    - only exit once the window has been unmapped (is hidden)\n"
    HELP_SEE_WINDOW_STACK;

  int option_index;
  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
      case opt_help:
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      case opt_sync:
        opsync = 1;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);
  
  if (!window_get_arg(context, 0, 0, &window_arg)) {
    fprintf(stderr, usage, cmd);
    return EXIT_FAILURE;
  }

  window_each(context, window_arg, {
    ret = xdo_window_unmap(context->xdo, window);
    if (ret) {
      fprintf(stderr, "xdo_window_unmap reported an error\n");
    }

    if (opsync) {
      xdo_window_wait_for_map_state(context->xdo, window, IsUnmapped);
    }
  }); /* window_each(...) */
int cmd_getmouselocation(context_t *context) {
  int x, y, screen_num;
  Window window;
  int ret;
  char *cmd = context->argv[0];

  int c;
  static struct option longopts[] = {
    { "help", no_argument, NULL, 'h' },
    { "shell", no_argument, NULL, 's' },
    { "prefix", required_argument, NULL, 'p' },
    { 0, 0, 0, 0 },
  };
  static const char *usage = 
    "Usage: %s [--shell] [--prefix <STR>]\n"
    "--shell      - output shell variables for use with eval\n"
    "--prefix STR - use prefix for shell variables names (max 16 chars) \n";
  int option_index;
  int output_shell = 0;
  char out_prefix[17] = {'\0'};

  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      case 's':
        output_shell = 1;
        break;
      case 'p':
        strncpy(out_prefix, optarg, sizeof(out_prefix)-1);
        out_prefix[ sizeof(out_prefix)-1 ] = '\0'; //just in case
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  ret = xdo_get_mouse_location2(context->xdo, &x, &y, &screen_num, &window);

  if (output_shell) {
    xdotool_output(context, "%sX=%d", out_prefix, x);
    xdotool_output(context, "%sY=%d", out_prefix, y);
    xdotool_output(context, "%sSCREEN=%d", out_prefix, screen_num);
    xdotool_output(context, "%sWINDOW=%d", out_prefix, window);
  } else {
    xdotool_output(context, "x:%d y:%d screen:%d window:%ld", x, y, screen_num, window);
  }
  return ret;
}
Exemple #5
0
int window_get_arg(context_t *context, int min_arg, int window_arg_pos,
                   const char **window_arg) {
  if (context->argc < min_arg) {
    fprintf(stderr, "Too few arguments (got %d, minimum is %d)\n",
            context->argc, min_arg);
    return False;
  } else if (context->argc == min_arg) {
    //fprintf(stderr, "Using default arg\n");
    /* nothing, keep default */
  } else if (context->argc > min_arg) {
    if (is_command(context->argv[min_arg])) {
      //fprintf(stderr, "arg is command, using default\n");
      /* keep default */
    } else {
      /* got enough args, let's use the window you asked for */
      //fprintf(stderr, "got enough args\n");
      *window_arg = context->argv[window_arg_pos];
      consume_args(context, 1);
    }
  }

  if (!window_is_valid(context, *window_arg)) {
    fprintf(stderr, "Invalid window '%s'\n", *window_arg);
    return False;
  }

  return True;
} /* int window_get_arg(context_t *, int, int, char **, int *) */
Exemple #6
0
int cmd_getwindowpid(context_t *context) {
  int pid;
  char *cmd = context->argv[0];

  int c;
  static struct option longopts[] = {
    { "help", no_argument, NULL, 'h' },
    { 0, 0, 0, 0 },
  };
  static const char *usage = 
    "Usage: %s [window=%1]\n"
    HELP_SEE_WINDOW_STACK;
  int option_index;

  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  const char *window_arg = "%1";
  if (!window_get_arg(context, 0, 0, &window_arg)) {
    fprintf(stderr, usage, cmd);
    return EXIT_FAILURE;
  }

  window_each(context, window_arg, {
    pid = xdo_window_get_pid(context->xdo, window);
    if (pid == 0) {
      /* TODO(sissel): probably shouldn't exit failure when iterating over
       * a list of windows. What should we do? */
      fprintf(stderr, "window %ld has no pid associated with it.\n", window);
      return EXIT_FAILURE;
    } else {
      xdotool_output(context, "%d", pid);
    }
  }); /* window_each(...) */
Exemple #7
0
int cmd_sleep(context_t *context) {
  char *cmd = *context->argv;
  int ret = EXIT_SUCCESS;
  int c;
  double duration_usec;

  typedef enum {
    opt_unused, opt_help
  } optlist_t;
  static struct option longopts[] = {
    { "help", no_argument, NULL, opt_help },
    { 0, 0, 0, 0 },
  };
  static const char *usage = 
    "Usage: %s seconds\n" \
    "Sleep a given number of seconds. Fractions of seconds are valid.\n";
  
  int option_index;
  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
      case opt_help:
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  if (context->argc == 0) {
    fprintf(stderr, "No arguments given.\n");
    fprintf(stderr, usage, cmd);
    return EXIT_FAILURE;
  }

  duration_usec = atof(context->argv[0]) * (1000000);
  usleep(duration_usec);
  consume_args(context, 1);
  return ret;
}
Exemple #8
0
int cmd_version(context_t *context) {
  xdotool_output(context, "xdotool version %s", xdo_version());
  if (context != NULL) {
    consume_args(context, 1);
  }

  return 0;
}
int cmd_get_desktop_viewport(context_t *context) {
  int ret = 0;
  char *cmd = context->argv[0];
  int shell_output = 0;

  int c;
  static struct option longopts[] = {
    { "help", no_argument, NULL, 'h' },
    { "shell", no_argument, NULL, 's' },
    { 0, 0, 0, 0 },
  };
  static const char *usage = "Usage: %s\n";
  int option_index;

  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      case 's':
        shell_output = 1;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  int x, y;
  consume_args(context, optind);
  ret = xdo_get_desktop_viewport(context->xdo, &x, &y);

  if (shell_output) {
    xdotool_output(context, "X=%d", x);
    xdotool_output(context, "Y=%d", y);
  } else {
    xdotool_output(context, "%d %d", x, y);
  }

  return ret;
}
Exemple #10
0
int cmd_getwindowname(context_t *context) {
  char *cmd = context->argv[0];

  int c;
  static struct option longopts[] = {
    { "help", no_argument, NULL, 'h' },
    { 0, 0, 0, 0 },
  };
  static const char *usage = 
    "Usage: %s [window=%1]\n"
    HELP_SEE_WINDOW_STACK;
  int option_index;

  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  const char *window_arg = "%1";
  if (!window_get_arg(context, 0, 0, &window_arg)) {
    fprintf(stderr, usage, cmd);
    return EXIT_FAILURE;
  }

  char *name;
  int name_len;
  int name_type;

  window_each(context, window_arg, {
    xdo_get_window_name(context->xdo, window, &name, &name_len, &name_type);
    xdotool_output(context, "%.*s", name_len, name);
    XFree(name);
  }); /* window_each(...) */
Exemple #11
0
int cmd_windowlower(context_t *context) {
  int ret = 0;
  char *cmd = *context->argv;
  const char *window_arg = "%1";

  int c;
  static struct option longopts[] = {
    { "help", no_argument, NULL, 'h' },
    { 0, 0, 0, 0 },
  };
  static const char *usage = 
    "Usage: %s [window=%1]\n"
    HELP_SEE_WINDOW_STACK;
  int option_index;

  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  if (!window_get_arg(context, 0, 0, &window_arg)) {
    fprintf(stderr, usage, cmd);
    return EXIT_FAILURE;
  }

  window_each(context, window_arg, {
    ret = xdo_lower_window(context->xdo, window);
    if (ret) {
      fprintf(stderr, "xdo_lower_window reported an error on window %ld\n",
              window);
    }
  }); /* window_each(...) */
Exemple #12
0
int cmd_getactivewindow(context_t *context) {
  Window window = 0;
  int ret;
  char *cmd = context->argv[0];

  int c;
  static struct option longopts[] = {
    { "help", no_argument, NULL, 'h' },
    { 0, 0, 0, 0 },
  };
  static const char *usage = "Usage: %s\n";
  int option_index;

  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  ret = xdo_window_get_active(context->xdo, &window);

  if (ret) {
    fprintf(stderr, "xdo_get_active_window reported an error\n");
  } else {
    /* only print if we're the last command */
    if (context->argc == 0) {
      window_print(window);
    }
    window_save(context, window);
  }

  return ret;
}
int cmd_set_desktop_viewport(context_t *context) {
    int ret = 0;
    char *cmd = context->argv[0];

    int c;
    static struct option longopts[] = {
        { "help", no_argument, NULL, 'h' },
        { 0, 0, 0, 0 },
    };
    static const char *usage = "Usage: %s x y\n";
    int option_index;

    while ((c = getopt_long_only(context->argc, context->argv, "+h",
                                 longopts, &option_index)) != -1) {
        switch (c) {
        case 'h':
            printf(usage, cmd);
            consume_args(context, context->argc);
            return EXIT_SUCCESS;
            break;
        default:
            fprintf(stderr, usage, cmd);
            return EXIT_FAILURE;
        }
    }

    consume_args(context, optind);
    int x, y;

    if (context->argc < 2) {
        fprintf(stderr, "Not enough arguments given.\n");
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }

    x = atoi(context->argv[0]);
    y = atoi(context->argv[1]);
    consume_args(context, 2);

    ret = xdo_set_desktop_viewport(context->xdo, x, y);
    return ret;
}
int cmd_set_num_desktops(context_t *context) {
  char *cmd = *context->argv;
  long ndesktops;

  int c;
  static struct option longopts[] = {
    { "help", no_argument, NULL, 'h' },
    { 0, 0, 0, 0 },
  };
  static const char *usage = "Usage: %s num_desktops\n";
  int option_index;

  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  if (context->argc < 1) {
    fprintf(stderr, usage, cmd);
    return 1;
  }

  ndesktops = strtol(context->argv[0], NULL, 0);

  consume_args(context, 1);
  return xdo_set_number_of_desktops(context->xdo, ndesktops);
}
Exemple #15
0
int cmd_help(context_t *context) {
  int i;
  printf("Available commands:\n");
  for (i = 0; dispatch[i].name != NULL; i++)
    printf("  %s\n", dispatch[i].name);

  /* "help" can be invoked on errors, like when xdotool is given no arguments,
   * so let's make sure we only consume if we have a context */
  if (context != NULL) {
    consume_args(context, 1);
  }

  return 0;
}
int cmd_mousemove_relative(context_t *context) {
	int dx, dy;
	int ret;
	char c;
	int option_index;
	char *cmd = *context->argv;
	
	static const char *usage =
	"\x1b[1;32mUsage:\x1b[0m \x1b[31m%s\x1b[0m \x1b[1;34m[--help] <x> <y>\x1b[0m \n"
    "\x1b[32mMove the pointer relative to the current position.\x1b[0m\n"
    "\x1b[1;34m--help       -h\x1b[0m           \x1b[32m- get help\x1b[0m\n";
	
	static struct option longopts[] = {
		{ "help", no_argument, NULL, 'h'},
		{ 0, 0, 0, 0 },
	};
	
	while ((c = getopt_long_only(context->argc, context->argv, "rh",
                               longopts, &option_index)) != -1) {
		switch (c) {
		  	case 'h':
				printf(usage, cmd);
				return ZDO_SUCCESS;
				break;
		  	default:
				zdo_alert('r', "unknown option.\n");
				fprintf(stderr, usage, cmd);
				return ZDO_ERROR;
		}
	}
	consume_args(context, optind);
	
	if (context->argc < 2) {
		zdo_alert('r', "You specified the wrong number of args (expected 2 coordinates).\n");
		fprintf(stderr, usage, cmd);
	}
	else {
		dx = atoi(context->argv[0]);
    	dy = atoi(context->argv[1]);
    	ret = zdo_mousemove_relative(dx, dy);
    	if (ret == ZDO_ERROR) {
    		zdo_alert('r', "zdo_mousemove_relative reported an error.\n");
		}
	}
	
	return ZDO_SUCCESS;
}
Exemple #17
0
int cmd_mouseup(context_t *context) {
	int ret;
	int option_index;
	char *cmd = *context->argv;
	char c;
	
	static const char *usage =
	"\x1b[1;32mUsage:\x1b[0m \x1b[31m%s\x1b[0m \x1b[1;34m[--help] <button>\x1b[0m \n"
    "\x1b[32mRelease a mouse button.\x1b[0m\n"
    "\x1b[32m1     for     left(default).\x1b[0m\n"
    "\x1b[32m2     for     middle.\x1b[0m\n"
    "\x1b[32m3     for     right.\x1b[0m\n"
    "\x1b[32m4     for     wheel up.\x1b[0m\n"
    "\x1b[32m5     for     wheel down.\x1b[0m\n"
    "\x1b[1;34m--help     -h\x1b[0m           \x1b[32m- get help\x1b[0m\n";
	
	static struct option longopts[] = {
		{ "help", no_argument, NULL, 'h'},
		{ 0, 0, 0, 0 },
	};
	
	while ((c = getopt_long_only(context->argc, context->argv, "h",
                               longopts, &option_index)) != -1) {
		switch (c) {
		  	case 'h':
				printf(usage, cmd);
				return ZDO_SUCCESS;
				break;
		  	default:
				zdo_alert('r', "unknown option.\n");
				fprintf(stderr, usage, cmd);
				return ZDO_ERROR;
		}
	}
	consume_args(context, optind);
	
	if (context->argc != 0)
		ret = zdo_mouseup(atoi(context->argv[0]));
	else
		ret = zdo_mouseup(1);
	if (ret == ZDO_ERROR) {
		zdo_alert('r', "zdo_mouseup reported an error.\n");
	}
	return 1;
}
Exemple #18
0
int cmd_search(context_t *context) {
  Window *list;
  xdo_search_t search;
  unsigned int nwindows;
  unsigned int i;
  int c;
  int op_sync = False;

  int search_title = 0;
  int search_name = 0;
  int out_shell = 0;
  char out_prefix[17] = {'\0'};
  int search_class = 0;
  int search_classname = 0;
  typedef enum {
    opt_unused, opt_title, opt_onlyvisible, opt_name, opt_shell, opt_prefix, opt_class, opt_maxdepth,
    opt_pid, opt_help, opt_any, opt_all, opt_screen, opt_classname, opt_desktop,
    opt_limit, opt_sync
  } optlist_t;
  struct option longopts[] = {
    { "all", no_argument, NULL, opt_all },
    { "any", no_argument, NULL, opt_any },
    { "class", no_argument, NULL, opt_class },
    { "classname", no_argument, NULL, opt_classname },
    { "help", no_argument, NULL, opt_help },
    { "maxdepth", required_argument, NULL, opt_maxdepth },
    { "name", no_argument, NULL, opt_name },
    { "shell", no_argument, NULL, opt_shell },
    { "prefix", required_argument, NULL, opt_prefix },
    { "onlyvisible", 0, NULL, opt_onlyvisible },
    { "pid", required_argument, NULL, opt_pid },
    { "screen", required_argument, NULL, opt_screen },
    { "title", no_argument, NULL, opt_title },
    { "desktop", required_argument, NULL, opt_desktop },
    { "limit", required_argument, NULL, opt_limit },
    { "sync", no_argument, NULL, opt_sync },
    { 0, 0, 0, 0 },
  };
  static const char *usage =
      "Usage: xdotool %s "
      "[options] regexp_pattern\n"
      "--class         check regexp_pattern agains the window class\n"
      "--classname     check regexp_pattern agains the window classname\n"
      "--maxdepth N    set search depth to N. Default is infinite.\n"
      "                -1 also means infinite.\n"
      "--onlyvisible   matches only windows currently visible\n"
      "--pid PID       only show windows belonging to specific process\n"
      "                Not supported by all X11 applications\n"
      "--screen N      only search a specific screen. Default is all screens\n"
      "--desktop N     only search a specific desktop number\n"
      "--limit N       break search after N results\n"
      "--name          check regexp_pattern agains the window name\n"
      "--shell         print results as shell array WINDOWS=( ... )\n"
      "--prefix STR    use prefix (max 16 chars) for array name STRWINDOWS\n"
      "--title         DEPRECATED. Same as --name.\n"
      "--all           Require all conditions match a window. Default is --any\n"
      "--any           Windows matching any condition will be reported\n"
      "--sync          Wait until a search result is found.\n"
      "-h, --help      show this help output\n"
      "\n"
      "If none of --name, --classname, or --class are specified, the \n"
      "defaults are: --name --classname --class\n";

  memset(&search, 0, sizeof(xdo_search_t));
  search.max_depth = -1;
  search.require = SEARCH_ANY;

  char *cmd = *context->argv;
  int option_index;

  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 0:
        break;
      case 'h':
      case opt_help:
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
      case opt_maxdepth:
        search.max_depth = strtol(optarg, NULL, 0);
        break;
      case opt_pid:
        search.pid = atoi(optarg);
        search.searchmask |= SEARCH_PID;
        break;
      case opt_any:
        search.require = SEARCH_ANY;
        break;
      case opt_all:
        search.require = SEARCH_ALL;
        break;
      case opt_screen:
        search.screen = strtoul(optarg, NULL, 0);
        search.searchmask |= SEARCH_SCREEN;
        break;
      case opt_onlyvisible:
        search.only_visible = True;
        search.searchmask |= SEARCH_ONLYVISIBLE;
        break;
      case opt_class:
        search_class = True;
        break;
      case opt_classname:
        search_classname = True;
        break;
      case opt_title:
        fprintf(stderr, "This flag is deprecated. Assuming you mean --name (the"
                " window name).\n");
        /* fall through */
      case opt_name:
        search_name = True;
        break;
      case opt_shell:
        out_shell = True;
        break;
      case opt_prefix:
        strncpy(out_prefix, optarg, sizeof(out_prefix)-1);
        out_prefix[ sizeof(out_prefix)-1 ] = '\0'; //just in case
        break;
      case opt_desktop:
        search.desktop = strtol(optarg, NULL, 0);
        search.searchmask |= SEARCH_DESKTOP;
        break;
      case opt_limit:
        search.limit = atoi(optarg);
        break;
      case opt_sync:
        op_sync = True;
        break;
      default:
        fprintf(stderr, "Invalid usage\n");
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  /* We require a pattern or a pid to search for */
  if (context->argc < 1 && search.pid == 0) {
    fprintf(stderr, usage, cmd);
    return EXIT_FAILURE;
  }

  if (context->argc > 0) {
    if (!search_title && !search_name && !search_class && !search_classname) {
      fprintf(stderr, "Defaulting to search window name, class, and classname\n");
      search.searchmask |= (SEARCH_NAME | SEARCH_CLASS | SEARCH_CLASSNAME);
      search_name = 1;
      search_class = 1;
      search_classname = 1;
    }

    if (search_title) {
      search.searchmask |= SEARCH_NAME;
      search.winname = context->argv[0];
    }
    if (search_name) {
      search.searchmask |= SEARCH_NAME;
      search.winname = context->argv[0];
    }
    if (search_class) {
      search.searchmask |= SEARCH_CLASS;
      search.winclass = context->argv[0];
    }
    if (search_classname) {
      search.searchmask |= SEARCH_CLASSNAME;
      search.winclassname = context->argv[0];
    }
    consume_args(context, 1);
  }

  do {
    xdo_search_windows(context->xdo, &search, &list, &nwindows);

    if ( (context->argc == 0) || out_shell ) {
      /* only print if we're the last command or printing to shell*/
      if (out_shell) printf("%s%s", out_prefix, "WINDOWS=(");
      for (i = 0; i < nwindows; i++) {
        window_print(list[i]);
      }
      if (out_shell) printf("%s",")\n");
    }

    if (op_sync && nwindows == 0) {
      xdotool_debug(context, "No search results, still waiting...");

      /* TODO(sissel): Make this tunable */
      usleep(500000);
    }
  } while (op_sync && nwindows == 0);

  /* Free old list as it's malloc'd by xdo_search_windows */
  if (context->windows != NULL) {
    free(context->windows);
  }
  context->windows = list;
  context->nwindows = nwindows;

  /* error if number of windows found is zero (behave like grep) 
  but return success when being used inside eval (--shell option)*/
  return (nwindows || out_shell ? EXIT_SUCCESS : EXIT_FAILURE);
}
Exemple #19
0
int cmd_key(context_t *context) {
  int ret = 0;
  int i, j;
  int c;
  char *cmd = *context->argv;
  charcodemap_t *active_mods = NULL;
  int active_mods_n;
  useconds_t key_delay = 12000;
  useconds_t repeat_delay = 0;
  int repeat = 1;
  const char *window_arg = NULL;
  int free_arg = 0;

  /* Options */
  int clear_modifiers = 0;

  static struct option longopts[] = {
    { "clearmodifiers", no_argument, NULL, 'c' },
    { "delay", required_argument, NULL, 'd' },
    { "repeat-delay", required_argument, NULL, 'R' },
    { "help", no_argument, NULL, 'h' },
    { "window", required_argument, NULL, 'w' },
    { "repeat", required_argument, NULL, 'r' },
    { 0, 0, 0, 0 },
  };

  static const char *usage = 
     "Usage: %s [options] <keysequence> [keysequence ...]\n"
     "--clearmodifiers     - clear active keyboard modifiers during keystrokes\n"
     "--delay DELAY        - Use DELAY milliseconds between keystrokes\n"
     "--repeat TIMES       - How many times to repeat the key sequence\n"
     "--repeat-delay DELAY - DELAY milliseconds between repetitions\n"
     "--window WINDOW      - send keystrokes to a specific window\n"
     "Each keysequence can be any number of modifiers and keys, separated by plus (+)\n"
     "  For example: alt+r\n"
     "\n"
     "Any letter or key symbol such as Shift_L, Return, Dollar, a, space are valid,\n"
     "including those not currently available on your keyboard.\n"
     "\n"
     "If no window is given, and there are windows in the stack, %1 is used. Otherwise\n"
     "the currently-focused window is used\n"
     HELP_CHAINING_ENDS;
  int option_index;

  while ((c = getopt_long_only(context->argc, context->argv, "+d:hcw:",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'w':
        window_arg = strdup(optarg);
        free_arg = 1;
        break;
      case 'c':
        clear_modifiers = 1;
        break;
      case 'h':
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      case 'd':
        /* Argument is in milliseconds, keysequence delay is in microseconds. */
        key_delay = strtoul(optarg, NULL, 0) * 1000;
        break;
      case 'r':
        repeat = atoi(optarg);
        if (repeat < 1) {
          fprintf(stderr, "Invalid '--repeat' value given: %s\n", optarg);
          return EXIT_FAILURE;
        }
        break;
      case 'R': // --repeat-delay
        /* Argument is in milliseconds, keysequence delay is in microseconds. */
        repeat_delay = strtoul(optarg, NULL, 0) * 1000;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  if (context->argc == 0) {
    fprintf(stderr, "You specified the wrong number of args.\n");
    fprintf(stderr, usage, cmd);
    return 1;
  }

  /* use %1 if there is a window stack */
  if (window_arg == NULL && context->nwindows > 0) {
    window_arg = "%1";
  }

  int (*keyfunc)(const xdo_t *, Window, const char *, useconds_t) = NULL;

  if (!strcmp(cmd, "key")) {
    keyfunc = xdo_send_keysequence_window;
  } else if (!strcmp(cmd, "keyup")) {
    keyfunc = xdo_send_keysequence_window_up;
  } else if (!strcmp(cmd, "keydown")) {
    keyfunc = xdo_send_keysequence_window_down;
  } else {
    fprintf(stderr, "Unknown command '%s'\n", cmd);
    return 1;
  }

  int max_arg = context->argc;
  window_each(context, window_arg, {
    if (clear_modifiers) {
      xdo_get_active_modifiers(context->xdo, &active_mods, &active_mods_n);
      xdo_clear_active_modifiers(context->xdo, window, active_mods, active_mods_n);
    }

    for (j = 0; j < repeat; j++) {
      for (i = 0; i < context->argc; i++) {
        if (is_command(context->argv[i])) {
          max_arg = i;
          break;
        }
        int tmp = keyfunc(context->xdo, window, context->argv[i], key_delay);
        if (tmp != 0) {
          fprintf(stderr,
                  "xdo_send_keysequence_window reported an error for string '%s'\n",
                  context->argv[i]);
        }
        ret += tmp;
      } /* each keysequence */

      /* Sleep if --repeat-delay given and not on the last repetition */
      if (repeat_delay > 0 && j < (repeat-1))  {
        usleep(repeat_delay);
      }
    } /* repeat */

    if (clear_modifiers) {
      xdo_set_active_modifiers(context->xdo, window, active_mods, active_mods_n);
      free(active_mods);
    }
  }); /* window_each(...) */
Exemple #20
0
int cmd_click(context_t *context) {
	int ret;
	int option_index;
	char *cmd = *context->argv;
	char c;
	int repeat = 1;
	unsigned long delay = 100; // 100ms
	
	static const char *usage =
	"\x1b[1;32mUsage:\x1b[0m \x1b[31m%s\x1b[0m \x1b[1;34m[--repeat N] [--delay MS] [--help] <button>\x1b[0m \n"
    "\x1b[32mClick a mouse button.\x1b[0m\n"
    "\x1b[32m1     for     left(default).\x1b[0m\n"
    "\x1b[32m2     for     middle.\x1b[0m\n"
    "\x1b[32m3     for     right.\x1b[0m\n"
    "\x1b[32m4     for     wheel up.\x1b[0m\n"
    "\x1b[32m5     for     wheel down.\x1b[0m\n"
    "\x1b[1;34m--repeat   -r N\x1b[0m         \x1b[32m- repeat N times to click. Default is 1.\x1b[0m\n"
    "\x1b[1;34m--delay    -d MS\x1b[0m        \x1b[32m- delay in MS between clicks. Default is 100ms.\x1b[0m\n"
    "\x1b[1;34m--help     -h\x1b[0m           \x1b[32m- get help\x1b[0m\n";
	
	static struct option longopts[] = {
		{ "repeat", required_argument, NULL, 'r'},
		{ "delay", required_argument, NULL, 'd' },
		{ "help", no_argument, NULL, 'h'},
		{ 0, 0, 0, 0 },
	};
	
	while ((c = getopt_long_only(context->argc, context->argv, "r:d:h",
                               longopts, &option_index)) != -1) {
		switch (c) {
		  	case 'r':
				repeat = atoi(optarg);
				if (repeat <= 0) { 
					zdo_alert('r', "Invalid repeat value '%s' (must be >= 1).\n", optarg);
					//fprintf(stderr, "Invalid repeat value '%s' (must be >= 1)\n", optarg);
					fprintf(stderr, usage, cmd);
					return ZDO_ERROR;
				}
				break;
		    case 'd':
				delay = strtoul(optarg, NULL, 10);
				break;
		  	case 'h':
				printf(usage, cmd);
				return ZDO_SUCCESS;
				break;
		  	default:
				zdo_alert('r', "unknown option.\n");
				fprintf(stderr, usage, cmd);
				return ZDO_ERROR;
		}
	}
	consume_args(context, optind);
	
	if (context->argc != 0)
		ret = zdo_click(atoi(context->argv[0]), repeat, delay * 1000);
	else
		ret = zdo_click(1, repeat, delay * 1000);
	if (ret == ZDO_ERROR) {
		zdo_alert('r', "zdo_click reported an error.\n");
	}
	return 1;
}
int cmd_mousemove_relative(context_t *context) {
  int x, y;
  int ret = 0;
  char *cmd = *context->argv;
  int polar_coordinates = 0;
  int clear_modifiers = 0;
  int opsync = 0;
  int origin_x = -1, origin_y = -1;

  xdo_active_mods_t *active_mods = NULL;
  int c;
  typedef enum {
    opt_unused, opt_help, opt_sync, opt_clearmodifiers, opt_polar
  } optlist_t;
  static struct option longopts[] = {
    { "help", no_argument, NULL, opt_help },
    { "sync", no_argument, NULL, opt_sync },
    { "polar", no_argument, NULL, opt_polar },
    { "clearmodifiers", no_argument, NULL, opt_clearmodifiers },
    { 0, 0, 0, 0 },
  };
  static const char *usage =
      "Usage: %s [options] <x> <y>\n"
      "-c, --clearmodifiers      - reset active modifiers (alt, etc) while typing\n"
      "-p, --polar               - Use polar coordinates. X as an angle, Y as distance\n"
      "--sync                    - only exit once the mouse has moved\n"
      "\n"
      "Using polar coordinate mode makes 'x' the angle (in degrees) and\n"
      "'y' the distance.\n"
      "\n"
      "If you want to use negative numbers for a coordinate, you'll need to\n"
      "invoke it this way (with the '--'):\n"
      "   %s -- -20 -15\n"
      "otherwise, normal usage looks like this:\n"
      "   %s 100 140\n";
  int option_index;

  while ((c = getopt_long_only(context->argc, context->argv, "+cph",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
      case opt_help:
        printf(usage, cmd, cmd, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      case 'p':
      case opt_polar:
        polar_coordinates = 1;
        break;
      case opt_sync:
        opsync = 1;
        break;
      case 'c':
      case opt_clearmodifiers:
        clear_modifiers = 1;
        break;
      default:
        fprintf(stderr, usage, cmd, cmd, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  if (context->argc < 2) {
    fprintf(stderr, usage, cmd, cmd, cmd);
    fprintf(stderr, "You specified the wrong number of args (expected 2).\n");
    return EXIT_FAILURE;
  }

  x = atoi(context->argv[0]);
  y = atoi(context->argv[1]);
  consume_args(context, 2);

  /* Quit early if we don't have to move. */
  if (x == 0 && y == 0) {
    return EXIT_SUCCESS;
  }

  if (polar_coordinates) {
    /* The original request for polar support was that '0' degrees is up
     * and that rotation was clockwise, so 0 is up, 90 right, 180 down, 270
     * left. This conversion can be done with (360 - degrees) + 90 */
    double radians = ((360 - x) + 90) * (M_PI / 180);
    double distance = y;
    x = (cos(radians) * distance);

    /* Negative sin, since screen Y coordinates are descending, where cartesian
     * is ascending */
    y = (-sin(radians) * distance);
  }
 
  if (clear_modifiers) {
    active_mods = xdo_get_active_modifiers(context->xdo);
    xdo_clear_active_modifiers(context->xdo, CURRENTWINDOW, active_mods);
  }

  if (opsync) {
    xdo_mouselocation(context->xdo, &origin_x, &origin_y, NULL);
  }

  ret = xdo_mousemove_relative(context->xdo, x, y);

  if (ret) {
    fprintf(stderr, "xdo_mousemove_relative reported an error\n");
  } else {
    if (opsync) {
      /* Wait until the mouse moves away from its current position */
      xdo_mouse_wait_for_move_from(context->xdo, origin_x, origin_y);
    }
  }

  if (clear_modifiers) {
    xdo_set_active_modifiers(context->xdo, CURRENTWINDOW, active_mods);
    xdo_free_active_modifiers(active_mods);
  }

  return ret;
}
Exemple #22
0
int cmd_type(context_t *context) {
  int ret = 0;
  int i;
  int c;
  char *cmd = *context->argv;
  char *window_arg = NULL;
  int arity = -1;
  char *terminator = NULL;
  char *file = NULL;

  FILE *input = NULL;
  char *buffer = NULL;
  char *marker = NULL;
  size_t bytes_read = 0;

  char **data = NULL; /* stuff to type */
  int data_count = 0;
  int args_count = 0;
  charcodemap_t *active_mods = NULL;
  int active_mods_n;

  /* Options */
  int clear_modifiers = 0;
  useconds_t delay = 12000; /* 12ms between keystrokes default */

  typedef enum {
    opt_unused, opt_clearmodifiers, opt_delay, opt_help, opt_window, opt_args,
    opt_terminator, opt_file
  } optlist_t;

  struct option longopts[] = {
    { "clearmodifiers", no_argument, NULL, opt_clearmodifiers },
    { "delay", required_argument, NULL, opt_delay },
    { "help", no_argument, NULL, opt_help },
    { "window", required_argument, NULL, opt_window },
    { "args", required_argument, NULL, opt_args },
    { "terminator", required_argument, NULL, opt_terminator },
    { "file", required_argument, NULL, opt_file },
    { 0, 0, 0, 0 },
  };

  static const char *usage =
    "Usage: %s [--window windowid] [--delay milliseconds] "
    "<things to type>\n"
    "--window <windowid>    - specify a window to send keys to\n"
    "--delay <milliseconds> - delay between keystrokes\n"
    "--clearmodifiers       - reset active modifiers (alt, etc) while typing\n"
    "--args N  - how many arguments to expect in the exec command. This is\n"
    "            useful for ending an exec and continuing with more xdotool\n"
    "            commands\n"
    "--terminator TERM - similar to --args, specifies a terminator that\n"
    "                    marks the end of 'exec' arguments. This is useful\n"
    "                    for continuing with more xdotool commands.\n"
    "--file <filepath> - specify a file, the contents of which will be\n"
    "                    be typed as if passed as an argument. The filepath\n"
    "                    may also be '-' to read from stdin.\n"
            "-h, --help             - show this help output\n";
  int option_index;

  while ((c = getopt_long_only(context->argc, context->argv, "+w:d:ch",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case opt_window:
        window_arg = strdup(optarg);
        break;
      case opt_delay:
        /* --delay is in milliseconds, convert to microseconds */
        delay = strtoul(optarg, NULL, 0) * 1000;
        break;
      case opt_clearmodifiers:
        clear_modifiers = 1;
        break;
      case opt_help:
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      case opt_args:
        arity = atoi(optarg);
        break;
      case opt_terminator:
        terminator = strdup(optarg);
        break;
      case opt_file:
	file = strdup(optarg);
	break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  if (context->argc == 0 && file == NULL) {
    fprintf(stderr, "You specified the wrong number of args.\n");
    fprintf(stderr, usage, cmd);
    return 1;
  }

  if (arity > 0 && terminator != NULL) {
    fprintf(stderr, "Don't use both --terminator and --args.\n");
    return EXIT_FAILURE;
  }

  if (context->argc < arity) {
    fprintf(stderr, "You said '--args %d' but only gave %d arguments.\n",
            arity, context->argc);
    return EXIT_FAILURE;
  }

  if (file != NULL) {
    data = calloc(1 + context->argc, sizeof(char *));

    /* determine whether reading from a file or from stdin */
    if (!strcmp(file, "-")) {
      input = fdopen(0, "r");
    } else {
      input = fopen(file, "r");
      if (input == NULL) {
        fprintf(stderr, "Failure opening '%s': %s\n", file, strerror(errno));
        return EXIT_FAILURE;
      }
    }

    while (feof(input) == 0) {
      marker = realloc(buffer, bytes_read + 4096);
      if (marker == NULL) {
        fprintf(stderr, "Failure allocating for '%s': %s\n", file, strerror(errno));
        return EXIT_FAILURE;
      }

      buffer = marker;
      marker = buffer + bytes_read;
      if (fgets(marker, 4096, input) != NULL) {
        bytes_read = (marker - buffer) + strlen(marker);
      }

      if (ferror(input) != 0) {
        fprintf(stderr, "Failure reading '%s': %s\n", file, strerror(errno));
        return EXIT_FAILURE;
      }
    }

    data[0] = buffer;
    data_count++;

    fclose(input);
  }
  else {
    data = calloc(context->argc, sizeof(char *));
  }

  /* Apply any --arity or --terminator */
  for (i=0; i < context->argc; i++) {
    if (arity > 0 && i == arity) {
      data[data_count] = NULL;
      break;
    }

    /* if we have a terminator and the current argument matches it... */
    if (terminator != NULL && strcmp(terminator, context->argv[i]) == 0) {
      data[data_count] = NULL;
      args_count++; /* Consume the terminator, too */
      break;
    }

    data[data_count] = strdup(context->argv[i]);
    xdotool_debug(context, "Exec arg[%d]: %s", i, data[data_count]);
    data_count++;
    args_count++;
  }

  window_each(context, window_arg, {
    if (clear_modifiers) {
      xdo_get_active_modifiers(context->xdo, &active_mods, &active_mods_n);
      xdo_clear_active_modifiers(context->xdo, window, active_mods, active_mods_n);
    }

    for (i = 0; i < data_count; i++) {
      //printf("Typing: '%s'\n", context->argv[i]);
      int tmp = xdo_enter_text_window(context->xdo, window, data[i], delay);

      if (tmp) {
        fprintf(stderr, "xdo_enter_text_window reported an error\n");
      }

      ret += tmp;
    }

    if (clear_modifiers) {
      xdo_set_active_modifiers(context->xdo, window, active_mods, active_mods_n);
      free(active_mods);
    }
  }); /* window_each(...) */
Exemple #23
0
int cmd_windowsize(context_t *context) {
  int ret = 0;
  unsigned int width, height;
  int is_width_percent = 0, is_height_percent = 0;
  int c;
  int opsync = 0;

  int use_hints = 0;
  typedef enum {
    opt_unused, opt_help, opt_usehints, opt_sync
  } optlist_t;
  struct option longopts[] = {
    { "usehints", 0, NULL, opt_usehints },
    { "help", no_argument, NULL, opt_help },
    { "sync", no_argument, NULL, opt_sync },
    { 0, 0, 0, 0 },
  };

  int size_flags = 0;
  char *cmd = *context->argv;
  int option_index;
  static const char *usage =
            "Usage: %s [--sync] [--usehints] [window=%1] width height\n"
            HELP_SEE_WINDOW_STACK
            "--usehints  - Use window sizing hints (like font size in terminals)\n"
            "--sync      - only exit once the window has resized\n";


  while ((c = getopt_long_only(context->argc, context->argv, "+uh",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
      case opt_help:
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
      case 'u':
      case opt_usehints:
        use_hints = 1;
        break;
      case opt_sync:
        opsync = 1;
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  const char *window_arg = "%1";

  if (!window_get_arg(context, 2, 0, &window_arg)) {
    fprintf(stderr, "Invalid argument count, got %d, expected %d\n", 
            3, context->argc);
    fprintf(stderr, usage, cmd);
    return EXIT_FAILURE;
  }

  /* Use percentage if given a percent. */
  if (strchr(context->argv[0], '%')) {
    is_width_percent = 1;
  }

  if (strchr(context->argv[1], '%')) {
    is_height_percent = 1;
  }

  if (use_hints) {
    if (!is_height_percent) {
      size_flags |= SIZE_USEHINTS_Y;
    }
    if (!is_width_percent) {
      size_flags |= SIZE_USEHINTS_X;
    }
  }

  width = (unsigned int)strtoul(context->argv[0], NULL, 0);
  height = (unsigned int)strtoul(context->argv[1], NULL, 0);
  consume_args(context, 2);

  XWindowAttributes wattr;
  unsigned int original_w, original_h;
  unsigned int root_w, root_h; /* for percent */

  window_each(context, window_arg, {
    if (is_width_percent || is_height_percent) {
      Window root = 0;
      XGetWindowAttributes(context->xdo->xdpy, window, &wattr);
      root = wattr.root;
      xdo_get_window_size(context->xdo, root, &root_w, &root_h);

      if (is_width_percent) {
        width = (root_w * width / 100);
      }

      if (is_height_percent) {
        height = (root_h * height / 100);
      }
    }

    if (opsync) {
      unsigned int w = width;
      unsigned int h = height;
      xdo_get_window_size(context->xdo, window, &original_w, &original_h);
      if (size_flags & SIZE_USEHINTS_X) {
        xdo_translate_window_with_sizehint(context->xdo, window, w, h, &w, NULL);
      }
      if (size_flags & SIZE_USEHINTS_Y) {
        xdo_translate_window_with_sizehint(context->xdo, window, w, h, NULL, &h);
      }

      if (original_w == w && original_h == h) {
        /* Skip, this window doesn't need to move. */
        break;
      }
    }

    ret = xdo_set_window_size(context->xdo, window, width, height, size_flags);
    if (ret) {
      fprintf(stderr, "xdo_set_window_size on window:%ld reported an error\n",
              window);
      return ret;
    }
    if (opsync) {
      //xdo_wait_for_window_size(context->xdo, window, width, height, 0,
                               //SIZE_TO);
      xdo_wait_for_window_size(context->xdo, window, original_w, original_h, 0,
                               SIZE_FROM);
    }
  }); /* window_each(...) */
Exemple #24
0
int cmd_exec(context_t *context) {
  char *cmd = *context->argv;
  char **command = NULL;
  int command_count = 0;
  int ret = EXIT_SUCCESS;
  int opsync = 0;
  int arity = -1;
  char *terminator = NULL;
  int c, i;

  typedef enum {
    opt_unused, opt_help, opt_sync, opt_args, opt_terminator
  } optlist_t;
  static struct option longopts[] = {
    { "help", no_argument, NULL, opt_help },
    { "sync", no_argument, NULL, opt_sync },
    { "args", required_argument, NULL, opt_args },
    { "terminator", required_argument, NULL, opt_terminator },
    { 0, 0, 0, 0 },
  };
  static const char *usage = 
    "Usage: %s [options] command [arg1 arg2 ...] [terminator]\n"
    "--sync    - only exit when the command given finishes. The default\n"
    "            is to fork a child process and continue.\n"
    "--args N  - how many arguments to expect in the exec command. This is\n"
    "            useful for ending an exec and continuing with more xdotool\n"
    "            commands\n"
    "--terminator TERM - similar to --args, specifies a terminator that\n"
    "                    marks the end of 'exec' arguments. This is useful\n"
    "                    for continuing with more xdotool commands.\n"
    "\n"
    "Unless --args OR --terminator is specified, the exec command is assumed\n"
    "to be the remainder of the command line.\n";
  
  int option_index;
  while ((c = getopt_long_only(context->argc, context->argv, "+h",
                               longopts, &option_index)) != -1) {
    switch (c) {
      case 'h':
      case opt_help:
        printf(usage, cmd);
        consume_args(context, context->argc);
        return EXIT_SUCCESS;
        break;
      case opt_sync:
        opsync = 1;
        break;
      case opt_args:
        arity = atoi(optarg);
        break;
      case opt_terminator:
        terminator = strdup(optarg);
        break;
      default:
        fprintf(stderr, usage, cmd);
        return EXIT_FAILURE;
    }
  }

  consume_args(context, optind);

  if (context->argc == 0) {
    fprintf(stderr, "No arguments given.\n");
    fprintf(stderr, usage, cmd);
    return EXIT_FAILURE;
  }

  if (arity > 0 && terminator != NULL) {
    fprintf(stderr, "Don't use both --terminator and --args.\n");
    return EXIT_FAILURE;
  }

  if (context->argc < arity) {
    fprintf(stderr, "You said '--args %d' but only gave %d arguments.\n",
            arity, context->argc);
    return EXIT_FAILURE;
  }

  command = calloc(context->argc + 1, sizeof(char *));

  for (i=0; i < context->argc; i++) {
    if (arity > 0 && i == arity) {
      break;
    }

    /* if we have a terminator and the current argument matches it... */
    if (terminator != NULL && strcmp(terminator, context->argv[i]) == 0) {
      command_count++; /* Consume the terminator, too */
      break;
    }

    command[i] = strdup(context->argv[i]);
    command_count = i + 1; /* i starts at 0 */
    xdotool_debug(context, "Exec arg[%d]: %s", i, command[i]);
  }
  command[i] = NULL;
  
  pid_t child;
  child = fork();
  if (child == 0) { /* child */
    execvp(command[0], command);

    /* if we get here, there was an error */
    perror("execvp failed");
    exit(errno);
  } else { /* parent */
    if (opsync) {
      int status = 0;
      waitpid(child, &status, 0);
      ret = WEXITSTATUS(status);
    }
  }

  consume_args(context, command_count);
  if (terminator != NULL) {
    free(terminator);
  }

  for (i=0; i < command_count; i++) {
    free(command[i]);
  }
  free(command);
  return ret;
}