加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
xdotool.c 18.71 KB
一键复制 编辑 原始数据 按行查看 历史
KT-lcz 提交于 2022-06-30 13:09 . Keep code style & add va_end (#372)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
/* xdotool
*
* command line interface to the xdo library
*
* getwindowfocus contributed by Lee Pumphret
* keyup/down contributed by Lee Pumphret
*
* vi: expandtab shiftwidth=2 softtabstop=2
*/
#define _GNU_SOURCE 1
#ifndef __USE_BSD
#define __USE_BSD /* for strdup on linux/glibc */
#endif /* __USE_BSD */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>
#include "xdo.h"
#include "xdotool.h"
#include "xdo_cmd.h"
static int script_main(int argc, char **argv);
static int args_main(int argc, char **argv);
void consume_args(context_t *context, int argc) {
if (argc > context->argc) {
fprintf(stderr,
"Can't consume %d args; are only %d available. This is a bug.\n",
argc, context->argc);
context->argv += context->argc;
context->argc = 0;
return;
}
context->argv += argc;
context->argc -= argc;
} /* void consume_args(context_t *, int) */
void window_save(context_t *context, Window window) {
free(context->windows);
context->windows = calloc(1, sizeof(Window));
context->nwindows = 1;
context->windows[0] = window;
} /* void window_save(context_t *, Window) */
static int window_is_valid(context_t *context, const char *window_arg) {
if (window_arg == NULL) {
return True;
}
if (window_arg[0] != '%') {
return True;
}
/* Selected a window with %N or %@, but are there windows on the stack? */
if (context->nwindows == 0) {
fprintf(stderr, "There are no windows in the stack\n");
return False;
}
if (window_arg[1] == '\0') {
fprintf(stderr, "Invalid window stack selection '%s'\n", window_arg);
return False;
}
if (window_arg[1] == '@') {
return True;
}
int window_index = atoi(window_arg + 1);
if (abs(window_index - 1) >= context->nwindows || (window_index == 0)) {
fprintf(stderr, "Invalid window stack selection '%s' (out of range)\n", window_arg);
return False;
}
return True;
} /* int window_is_valid(context_t *, const char *) */
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) {
/* nothing, keep default */
} else if (context->argc > min_arg) {
if (is_command(context->argv[min_arg])) {
/* keep default */
} else {
/* got enough args, let's use the window you asked for */
*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 *) */
void window_list(context_t *context, const char *window_arg,
Window **windowlist_ret, int *nwindows_ret,
const int add_to_list) {
/* If window_arg is NULL and we have windows in the list, use the list.
* If window_arg is "%@" and we have windows in the list, use the list.
* If window_arg is "%N" and we have windows in the list, use Nth window.
* 'N' above must be a positive number.
* Otherwise, assume it's a window id.
*
* TODO(sissel): Not implemented yet:
* If window_arg is "%r" it means the root window of the current screen.
* If window_arg is "%q" it means we will wait for you to select a window
* by clicking on it. (May not be necessary since we have 'selectwindow')
* If window_arg is "%c" it means the currently-active window.
*/
*nwindows_ret = 0;
*windowlist_ret = NULL;
if (window_arg != NULL && window_arg[0] == '%') {
if (context->nwindows == 0) {
fprintf(stderr, "There are no windows on the stack, Can't continue.\n");
return;
}
if (strlen(window_arg) < 2) {
fprintf(stderr, "Invalid window selection '%s'\n", window_arg);
return;
}
/* options.
* %N selects the Nth window. %1, %2, %-1 (last), %-2, etc.
* %@ selects all
*/
if (window_arg[1] == '@') {
*windowlist_ret = context->windows;
*nwindows_ret = context->nwindows;
} else if (window_arg[1] == 'q') {
/* TODO(sissel): Wait for you to click on the window. */
} else if (window_arg[1] == 'r') {
/* TODO(sissel): Get the root window of the current screen */
} else if (window_arg[1] == 'c') {
/* TODO(sissel): Get the current window */
} else {
/* Otherwise assume %N */
int window_index = atoi(window_arg + 1);
if (window_index < 0) {
/* negative offset */
window_index = context->nwindows + window_index;
}
if (window_index > context->nwindows || window_index <= 0) {
fprintf(stderr, "%d is out of range (only %d windows in list)\n",
window_index, context->nwindows);
return;
}
/* Subtract 1 since %1 is the first window in the list */
context->window_placeholder[0] = context->windows[window_index - 1];
*windowlist_ret = context->window_placeholder;
*nwindows_ret = 1;
}
} else {
/* Otherwise, window_arg is either invalid or null. Default to CURRENTWINDOW
*/
/* We can't return a pointer to a piece of the stack in this function,
* so we'll store the window in the context_t and return a pointer
* to that.
*/
Window window = CURRENTWINDOW;
if (window_arg != NULL) {
window = (Window)strtol(window_arg, NULL, 0);
}
context->window_placeholder[0] = window;
*nwindows_ret = 1;
*windowlist_ret = context->window_placeholder;
}
if (add_to_list) {
/* save the window to the windowlist */
}
}
struct dispatch {
const char *name;
int (*func)(context_t *context);
} dispatch[] = {
/* Query functions */
{ "getactivewindow", cmd_getactivewindow, },
{ "getwindowfocus", cmd_getwindowfocus, },
{ "getwindowname", cmd_getwindowname, },
{ "getwindowclassname", cmd_getwindowclassname},
{ "getwindowpid", cmd_getwindowpid, },
{ "getwindowgeometry", cmd_getwindowgeometry, },
{ "getdisplaygeometry", cmd_get_display_geometry, },
{ "search", cmd_search, },
{ "selectwindow", cmd_window_select, },
/* Help me! */
{ "help", cmd_help, },
{ "version", cmd_version, },
/* Action functions */
{ "behave", cmd_behave, },
{ "behave_screen_edge", cmd_behave_screen_edge, },
{ "click", cmd_click, },
{ "getmouselocation", cmd_getmouselocation, },
{ "key", cmd_key, },
{ "keydown", cmd_key, },
{ "keyup", cmd_key, },
{ "mousedown", cmd_mousedown, },
{ "mousemove", cmd_mousemove, },
{ "mousemove_relative", cmd_mousemove_relative, },
{ "mouseup", cmd_mouseup, },
{ "set_window", cmd_set_window, },
{ "type", cmd_type, },
{ "windowactivate", cmd_windowactivate, },
{ "windowfocus", cmd_windowfocus, },
{ "windowkill", cmd_windowkill, },
{ "windowclose", cmd_windowclose, },
{ "windowquit", cmd_windowquit, },
{ "windowmap", cmd_windowmap, },
{ "windowminimize", cmd_windowminimize, },
{ "windowmove", cmd_windowmove, },
{ "windowraise", cmd_windowraise, },
{ "windowreparent", cmd_windowreparent, },
{ "windowsize", cmd_windowsize, },
{ "windowstate", cmd_windowstate, },
{ "windowunmap", cmd_windowunmap, },
{ "set_num_desktops", cmd_set_num_desktops, },
{ "get_num_desktops", cmd_get_num_desktops, },
{ "set_desktop", cmd_set_desktop, },
{ "get_desktop", cmd_get_desktop, },
{ "set_desktop_for_window", cmd_set_desktop_for_window, },
{ "get_desktop_for_window", cmd_get_desktop_for_window, },
{ "get_desktop_viewport", cmd_get_desktop_viewport, },
{ "set_desktop_viewport", cmd_set_desktop_viewport, },
{ "exec", cmd_exec, },
{ "sleep", cmd_sleep, },
{ NULL, NULL, },
};
int is_command(char* cmd) {
int i;
for (i = 0; dispatch[i].name != NULL; i++) {
if (!strcasecmp(dispatch[i].name, cmd)) {
return 1;
}
}
return 0;
}
int main(int argc, char **argv) {
return xdotool_main(argc, argv);
}
int xdotool_main(int argc, char **argv) {
/* If argv[1] is a file or "-", read commands from file or stdin,
* else use commands from argv.
*/
struct stat data;
int stat_ret;
if (argc >= 2) {
/* See if the first argument is an existing file */
stat_ret = stat(argv[1], &data);
int i = 0;
int argv1_is_command= 0;
for (i = 0; dispatch[i].name != NULL; i++) {
if (!strcasecmp(dispatch[i].name, argv[1])) {
argv1_is_command = 1;
break;
}
}
if (!argv1_is_command && (strcmp(argv[1], "-") == 0 || stat_ret == 0)) {
return script_main(argc, argv);
}
}
return args_main(argc, argv);
}
int script_main(int argc, char **argv) {
/* Tokenize the input file while expanding positional parameters and
* environment variables. Pass the resulting argument list to
* args_main().
*/
int result = XDO_SUCCESS;
FILE *input = NULL;
char buffer[4096];
int script_argc = 0;
int script_argc_max = 0;
size_t token_len, total_len = 0;
char *token = NULL;
const char *path = argv[1];
char **script_argv = (char **) calloc(1, sizeof(char *));
int continues_next_buffer = 0;
if (script_argv == NULL) {
fprintf(stderr, "%s: error: failed to allocate memory while parsing `%s'.\n",
argv[0], argv[1]);
exit(EXIT_FAILURE);
}
/* determine whether reading from a file or from stdin */
if (!strcmp(path, "-")) {
input = fdopen(0, "r");
} else {
input = fopen(path, "r");
if (input == NULL) {
fprintf(stderr, "Failure opening '%s': %s\n", path, strerror(errno));
return EXIT_FAILURE;
}
}
context_t context;
context.xdo = xdo_new(NULL);
context.prog = *argv;
context.windows = NULL;
context.nwindows = 0;
context.have_last_mouse = False;
context.debug = (getenv("DEBUG") != NULL);
if (context.xdo == NULL) {
fprintf(stderr, "Failed creating new xdo instance\n");
return EXIT_FAILURE;
}
context.xdo->debug = context.debug;
/* read input... */
while (fgets(buffer, sizeof(buffer), input) != NULL) {
char *line = buffer;
const size_t end = strcspn(line, "\n");
/* replace newline with nul */
line[end] = '\0';
/* tokenize line into script_argv... */
while (*line != '\0') {
/* skip leading whitespace */
line += strspn(line, " \t");
/* ignore comment lines */
if (line[0] == '#') {
line += strlen(line);
continue;
}
/* modify line to contain the current token. Tokens are
* separated by whitespace, or quoted with single/double quotes.
*/
if (line[0] == '"') {
line++;
line[strcspn(line, "\"")] = '\0';
}
else if (line[0] == '\'') {
line++;
line[strcspn(line, "\'")] = '\0';
}
else {
line[strcspn(line, " \t")] = '\0';
}
/* if a token begins with "$", append the corresponding
* positional parameter or environment variable to
* script_argv...
*/
if (line[0] == '$') {
/* ignore dollar sign */
line++;
if (isdigit(line[0])) {
int pos;
/* get the position of this parameter in argv */
pos = atoi(line) + 1; /* $1 is actually index 2 in the argv array */
/* bail if no argument was given for this parameter */
if (pos >= argc) {
fprintf (stderr, "%s: error: `%s' needs at least %d %s; only %d given\n",
argv[0], argv[1], pos - 1, pos == 2 ? "argument" : "arguments",
argc - 2);
return EXIT_FAILURE;
}
/* use command line argument */
token = argv[pos];
}
else {
/* use environment variable */
token = getenv(line);
if (token == NULL) {
/* since it's not clear what we should do if this env var is not
* present, let's abort */
fprintf(stderr, "%s: error: environment variable $%s is not set.\n",
argv[0], line);
return EXIT_FAILURE;
}
}
}
else {
/* use the verbatim token */
token = line;
}
token_len = strlen(token);
if (token_len == 0) {
continue; /* nothing there */
}
/* append token */
/* allocate memory for the new token if needed */
if (script_argc + 1 > script_argc_max) {
script_argv = realloc(script_argv, (script_argc + 1) * sizeof(char *));
if (script_argv == NULL) {
fprintf(stderr, "%s: error: failed to allocate memory while parsing `%s'.\n",
argv[0], argv[1]);
exit(EXIT_FAILURE);
}
script_argv[script_argc] = NULL;
script_argc_max++;
}
script_argv[script_argc] = realloc(script_argv[script_argc],
(total_len + token_len + 1) * sizeof(char)
);
if (script_argv[script_argc] == NULL) {
fprintf(stderr, "%s: error: failed to allocate memory while parsing `%s'.\n",
argv[0], argv[1]);
exit(EXIT_FAILURE);
}
/* We _know_ that token fits and hence use strcpy */
strcpy(script_argv[script_argc] + total_len, token);
/* advance line to the next token */
line += token_len;
total_len += token_len;
/* we did not do exact byte-counting, so simply look at the buffer */
/* did we reach the end of the buffer? */
if (line == &buffer[sizeof(buffer) - 1]) {
/* yes. The remainder of the command/arg is in the next buffer */
continues_next_buffer = 1;
//if (context.debug) {
// fprintf(stderr, "token so far (continues): %s\n", token);
//}
} else {
/* no, get next argument */
line++; /* skip past current '\0' */
script_argc++;
total_len = 0;
continues_next_buffer = 0;
//if (context.debug) {
// fprintf(stderr, "token: %s\n", token);
//}
}
} /* while line being tokenized */
/*
* Reallocate memory if needed for the final NULL at the end.
*/
if(script_argc_max <= script_argc){
script_argv = realloc(script_argv, (script_argc+1) * sizeof(char *));
/* TODO(sissel): STOPPED HERE */
if (script_argv == NULL) {
fprintf(stderr, "%s: error: failed to allocate memory while parsing `%s'.\n",
argv[0], argv[1]);
exit(EXIT_FAILURE);
}
script_argv[script_argc] = NULL;
script_argc_max++;
}
/* continues_next_buffer is true if a command or argument continues
* in the next buffer.
* Think: printf "mousemove%4107s" "--polar -- 0 0 sleep 0.1" > /tmp/i
* xdotool - < /tmp/i
* should ideally parse until we see a supported command, i.e.
* do not break "--p" "olar" in half but continue reading until "sleep".
*/
if(!continues_next_buffer && script_argc > 0){
context.argc = script_argc;
context.argv = script_argv;
result = context_execute(&context);
/*
* Free the allocated memory for tokens.
*/
for(int j = 0; j < script_argc + 1; j++){
free(*(script_argv + j));
*(script_argv + j) = NULL;
}
script_argc = 0;
}
}
fclose(input);
xdo_free(context.xdo);
free(context.windows);
for(int i=0; i<script_argc+1; ++i) {
free(script_argv[i]);
}
free(script_argv);
return result;
}
int args_main(int argc, char **argv) {
int ret = 0;
int opt;
int option_index;
const char *usage = "Usage: %s <cmd> <args>\n";
static struct option long_options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'v' },
{ 0, 0, 0, 0 }
};
if (argc < 2) {
fprintf(stderr, usage, argv[0]);
cmd_help(NULL);
exit(1);
}
if (!strcasecmp(argv[1], "help")) {
cmd_help(NULL);
exit(EXIT_SUCCESS);
} else if (!strcasecmp(argv[1], "version")) {
cmd_version(NULL);
exit(EXIT_SUCCESS);
}
while ((opt = getopt_long_only(argc, argv, "++hv", long_options, &option_index)) != -1) {
switch (opt) {
case 'h':
cmd_help(NULL);
exit(EXIT_SUCCESS);
case 'v':
cmd_version(NULL);
exit(EXIT_SUCCESS);
default:
fprintf(stderr, usage, argv[0]);
exit(EXIT_FAILURE);
}
}
context_t context;
context.xdo = xdo_new(NULL);
context.prog = *argv;
argv++; argc--;
context.argc = argc;
context.argv = argv;
context.windows = NULL;
context.nwindows = 0;
context.have_last_mouse = False;
context.debug = (getenv("DEBUG") != NULL);
if (context.xdo == NULL) {
fprintf(stderr, "Failed creating new xdo instance.\n");
return 1;
}
context.xdo->debug = context.debug;
ret = context_execute(&context);
xdo_free(context.xdo);
free(context.windows);
return ret;
} /* int args_main(int, char **) */
int context_execute(context_t *context) {
int cmd_found = 0;
int i = 0;
char *cmd = NULL;
int ret = XDO_SUCCESS;
/* Loop until all argv is consumed. */
while (context->argc > 0 && ret == XDO_SUCCESS) {
cmd = context->argv[0];
cmd_found = 0;
for (i = 0; dispatch[i].name != NULL && !cmd_found; i++) {
if (!strcasecmp(dispatch[i].name, cmd)) {
cmd_found = 1;
optind = 0;
if (context->debug) {
fprintf(stderr, "command: %s\n", cmd);
}
ret = dispatch[i].func(context);
}
}
if (!cmd_found) {
fprintf(stderr, "%s: Unknown command: %s\n", context->prog, cmd);
fprintf(stderr, "Run '%s help' if you want a command list\n", context->prog);
ret = 1;
}
} /* while ... */
return ret;
} /* int args_main(int, char **) */
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_version(context_t *context) {
xdotool_output(context, "xdotool version %s", xdo_version());
if (context != NULL) {
consume_args(context, 1);
}
return 0;
}
void xdotool_debug(context_t *context, const char *format, ...) {
va_list args;
va_start(args, format);
if (context->debug) {
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
}
va_end(args);
} /* xdotool_debug */
void xdotool_output(context_t *context, const char *format, ...) {
context = context; /* Do something with context to avoid warnings */
va_list args;
va_start(args, format);
vfprintf(stdout, format, args);
fprintf(stdout, "\n");
fflush(stdout);
va_end(args);
} /* xdotool_output */
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化