added command aliases

xkbcommon
Alessandro Mauri 3 years ago
parent 6904aff298
commit dfa7976f18
  1. 3
      TODO
  2. 14
      config.template
  3. 41
      hkd.1
  4. 142
      hkd.c

@ -3,9 +3,6 @@ Improvements
* Add option for autorepeat (useful for volume) * Add option for autorepeat (useful for volume)
- User can configure autorepeat speed - User can configure autorepeat speed
* add command aliases:
@ <name>: <command>
Future plans Future plans
============ ============

@ -30,3 +30,17 @@
# - LEFALT,LEFTSHIFT,S: ~/screenshot.sh -c # - LEFALT,LEFTSHIFT,S: ~/screenshot.sh -c
# * LEFTMETA,1,D: $SCRIPTDIR/wonkyscript # * LEFTMETA,1,D: $SCRIPTDIR/wonkyscript
# - LEFTMETA,LEFTALT,LEFTSHIFT,S: shutdown now # - LEFTMETA,LEFTALT,LEFTSHIFT,S: shutdown now
# Aliases are a way to give a name to a possibly complex, long or recurring
# command, to declaring aliases is similar to declaring an hotkey, you must
# start the line with '@' to indicate an alias, give it a name (mind that this
# is case sensitive), and a command after a ':', just like in hotkeys.
# To use an alias in an hotkey you have to replace the ':' before the command
# with '<' to indicate that the following string is an alias and not a command.
# Aliases have to be declared before using them, they can also be concatenated.
# Examples:
# @ term : alacritty
# @ volumeup: amixer -q sset Master 3%+
# - leftmeta, p < term
# - VOLUMEUP < volumeup

41
hkd.1

@ -51,26 +51,43 @@ The config file is parsed as follows:
lines staring with '#' are comments lines staring with '#' are comments
.PP .PP
Every new hotkey starts with one of these markers: Every new hotkey starts with one of these markers:
.IP - .IP \-
normal matching normal matching
.IP * .IP *
fuzzy matching fuzzy matching
.IP @
alias declaration
.PP .PP
Normal matching means that the keys need to be pressed in the same order as they Normal matching means that the keys need to be pressed in the same order as they
are declared, whereas fuzzy matching means that they can be pressed in any order. are declared, whereas fuzzy matching means that they can be pressed in any order.
.PP Aliases are a name-command pair that can be used instead of commands in hotkey
definitions.
.SS "Hotkey definition"
Leading or trailing whitespaces are ignored, whitespaces between the marker and Leading or trailing whitespaces are ignored, whitespaces between the marker and
the ':' are also ignored, whitespaces after the ':' are not ignored. The general the ':' or '<' are also ignored, whitespaces after the ':' are not ignored.
syntax for a hotkey is: The general syntax for a hotkey is:
.I <marker> <keys>: <command> .EX
.PP <'*' or '\-'> <keys>: <command>
.EE
if a hotkeys uses an explicit command, if you want to use an alias it becomes:
.EX
<'*' or '\-'> <keys> < <alias name>
.EE
note the '<' instead of ':'.
.SS "Alias definition"
The general syntax for aliases is:
.EX
@ <name>: <command>
.EE
beware that alias names are case sensitive.
.SS "Command field"
Commads are expanded using Commads are expanded using
.BR wordexp(3) .BR wordexp(3)
so "|&;<>(){}" as well as unescaped newlines are forbidden and will result in so "|&;<>(){}" as well as unescaped newlines are forbidden and will result in
error, read the manpage for error, read the manpage for
.BR wordexp(3) .BR wordexp(3)
for more info about the possible word expansion capabilities. for more info about the possible word expansion capabilities.
.PP .SS "Keys field"
Possible keys are taken directly from linux's input.h header file, those Possible keys are taken directly from linux's input.h header file, those
include normal keys, multimedia keys, special keys and button events, for the include normal keys, multimedia keys, special keys and button events, for the
full list of available keys either refer to the linux header file input.h or full list of available keys either refer to the linux header file input.h or
@ -82,7 +99,7 @@ strings, such as: 'leftmeta,UP', 'VOLUMEUP' or 'leftctrl,LEFTALT,cancel'.
Some aliases are in place to avoid overly verbose repetitive definitions, those Some aliases are in place to avoid overly verbose repetitive definitions, those
are: CTRL \-> LEFTCTRL, META \-> LEFTMETA, ALT \-> LEFTALT, SHIFT \-> LEFTSHIFT, are: CTRL \-> LEFTCTRL, META \-> LEFTMETA, ALT \-> LEFTALT, SHIFT \-> LEFTSHIFT,
PRINTSCR \-> SYSRQ, MIC_MUTE \-> F20. PRINTSCR \-> SYSRQ, MIC_MUTE \-> F20.
.PP .SS "Live reloading"
hkd can live reload the configuration file by signaling it with hkd can live reload the configuration file by signaling it with
.I SIGUSR1 .I SIGUSR1
for example with the command "$ pkill -USR1 -x hkd", for easier use one could add for example with the command "$ pkill -USR1 -x hkd", for easier use one could add
@ -94,9 +111,11 @@ This is a valid config file example
.EX .EX
# Contents of # Contents of
# $HOME/.config/hkd/config # $HOME/.config/hkd/config
\- LEFALT,LEFTSHIFT,S: ~/screenshot.sh \-c @ term: xterm
* LEFTMETA,1,D: $SCRIPTDIR/wonkyscript \- LEFALT,leftshift,S: ~/screenshot.sh \-c
\- LEFTMETA,LEFTALT,LEFTSHIFT,S: shutdown now * LEFTMETA,1, D: $SCRIPTDIR/wonkyscript
\- LEFTMETA ,LEFTALT,LEFTSHIFT,S : shutdown now
\- leftmeta, enter < term
.EE .EE
.SH BUGS .SH BUGS

142
hkd.c

@ -81,8 +81,14 @@ struct key_buffer {
/* Hotkey list: linked list that holds all valid hoteys parsed from the /* Hotkey list: linked list that holds all valid hoteys parsed from the
* config file and the corresponding command */ * config file and the corresponding command */
struct hotkey_list_e {
union hotkey_main_data {
struct key_buffer kb; struct key_buffer kb;
char * name;
};
struct hotkey_list_e {
union hotkey_main_data data;
char *command; char *command;
int fuzzy; int fuzzy;
struct hotkey_list_e *next; struct hotkey_list_e *next;
@ -112,8 +118,9 @@ int prepare_epoll (int *, int, int);
unsigned short key_to_code (char *); unsigned short key_to_code (char *);
const char * code_to_name (unsigned int); const char * code_to_name (unsigned int);
/* hotkey list operations */ /* hotkey list operations */
void hotkey_list_add (struct hotkey_list_e *, struct key_buffer *, char *, int); void hotkey_list_add (struct hotkey_list_e *, union hotkey_main_data *, char *, int);
void hotkey_list_destroy (struct hotkey_list_e *); void hotkey_list_destroy (struct hotkey_list_e *);
void hotkey_list_remove (struct hotkey_list_e *, struct hotkey_list_e *);
int main (int argc, char *argv[]) int main (int argc, char *argv[])
{ {
@ -181,8 +188,8 @@ int main (int argc, char *argv[])
for (struct hotkey_list_e *tmp = hotkey_list; tmp; tmp = tmp->next) { for (struct hotkey_list_e *tmp = hotkey_list; tmp; tmp = tmp->next) {
printf("Hotkey\n"); printf("Hotkey\n");
printf("\tKeys: "); printf("\tKeys: ");
for (unsigned int i = 0; i < tmp->kb.size; i++) for (unsigned int i = 0; i < tmp->data.kb.size; i++)
printf("%s ", code_to_name(tmp->kb.buf[i])); printf("%s ", code_to_name(tmp->data.kb.buf[i]));
printf("\n\tMatching: %s\n", tmp->fuzzy ? "fuzzy" : "ordered"); printf("\n\tMatching: %s\n", tmp->fuzzy ? "fuzzy" : "ordered");
printf("\tCommand: %s\n\n", tmp->command); printf("\tCommand: %s\n\n", tmp->command);
} }
@ -268,9 +275,9 @@ int main (int argc, char *argv[])
if (hotkey_size_mask & 1 << (pb.size - 1)) { if (hotkey_size_mask & 1 << (pb.size - 1)) {
for (tmp = hotkey_list; tmp != NULL; tmp = tmp->next) { for (tmp = hotkey_list; tmp != NULL; tmp = tmp->next) {
if (tmp->fuzzy) if (tmp->fuzzy)
t = key_buffer_compare_fuzzy(&pb, &tmp->kb); t = key_buffer_compare_fuzzy(&pb, &tmp->data.kb);
else else
t = key_buffer_compare(&pb, &tmp->kb); t = key_buffer_compare(&pb, &tmp->data.kb);
if (t) if (t)
exec_command(tmp->command); exec_command(tmp->command);
@ -507,7 +514,8 @@ void hotkey_list_destroy (struct hotkey_list_e *head)
} }
} }
void hotkey_list_add (struct hotkey_list_e *head, struct key_buffer *kb, char *cmd, int f) // FIXME: use **head or hardcode to hotkey_list
void hotkey_list_add (struct hotkey_list_e *head, union hotkey_main_data *dt, char *cmd, int f)
{ {
int size; int size;
struct hotkey_list_e *tmp; struct hotkey_list_e *tmp;
@ -518,7 +526,7 @@ void hotkey_list_add (struct hotkey_list_e *head, struct key_buffer *kb, char *c
if (!(tmp->command = malloc(size + 1))) if (!(tmp->command = malloc(size + 1)))
die("Memory allocation failed in hotkey_list_add():"); die("Memory allocation failed in hotkey_list_add():");
strcpy(tmp->command, cmd); strcpy(tmp->command, cmd);
tmp->kb = *kb; tmp->data = *dt;
tmp->fuzzy = f; tmp->fuzzy = f;
tmp->next = NULL; tmp->next = NULL;
@ -529,25 +537,45 @@ void hotkey_list_add (struct hotkey_list_e *head, struct key_buffer *kb, char *c
hotkey_list = tmp; hotkey_list = tmp;
} }
void hotkey_list_remove (struct hotkey_list_e *head, struct hotkey_list_e *elem)
{
if(!head)
return;
if (head == elem) {
hotkey_list = head->next;
} else {
for (; head && head->next != elem; head = head->next);
if (head && head->next)
head->next = head->next->next;
}
if (elem) {
if (elem->fuzzy == -1)
free(elem->data.name);
free(elem->command);
free(elem);
}
}
void parse_config_file (void) void parse_config_file (void)
{ {
wordexp_t result = {0}; wordexp_t result = {0};
FILE *fd; FILE *fd;
/* normal, skip line, get matching, get keys, get command, output */ /* normal, skip line, get matching, get keys, get command, output */
enum {NORM, LINE_SKIP, GET_MATCH, GET_KEYS, GET_CMD, LAST} parse_state = NORM; enum {NORM, LINE_SKIP, GET_TYPE, GET_KEYS, GET_CMD, LAST} parse_state = NORM;
enum {CONT, NEW_BL, LAST_BL, END} block_state = CONT; /* continue, new block, last block, end */ enum {CONT, NEW_BL, LAST_BL, END} block_state = CONT; /* continue, new block, last block, end */
enum {HK_NORM = 0, HK_FUZZY = 1, ALIAS = -1} type;
int cmd_is_alias = 0;
int alloc_tmp = 0, alloc_size = 0; int alloc_tmp = 0, alloc_size = 0;
int fuzzy = 0;
int i_tmp = 0, linenum = 1; int i_tmp = 0, linenum = 1;
char block[BLOCK_SIZE + 1] = {0}; char block[BLOCK_SIZE + 1] = {0};
char *bb = NULL; char *bb = NULL;
char *keys = NULL; char *keys = NULL;
char *cmd = NULL; char *cmd = NULL;
char *cp_tmp = NULL; char *cp_tmp = NULL;
struct key_buffer kb; union hotkey_main_data dt = {0};
unsigned short us_tmp = 0; unsigned short us_tmp = 0;
key_buffer_reset(&kb);
if (ext_config_file) { if (ext_config_file) {
switch (wordexp(ext_config_file, &result, 0)) { switch (wordexp(ext_config_file, &result, 0)) {
case 0: case 0:
@ -627,12 +655,12 @@ void parse_config_file (void)
parse_state = LINE_SKIP; parse_state = LINE_SKIP;
break; break;
default: default:
parse_state = GET_MATCH; parse_state = GET_TYPE;
break; break;
} }
break; break;
// Skip line (comment) // Skip line (comment)
case 1: case LINE_SKIP:
while (*bb != '\n' && *bb) while (*bb != '\n' && *bb)
bb++; bb++;
if (*bb) { if (*bb) {
@ -644,17 +672,20 @@ void parse_config_file (void)
} }
break; break;
// Get compairson method // Get compairson method
case 2: case GET_TYPE:
switch (*bb) { switch (*bb) {
case '-': case '-':
fuzzy = 0; type = HK_NORM;
break; break;
case '*': case '*':
fuzzy = 1; type = HK_FUZZY;
break;
case '@':
type = ALIAS;
break; break;
default: default:
die("Error at line %d: " die("Error at line %d: "
"hotkey definition must start with '-' or '*'", "hotkey definition must start with '-', '*' or '@'",
linenum); linenum);
break; break;
} }
@ -662,7 +693,7 @@ void parse_config_file (void)
parse_state = GET_KEYS; parse_state = GET_KEYS;
break; break;
// Get keys // Get keys
case 3: case GET_KEYS:
if (!keys) { if (!keys) {
if (!(keys = malloc(alloc_size = (sizeof(char) * 64)))) if (!(keys = malloc(alloc_size = (sizeof(char) * 64))))
die("malloc for keys in parse_config_file():"); die("malloc for keys in parse_config_file():");
@ -674,8 +705,10 @@ void parse_config_file (void)
} }
for (alloc_tmp = 0; bb[alloc_tmp] && for (alloc_tmp = 0; bb[alloc_tmp] &&
bb[alloc_tmp] != ':' && bb[alloc_tmp] != '\n' && bb[alloc_tmp] != ':' &&
alloc_tmp < alloc_size; alloc_tmp++); bb[alloc_tmp] != '<' &&
bb[alloc_tmp] != '\n' &&
alloc_tmp < alloc_size; alloc_tmp++);
if (!bb[alloc_tmp] || alloc_tmp == alloc_size) { if (!bb[alloc_tmp] || alloc_tmp == alloc_size) {
strncat(keys, bb, alloc_tmp); strncat(keys, bb, alloc_tmp);
@ -685,19 +718,20 @@ void parse_config_file (void)
else else
block_state = NEW_BL; block_state = NEW_BL;
break; break;
} else if (bb[alloc_tmp] == ':') { } else if (bb[alloc_tmp] == ':' || bb[alloc_tmp] == '<') {
cmd_is_alias = (bb[alloc_tmp] == '<');
strncat(keys, bb, alloc_tmp); strncat(keys, bb, alloc_tmp);
bb += alloc_tmp + 1; bb += alloc_tmp + 1;
parse_state = GET_CMD; parse_state = GET_CMD;
break; break;
} else { } else {
die("Error at line %d: " die("Error at line %d: "
"no command specified, missing ':' after keys", "no command specified, missing ':' or '<' after keys",
linenum); linenum);
} }
break; break;
// Get command // Get command
case 4: case GET_CMD:
if (!cmd) { if (!cmd) {
if (!(cmd = malloc(alloc_size = (sizeof(char) * 128)))) if (!(cmd = malloc(alloc_size = (sizeof(char) * 128))))
die("malloc for cmd in parse_config_file():"); die("malloc for cmd in parse_config_file():");
@ -728,7 +762,7 @@ void parse_config_file (void)
break; break;
} }
break; break;
case 5: case LAST:
if (!keys) if (!keys)
die("error"); die("error");
i_tmp = strlen(keys); i_tmp = strlen(keys);
@ -743,15 +777,21 @@ void parse_config_file (void)
die("Error at line %d: " die("Error at line %d: "
"keys not present", linenum - 1); "keys not present", linenum - 1);
do { if (type != ALIAS) {
if (!(us_tmp = key_to_code(cp_tmp))) { do {
die("Error at line %d: " if (!(us_tmp = key_to_code(cp_tmp))) {
"%s is not a valid key", die("Error at line %d: "
linenum - 1, cp_tmp); "%s is not a valid key",
} linenum - 1, cp_tmp);
if (key_buffer_add(&kb, us_tmp)) }
die("Too many keys"); if (key_buffer_add(&dt.kb, us_tmp))
} while ((cp_tmp = strtok(NULL, ","))); die("Too many keys");
} while ((cp_tmp = strtok(NULL, ",")));
} else {
if (!(dt.name = malloc(strlen(cp_tmp) + 1)))
die("malloc in parse_config_file():");
strcpy(dt.name, cp_tmp);
}
cp_tmp = cmd; cp_tmp = cmd;
while (isblank(*cp_tmp)) while (isblank(*cp_tmp))
@ -759,11 +799,28 @@ void parse_config_file (void)
if (*cp_tmp == '\0') if (*cp_tmp == '\0')
die("Error at line %d: " die("Error at line %d: "
"command not present", linenum - 1); "command not present", linenum - 1);
if (cmd_is_alias) {
struct hotkey_list_e *hkl = hotkey_list;
// stolen way of removing leading spaces
char * end = cp_tmp + strlen(cp_tmp) - 1;
while(end > cp_tmp && isspace((unsigned char)*end)) end--;
end[1] = '\0';
while (hkl && !(hkl->fuzzy == ALIAS && strstr(hkl->data.name, cp_tmp)))
hkl = hkl->next;
if (hkl) {
cp_tmp = hkl->command;
} else {
die("Error at line %d: "
"alias %s not found", linenum - 1,
cp_tmp);
}
}
hotkey_list_add(hotkey_list, &kb, cp_tmp, fuzzy); hotkey_list_add(hotkey_list, &dt, cp_tmp, type);
hotkey_size_mask |= 1 << (kb.size - 1);
key_buffer_reset(&kb); if (type != ALIAS)
key_buffer_reset(&dt.kb);
free(keys); free(keys);
free(cmd); free(cmd);
cp_tmp = keys = cmd = NULL; cp_tmp = keys = cmd = NULL;
@ -777,6 +834,15 @@ void parse_config_file (void)
} }
} }
} }
for (struct hotkey_list_e *hkl = hotkey_list, *tmp; hkl; hkl = hkl->next) {
if (hkl->fuzzy == ALIAS) {
tmp = hkl;
hkl = hkl->next;
hotkey_list_remove(hotkey_list, tmp);
} else {
hotkey_size_mask |= 1 << (hkl->data.kb.size - 1);
}
}
} }
unsigned short key_to_code (char *key) unsigned short key_to_code (char *key)
@ -832,6 +898,6 @@ void usage (void)
"\t-v verbose, prints all the key presses and debug information\n" "\t-v verbose, prints all the key presses and debug information\n"
"\t-d dump, dumps the hotkey list and exits\n" "\t-d dump, dumps the hotkey list and exits\n"
"\t-h prints this help message\n" "\t-h prints this help message\n"
"\t-f file uses the specified file as config\n"); "\t-c file uses the specified file as config\n");
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }

Loading…
Cancel
Save