#include #include #include #include #include #include "macros.h" #define LINELEN 2048 char *pname; char *TYPES[5] = { "int", "string", "var", "func", "undef" }; Arg success, failure; #define FAILURE &failure #define SUCCESS &success /* * For functions that want a solid value to work with, * and don't care what type it is. get_arg_real evaluates * variables and functions until it gets down to a real * value: int, string, or undef. */ Arg *get_arg_real(e, name, a, dest) XEvent *e; char *name; Arg **a; Arg **dest; { Arg *work; work = *a; if (work == NULL) { fprintf(stderr, "%s: too few arguments to %s\n", pname, name); return FAILURE; } while (work->type == ARG_VAR || work->type == ARG_FUNC) { if (work->type == ARG_VAR) work = work->data.v; else work = (Arg *)work->data.ProcCall.p(e, work->data.ProcCall.args); } *dest = work; *a = (*a)->next; return SUCCESS; } Arg *get_arg_int(e, name, a, dest) XEvent *e; char *name; Arg **a; int *dest; { Arg *work, *b; b = *a; if (get_arg_real(e, name, &b, &work) == FAILURE) return FAILURE; if (work->type != ARG_INT) { fprintf(stderr, "%s: %s expected an int, got %s\n", pname, name, TYPES[work->type]); return FAILURE; } *dest = work->data.i; *a = (*a)->next; return SUCCESS; } Arg *get_arg_var(e, name, a, dest) XEvent *e; char *name; Arg **a; Arg **dest; { if (*a == NULL) { fprintf(stderr, "%s: too few arguments to %s\n", pname, name); return FAILURE; } if ((*a)->type != ARG_VAR) { fprintf(stderr, "%s: %s expected a var, got %s\n", pname, name, TYPES[(*a)->type]); return FAILURE; } *dest = (*a)->data.v; *a = (*a)->next; return SUCCESS; } Arg *set(e, a) XEvent *e; Arg *a; { Arg *v; if (get_arg_var(e, "set", &a, &v) == FAILURE) return FAILURE; if (a == NULL) { fprintf(stderr, "%s: too few arguments to set\n", pname); return FAILURE; } v->type = a->type; v->data = a->data; return v; } /* * setv returns a var, with type {int, string, undef} */ Arg *setv(e, a) XEvent *e; Arg *a; { Arg *v, *w; if (get_arg_var(e, "set", &a, &v) == FAILURE) return FAILURE; if (get_arg_real(e, "set", &a, &w) == FAILURE) return FAILURE; v->type = w->type; v->data = w->data; return v; } Arg *led(e, a) XEvent *e; Arg *a; { XKeyboardControl k; if (get_arg_int(e, "led", &a, &k.led) == FAILURE) return FAILURE; if (get_arg_int(e, "led", &a, &k.led_mode) == FAILURE) return FAILURE; k.led_mode = k.led_mode ? LedModeOn : LedModeOff; XChangeKeyboardControl(e->xkey.display, KBLed | KBLedMode, &k); return SUCCESS; } /* * minus returns an int arg */ Arg *minus(e, a) XEvent *e; Arg *a; { static Arg ret; int x, y; if (get_arg_int(e, "minus", &a, &x) == FAILURE) return FAILURE; if (get_arg_int(e, "minus", &a, &y) == FAILURE) return FAILURE; ret.type = ARG_INT; ret.data.i = x - y; return &ret; } char *parseNum(s, a, b) char *s; int *a, b; { int sign; sign = (*s == '+') ? 1 : -1; s++; if (*s == 'c') { *a = b / 2; return s+1; } if (sign == 1) *a = atoi(s); else *a = b - atoi(s); while (isdigit(*s)) s++; return s; } void parseGeom(s, x, y, width, height) char *s; int *x, *y, width, height; { s = parseNum(s, x, width); s = parseNum(s, y, height); } void printArgs(args) Arg *args; { while (args) { switch(args->type) { case ARG_INT: fprintf(stdout, "int"); break; case ARG_STRING: fprintf(stdout, "string"); break; case ARG_VAR: case ARG_UNDEF: fprintf(stdout, "var"); break; case ARG_FUNC: fprintf(stdout, "func("); printArgs(args->data.ProcCall.args); fprintf(stdout, ")"); break; default: break; } args = args->next; if (args) fprintf(stdout, ","); } } Arg *swv(e, a) XEvent *e; Arg *a; { int s, i; Arg *ret; if (get_arg_int(e, "switch", &a, &s) == FAILURE) return FAILURE; i = s; while(i && a && a->next) { i--; a = a->next; } if (i != 0) { fprintf(stderr, "%s: switchv(%d,,,...) has too few arguments\n", pname, s); return(FAILURE); } if (get_arg_real(e, "switchv", &a, &ret) == FAILURE) return FAILURE; return ret; } click(event, button, x, y) XEvent *event; int button, x, y; { Display *dpy; XWindowAttributes attr; XEvent send; Window junk, *list, cwin; int i, screen; unsigned int length; dpy = event->xkey.display; screen = DefaultScreen(dpy), XQueryTree(dpy, RootWindow(dpy, screen), &junk, &junk, &list, &length); for (i = length-1; i >= 0; i--) { XGetWindowAttributes(dpy, list[i], &attr); if (x > attr.x && x < (attr.x + attr.width) && y > attr.y && y < (attr.y + attr.height)) break; } if (i != -1) { cwin = XmuClientWindow(dpy, list[i]); bcopy(event, &send, sizeof(XEvent)); send.type = ButtonPress; send.xbutton.window = cwin; send.xbutton.subwindow = None; send.xbutton.x = x - attr.x; send.xbutton.y = y - attr.y; send.xbutton.x_root = x; send.xbutton.y_root = y; switch(button) { case 1: send.xbutton.button = Button1; break; case 2: send.xbutton.button = Button2; break; case 3: send.xbutton.button = Button3; break; default: send.xbutton.button = Button1; break; } XSendEvent(dpy, cwin, True, ButtonPressMask, &send); send.type = ButtonRelease; send.xbutton.time += 10; XSendEvent(dpy, cwin, True, ButtonReleaseMask, &send); } XFree((char *)list); } Arg *sendclick(event, args) XEvent *event; Arg *args; { Display *dpy; int width, height; int screen, button, x, y; dpy = event->xkey.display; screen = DefaultScreen(dpy), width = DisplayWidth(dpy, screen); height = DisplayHeight(dpy, screen); if (!args) { fprintf(stderr, "%s: sendclick passed no args\n", pname); return FAILURE; } if (args->type != ARG_INT) { fprintf(stderr, "%s: sendclick takes int as first arg\n", pname); return FAILURE; } button = args->data.i; args = args->next; if (args->type != ARG_STRING) { fprintf(stderr, "%s: sendclick takes string as second arg\n", pname); return FAILURE; } parseGeom(args->data.s, &x, &y, width, height); click(event, button, x, y); return SUCCESS; } Arg *sendclickmasked(event, args) XEvent *event; Arg *args; { event->xkey.state = 0; return sendclick(event, args); } Arg *mouseclick(e, a) XEvent *e; Arg *a; { int button; if (get_arg_int(e, "mouseclick", &a, &button) == FAILURE) return FAILURE; click(e, button, e->xkey.x_root, e->xkey.y_root); return SUCCESS; } Arg *warpdelta(e, a) XEvent *e; Arg *a; { int delta, dx, dy; if (get_arg_int(e, "warpdelta", &a, &delta) == FAILURE) return FAILURE; if (get_arg_int(e, "warpdelta", &a, &dx) == FAILURE) return FAILURE; if (get_arg_int(e, "warpdelta", &a, &dy) == FAILURE) return FAILURE; XWarpPointer(e->xkey.display, None, None, 0, 0, 0, 0, delta * dx, delta * dy); return SUCCESS; } Arg *findmouse(e, a) XEvent *e; Arg *a; { int min, max, delta, delay; static GC findgc; static int gcinit = 0; Display *dpy; Window root; int dummyint, x, y, i; Window dummywin; dpy = e->xkey.display; root = DefaultRootWindow(dpy); if (get_arg_int(e, "warpdelta", &a, &min) == FAILURE) return FAILURE; if (get_arg_int(e, "warpdelta", &a, &max) == FAILURE) return FAILURE; if (get_arg_int(e, "warpdelta", &a, &delta) == FAILURE) return FAILURE; if (!gcinit) { XGCValues values; XColor x_color; if (a->type != ARG_STRING) { fprintf(stderr, "%s: findmouse takes string as fourth arg\n", pname); return FAILURE; } if (!XParseColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), a->data.s, &x_color)) { fprintf(stderr, "%s: findmouse couldn't figure out color %s\n", pname, a->data.s); return FAILURE; } if (!XAllocColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), &x_color)) { fprintf(stderr, "%s: findmouse couldn't allocate color %s\n", pname, a->data.s); return FAILURE; } values.foreground = x_color.pixel; a = a->next; if (a->type != ARG_STRING) { fprintf(stderr, "%s: findmouse takes string as fifth arg\n", pname); return FAILURE; } if (!XParseColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), a->data.s, &x_color)) { fprintf(stderr, "%s: findmouse couldn't figure out color %s\n", pname, a->data.s); return FAILURE; } if (!XAllocColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), &x_color)) { fprintf(stderr, "%s: findmouse couldn't allocate color %s\n", pname, a->data.s); return FAILURE; } values.foreground ^= x_color.pixel; gcinit = 1; values.function = GXxor; values.line_width = 0; values.subwindow_mode = IncludeInferiors; findgc = XCreateGC(dpy, root, GCFunction | GCForeground | GCLineWidth | GCSubwindowMode, &values); } XQueryPointer(dpy, root, &dummywin, &dummywin, &x, &y, &dummyint, &dummyint, &dummyint); for (i = max; i > min; i -= delta) { XDrawArc(dpy, root, findgc, x - i/2, y - i/2, i, i, 0, 360*64); XSync(dpy, False); XDrawArc(dpy, root, findgc, x - i/2, y - i/2, i, i, 0, 360*64); } } #define MAXVARS 50 int numVars = 0; VarMapping varMap[MAXVARS]; FuncMapping funcMap[] = { { "led", (Proc)led }, { "minus", (Proc)minus }, { "mouseclick", (Proc)mouseclick }, { "sendclick", (Proc)sendclick }, { "sendclickmasked", (Proc)sendclickmasked }, { "set", (Proc)set }, { "setv", (Proc)setv }, { "switchv", (Proc)swv }, { "warpdelta", (Proc)warpdelta }, { "findmouse", (Proc)findmouse }, { NULL, (Proc)NULL } }; #define skipwhite(ptr) while (*ptr == '\n' || *ptr == ' ' || \ *ptr == '\t') ptr++ char *stringToArg(); char *parse_int(ptr, arg) char *ptr; Arg *arg; { int i; char buffer[50]; i = 0; if (*ptr == '-' || *ptr == '+') buffer[i++] = *ptr++; while (*ptr >= '0' && *ptr <= '9') buffer[i++] = *ptr++; buffer[i] = '\0'; arg->data.i = atoi(buffer); return ptr; } char *parse_string(ptr, arg) char *ptr; Arg *arg; { char *ptrstart, buffer[500]; int i; ptrstart = ptr; i = 0; ptr++; /* skip quote */ while (*ptr != '"' && *ptr != '\0') { buffer[i] = *ptr; if (*ptr == '\\') { switch(*(ptr+1)) { case 'n': buffer[i] = '\n'; break; case 't': buffer[i] = '\t'; break; default: buffer[i] = *(ptr+1); break; } ptr += 2; } else ptr++; i++; } if (*ptr == '\0') { fprintf(stderr, "%s: missing close \":\n%s", pname, ptrstart); return NULL; } else /* *ptr == '"' */ { ptr++; buffer[i] = '\0'; arg->data.s = (char *)malloc(strlen(buffer)+1); strcpy(arg->data.s, buffer); return ptr; } } char *parse_var(ptr, arg) char *ptr; Arg *arg; { char *ptrstart, buffer[50]; /* dump core */ int i; ptrstart = ptr; i = 0; while (isgraph(*ptr) && *ptr != ',' && *ptr != ')') buffer[i++] = *ptr++; buffer[i] = 0; if (i == 0) { fprintf(stderr, "%s: line ends prematurely:\n%s", pname, ptrstart); return NULL; } for (i = 0; i < numVars; i++) if (!strcmp(varMap[i].name, buffer)) { arg->data.v = varMap[i].arg; return ptr; } numVars++; varMap[i].name = (char *)malloc(strlen(buffer)+1); strcpy(varMap[i].name, buffer); varMap[i].arg = (Arg *)malloc(sizeof(Arg)); varMap[i].arg->type = ARG_UNDEF; arg->data.v = varMap[i].arg; return ptr; } char *parse_func(ptr, arg) char *ptr; Arg *arg; { Arg *first, *last, **argptr; char *ptrstart, buffer[50]; int i, parsing = 1; ptrstart = ptr; i = 0; while (*ptr != '(') buffer[i++] = *ptr++; buffer[i] = 0; if (i == 0) { fprintf(stderr, "%s: ( implies function - not named:\n%s", pname, ptrstart); return NULL; } i = 0; while (funcMap[i].name != NULL) { if (!strcmp(buffer, funcMap[i].name)) break; i++; } if (funcMap[i].name == NULL) { fprintf(stderr, "%s: function \"%s\" unknown\n", pname, buffer); return NULL; } arg->data.ProcCall.p = funcMap[i].proc; argptr = &arg->data.ProcCall.args; ptr++; /* skip "(" */ while(parsing) { skipwhite(ptr); switch(*ptr) { case ')': *argptr = NULL; /* no more args */ ptr++; parsing = 0; break; case ',': /* so, (,foo) is legit right now */ ptr++; skipwhite(ptr); default: /* things not nec. comma separated */ ptr = stringToArg(ptr, argptr); if (ptr != NULL) argptr = &((*argptr)->next); else { *argptr = NULL; parsing = 0; } break; } } return ptr; } char *stringToArg(ptr, retarg) char *ptr; Arg **retarg; { char *ptrend; Arg arg; skipwhite(ptr); /* * Figure out what we're parsing - int, string, var, or func */ if (*ptr >= '0' && *ptr <= '9' || *ptr == '+' || *ptr == '-') arg.type = ARG_INT; else if (*ptr == '"') arg.type = ARG_STRING; else { ptrend = ptr; while (*ptrend != '\0' && *ptrend != '(' && *ptrend != ',' && *ptrend != ')') ptrend++; if (*ptrend == '(') arg.type = ARG_FUNC; else arg.type = ARG_VAR; } /* * Parse it */ switch(arg.type) { case ARG_INT: ptrend = parse_int(ptr, &arg); break; case ARG_STRING: ptrend = parse_string(ptr, &arg); break; case ARG_VAR: ptrend = parse_var(ptr, &arg); break; case ARG_FUNC: ptrend = parse_func(ptr, &arg); break; } if (ptrend) { arg.next = NULL; *retarg = (Arg *)malloc(sizeof(Arg)); bcopy(&arg, *retarg, sizeof(Arg)); } return ptrend; } KeyToArg *parseLine(e, line) XEvent *e; char *line; { int i, exec = 0; char buffer[LINELEN], *ptrstart, *ptrend, *ptrmod = NULL; KeyToArg work, *ret; if (line == NULL) { fprintf(stderr, "%s: NULL string passed to parseLine\n", pname); return NULL; } if (line[0] == '#') /* comment line */ return NULL; strncpy(buffer, line, LINELEN); buffer[LINELEN-1] = '\0'; /* * Parse KeySym from line */ ptrstart = buffer; skipwhite(ptrstart); if (ptrstart == '\0') return NULL; ptrend = ptrstart; while (*ptrend != '\0' && *ptrend != ':' && *ptrend!= '(') ptrend++; if (*ptrend == '(') ptrmod = ptrend + 1; if (*ptrend == ':' || *ptrend == '(') *ptrend = '\0'; else { fprintf(stderr, "%s: line has no \":\" :\n%s", pname, line); return NULL; } if (*ptrstart != '\0') { work.keysym = XStringToKeysym(ptrstart); if (work.keysym == NoSymbol) { fprintf(stderr, "%s: keysym \"%s\" unknown\n", pname, ptrstart); return NULL; } work.modifiers = 0; if (ptrmod) { while (*ptrmod != ')' && *ptrmod != '\0') { switch(*ptrmod) { case 'a': work.modifiers |= AnyModifier; break; case 's': work.modifiers |= ShiftMask; break; case 'l': work.modifiers |= LockMask; break; case 'c': work.modifiers |= ControlMask; break; case 'm': work.modifiers |= Mod1Mask; break; default: fprintf(stderr, "%s: unknown modifier: '%c'\n", pname, *ptrmod); } ptrmod++; } if (*ptrmod == '\0') { fprintf(stderr, "%s: line needs \")\" :\n%s", pname, line); return NULL; } else { ptrend = ptrmod + 1; skipwhite(ptrend); if (*ptrend != ':') { fprintf(stderr, "%s: \":\" should follow \")\" :\n%s", pname, line); return NULL; } } } } else exec = 1; /* * Parse the rest */ ptrstart = ptrend + 1; (void)stringToArg(ptrstart, &work.arglist); if (work.arglist == NULL) return NULL; if (exec) { Arg *dummy; get_arg_real(e, "init", &work.arglist, &dummy); /* write a free routine and use it here XXX */ return NULL; } else { ret = (KeyToArg *)malloc(sizeof(KeyToArg)); bcopy(&work, ret, sizeof(KeyToArg)); return ret; } } KeyToArg *parse(e, file) XEvent *e; char *file; { FILE *f; char line[LINELEN]; KeyToArg *begin = NULL, *end = NULL, *new; f = fopen(file, "r"); if (f == NULL) { perror(pname); return NULL; } while (1) { if (NULL == fgets(line, sizeof(line), f)) { if (feof(f)) { fclose(f); return begin; } fprintf(stderr, "%s: fgets returns %d in parse\n", pname, ferror(f)); fclose(f); return NULL; } new = parseLine(e, line); if (new != NULL) { if (begin == NULL) { begin = new; end = new; new->next = NULL; } else { end->next = new; end = new; new->next = NULL; } } } } int (*defaultHandler)(); myHandler(dpy, event) Display *dpy; XErrorEvent *event; { if (event->error_code == BadWindow) return 0; else return(defaultHandler(dpy, event)); } main(argc, argv) int argc; char **argv; { Display *dpy; XEvent event; KeySym keysym; int i, dx, dy; static int oldcode = 0, speed = 25; char config[MAXPATHLEN], *home; KeyToArg *list, *a; success.type = ARG_UNDEF; failure.type = ARG_UNDEF; pname = argv[0]; if ((dpy = XOpenDisplay(NULL)) == NULL) { fprintf(stderr, "%s: couldn't open display\n", pname); exit(1); } defaultHandler = XSetErrorHandler(myHandler); home = (char *)getenv("HOME"); if (home != NULL) strncpy(config, home, MAXPATHLEN); else config[0] = '\0'; strncat(config, "/.macros", MAXPATHLEN - strlen(config) - 1); event.xkey.display = dpy; list = parse(&event, config); if (list == NULL) { fprintf(stderr, "%s: no functions defined, exiting\n", pname); exit(0); } for (a = list; a != NULL; a = a->next) XGrabKey(dpy, XKeysymToKeycode(dpy, a->keysym), a->modifiers, DefaultRootWindow(dpy), True, GrabModeAsync, GrabModeAsync); while (1) { XNextEvent(dpy, &event); if (event.type == KeyPress) { keysym = XKeycodeToKeysym(dpy, event.xkey.keycode, 0); for (a = list; a != NULL; a = a->next) if (a->keysym == keysym && (a->modifiers == AnyModifier || a->modifiers == event.xkey.state)) { if (a->arglist->type == ARG_FUNC) a->arglist->data.ProcCall.p(&event, a->arglist->data.ProcCall.args); } } } }