From 092392921823969b1f3487aef3f28786738344d9 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 13:20:19 -0800 Subject: [PATCH 01/24] Add rudimentary file support --- xcape.1 | 5 +++ xcape.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/xcape.1 b/xcape.1 index 79b4e1c..caae280 100644 --- a/xcape.1 +++ b/xcape.1 @@ -5,6 +5,7 @@ xcape \- use a modifier key as another key .SH SYNOPSIS .B xcape +[file ...] [\fB-d\fR] [\fB-f\fR] [\fB-t\fR \fItimeout\fR] @@ -17,6 +18,10 @@ key in place of \fIControl_L\fR (Left Control). .SH OPTIONS .TP +.BR file\ ... +Configuration Files. Expressions will be read from the files in a line-by-line +manner. "\-" can be used to read from standard input. +.TP .BR \-d Debug mode. Will run as a foreground process and print debug information. .TP diff --git a/xcape.c b/xcape.c index 12f48c6..0b27cc0 100644 --- a/xcape.c +++ b/xcape.c @@ -76,6 +76,8 @@ void *sig_handler (void *user_data); void intercept (XPointer user_data, XRecordInterceptData *data); +KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool debug); + KeyMap_t *parse_mapping (Display *ctrl_conn, char *mapping, Bool debug); void delete_mapping (KeyMap_t *map); @@ -97,6 +99,8 @@ int main (int argc, char **argv) static char default_mapping[] = "Control_L=Escape"; char *mapping = default_mapping; + char **conf_files = NULL; + size_t n_conf = 0; XRecordRange *rec_range = XRecordAllocRange(); XRecordClientSpec client_spec = XRecordAllClients; @@ -147,9 +151,14 @@ int main (int argc, char **argv) if (optind < argc) { - fprintf (stderr, "Not a command line option: '%s'\n", argv[optind]); - print_usage (argv[0]); - return EXIT_SUCCESS; + if(mapping == default_mapping){ + conf_files = argv + optind; + n_conf = argc - optind; + }else{ + fprintf (stderr, "Not a command line option: '%s'\n", argv[optind]); + print_usage (argv[0]); + return EXIT_SUCCESS; + } } if (!XInitThreads ()) @@ -192,6 +201,25 @@ int main (int argc, char **argv) exit (EXIT_FAILURE); } + if (conf_files) + { + KeyMap_t *conf_map = parse_confs (self->ctrl_conn, conf_files, n_conf, self->debug); + if (conf_map == NULL) + { + fprintf (stderr, "Failed to parse_confs\n"); + exit (EXIT_FAILURE); + } + + if (self->map) + { + self->map->next = conf_map; + } + else + { + self->map = conf_map; + } + } + if (self->foreground != True) daemon (0, 0); @@ -438,7 +466,7 @@ KeyMap_t *parse_token (Display *dpy, char *token, Bool debug) errno = 0; parsed_code = strtoul (from, NULL, 0); /* dec, oct, hex automatically */ if (errno == 0 - && parsed_code <=255 + && parsed_code <= 255 && XkbKeycodeToKeysym (dpy, (KeyCode) parsed_code, 0, 0) != NoSymbol) { km->UseKeyCode = True; @@ -538,6 +566,77 @@ KeyMap_t *parse_token (Display *dpy, char *token, Bool debug) return km; } +char *read_line (FILE *file) +{ + /* TODO read arbitrary line size */ + size_t bufsz = 1024; + char buffer[bufsz]; + if (fgets(buffer, bufsz, file) == NULL) + { + return NULL; + } + char *line = strdup(buffer); + size_t nlen = strlen(line); + /* remove newline characters */ + switch(line[nlen - 1]){ + case '\n': + if(nlen >= 2 && line[nlen - 2] == '\r') + --nlen; + /* FALLTHROUGH */ + case '\r': + line[nlen - 1] = '\0'; + break; + } + return line; +} + +KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool debug) +{ + KeyMap_t *rval = NULL; + KeyMap_t **walker = &rval; + for(size_t i = 0; i < n_confs; ++i) + { + Bool close = True; + char *filename = files[i]; + FILE *file = NULL; + + if(!strcmp(filename, "-")) + { + close = False; + file = stdin; + } + else + { + close = True; + file = fopen (filename, "r"); + if (file == NULL) + { + fprintf (stderr, "unable to open file %s: %s\n", filename, strerror(errno)); + break; + } + } + + /* read and parse file */ + char *line = NULL; + while((line = read_line(file)) != NULL) + { + *walker = parse_token(ctrl_conn, line, debug); + free(line); + if(*walker == NULL) + { + break; + } + walker = &(*walker)->next; + } + + if(close) + { + fclose (file); + } + } + return rval; +} + KeyMap_t *parse_mapping (Display *ctrl_conn, char *mapping, Bool debug) { char *token; @@ -589,6 +688,6 @@ void delete_keys (Key_t *keys) void print_usage (const char *program_name) { - fprintf (stdout, "Usage: %s [-d] [-f] [-t timeout_ms] [-e ]\n", program_name); + fprintf (stdout, "Usage: %s [file] [-d] [-f] [-t timeout_ms] [-e ]\n", program_name); fprintf (stdout, "Runs as a daemon unless -d or -f flag is set\n"); } From 8db0360a88a7130cfd419f6e1ef1e79c09c687bc Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 13:25:29 -0800 Subject: [PATCH 02/24] Fix argument parsing bug --- xcape.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/xcape.c b/xcape.c index 0b27cc0..74ed8b0 100644 --- a/xcape.c +++ b/xcape.c @@ -151,14 +151,8 @@ int main (int argc, char **argv) if (optind < argc) { - if(mapping == default_mapping){ - conf_files = argv + optind; - n_conf = argc - optind; - }else{ - fprintf (stderr, "Not a command line option: '%s'\n", argv[optind]); - print_usage (argv[0]); - return EXIT_SUCCESS; - } + conf_files = argv + optind; + n_conf = argc - optind; } if (!XInitThreads ()) From 7dc1bef32b65615b200d47b54e2e5be0ef3f6500 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 13:31:24 -0800 Subject: [PATCH 03/24] Fix code style --- xcape.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/xcape.c b/xcape.c index 74ed8b0..c197900 100644 --- a/xcape.c +++ b/xcape.c @@ -565,14 +565,15 @@ char *read_line (FILE *file) /* TODO read arbitrary line size */ size_t bufsz = 1024; char buffer[bufsz]; - if (fgets(buffer, bufsz, file) == NULL) + if (fgets (buffer, bufsz, file) == NULL) { return NULL; } - char *line = strdup(buffer); - size_t nlen = strlen(line); + char *line = strdup (buffer); + size_t nlen = strlen (line); /* remove newline characters */ - switch(line[nlen - 1]){ + switch (line[nlen - 1]) + { case '\n': if(nlen >= 2 && line[nlen - 2] == '\r') --nlen; @@ -580,21 +581,21 @@ char *read_line (FILE *file) case '\r': line[nlen - 1] = '\0'; break; - } + }; return line; } KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool debug) { KeyMap_t *rval = NULL; - KeyMap_t **walker = &rval; - for(size_t i = 0; i < n_confs; ++i) + KeyMap_t **current = &rval; + for (size_t i = 0; i < n_confs; ++i) { Bool close = True; char *filename = files[i]; FILE *file = NULL; - if(!strcmp(filename, "-")) + if (!strcmp (filename, "-")) { close = False; file = stdin; @@ -612,21 +613,19 @@ KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool de /* read and parse file */ char *line = NULL; - while((line = read_line(file)) != NULL) + while ((line = read_line (file)) != NULL) { - *walker = parse_token(ctrl_conn, line, debug); - free(line); - if(*walker == NULL) + *current = parse_token (ctrl_conn, line, debug); + free (line); + if (*current == NULL) { break; } - walker = &(*walker)->next; + current = &(*current)->next; } - if(close) - { + if (close) fclose (file); - } } return rval; } From 2f178fa1c1bc58c017d50473d246cf23265762a0 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 13:32:05 -0800 Subject: [PATCH 04/24] Fix usage --- xcape.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcape.c b/xcape.c index c197900..c74e09c 100644 --- a/xcape.c +++ b/xcape.c @@ -681,6 +681,6 @@ void delete_keys (Key_t *keys) void print_usage (const char *program_name) { - fprintf (stdout, "Usage: %s [file] [-d] [-f] [-t timeout_ms] [-e ]\n", program_name); + fprintf (stdout, "Usage: %s [file ...] [-d] [-f] [-t timeout_ms] [-e ]\n", program_name); fprintf (stdout, "Runs as a daemon unless -d or -f flag is set\n"); } From ce1149b67ccb30786b61f5b3d522b7756e8b8969 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 13:54:46 -0800 Subject: [PATCH 05/24] Add arbitrary length line reading --- xcape.c | 52 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/xcape.c b/xcape.c index c74e09c..7c6f711 100644 --- a/xcape.c +++ b/xcape.c @@ -562,26 +562,40 @@ KeyMap_t *parse_token (Display *dpy, char *token, Bool debug) char *read_line (FILE *file) { - /* TODO read arbitrary line size */ - size_t bufsz = 1024; - char buffer[bufsz]; - if (fgets (buffer, bufsz, file) == NULL) + size_t cap = 1024; + size_t nlen = 0; + char *line = realloc (NULL, cap*sizeof(char)); + + int c = EOF; + int reading = 1; + while (reading) { - return NULL; + c = fgetc(file); + if (nlen == cap) + { + cap *= 2; + line = realloc (line, cap*sizeof(char)); + } + switch (c) + { + case '\r': /* FALLTHROUGH */ + case '\n': /* FALLTHROUGH */ + case '\0': + line[nlen++] = '\0'; + reading = 0; + break; + case EOF: + reading = 0; + break; + default: + line[nlen] = c; + ++nlen; + break; + } } - char *line = strdup (buffer); - size_t nlen = strlen (line); - /* remove newline characters */ - switch (line[nlen - 1]) - { - case '\n': - if(nlen >= 2 && line[nlen - 2] == '\r') - --nlen; - /* FALLTHROUGH */ - case '\r': - line[nlen - 1] = '\0'; - break; - }; + /* TODO remove extraneous \r|\n|\0 */ + if(nlen == 0) + return NULL; return line; } @@ -611,7 +625,7 @@ KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool de } } - /* read and parse file */ + /* Read file line by line, treating each line as an expression */ char *line = NULL; while ((line = read_line (file)) != NULL) { From cfd5e5cf87711e6bf80802d990eafe99fdbc3c15 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 13:56:56 -0800 Subject: [PATCH 06/24] Remove extraneous memory allocation --- xcape.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xcape.c b/xcape.c index 7c6f711..4d9981c 100644 --- a/xcape.c +++ b/xcape.c @@ -595,7 +595,11 @@ char *read_line (FILE *file) } /* TODO remove extraneous \r|\n|\0 */ if(nlen == 0) + { + free (line); return NULL; + } + line = realloc (line, nlen); return line; } From 2a9fa07a806ae8d78ed2e2aeff0b436b47bac245 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 17:05:27 -0800 Subject: [PATCH 07/24] Fix \r\n handling logic --- xcape.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/xcape.c b/xcape.c index 4d9981c..55aa6d0 100644 --- a/xcape.c +++ b/xcape.c @@ -578,13 +578,22 @@ char *read_line (FILE *file) } switch (c) { - case '\r': /* FALLTHROUGH */ + case '\r': + /* check for \r\n */ + { + int c = fgetc(file); + if(c != '\n'){ + ungetc(c, file); + } + } + break; case '\n': /* FALLTHROUGH */ case '\0': line[nlen++] = '\0'; reading = 0; break; case EOF: + ungetc(EOF, file); reading = 0; break; default: @@ -593,12 +602,12 @@ char *read_line (FILE *file) break; } } - /* TODO remove extraneous \r|\n|\0 */ if(nlen == 0) { free (line); return NULL; } + /* shrink down to size */ line = realloc (line, nlen); return line; } From 41239476197782aa57a9f47b641b640bb7c6b1bb Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 17:07:56 -0800 Subject: [PATCH 08/24] Fix EOF handling --- xcape.c | 1 - 1 file changed, 1 deletion(-) diff --git a/xcape.c b/xcape.c index 55aa6d0..7757cf7 100644 --- a/xcape.c +++ b/xcape.c @@ -593,7 +593,6 @@ char *read_line (FILE *file) reading = 0; break; case EOF: - ungetc(EOF, file); reading = 0; break; default: From c5bae23f3226019aaec162b0830a28ba8b457c8a Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 17:18:11 -0800 Subject: [PATCH 09/24] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 66a462f..b5e06b0 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,12 @@ Then run: Usage ----- - $ xcape [-d] [-f] [-t ] [-e ] + $ xcape [file ...] [-d] [-f] [-t ] [-e ] + +### `file ...` + +Configuration files containing expressions. Each line is an expression +following the form `'ModKey=Key[|OtherKey]`. ### `-d` From 06078c2c4d338d1d3865cdfac268bc3366397fd8 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 17:29:08 -0800 Subject: [PATCH 10/24] Add comments to config files and trim leading whitespace --- xcape.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/xcape.c b/xcape.c index 7757cf7..8645f0e 100644 --- a/xcape.c +++ b/xcape.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -641,13 +642,19 @@ KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool de char *line = NULL; while ((line = read_line (file)) != NULL) { - *current = parse_token (ctrl_conn, line, debug); - free (line); - if (*current == NULL) - { - break; + /* trim leading whitespace */ + char *trimmed = line; + while(isspace(*trimmed)) ++trimmed; + /* check for comments */ + if(*trimmed && *trimmed != '#'){ + *current = parse_token (ctrl_conn, trimmed, debug); + if (*current == NULL) + { + break; + } + current = &(*current)->next; } - current = &(*current)->next; + free (line); } if (close) From 85e416e53e50dfe5e0cdabdb4c856fbc5e7225d9 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 17:44:34 -0800 Subject: [PATCH 11/24] Improve parsing logic and remove bug for default mapping --- xcape.c | 49 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/xcape.c b/xcape.c index 8645f0e..25b61b4 100644 --- a/xcape.c +++ b/xcape.c @@ -99,7 +99,7 @@ int main (int argc, char **argv) int dummy, ch; static char default_mapping[] = "Control_L=Escape"; - char *mapping = default_mapping; + char *mapping = NULL; char **conf_files = NULL; size_t n_conf = 0; @@ -188,31 +188,58 @@ int main (int argc, char **argv) exit (EXIT_FAILURE); } - self->map = parse_mapping (self->ctrl_conn, mapping, self->debug); + /* This reduces error-prone logic when rearranging the parsing order */ - if (self->map == NULL) + KeyMap_t **current_map = &self->map; + + /* parse mappings given by -e first */ + + if (mapping) { - fprintf (stderr, "Failed to parse_mapping\n"); - exit (EXIT_FAILURE); + KeyMap_t *emap = parse_mapping (self->ctrl_conn, mapping, self->debug); + + if (emap == NULL) + { + fprintf (stderr, "Failed to parse_mapping\n"); + exit (EXIT_FAILURE); + } + + *current_map = emap; + current_map = &(*current_map)->next; } + /* parse config files */ + if (conf_files) { KeyMap_t *conf_map = parse_confs (self->ctrl_conn, conf_files, n_conf, self->debug); + if (conf_map == NULL) { fprintf (stderr, "Failed to parse_confs\n"); exit (EXIT_FAILURE); } - if (self->map) - { - self->map->next = conf_map; - } - else + *current_map = conf_map; + current_map = &(*current_map)->next; + } + + /* if there were no config files or mappings supplied, try + * the default mapping */ + + if (!conf_files && !mapping) + { + KeyMap_t *def_map = parse_mapping (self->ctrl_conn, default_mapping, self->debug); + + if (def_map == NULL) { - self->map = conf_map; + fprintf (stderr, "Failed to parse_mapping default\n"); + exit (EXIT_FAILURE); } + + *current_map = def_map; + /* extraneous, but allows rearranging if needed */ + current_map = &(*current_map)->next; } if (self->foreground != True) From 5a514c19743d71da538749ef4cd5c581f785bd28 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 17:53:59 -0800 Subject: [PATCH 12/24] Add example and Update README.md --- README.md | 13 ++++++++++++- xcape.1 | 3 ++- xcape.c | 3 +-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b5e06b0..f9271d5 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,8 @@ Usage ### `file ...` Configuration files containing expressions. Each line is an expression -following the form `'ModKey=Key[|OtherKey]`. +following the form `'ModKey=Key[|OtherKey]`. Comments may be introduced +via the '#' character, which will ignore the proceeding line. ### `-d` @@ -73,6 +74,16 @@ key name is found. xcape -e 'Shift_L=Escape;Control_L=Control_L|O' ++ This configuration file produces the same behavior as above. + + `xcape map.xcape` + + # map.xcape + # Map left shift to Escape + Shift_L=Escape + # Map Left control to Ctrl-O + Control_L=Control_L|O + + In conjunction with xmodmap it is possible to make an ordinary key act as an extra modifier. First map the key to the modifier with xmodmap and then the modifier back to the key with xcape. However, this has diff --git a/xcape.1 b/xcape.1 index caae280..cc66a6e 100644 --- a/xcape.1 +++ b/xcape.1 @@ -20,7 +20,8 @@ key in place of \fIControl_L\fR (Left Control). .TP .BR file\ ... Configuration Files. Expressions will be read from the files in a line-by-line -manner. "\-" can be used to read from standard input. +manner. "\-" can be used to read from standard input. Comments are indicated +by the character '#', which will ignore an entire line. .TP .BR \-d Debug mode. Will run as a foreground process and print debug information. diff --git a/xcape.c b/xcape.c index 25b61b4..9f41190 100644 --- a/xcape.c +++ b/xcape.c @@ -224,8 +224,7 @@ int main (int argc, char **argv) current_map = &(*current_map)->next; } - /* if there were no config files or mappings supplied, try - * the default mapping */ + /* if there were no config files or mappings supplied, try the default mapping */ if (!conf_files && !mapping) { From 45685403f07028728d9ead10047be62e7cfa602f Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 17:55:08 -0800 Subject: [PATCH 13/24] Fix README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f9271d5..e9cc2b9 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Usage Configuration files containing expressions. Each line is an expression following the form `'ModKey=Key[|OtherKey]`. Comments may be introduced -via the '#' character, which will ignore the proceeding line. +via the `'#'` character, which will ignore the proceeding line. ### `-d` @@ -76,14 +76,16 @@ key name is found. + This configuration file produces the same behavior as above. - `xcape map.xcape` - # map.xcape # Map left shift to Escape Shift_L=Escape # Map Left control to Ctrl-O Control_L=Control_L|O + which can then be read via xcape using the following command + + xcape map.xcape + + In conjunction with xmodmap it is possible to make an ordinary key act as an extra modifier. First map the key to the modifier with xmodmap and then the modifier back to the key with xcape. However, this has From 22ba74c956515b7bd33af4c44bdfde5a7f277cfe Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 23:18:49 -0800 Subject: [PATCH 14/24] Remove boolean logic for closing file --- xcape.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/xcape.c b/xcape.c index 9f41190..dbf9ae6 100644 --- a/xcape.c +++ b/xcape.c @@ -188,7 +188,8 @@ int main (int argc, char **argv) exit (EXIT_FAILURE); } - /* This reduces error-prone logic when rearranging the parsing order */ + /* This reduces error-prone logic when rearranging the parsing order, + * as we don't need to check for self->map existence */ KeyMap_t **current_map = &self->map; @@ -196,15 +197,15 @@ int main (int argc, char **argv) if (mapping) { - KeyMap_t *emap = parse_mapping (self->ctrl_conn, mapping, self->debug); + KeyMap_t *emapping = parse_mapping (self->ctrl_conn, mapping, self->debug); - if (emap == NULL) + if (emapping == NULL) { fprintf (stderr, "Failed to parse_mapping\n"); exit (EXIT_FAILURE); } - *current_map = emap; + *current_map = emapping; current_map = &(*current_map)->next; } @@ -212,15 +213,15 @@ int main (int argc, char **argv) if (conf_files) { - KeyMap_t *conf_map = parse_confs (self->ctrl_conn, conf_files, n_conf, self->debug); + KeyMap_t *conf_mapping = parse_confs (self->ctrl_conn, conf_files, n_conf, self->debug); - if (conf_map == NULL) + if (conf_mapping == NULL) { fprintf (stderr, "Failed to parse_confs\n"); exit (EXIT_FAILURE); } - *current_map = conf_map; + *current_map = conf_mapping; current_map = &(*current_map)->next; } @@ -228,17 +229,15 @@ int main (int argc, char **argv) if (!conf_files && !mapping) { - KeyMap_t *def_map = parse_mapping (self->ctrl_conn, default_mapping, self->debug); + KeyMap_t *def_mapping = parse_mapping (self->ctrl_conn, default_mapping, self->debug); - if (def_map == NULL) + if (def_mapping == NULL) { fprintf (stderr, "Failed to parse_mapping default\n"); exit (EXIT_FAILURE); } - *current_map = def_map; - /* extraneous, but allows rearranging if needed */ - current_map = &(*current_map)->next; + *current_map = def_mapping; } if (self->foreground != True) @@ -623,8 +622,7 @@ char *read_line (FILE *file) reading = 0; break; default: - line[nlen] = c; - ++nlen; + line[nlen++] = c; break; } } @@ -644,18 +642,16 @@ KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool de KeyMap_t **current = &rval; for (size_t i = 0; i < n_confs; ++i) { - Bool close = True; char *filename = files[i]; FILE *file = NULL; + /* determine if reading from stdin or file */ if (!strcmp (filename, "-")) { - close = False; file = stdin; } else { - close = True; file = fopen (filename, "r"); if (file == NULL) { @@ -671,7 +667,7 @@ KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool de /* trim leading whitespace */ char *trimmed = line; while(isspace(*trimmed)) ++trimmed; - /* check for comments */ + /* check for comments or empty lines */ if(*trimmed && *trimmed != '#'){ *current = parse_token (ctrl_conn, trimmed, debug); if (*current == NULL) @@ -683,7 +679,7 @@ KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool de free (line); } - if (close) + if (file != stdin) fclose (file); } return rval; From 87a6e5e8b7e13d5bbd9385a72ad33d43f7c80033 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Mon, 5 Mar 2018 23:22:35 -0800 Subject: [PATCH 15/24] Fix README.md and xcape.1 --- README.md | 3 ++- xcape.1 | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e9cc2b9..d1b59e7 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,8 @@ Usage Configuration files containing expressions. Each line is an expression following the form `'ModKey=Key[|OtherKey]`. Comments may be introduced -via the `'#'` character, which will ignore the proceeding line. +via the `#` character, which will ignore the proceeding line. To read +from standard input, you may use the `-` character. ### `-d` diff --git a/xcape.1 b/xcape.1 index cc66a6e..44717a2 100644 --- a/xcape.1 +++ b/xcape.1 @@ -19,9 +19,11 @@ key in place of \fIControl_L\fR (Left Control). .SH OPTIONS .TP .BR file\ ... -Configuration Files. Expressions will be read from the files in a line-by-line -manner. "\-" can be used to read from standard input. Comments are indicated -by the character '#', which will ignore an entire line. +Configuration files containing expressions. Each line is an expression +following the form \'\fBModKey\fR=\fBKey\fR[|\fBOtherKey\fR]\'. +Comments may be introduced via the '#' character, which will +ignore the proceeding line. To read from standard input, you may use +the '\-' character. .TP .BR \-d Debug mode. Will run as a foreground process and print debug information. From 60af0353ad5b34f7fd0d659d438dd69585663c5b Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Thu, 10 May 2018 12:15:33 -0700 Subject: [PATCH 16/24] Change realloc() to calloc() in read_line() --- xcape.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcape.c b/xcape.c index dbf9ae6..85aba3f 100644 --- a/xcape.c +++ b/xcape.c @@ -590,7 +590,7 @@ char *read_line (FILE *file) { size_t cap = 1024; size_t nlen = 0; - char *line = realloc (NULL, cap*sizeof(char)); + char *line = calloc (cap, sizeof(char)); int c = EOF; int reading = 1; From be04a6680186d63825fcad135c83bd51003ab46e Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Thu, 10 May 2018 14:21:10 -0700 Subject: [PATCH 17/24] Fix config file argument parsing --- xcape.c | 70 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/xcape.c b/xcape.c index 85aba3f..42ce2ee 100644 --- a/xcape.c +++ b/xcape.c @@ -77,7 +77,7 @@ void *sig_handler (void *user_data); void intercept (XPointer user_data, XRecordInterceptData *data); -KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool debug); +KeyMap_t *parse_confs (Display *ctrl_conn, const char **files, size_t n_confs, Bool debug); KeyMap_t *parse_mapping (Display *ctrl_conn, char *mapping, Bool debug); @@ -100,8 +100,6 @@ int main (int argc, char **argv) static char default_mapping[] = "Control_L=Escape"; char *mapping = NULL; - char **conf_files = NULL; - size_t n_conf = 0; XRecordRange *rec_range = XRecordAllocRange(); XRecordClientSpec client_spec = XRecordAllClients; @@ -115,10 +113,27 @@ int main (int argc, char **argv) rec_range->device_events.first = KeyPress; rec_range->device_events.last = ButtonRelease; - while ((ch = getopt (argc, argv, "dfe:t:")) != -1) + /* This array holds the configuration files used, using (argc - 1)/2 because + * in the worst case we have (argc - 1)/2 `-c ` pairs and no other + * arguments. Much less tedious than a resizable array */ + + size_t cfgs_size = (argc - 1)/2; + const char *cfg_files[cfgs_size]; + /* NULL out the array */ + memset(cfg_files, 0, sizeof(const char *) * cfgs_size); + /* pointer to current string entry in cfg_files array */ + const char **cur_cfg = cfg_files; + size_t cfgs_supplied = 0; + + while ((ch = getopt (argc, argv, "dfe:t:c:")) != -1) { switch (ch) { + case 'c': + /* assign passed file to current entry, moving + * to the next entry in cfg_files */ + cur_cfg[cfgs_supplied++] = optarg; + break; case 'd': self->debug = True; /* imply -f (no break) */ @@ -150,12 +165,6 @@ int main (int argc, char **argv) } } - if (optind < argc) - { - conf_files = argv + optind; - n_conf = argc - optind; - } - if (!XInitThreads ()) { fprintf (stderr, "Failed to initialize threads.\n"); @@ -193,6 +202,8 @@ int main (int argc, char **argv) KeyMap_t **current_map = &self->map; +#define NEXT_MAP(cur) cur = &((*cur)->next) + /* parse mappings given by -e first */ if (mapping) @@ -206,14 +217,14 @@ int main (int argc, char **argv) } *current_map = emapping; - current_map = &(*current_map)->next; + NEXT_MAP (current_map); } /* parse config files */ - if (conf_files) + if (cfgs_supplied) { - KeyMap_t *conf_mapping = parse_confs (self->ctrl_conn, conf_files, n_conf, self->debug); + KeyMap_t *conf_mapping = parse_confs (self->ctrl_conn, cfg_files, cfgs_supplied, self->debug); if (conf_mapping == NULL) { @@ -222,12 +233,12 @@ int main (int argc, char **argv) } *current_map = conf_mapping; - current_map = &(*current_map)->next; + NEXT_MAP (current_map); } /* if there were no config files or mappings supplied, try the default mapping */ - if (!conf_files && !mapping) + if (!(cfgs_supplied || mapping)) { KeyMap_t *def_mapping = parse_mapping (self->ctrl_conn, default_mapping, self->debug); @@ -240,6 +251,8 @@ int main (int argc, char **argv) *current_map = def_mapping; } +#undef NEXT_MAP + if (self->foreground != True) daemon (0, 0); @@ -636,28 +649,18 @@ char *read_line (FILE *file) return line; } -KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool debug) +KeyMap_t *parse_confs (Display *ctrl_conn, const char **files, size_t n_confs, Bool debug) { KeyMap_t *rval = NULL; KeyMap_t **current = &rval; for (size_t i = 0; i < n_confs; ++i) { - char *filename = files[i]; - FILE *file = NULL; - - /* determine if reading from stdin or file */ - if (!strcmp (filename, "-")) + const char *filename = files[i]; + FILE *file = fopen (filename, "r"); + if (file == NULL) { - file = stdin; - } - else - { - file = fopen (filename, "r"); - if (file == NULL) - { - fprintf (stderr, "unable to open file %s: %s\n", filename, strerror(errno)); - break; - } + fprintf (stderr, "unable to open file %s: %s\n", filename, strerror(errno)); + break; } /* Read file line by line, treating each line as an expression */ @@ -674,13 +677,10 @@ KeyMap_t *parse_confs (Display *ctrl_conn, char **files, size_t n_confs, Bool de { break; } - current = &(*current)->next; + current = &((*current)->next); } free (line); } - - if (file != stdin) - fclose (file); } return rval; } From 9f3bc2c3414c9a67ff007ab3461a334961710339 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Thu, 10 May 2018 14:26:34 -0700 Subject: [PATCH 18/24] Update xcape.1 and README.md --- README.md | 17 +++++++++-------- xcape.1 | 16 ++++++++-------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index d1b59e7..bf8f4c3 100644 --- a/README.md +++ b/README.md @@ -30,14 +30,7 @@ Then run: Usage ----- - $ xcape [file ...] [-d] [-f] [-t ] [-e ] - -### `file ...` - -Configuration files containing expressions. Each line is an expression -following the form `'ModKey=Key[|OtherKey]`. Comments may be introduced -via the `#` character, which will ignore the proceeding line. To read -from standard input, you may use the `-` character. + $ xcape [-d] [-f] [-c ] [-t ] [-e ] ### `-d` @@ -47,6 +40,14 @@ Debug mode. Does not fork into the background. Prints debug information. Foreground mode. Does not fork into the background. +### `-c ` + +Supply a configuration file to be parsed xcape. Each line is an expression +following the form `'ModKey=Key[|OtherKey]`. Comments may be introduced +via the `#` character, which will ignore the proceeding line. The `-c` option +can be used multiple times in order to supply multiple configuration files. + + ### `-t ` If you hold a key longer than this timeout, xcape will not generate a key diff --git a/xcape.1 b/xcape.1 index 44717a2..22c34c9 100644 --- a/xcape.1 +++ b/xcape.1 @@ -5,9 +5,9 @@ xcape \- use a modifier key as another key .SH SYNOPSIS .B xcape -[file ...] [\fB-d\fR] [\fB-f\fR] +[\fB-c\fR \fIconfig-file\fR] [\fB-t\fR \fItimeout\fR] [\fB-e\fR \fImap-expression\fR] @@ -18,19 +18,19 @@ key in place of \fIControl_L\fR (Left Control). .SH OPTIONS .TP -.BR file\ ... -Configuration files containing expressions. Each line is an expression -following the form \'\fBModKey\fR=\fBKey\fR[|\fBOtherKey\fR]\'. -Comments may be introduced via the '#' character, which will -ignore the proceeding line. To read from standard input, you may use -the '\-' character. -.TP .BR \-d Debug mode. Will run as a foreground process and print debug information. .TP .BR \-f Foreground mode. Will run as a foreground process. .TP +.BR \-c " " \fIconfig-file\fR +Supply a configuration file to be parsed xcape. Each line is an expression +following the form \'\fBModKey\fR=\fBKey\fR[|\fBOtherKey\fR]\'. +Comments may be introduced via the '#' character, which will +ignore the proceeding line. The \-c option can be used multiple +times in order to supply multiple configuration files. +.TP .BR \-t " " \fItimeout\fR Give a \fItimeout\fR in milliseconds. If you hold a key longer than \fItimeout\fR a key event will not be generated. From 4b41b7e1581438ef5fd94fedc213d8f2f53690ec Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Thu, 10 May 2018 14:28:43 -0700 Subject: [PATCH 19/24] Fix partial update in README.txt --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bf8f4c3..5be5857 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ key name is found. which can then be read via xcape using the following command - xcape map.xcape + xcape -c map.xcape + In conjunction with xmodmap it is possible to make an ordinary key act as an extra modifier. First map the key to the modifier with xmodmap From 736c1edc7be26394857a37a44b7a717b2f7bedbc Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Fri, 11 May 2018 09:48:53 -0700 Subject: [PATCH 20/24] Fix read_line() --- xcape.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/xcape.c b/xcape.c index 42ce2ee..9f8f6a5 100644 --- a/xcape.c +++ b/xcape.c @@ -1,6 +1,6 @@ /************************************************************************ * xcape.c - * +* * Copyright 2015 Albin Olsson * * This program is free software: you can redistribute it and/or modify @@ -618,23 +618,23 @@ char *read_line (FILE *file) switch (c) { case '\r': - /* check for \r\n */ { - int c = fgetc(file); - if(c != '\n'){ - ungetc(c, file); + int c = fgetc (file); + /* check for \r\n */ + if(c != '\n') + { + ungetc (c, file); + break; } } - break; + /* FALLTHROUGH */ case '\n': /* FALLTHROUGH */ - case '\0': - line[nlen++] = '\0'; - reading = 0; - break; + case '\0': /* FALLTHROUGH */ case EOF: reading = 0; break; default: + /* add the character to the line */ line[nlen++] = c; break; } @@ -644,8 +644,10 @@ char *read_line (FILE *file) free (line); return NULL; } + /* terminate the line */ + line = realloc (line, nlen + 1); + line[nlen] = '\0'; /* shrink down to size */ - line = realloc (line, nlen); return line; } From f3955c6f5630a0029012fdc9f540e8af4fe05a35 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Fri, 11 May 2018 09:58:36 -0700 Subject: [PATCH 21/24] Fix miscellaneous bugs and change comment to '--' --- xcape.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/xcape.c b/xcape.c index 9f8f6a5..d2a8a7e 100644 --- a/xcape.c +++ b/xcape.c @@ -165,6 +165,12 @@ int main (int argc, char **argv) } } + /* user supplied more arguments than needed */ + if(optind != argc){ + print_usage (argv[0]); + return EXIT_SUCCESS; + } + if (!XInitThreads ()) { fprintf (stderr, "Failed to initialize threads.\n"); @@ -673,7 +679,7 @@ KeyMap_t *parse_confs (Display *ctrl_conn, const char **files, size_t n_confs, B char *trimmed = line; while(isspace(*trimmed)) ++trimmed; /* check for comments or empty lines */ - if(*trimmed && *trimmed != '#'){ + if(*trimmed && strncmp(trimmed, "--", 2)){ *current = parse_token (ctrl_conn, trimmed, debug); if (*current == NULL) { @@ -738,6 +744,6 @@ void delete_keys (Key_t *keys) void print_usage (const char *program_name) { - fprintf (stdout, "Usage: %s [file ...] [-d] [-f] [-t timeout_ms] [-e ]\n", program_name); + fprintf (stdout, "Usage: %s [-d] [-f] [-c ] [-t timeout_ms] [-e ]\n", program_name); fprintf (stdout, "Runs as a daemon unless -d or -f flag is set\n"); } From c06a0bfa535141bd1bce62867e962b7e699bf5a6 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Fri, 11 May 2018 11:25:21 -0700 Subject: [PATCH 22/24] add sizeof(char) to realloc() --- xcape.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xcape.c b/xcape.c index d2a8a7e..794fec6 100644 --- a/xcape.c +++ b/xcape.c @@ -650,8 +650,8 @@ char *read_line (FILE *file) free (line); return NULL; } - /* terminate the line */ - line = realloc (line, nlen + 1); + /* terminate the line and reduce size */ + line = realloc (line, (nlen + 1)*sizeof(char)); line[nlen] = '\0'; /* shrink down to size */ return line; From 001e7c1a928845a060d752bd21d99865ea6e0048 Mon Sep 17 00:00:00 2001 From: tkaden4 Date: Fri, 11 May 2018 11:37:07 -0700 Subject: [PATCH 23/24] Fix README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5be5857..dd6bf08 100644 --- a/README.md +++ b/README.md @@ -78,10 +78,10 @@ key name is found. + This configuration file produces the same behavior as above. - # map.xcape - # Map left shift to Escape + -- map.xcape + -- Map left shift to Escape Shift_L=Escape - # Map Left control to Ctrl-O + -- Map Left control to Ctrl-O Control_L=Control_L|O which can then be read via xcape using the following command From 559dce8ee7a7bf03e3a6053dce7e1d088408e665 Mon Sep 17 00:00:00 2001 From: Kaden Thomas Date: Sat, 12 May 2018 09:36:49 -0700 Subject: [PATCH 24/24] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dd6bf08..f9bcd1a 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,9 @@ Foreground mode. Does not fork into the background. Supply a configuration file to be parsed xcape. Each line is an expression following the form `'ModKey=Key[|OtherKey]`. Comments may be introduced -via the `#` character, which will ignore the proceeding line. The `-c` option -can be used multiple times in order to supply multiple configuration files. +by inserting `--` at the start of the line, which will cause it to be ignored. +The `-c` option can be used multiple times in order to supply multiple configuration +files. ### `-t `