sxhkd-rc

[fork] simple X hotkey daemon (but for the rc shell)
Log | Files | Refs | README | LICENSE

commit b464641cec963be7869fead5c73ebec53ce195c5
parent cbba51e1f3ffae2a1597fe8dcbcc25f505f25960
Author: Bastien Dejean <nihilhill@gmail.com>
Date:   Fri,  4 Jan 2013 22:43:50 +0100

Add support for mouse hotkeys

Diffstat:
MREADME.md | 2++
Mexamples/sxhkdrc | 3+++
Mkeys.c | 105+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mkeys.h | 16+++++++++-------
Msxhkd.1 | 3+++
Msxhkd.c | 41++++++++++++++++++++++++++++++-----------
Msxhkd.h | 1+
7 files changed, 116 insertions(+), 55 deletions(-)

diff --git a/README.md b/README.md @@ -32,6 +32,8 @@ If `@` is added at the beginning of the keysym, the command will be run on key r The keysym names are those your will get from `xev` (minus the prefix if any). +Mouse hotkeys can be defined by using one of the following special keysym names: `button1`, `button2`, `button3`, ..., `button24`. + What is actually executed is `/bin/sh -c COMMAND`, which means you can use environment variables in `COMMAND`. If *sxhkd* receives a `SIGUSR1` signal, it will reload its configuration file. diff --git a/examples/sxhkdrc b/examples/sxhkdrc @@ -19,4 +19,7 @@ super + shift + @LaunchA super + shift + equal mosaic "$HOME/image" +button8 + bspc toggle_visibility + # vim: set ft=conf: diff --git a/keys.c b/keys.c @@ -2345,39 +2345,48 @@ void grab(void) { PUTS("grab"); for (hotkey_t *hk = hotkeys; hk != NULL; hk = hk->next) { - xcb_keycode_t *keycodes = keycodes_from_keysym(hk->keysym); - if (keycodes != NULL) - for (xcb_keycode_t *kc = keycodes; *kc != XCB_NO_SYMBOL; kc++) - if (hk->keysym == xcb_key_symbols_get_keysym(symbols, *kc, 0)) { - PRINTF("keycode for %u is %u\n", hk->keysym, *kc); - grab_key(*kc, hk->modfield); - } - free(keycodes); + if (hk->button == XCB_NONE) { + xcb_keycode_t *keycodes = keycodes_from_keysym(hk->keysym); + if (keycodes != NULL) + for (xcb_keycode_t *kc = keycodes; *kc != XCB_NO_SYMBOL; kc++) + if (hk->keysym == xcb_key_symbols_get_keysym(symbols, *kc, 0)) { + PRINTF("keycode for %u is %u\n", hk->keysym, *kc); + grab_key_button(*kc, hk->button, hk->modfield); + } + free(keycodes); + } else { + grab_key_button(XCB_NONE, hk->button, hk->modfield); + } } } -void grab_key(xcb_keycode_t keycode, uint16_t modfield) +void grab_key_button(xcb_keycode_t keycode, xcb_button_t button, uint16_t modfield) { - grab_key_checked(keycode, modfield); + grab_key_button_checked(keycode, button, modfield); for (uint8_t i = 0; i < 8; i++) { uint16_t lockfield = (i & 1 ? num_lock : 0) | (i & 2 ? caps_lock : 0) | (i & 4 ? scroll_lock : 0); - grab_key_checked(keycode, modfield | lockfield); + grab_key_button_checked(keycode, button, modfield | lockfield); } } -void grab_key_checked(xcb_keycode_t keycode, uint16_t modfield) +void grab_key_button_checked(xcb_keycode_t keycode, xcb_button_t button, uint16_t modfield) { xcb_generic_error_t *err; - err = xcb_request_check(dpy, xcb_grab_key_checked(dpy, false, root, modfield, keycode, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC)); + if (button == XCB_NONE) + err = xcb_request_check(dpy, xcb_grab_key_checked(dpy, false, root, modfield, keycode, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC)); + else + err = xcb_request_check(dpy, xcb_grab_button_checked(dpy, false, root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, button, modfield)); + unsigned int value = (button == XCB_NONE ? keycode : button); + char *type = (button == XCB_NONE ? "key" : "button"); if (err != NULL) { - warn("Could not grab keycode %u with modfield %u: ", keycode, modfield); + warn("Could not grab %s %u with modfield %u: ", type, value, modfield); if (err->error_code == XCB_ACCESS) - warn("the key combination is already grabbed.\n"); + warn("the combination is already grabbed.\n"); else warn("error no %u encountered.\n", err->error_code); free(err); } else { - PRINTF("grab key %u %u\n", keycode, modfield); + PRINTF("grab %s %u %u\n", type, value, modfield); } } @@ -2385,6 +2394,7 @@ void ungrab(void) { PUTS("ungrab"); xcb_ungrab_key(dpy, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY); + xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, root, XCB_MOD_MASK_ANY); } int16_t modfield_from_keysym(xcb_keysym_t keysym) @@ -2445,7 +2455,7 @@ xcb_keycode_t *keycodes_from_keysym(xcb_keysym_t keysym) return result; } -bool parse_keysym(char *name, xcb_keysym_t *keysym) +bool parse_key(char *name, xcb_keysym_t *keysym) { for (unsigned int i = 0; i < LENGTH(nks_dict); i++) { keysym_dict_t nks = nks_dict[i]; @@ -2457,7 +2467,13 @@ bool parse_keysym(char *name, xcb_keysym_t *keysym) return false; } -bool parse_modmask(char *name, uint16_t *modfield) +bool parse_button(char *name, xcb_button_t *butidx) +{ + /* X handles up to 24 buttons */ + return (sscanf(name, "button%hhu", butidx) == 1); +} + +bool parse_modifier(char *name, uint16_t *modfield) { if (strcmp(name, "shift") == 0) { *modfield |= XCB_MOD_MASK_SHIFT; @@ -2502,6 +2518,15 @@ bool parse_modmask(char *name, uint16_t *modfield) return false; } +xcb_event_mask_t key_to_mouse(xcb_event_mask_t event_mask) +{ + if (event_mask == XCB_KEY_PRESS) + return XCB_BUTTON_PRESS; + else if (event_mask == XCB_KEY_RELEASE) + return XCB_BUTTON_RELEASE; + return event_mask; +} + void get_lock_fields(void) { num_lock = modfield_from_keysym(XK_Num_Lock); @@ -2510,31 +2535,37 @@ void get_lock_fields(void) PRINTF("lock fields %u %u %u\n", num_lock, caps_lock, scroll_lock); } -void generate_hotkeys(xcb_keysym_t keysym, uint16_t modfield, xcb_event_mask_t event_mask, char *command) +void generate_hotkeys(xcb_keysym_t keysym, xcb_button_t button, uint16_t modfield, xcb_event_mask_t event_mask, char *command) { - xcb_keycode_t *keycodes = keycodes_from_keysym(keysym); - if (keycodes != NULL) - for (xcb_keycode_t *kc = keycodes; *kc != XCB_NO_SYMBOL; kc++) { - xcb_keysym_t natural_keysym = xcb_key_symbols_get_keysym(symbols, *kc, 0); - for (unsigned char col = 0; col < KEYSYMS_PER_KEYCODE; col++) { - xcb_keysym_t ks = xcb_key_symbols_get_keysym(symbols, *kc, col); - if (ks == keysym) { - uint16_t implicit_modfield = (col & 1 ? XCB_MOD_MASK_SHIFT : 0) | (col & 2 ? modfield_from_keysym(XK_Mode_switch) : 0); - uint16_t explicit_modfield = modfield | implicit_modfield; - hotkey_t *hk = make_hotkey(natural_keysym, explicit_modfield, event_mask, command); - add_hotkey(hk); - PRINTF("hotkey %u %u %u %s\n", natural_keysym, explicit_modfield, event_mask, command); - break; + if (button == XCB_NONE) { + xcb_keycode_t *keycodes = keycodes_from_keysym(keysym); + if (keycodes != NULL) + for (xcb_keycode_t *kc = keycodes; *kc != XCB_NO_SYMBOL; kc++) { + xcb_keysym_t natural_keysym = xcb_key_symbols_get_keysym(symbols, *kc, 0); + for (unsigned char col = 0; col < KEYSYMS_PER_KEYCODE; col++) { + xcb_keysym_t ks = xcb_key_symbols_get_keysym(symbols, *kc, col); + if (ks == keysym) { + uint16_t implicit_modfield = (col & 1 ? XCB_MOD_MASK_SHIFT : 0) | (col & 2 ? modfield_from_keysym(XK_Mode_switch) : 0); + uint16_t explicit_modfield = modfield | implicit_modfield; + hotkey_t *hk = make_hotkey(natural_keysym, button, explicit_modfield, event_mask, command); + add_hotkey(hk); + break; + } } } - } - free(keycodes); + free(keycodes); + } else { + hotkey_t *hk = make_hotkey(keysym, button, modfield, event_mask, command); + add_hotkey(hk); + } } -hotkey_t *make_hotkey(xcb_keysym_t keysym, uint16_t modfield, xcb_event_mask_t event_mask, char *command) +hotkey_t *make_hotkey(xcb_keysym_t keysym, xcb_button_t button, uint16_t modfield, xcb_event_mask_t event_mask, char *command) { + PRINTF("hotkey %u %u %u %u %s\n", keysym, button, modfield, event_mask, command); hotkey_t *hk = malloc(sizeof(hotkey_t)); hk->keysym = keysym; + hk->button = button; hk->modfield = modfield; hk->event_mask = event_mask; strncpy(hk->command, command, sizeof(hk->command)); @@ -2542,10 +2573,10 @@ hotkey_t *make_hotkey(xcb_keysym_t keysym, uint16_t modfield, xcb_event_mask_t e return hk; } -hotkey_t *find_hotkey(xcb_keysym_t keysym, uint16_t modfield, xcb_event_mask_t event_mask) +hotkey_t *find_hotkey(xcb_keysym_t keysym, xcb_button_t button, uint16_t modfield, xcb_event_mask_t event_mask) { for (hotkey_t *hk = hotkeys; hk != NULL; hk = hk->next) - if (hk->keysym == keysym && hk->modfield == modfield && hk->event_mask == event_mask) + if (hk->keysym == keysym && hk->button == button && hk->modfield == modfield && hk->event_mask == event_mask) return hk; return NULL; } diff --git a/keys.h b/keys.h @@ -6,17 +6,19 @@ #define KEYSYMS_PER_KEYCODE 4 void grab(void); -void grab_key(xcb_keycode_t, uint16_t); -void grab_key_checked(xcb_keycode_t, uint16_t); +void grab_key_button(xcb_keycode_t, xcb_button_t, uint16_t); +void grab_key_button_checked(xcb_keycode_t, xcb_button_t, uint16_t); void ungrab(void); int16_t modfield_from_keysym(xcb_keysym_t); xcb_keycode_t *keycodes_from_keysym(xcb_keysym_t); -bool parse_keysym(char *, xcb_keysym_t *); -bool parse_modmask(char *, uint16_t *); +bool parse_key(char *, xcb_keysym_t *); +bool parse_button(char *, xcb_button_t *); +bool parse_modifier(char *, uint16_t *); +xcb_event_mask_t key_to_mouse(xcb_event_mask_t); void get_lock_fields(void); -void generate_hotkeys(xcb_keysym_t, uint16_t, xcb_event_mask_t, char *); -hotkey_t *make_hotkey(xcb_keysym_t, uint16_t, xcb_event_mask_t, char *); -hotkey_t *find_hotkey(xcb_keysym_t, uint16_t, xcb_event_mask_t); +void generate_hotkeys(xcb_keysym_t, xcb_button_t, uint16_t, xcb_event_mask_t, char *); +hotkey_t *make_hotkey(xcb_keysym_t, xcb_button_t, uint16_t, xcb_event_mask_t, char *); +hotkey_t *find_hotkey(xcb_keysym_t, xcb_button_t, uint16_t, xcb_event_mask_t); void add_hotkey(hotkey_t *); #endif diff --git a/sxhkd.1 b/sxhkd.1 @@ -51,6 +51,9 @@ The keysym names are those your will get from .BR xev (1) (minus the prefix if any). .PP +Mouse hotkeys can be defined by using one of the following special keysym names: +.IR button1 ", " button2 ", " button3 ", ..., " button24 . +.PP What is actually executed is .IR "/bin/sh -c COMMAND" , which means you can use environment variables in diff --git a/sxhkd.c b/sxhkd.c @@ -70,6 +70,7 @@ void load_config(void) char line[MAXLEN]; xcb_keysym_t keysym = XCB_NO_SYMBOL; + xcb_button_t button = XCB_NONE; uint16_t modfield = 0; xcb_event_mask_t event_mask = XCB_KEY_PRESS; @@ -77,8 +78,10 @@ void load_config(void) if (strlen(line) < 2 || line[0] == '#') { continue; } else if (isspace(line[0])) { - if (keysym == XCB_NO_SYMBOL) + if (keysym == XCB_NO_SYMBOL && button == XCB_NONE) continue; + if (button != XCB_NONE) + event_mask = key_to_mouse(event_mask); unsigned int i = strlen(line) - 1; while (i > 0 && isspace(line[i])) line[i--] = '\0'; @@ -87,9 +90,10 @@ void load_config(void) i++; if (i < strlen(line)) { char *command = line + i; - generate_hotkeys(keysym, modfield, event_mask, command); + generate_hotkeys(keysym, button, modfield, event_mask, command); } keysym = XCB_NO_SYMBOL; + button = XCB_NONE; modfield = 0; event_mask = XCB_KEY_PRESS; } else { @@ -101,7 +105,7 @@ void load_config(void) event_mask = XCB_KEY_RELEASE; name++; } - if (!parse_modmask(name, &modfield) && !parse_keysym(name, &keysym)) + if (!parse_modifier(name, &modfield) && !parse_key(name, &keysym) && !parse_button(name, &button)) warn("Unrecognized key name: '%s'.\n", name); } while ((name = strtok(NULL, TOK_SEP)) != NULL); } @@ -126,26 +130,39 @@ void mapping_notify(xcb_generic_event_t *evt) } } -void key_event(xcb_generic_event_t *evt, xcb_event_mask_t event_mask) +void key_button_event(xcb_generic_event_t *evt, xcb_event_mask_t event_mask) { - xcb_keycode_t keycode = XCB_NO_SYMBOL; + xcb_keysym_t keysym = XCB_NO_SYMBOL; + xcb_keycode_t keycode = XCB_NONE; + xcb_button_t button = XCB_NONE; uint16_t modfield = 0; uint16_t lockfield = num_lock | caps_lock | scroll_lock; if (event_mask == XCB_KEY_PRESS) { xcb_key_press_event_t *e = (xcb_key_press_event_t *) evt; keycode = e->detail; modfield = e->state; + keysym = xcb_key_symbols_get_keysym(symbols, keycode, 0); PRINTF("key press %u %u\n", keycode, modfield); - } else { + } else if (event_mask == XCB_KEY_RELEASE) { xcb_key_release_event_t *e = (xcb_key_release_event_t *) evt; keycode = e->detail; modfield = e->state; + keysym = xcb_key_symbols_get_keysym(symbols, keycode, 0); PRINTF("key release %u %u\n", keycode, modfield); + } else if (event_mask == XCB_BUTTON_PRESS) { + xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt; + button = e->detail; + modfield = e->state; + PRINTF("button press %u %u\n", button, modfield); + } else if (event_mask == XCB_BUTTON_RELEASE) { + xcb_button_release_event_t *e = (xcb_button_release_event_t *) evt; + button = e->detail; + modfield = e->state; + PRINTF("button release %u %u\n", button, modfield); } - xcb_keysym_t keysym = xcb_key_symbols_get_keysym(symbols, keycode, 0); - if (keysym != XCB_NO_SYMBOL) { - modfield &= ~lockfield; - hotkey_t *hk = find_hotkey(keysym, modfield, event_mask); + modfield &= ~lockfield; + if (keysym != XCB_NO_SYMBOL || button != XCB_NONE) { + hotkey_t *hk = find_hotkey(keysym, button, modfield, event_mask); if (hk != NULL) { char *cmd[] = {SHELL, "-c", hk->command, NULL}; spawn(cmd); @@ -204,7 +221,9 @@ int main(int argc, char *argv[]) switch (event_mask) { case XCB_KEY_PRESS: case XCB_KEY_RELEASE: - key_event(evt, event_mask); + case XCB_BUTTON_PRESS: + case XCB_BUTTON_RELEASE: + key_button_event(evt, event_mask); break; case XCB_MAPPING_NOTIFY: mapping_notify(evt); diff --git a/sxhkd.h b/sxhkd.h @@ -13,6 +13,7 @@ typedef struct hotkey_t hotkey_t; struct hotkey_t { xcb_keysym_t keysym; + xcb_button_t button; uint16_t modfield; xcb_event_mask_t event_mask; char command[MAXLEN];