#include #include #include #include #include #include #include #include typedef struct Key { char *command; unsigned int mod; KeySym keysym; } Key; // A horrible hack. struct Key * parse_key(char *shortcut, char *command) { Key *key = malloc(sizeof(struct Key)); if(key == NULL) { fprintf(stderr, "could not allocate keys\n"); exit(EXIT_FAILURE); } key->command = strndup(command, strlen(command) + 2); key->command[strlen(command)] = ' '; key->command[strlen(command) + 1] = '&'; size_t i = 0; char *token; char **tokens = malloc(sizeof(char *) * 16); size_t token_nb = 0; int key_set = 0; int mod_set = 0; // load our tokens while ((token = strsep(&shortcut, "+")) != NULL) { tokens[token_nb] = token; token_nb++; } if(token_nb != 0) { // Modifiers for(i = 0; i < token_nb; i++) { // if it's a regular key if(strlen(tokens[i]) == 1) { if(key_set) { return NULL; } key->keysym = XStringToKeysym(tokens[token_nb - 1]); key_set = 1; } // if it's a mod key else { int mask = 0; if(strcmp(tokens[i], "control") == 0) mask = ControlMask; else if(strcmp(tokens[i], "shift") == 0) mask = ShiftMask; else if(strcmp(tokens[i], "mod1") == 0) mask = Mod1Mask; else if(strcmp(tokens[i], "mod2") == 0) mask = Mod2Mask; else if(strcmp(tokens[i], "mod3") == 0) mask = Mod3Mask; else if(strcmp(tokens[i], "mod4") == 0) mask = Mod4Mask; else return NULL; if(mask) { key->mod = key->mod | mask ; mod_set = 1; } else { return NULL; } } } } if(key_set && mod_set) { return(key); } else { free(key); return NULL; } } struct Key **load_hotkeys(FILE *fh, size_t *key_len) { char line[4096]; int line_nb = 1; size_t realloc_size = 1; *key_len = 0; struct Key **keys = malloc(sizeof(struct Key **)); if(keys == NULL) { fprintf(stderr, "could not allocate keys\n"); return NULL; } while((fgets(line, 4096, fh)) != NULL) { int res; int error = 0; char *shortcut, *command; Key *key; res = sscanf(line, "%ms %m[^\n]+", &shortcut, &command); if(res == 2) { key = parse_key(shortcut, command); if(key == NULL) error = 1; } else { error = 1; } if(error) fprintf(stderr, "Error:could not parse line %d: %s\n", line_nb, line); else { keys[*key_len] = key; *key_len = *key_len + 1; keys = realloc(keys, sizeof(struct Key *) * realloc_size); if(keys == NULL) { fprintf(stderr, "could not realloc keys\n"); exit(EXIT_FAILURE); } realloc_size++; } line_nb++; } if(*key_len != 0) return keys; return NULL; } void wait_for_input(Display* dpy, Window root, struct Key **hotkeys, size_t len) { size_t i = 0; XSelectInput(dpy, root, KeyPressMask ); while(1) { XEvent ev; XNextEvent(dpy, &ev); if(ev.type == KeyPress) { XKeyEvent *kev = &ev.xkey; KeySym keysym = XKeycodeToKeysym(dpy, (KeyCode)kev->keycode, 0); for(i = 0; i < len; i++) { if(keysym == hotkeys[i]->keysym && hotkeys[i]->mod == kev->state) { printf("executing '%s'\n", hotkeys[i]->command); system(hotkeys[i]->command); } } } } } int main(int argc, char **argv) { Display* dpy = XOpenDisplay(0); Window root = DefaultRootWindow(dpy); unsigned int i = 0; FILE *config_fh = NULL; char *config_fn; size_t len = 0; struct Key **hotkeys = NULL; if(argc == 1) { asprintf(&config_fn, "%s%s", getenv("HOME"), "/.hotkeyrc"); config_fh = fopen(config_fn, "r"); } else if (argc == 2) { config_fh = fopen(argv[1], "r"); config_fn = argv[1]; } else { fprintf(stderr, "usage: hotkeys config_file (default is ~/.hotkeyrc)\n"); exit(EXIT_FAILURE); } if(config_fh == NULL) { fprintf(stderr, "could not open %s\n", config_fn); exit(EXIT_FAILURE); } hotkeys = load_hotkeys(config_fh, &len); if(hotkeys == NULL) { fprintf(stderr, "Could not load any keys from %s. quitting...\n", config_fn); exit(EXIT_FAILURE); } for(i = 0; i < len; i++) { XGrabKey(dpy, XKeysymToKeycode(dpy,hotkeys[i]->keysym), hotkeys[i]->mod, root, False, GrabModeAsync, GrabModeAsync); } wait_for_input(dpy, root, hotkeys, len); XCloseDisplay(dpy); return 0; }