Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
222 lines (189 sloc) 5.27 KB
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <X11/X.h>
#include <X11/XKBlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
typedef struct HotKey HotKey;
struct HotKey {
KeySym keysym;
unsigned int modifiers;
char* command;
HotKey* next;
};
/* The list of hot keys. */
HotKey* hotkeys = NULL;
/* The connection to the X server. */
Display* dpy;
/* The number of screens. */
int screen_count;
char* argv0;
const char* attempting_to_grab = 0;
int report_key_grab_error(Display* /*d*/, XErrorEvent* e) {
const char* reason = "unknown reason";
if (e->error_code == BadAccess) {
reason = "the key/button combination is already in use by another client";
} else if (e->error_code == BadValue) {
reason = "the key code was out of range for XGrabKey";
} else if (e->error_code == BadWindow) {
reason = "the root window we passed to XGrabKey was incorrect";
}
fprintf(stderr, "%s: couldn't grab key \"%s\": %s (X error code %i)\n", argv0,
attempting_to_grab, reason, e->error_code);
return 0;
}
static void reap_zombies() {
int status;
while (waitpid(-1, &status, WNOHANG) > 0)
;
}
static void sigchld_handler(int /*signal_number*/) {
reap_zombies();
/* Reinstall this signal handler ready for the next child. */
signal(SIGCHLD, sigchld_handler);
}
static void shell(const char* command) {
const char* sh = getenv("SHELL");
if (sh == 0) {
sh = "/bin/sh";
}
switch (fork()) {
case 0: /* Child. */
close(ConnectionNumber(dpy));
execl(sh, sh, "-c", command, (char*)NULL);
fprintf(stderr, "%s: can't exec \"%s -c %s\"\n", argv0, sh, command);
exit(EXIT_FAILURE);
case -1: /* Error. */
fprintf(stderr, "%s: couldn't fork\n", argv0);
break;
}
}
static void panic(const char* s) {
fprintf(stderr, "%s: %s\n", argv0, s);
exit(EXIT_FAILURE);
}
unsigned int parse_modifiers(char* name, const char* full_spec) {
char* separator = strchr(name, '-');
unsigned int modifiers = 0;
if (separator != NULL) {
*separator = 0;
modifiers |= parse_modifiers(separator + 1, full_spec);
}
if (!strcmp(name, "Shift")) {
modifiers |= ShiftMask;
} else if (!strcmp(name, "Control")) {
modifiers |= ControlMask;
} else if (!strcmp(name, "Alt") || !strcmp(name, "Mod1")) {
modifiers |= Mod1Mask;
} else if (!strcmp(name, "Mod2")) {
modifiers |= Mod2Mask;
} else if (!strcmp(name, "Mod3")) {
modifiers |= Mod3Mask;
} else if (!strcmp(name, "Super") || !strcmp(name, "Mod4")) {
modifiers |= Mod4Mask;
} else {
fprintf(stderr, "%s: ignoring unknown modifier \"%s\" in \"%s\"\n", argv0,
name, full_spec);
}
return modifiers;
}
static void add_hot_key(const char* keyname, const char* command) {
char* copy = strdup(keyname);
char* unmodified = strrchr(copy, '-');
unsigned int modifiers = 0;
if (unmodified == NULL) {
unmodified = copy;
} else {
*unmodified = 0;
++unmodified;
modifiers = parse_modifiers(copy, keyname);
}
HotKey* new_key = new HotKey;
new_key->keysym = XStringToKeysym(unmodified);
new_key->modifiers = modifiers;
new_key->command = strdup(command);
new_key->next = hotkeys;
hotkeys = new_key;
XSynchronize(dpy, True);
attempting_to_grab = keyname;
XSetErrorHandler(report_key_grab_error);
for (int screen = 0; screen < screen_count; ++screen) {
Window root = RootWindow(dpy, screen);
XGrabKey(dpy, XKeysymToKeycode(dpy, new_key->keysym), modifiers, root,
False, GrabModeAsync, GrabModeAsync);
}
XSetErrorHandler(NULL);
free(copy);
}
static void read_hot_key_file(const char* fname) {
FILE* fp = fopen(fname, "r");
if (fp == NULL) {
fprintf(stderr, "%s: couldn't read \"%s\"\n", argv0, fname);
return;
}
char buf[BUFSIZ];
while (fgets(buf, BUFSIZ, fp) != NULL) {
char* tab = strchr(buf, '\t');
if (*buf == '#' || tab == NULL) {
continue;
}
*tab = 0;
add_hot_key(buf, tab + 1);
}
fclose(fp);
}
static void keypress(XEvent* ev) {
KeySym keysym = XkbKeycodeToKeysym(dpy, ev->xkey.keycode, 0,
ev->xkey.state & ShiftMask ? 1 : 0);
HotKey* h = hotkeys;
for (; h != NULL; h = h->next) {
if (h->keysym == keysym && ev->xkey.state == h->modifiers) {
shell(h->command);
break;
}
}
}
int main(int argc, char* argv[]) {
argv0 = argv[0];
/* Open a connection to the X server. */
dpy = XOpenDisplay("");
if (dpy == 0) {
panic("can't open display.");
}
/* Set up signal handlers. */
#if 0
signal(SIGTERM, SIG_IGN);
signal(SIGINT, SIG_IGN);
#endif
signal(SIGHUP, SIG_IGN);
signal(SIGCHLD, sigchld_handler);
screen_count = ScreenCount(dpy);
if (argc != 2) {
panic("syntax: speckeysd <keys file>");
}
read_hot_key_file(argv[1]);
/* Make sure all our communication to the server got through. */
XSync(dpy, False);
/* The main event loop. */
for (;;) {
XEvent ev;
XNextEvent(dpy, &ev);
switch (ev.type) {
case KeyPress:
keypress(&ev);
break;
case MappingNotify:
XRefreshKeyboardMapping((XMappingEvent*)&ev);
break;
default:
/* Do I look like I care? */
break;
}
}
}