From aa146c847d7e03881a99382100f1f61e473475c7 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 20 Jun 2020 15:28:05 +0530 Subject: Renames project to shotkey --- .gitignore | 2 +- Makefile | 24 ++++---- README.md | 5 +- config.mk | 2 +- hotkeythingy.1 | 102 ------------------------------- hotkeythingy.c | 190 --------------------------------------------------------- shotkey.1 | 102 +++++++++++++++++++++++++++++++ shotkey.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 309 insertions(+), 308 deletions(-) delete mode 100644 hotkeythingy.1 delete mode 100644 hotkeythingy.c create mode 100644 shotkey.1 create mode 100644 shotkey.c diff --git a/.gitignore b/.gitignore index 26ca78a..edd2a7d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -hotkeythingy +shotkey *.o diff --git a/Makefile b/Makefile index 7c86960..be58030 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,15 @@ -# hotkeythingy - dynamic window manager +# shotkey - Suckless hot key daemon # See LICENSE file for copyright and license details. include config.mk -SRC = hotkeythingy.c +SRC = shotkey.c OBJ = ${SRC:.c=.o} -all: clean options hotkeythingy +all: clean options shotkey options: - @echo hotkeythingy build options: + @echo shotkey build options: @echo "CFLAGS = ${CFLAGS}" @echo "LDFLAGS = ${LDFLAGS}" @echo "CC = ${CC}" @@ -19,22 +19,22 @@ options: ${OBJ}: config.mk -hotkeythingy: ${OBJ} +shotkey: ${OBJ} ${CC} -o $@ ${OBJ} ${LDFLAGS} clean: - rm -f hotkeythingy ${OBJ} + rm -f shotkey ${OBJ} install: all mkdir -p ${DESTDIR}${PREFIX}/bin - cp -f hotkeythingy ${DESTDIR}${PREFIX}/bin - chmod 755 ${DESTDIR}${PREFIX}/bin/hotkeythingy + cp -f shotkey ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/shotkey mkdir -p ${DESTDIR}${MANPREFIX}/man1 - sed "s/VERSION/${VERSION}/g" < hotkeythingy.1 > ${DESTDIR}${MANPREFIX}/man1/hotkeythingy.1 - chmod 644 ${DESTDIR}${MANPREFIX}/man1/hotkeythingy.1 + sed "s/VERSION/${VERSION}/g" < shotkey.1 > ${DESTDIR}${MANPREFIX}/man1/shotkey.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/shotkey.1 uninstall: - rm -f ${DESTDIR}${PREFIX}/bin/hotkeythingy\ - ${DESTDIR}${MANPREFIX}/man1/hotkeythingy.1 + rm -f ${DESTDIR}${PREFIX}/bin/shotkey\ + ${DESTDIR}${MANPREFIX}/man1/shotkey.1 .PHONY: all options clean dist install uninstall diff --git a/README.md b/README.md index cd2a22d..6912bb9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# HotKeyThingy -A suckless hot key daemon for X inspired by the suckless philosophy with configurable custom modes. +# SHotKey +A Suckless Hot Key daemon for X inspired by the suckless philosophy with configurable custom modes. ~200 LOC. ~20KB binary. @@ -12,6 +12,7 @@ sudo make install ## Configuring +Fork this repo and configure it to your likings. You can edit `config.h` to configure key bindings. * `shell` - Configure the shell used for executing the commands diff --git a/config.mk b/config.mk index 1aab402..5ed880a 100644 --- a/config.mk +++ b/config.mk @@ -1,4 +1,4 @@ -# hotkeythingy version +# shotkey version VERSION = 0.1 # Customize below to fit your system diff --git a/hotkeythingy.1 b/hotkeythingy.1 deleted file mode 100644 index 8185679..0000000 --- a/hotkeythingy.1 +++ /dev/null @@ -1,102 +0,0 @@ -.TH HOTKEYTHINGY 1 hotkeythingy\-VERSION -.SH NAME -hotkeythingy \- A suckless hot key daemon for X inspired by the suckless philosophy with configurable custom modes. -.SH SYNOPSIS -.B hotkeythingy -.SH DESCRIPTION -A suckless hot key daemon for X inspired by the suckless philosophy with configurable custom modes - - -.SH Configuration -You can cofigure your key bindings, modes and other shit inside `config.h`. -This keeps it fast, secure and simple. - -.SS config.h - -.TP -.B shell -The shell to use to execute the commands. - - -.TP -.B keys -This is where you declare the list of key bindings. -Examples - -.B Super+d -opens dmenu_run; -.B Super+m -enters VolumeControl mode; -.B Super+l -enters Layout mode and quits mode after the next key press - -.EX -static Key keys[] = { - { Mod4Mask, XK_d, cmd("dmenu_run") }, - { Mod4Mask, XK_m, mode(VolumeControl, True) }, - { Mod4Mask, XK_l, mode(Layout, False) }, -}; -.EE - - -.TP -.B modes -This is where you define the key bindings inside your modes - -Once you are in volume control mode, (with persist: True), you can use j and k to increase/decrease the volume and then when you're done, press Escape to go back to normal mode. - -Once you are in Layout mode, (with persist: False), you can use t or m to set the layout to tiled/monocle and then immedietely quit out of the mode and go back to normal mode. - -.EX -enum { - VolumeControl, - Layout, - - // Declare modes above this - MODE_SIZE, // NOTE: Do not remove this -}; - -static Key modes[MODE_SIZE][10] = { - [VolumeControl] = { - { 0, XK_j, cmd("amixer sset Master '5%-'") }, - { 0, XK_k, cmd("amixer sset Master '5%+'") }, - }, - [Layout] = { - { 0, XK_t, cmd("dwmc setlayoutex 0") }, - { 0, XK_m, cmd("dwmc setlayoutex 2") }, - }, -}; -.EE - - -.TP -.B mode_properties -This list lets you set a label to your modes (Useful for -.B on_mode_change -hook) - - -.TP -.B on_mode_change -This option allows you to run a script on every layout change. -It will provide the following environment variables for scripting. - -.B MODE_ID -This is the `index` of the mode. Normal mode is `-1`. - -.B MODE_LABEL -This is the label assigned to a mode inside mode_properties - - - - - -.SH BUGS -Report issues to https://github.com/phenax/hotkeythingy/issues -.SH AUTHOR -.MT phenax5@gmail.com -Akshay Nair -.ME -.SH LINKS -.UR https://github.com/phenax/hotkeythingy -Homepage -.UE diff --git a/hotkeythingy.c b/hotkeythingy.c deleted file mode 100644 index dcbcc08..0000000 --- a/hotkeythingy.c +++ /dev/null @@ -1,190 +0,0 @@ -#include -#include -#include -#include -#include -#include - -typedef struct Command { - char* command; - unsigned int mode; - int persist; -} Command; - -typedef struct Key { - unsigned int mod; - KeySym key; - Command command; -} Key; - -typedef struct ModeProperties { - char* label; -} ModeProperties; - -#define NormalMode -1 - -#define cmd(c) (Command) { c, NormalMode, False } -#define mode(m, p) (Command) { NULL, m, p } - -#include "config.h" - -#define LENGTH(X) (sizeof X / sizeof X[0]) -#define CLEANMASK(mask) (mask & ~LockMask & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) - -int current_mode = NormalMode; -int is_mode_persistent = 0; - -extern char** environ; - -void bind_key(Display *dpy, Window win, unsigned int mod, KeySym key) { - int keycode = XKeysymToKeycode(dpy, key); - XGrabKey(dpy, keycode, mod, win, False, GrabModeAsync, GrabModeAsync); -} -void unbind_key(Display *dpy, Window win, unsigned int mod, KeySym key) { - int keycode = XKeysymToKeycode(dpy, key); - XUngrabKey(dpy, keycode, mod, win); -} - -int error_handler(Display *disp, XErrorEvent *xe) { - switch(xe->error_code) { - case BadAccess: - printf("hotkeythingy: [BadAccess] Cant grab key binding. Already grabbed\n"); - return 0; - } - - printf("hotkeythingy: Something went wrong\n"); - return 1; -} - -void spawn(char** command) { - if (fork() == 0) { - setsid(); - execve(command[0], command, environ); - fprintf(stderr, "hotkeythingy: execve %s", command[0]); - perror(" failed"); - exit(0); - } -} - -char* get_mode_label() { - if (current_mode == NormalMode) return ""; - if (LENGTH(mode_properties) <= current_mode) return ""; - ModeProperties props = mode_properties[current_mode]; - return props.label; -} - -void handle_mode_change() { - char str[255]; - - sprintf(str, "%d", current_mode); - setenv("MODE_ID", str, 1); - - sprintf(str, "%s", get_mode_label()); - setenv("MODE_LABEL", str, 1); - - char* cmd[] = {shell, "-c", on_mode_change, NULL}; - spawn(cmd); -} - -void set_mode(int mode, unsigned int persist) { - current_mode = mode; - is_mode_persistent = persist; - handle_mode_change(); -} - -void run(Display* dpy, Window win, Command command) { - Key mode_key; - unsigned int i; - - if (command.command) { - char* cmd[] = {shell, "-c", command.command, NULL}; - spawn(cmd); - } else if(command.mode != NormalMode) { - // Bind keyboard for mode - XGrabKeyboard(dpy, win, False, GrabModeAsync, GrabModeAsync, CurrentTime); - - // Bind an escape key to quit mode - bind_key(dpy, win, 0, XK_Escape); - - // Set mode - set_mode(command.mode, command.persist); - } -} - -void keypress(Display *dpy, Window win, XKeyEvent *ev) { - unsigned int i; - Key mode_key; - KeySym keysym = XKeycodeToKeysym(dpy, (KeyCode) ev->keycode, 0); - - if (current_mode == NormalMode) { - // Bind all the normal mode keys - for (i = 0; i < LENGTH(keys); i++) { - if (keysym == keys[i].key && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) { - run(dpy, win, keys[i].command); - } - } - } else { - // Will quit if the key pressed is not defined in the mode - is_mode_persistent = False; - - if (modes[current_mode] && current_mode < LENGTH(modes)) { - // Check if key is in mode and execute - for (i = 0; i < LENGTH(modes[current_mode]); i++) { - mode_key = modes[current_mode][i]; - - if (keysym == mode_key.key && CLEANMASK(mode_key.mod) == CLEANMASK(ev->state)) { - // Action taken so keep the mode alive - is_mode_persistent = True; - run(dpy, win, mode_key.command); - } - } - - if (!is_mode_persistent) { - // Unbind mode related keys - XUngrabKeyboard(dpy, CurrentTime); - - // Unbind escape key - unbind_key(dpy, win, 0, XK_Escape); - } - } - - if (!is_mode_persistent) { - set_mode(NormalMode, False); - } - } -} - -int main() { - XSetErrorHandler(error_handler); - - int running = 1, i = 0; - - Display *dpy = XOpenDisplay(0); - Window root = DefaultRootWindow(dpy); - - // Grab keys - for (i = 0; i < LENGTH(keys); i++) { - bind_key(dpy, root, keys[i].mod, keys[i].key); - } - - XSelectInput(dpy, root, KeyPressMask); - - handle_mode_change(); - - /* main event loop */ - XEvent ev; - XSync(dpy, False); - while (running) { - XMaskEvent(dpy, KeyPressMask, &ev); - - switch (ev.type) { - case KeyPress: { - keypress(dpy, root, &ev.xkey); - break; - } - } - } - - XCloseDisplay(dpy); -} - diff --git a/shotkey.1 b/shotkey.1 new file mode 100644 index 0000000..21659e6 --- /dev/null +++ b/shotkey.1 @@ -0,0 +1,102 @@ +.TH SHOTKEY 1 shotkey\-VERSION +.SH NAME +shotkey \- A suckless hot key daemon for X inspired by the suckless philosophy with configurable custom modes. +.SH SYNOPSIS +.B shotkey +.SH DESCRIPTION +A suckless hot key daemon for X inspired by the suckless philosophy with configurable custom modes + + +.SH Configuration +You can cofigure your key bindings, modes and other shit inside `config.h`. +This keeps it fast, secure and simple. + +.SS config.h + +.TP +.B shell +The shell to use to execute the commands. + + +.TP +.B keys +This is where you declare the list of key bindings. +Examples - +.B Super+d +opens dmenu_run; +.B Super+m +enters VolumeControl mode; +.B Super+l +enters Layout mode and quits mode after the next key press + +.EX +static Key keys[] = { + { Mod4Mask, XK_d, cmd("dmenu_run") }, + { Mod4Mask, XK_m, mode(VolumeControl, True) }, + { Mod4Mask, XK_l, mode(Layout, False) }, +}; +.EE + + +.TP +.B modes +This is where you define the key bindings inside your modes + +Once you are in volume control mode, (with persist: True), you can use j and k to increase/decrease the volume and then when you're done, press Escape to go back to normal mode. + +Once you are in Layout mode, (with persist: False), you can use t or m to set the layout to tiled/monocle and then immedietely quit out of the mode and go back to normal mode. + +.EX +enum { + VolumeControl, + Layout, + + // Declare modes above this + MODE_SIZE, // NOTE: Do not remove this +}; + +static Key modes[MODE_SIZE][10] = { + [VolumeControl] = { + { 0, XK_j, cmd("amixer sset Master '5%-'") }, + { 0, XK_k, cmd("amixer sset Master '5%+'") }, + }, + [Layout] = { + { 0, XK_t, cmd("dwmc setlayoutex 0") }, + { 0, XK_m, cmd("dwmc setlayoutex 2") }, + }, +}; +.EE + + +.TP +.B mode_properties +This list lets you set a label to your modes (Useful for +.B on_mode_change +hook) + + +.TP +.B on_mode_change +This option allows you to run a script on every layout change. +It will provide the following environment variables for scripting. + +.B MODE_ID +This is the `index` of the mode. Normal mode is `-1`. + +.B MODE_LABEL +This is the label assigned to a mode inside mode_properties + + + + + +.SH BUGS +Report issues to https://github.com/phenax/shotkey/issues +.SH AUTHOR +.MT phenax5@gmail.com +Akshay Nair +.ME +.SH LINKS +.UR https://github.com/phenax/shotkey +Homepage +.UE diff --git a/shotkey.c b/shotkey.c new file mode 100644 index 0000000..d839ef4 --- /dev/null +++ b/shotkey.c @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include + +typedef struct Command { + char* command; + unsigned int mode; + int persist; +} Command; + +typedef struct Key { + unsigned int mod; + KeySym key; + Command command; +} Key; + +typedef struct ModeProperties { + char* label; +} ModeProperties; + +#define NormalMode -1 + +#define cmd(c) (Command) { c, NormalMode, False } +#define mode(m, p) (Command) { NULL, m, p } + +#include "config.h" + +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define CLEANMASK(mask) (mask & ~LockMask & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) + +int current_mode = NormalMode; +int is_mode_persistent = 0; + +extern char** environ; + +void bind_key(Display *dpy, Window win, unsigned int mod, KeySym key) { + int keycode = XKeysymToKeycode(dpy, key); + XGrabKey(dpy, keycode, mod, win, False, GrabModeAsync, GrabModeAsync); +} +void unbind_key(Display *dpy, Window win, unsigned int mod, KeySym key) { + int keycode = XKeysymToKeycode(dpy, key); + XUngrabKey(dpy, keycode, mod, win); +} + +int error_handler(Display *disp, XErrorEvent *xe) { + switch(xe->error_code) { + case BadAccess: + printf("shotkey: [BadAccess] Cant grab key binding. Already grabbed\n"); + return 0; + } + + printf("shotkey: Something went wrong\n"); + return 1; +} + +void spawn(char** command) { + if (fork() == 0) { + setsid(); + execve(command[0], command, environ); + fprintf(stderr, "shotkey: execve %s", command[0]); + perror(" failed"); + exit(0); + } +} + +char* get_mode_label() { + if (current_mode == NormalMode) return ""; + if (LENGTH(mode_properties) <= current_mode) return ""; + ModeProperties props = mode_properties[current_mode]; + return props.label; +} + +void handle_mode_change() { + char str[255]; + + sprintf(str, "%d", current_mode); + setenv("MODE_ID", str, 1); + + sprintf(str, "%s", get_mode_label()); + setenv("MODE_LABEL", str, 1); + + char* cmd[] = {shell, "-c", on_mode_change, NULL}; + spawn(cmd); +} + +void set_mode(int mode, unsigned int persist) { + current_mode = mode; + is_mode_persistent = persist; + handle_mode_change(); +} + +void run(Display* dpy, Window win, Command command) { + Key mode_key; + unsigned int i; + + if (command.command) { + char* cmd[] = {shell, "-c", command.command, NULL}; + spawn(cmd); + } else if(command.mode != NormalMode) { + // Bind keyboard for mode + XGrabKeyboard(dpy, win, False, GrabModeAsync, GrabModeAsync, CurrentTime); + + // Bind an escape key to quit mode + bind_key(dpy, win, 0, XK_Escape); + + // Set mode + set_mode(command.mode, command.persist); + } +} + +void keypress(Display *dpy, Window win, XKeyEvent *ev) { + unsigned int i; + Key mode_key; + KeySym keysym = XKeycodeToKeysym(dpy, (KeyCode) ev->keycode, 0); + + if (current_mode == NormalMode) { + // Bind all the normal mode keys + for (i = 0; i < LENGTH(keys); i++) { + if (keysym == keys[i].key && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) { + run(dpy, win, keys[i].command); + } + } + } else { + // Will quit if the key pressed is not defined in the mode + is_mode_persistent = False; + + if (modes[current_mode] && current_mode < LENGTH(modes)) { + // Check if key is in mode and execute + for (i = 0; i < LENGTH(modes[current_mode]); i++) { + mode_key = modes[current_mode][i]; + + if (keysym == mode_key.key && CLEANMASK(mode_key.mod) == CLEANMASK(ev->state)) { + // Action taken so keep the mode alive + is_mode_persistent = True; + run(dpy, win, mode_key.command); + } + } + + if (!is_mode_persistent) { + // Unbind mode related keys + XUngrabKeyboard(dpy, CurrentTime); + + // Unbind escape key + unbind_key(dpy, win, 0, XK_Escape); + } + } + + if (!is_mode_persistent) { + set_mode(NormalMode, False); + } + } +} + +int main() { + XSetErrorHandler(error_handler); + + int running = 1, i = 0; + + Display *dpy = XOpenDisplay(0); + Window root = DefaultRootWindow(dpy); + + // Grab keys + for (i = 0; i < LENGTH(keys); i++) { + bind_key(dpy, root, keys[i].mod, keys[i].key); + } + + XSelectInput(dpy, root, KeyPressMask); + + handle_mode_change(); + + /* main event loop */ + XEvent ev; + XSync(dpy, False); + while (running) { + XMaskEvent(dpy, KeyPressMask, &ev); + + switch (ev.type) { + case KeyPress: { + keypress(dpy, root, &ev.xkey); + break; + } + } + } + + XCloseDisplay(dpy); +} + -- cgit v1.3.1