Browse Source

added command aliases

xkbcommon
Alessandro Mauri 8 months ago
parent
commit
dfa7976f18
4 changed files with 148 additions and 52 deletions
  1. +0
    -3
      TODO
  2. +14
    -0
      config.template
  3. +30
    -11
      hkd.1
  4. +104
    -38
      hkd.c

+ 0
- 3
TODO View File

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


+ 14
- 0
config.template View File

@ -30,3 +30,17 @@
# - LEFALT,LEFTSHIFT,S: ~/screenshot.sh -c
# * LEFTMETA,1,D: $SCRIPTDIR/wonkyscript
# - 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

+ 30
- 11
hkd.1 View File

@ -51,26 +51,43 @@ The config file is parsed as follows:
lines staring with '#' are comments
.PP
Every new hotkey starts with one of these markers:
.IP -
.IP \-
normal matching
.IP *
fuzzy matching
.IP @
alias declaration
.PP
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.
.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
the ':' are also ignored, whitespaces after the ':' are not ignored. The general
syntax for a hotkey is:
.I <marker> <keys>: <command>
.PP
the ':' or '<' are also ignored, whitespaces after the ':' are not ignored.
The general syntax for a hotkey is:
.EX
<'*' 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
.BR wordexp(3)
so "|&;<>(){}" as well as unescaped newlines are forbidden and will result in
error, read the manpage for
.BR wordexp(3)
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
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
@ -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
are: CTRL \-> LEFTCTRL, META \-> LEFTMETA, ALT \-> LEFTALT, SHIFT \-> LEFTSHIFT,
PRINTSCR \-> SYSRQ, MIC_MUTE \-> F20.
.PP
.SS "Live reloading"
hkd can live reload the configuration file by signaling it with
.I SIGUSR1
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
# Contents of
# $HOME/.config/hkd/config
\- LEFALT,LEFTSHIFT,S: ~/screenshot.sh \-c
* LEFTMETA,1,D: $SCRIPTDIR/wonkyscript
\- LEFTMETA,LEFTALT,LEFTSHIFT,S: shutdown now
@ term: xterm
\- LEFALT,leftshift,S: ~/screenshot.sh \-c
* LEFTMETA,1, D: $SCRIPTDIR/wonkyscript
\- LEFTMETA ,LEFTALT,LEFTSHIFT,S : shutdown now
\- leftmeta, enter < term
.EE
.SH BUGS


+ 104
- 38
hkd.c View File

@ -81,8 +81,14 @@ struct key_buffer {
/* Hotkey list: linked list that holds all valid hoteys parsed from the
* config file and the corresponding command */
struct hotkey_list_e {
union hotkey_main_data {
struct key_buffer kb;
char * name;
};
struct hotkey_list_e {
union hotkey_main_data data;
char *command;
int fuzzy;
struct hotkey_list_e *next;
@ -112,8 +118,9 @@ int prepare_epoll (int *, int, int);
unsigned short key_to_code (char *);
const char * code_to_name (unsigned int);
/* 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_remove (struct hotkey_list_e *, struct hotkey_list_e *);
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) {
printf("Hotkey\n");
printf("\tKeys: ");
for (unsigned int i = 0; i < tmp->kb.size; i++)
printf("%s ", code_to_name(tmp->kb.buf[i]));
for (unsigned int i = 0; i < tmp->data.kb.size; i++)
printf("%s ", code_to_name(tmp->data.kb.buf[i]));
printf("\n\tMatching: %s\n", tmp->fuzzy ? "fuzzy" : "ordered");
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)) {
for (tmp = hotkey_list; tmp != NULL; tmp = tmp->next) {
if (tmp->fuzzy)
t = key_buffer_compare_fuzzy(&pb, &tmp->kb);
t = key_buffer_compare_fuzzy(&pb, &tmp->data.kb);
else
t = key_buffer_compare(&pb, &tmp->kb);
t = key_buffer_compare(&pb, &tmp->data.kb);
if (t)
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;
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)))
die("Memory allocation failed in hotkey_list_add():");
strcpy(tmp->command, cmd);
tmp->kb = *kb;
tmp->data = *dt;
tmp->fuzzy = f;
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;
}
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)
{
wordexp_t result = {0};
FILE *fd;
/* 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 {HK_NORM = 0, HK_FUZZY = 1, ALIAS = -1} type;
int cmd_is_alias = 0;
int alloc_tmp = 0, alloc_size = 0;
int fuzzy = 0;
int i_tmp = 0, linenum = 1;
char block[BLOCK_SIZE + 1] = {0};
char *bb = NULL;
char *keys = NULL;
char *cmd = NULL;
char *cp_tmp = NULL;
struct key_buffer kb;
union hotkey_main_data dt = {0};
unsigned short us_tmp = 0;
key_buffer_reset(&kb);
if (ext_config_file) {
switch (wordexp(ext_config_file, &result, 0)) {
case 0:
@ -627,12 +655,12 @@ void parse_config_file (void)
parse_state = LINE_SKIP;
break;
default:
parse_state = GET_MATCH;
parse_state = GET_TYPE;
break;
}
break;
// Skip line (comment)
case 1:
case LINE_SKIP:
while (*bb != '\n' && *bb)
bb++;
if (*bb) {
@ -644,17 +672,20 @@ void parse_config_file (void)
}
break;
// Get compairson method
case 2:
case GET_TYPE:
switch (*bb) {
case '-':
fuzzy = 0;
type = HK_NORM;
break;
case '*':
fuzzy = 1;
type = HK_FUZZY;
break;
case '@':
type = ALIAS;
break;
default:
die("Error at line %d: "
"hotkey definition must start with '-' or '*'",
"hotkey definition must start with '-', '*' or '@'",
linenum);
break;
}
@ -662,7 +693,7 @@ void parse_config_file (void)
parse_state = GET_KEYS;
break;
// Get keys
case 3:
case GET_KEYS:
if (!keys) {
if (!(keys = malloc(alloc_size = (sizeof(char) * 64))))
die("malloc for keys in parse_config_file():");
@ -674,8 +705,10 @@ void parse_config_file (void)
}
for (alloc_tmp = 0; bb[alloc_tmp] &&
bb[alloc_tmp] != ':' && bb[alloc_tmp] != '\n' &&
alloc_tmp < alloc_size; alloc_tmp++);
bb[alloc_tmp] != ':' &&
bb[alloc_tmp] != '<' &&
bb[alloc_tmp] != '\n' &&
alloc_tmp < alloc_size; alloc_tmp++);
if (!bb[alloc_tmp] || alloc_tmp == alloc_size) {
strncat(keys, bb, alloc_tmp);
@ -685,19 +718,20 @@ void parse_config_file (void)
else
block_state = NEW_BL;
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);
bb += alloc_tmp + 1;
parse_state = GET_CMD;
break;
} else {
die("Error at line %d: "
"no command specified, missing ':' after keys",
"no command specified, missing ':' or '<' after keys",
linenum);
}
break;
// Get command
case 4:
case GET_CMD:
if (!cmd) {
if (!(cmd = malloc(alloc_size = (sizeof(char) * 128))))
die("malloc for cmd in parse_config_file():");
@ -728,7 +762,7 @@ void parse_config_file (void)
break;
}
break;
case 5:
case LAST:
if (!keys)
die("error");
i_tmp = strlen(keys);
@ -743,15 +777,21 @@ void parse_config_file (void)
die("Error at line %d: "
"keys not present", linenum - 1);
do {
if (!(us_tmp = key_to_code(cp_tmp))) {
die("Error at line %d: "
"%s is not a valid key",
linenum - 1, cp_tmp);
}
if (key_buffer_add(&kb, us_tmp))
die("Too many keys");
} while ((cp_tmp = strtok(NULL, ",")));
if (type != ALIAS) {
do {
if (!(us_tmp = key_to_code(cp_tmp))) {
die("Error at line %d: "
"%s is not a valid key",
linenum - 1, cp_tmp);
}
if (key_buffer_add(&dt.kb, us_tmp))
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;
while (isblank(*cp_tmp))
@ -759,11 +799,28 @@ void parse_config_file (void)
if (*cp_tmp == '\0')
die("Error at line %d: "
"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_size_mask |= 1 << (kb.size - 1);
hotkey_list_add(hotkey_list, &dt, cp_tmp, type);
key_buffer_reset(&kb);
if (type != ALIAS)
key_buffer_reset(&dt.kb);
free(keys);
free(cmd);
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)
@ -832,6 +898,6 @@ void usage (void)
"\t-v verbose, prints all the key presses and debug information\n"
"\t-d dump, dumps the hotkey list and exits\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);
}

Loading…
Cancel
Save