diff options
author | David Härdeman <david@hardeman.nu> | 2015-07-01 16:59:01 +0200 |
---|---|---|
committer | David Härdeman <david@hardeman.nu> | 2015-07-01 16:59:01 +0200 |
commit | b6d82ecee6e38b83776d3a9d4637c51fc5151beb (patch) | |
tree | 6045c7440407e215a83df0e4aa37352440aa752b | |
parent | 99f8010565df11645582262f03c186036d4211c5 (diff) |
Add support for actually reading keymaps
-rw-r--r-- | rcm-server-keymap.c | 304 | ||||
-rw-r--r-- | rcm-server-keymap.h | 5 | ||||
-rw-r--r-- | rcm-server.c | 4 | ||||
-rw-r--r-- | utils.h | 26 |
4 files changed, 303 insertions, 36 deletions
diff --git a/rcm-server-keymap.c b/rcm-server-keymap.c index a5c194e..41c9216 100644 --- a/rcm-server-keymap.c +++ b/rcm-server-keymap.c @@ -9,29 +9,245 @@ #include <fcntl.h> #include <dirent.h> #include <unistd.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> #include "utils.h" #include "rcm-server.h" #include "rcm-server-keymap.h" -struct keymap * -find_keymap_by_name(struct device *dev, const char *name) +enum ini_section { + INI_SECTION_UNDEFINED, + INI_SECTION_DESCRIPTION, + INI_SECTION_KEYMAP, + INI_SECTION_LAYOUT, +}; + +struct inis { + const char *name; + enum ini_section value; +}; + +struct inis iniss[] = { + { .name = "Description", .value = INI_SECTION_DESCRIPTION }, + { .name = "Keymap", .value = INI_SECTION_KEYMAP }, + { .name = "Layout", .value = INI_SECTION_LAYOUT }, + { .name = NULL, .value = INI_SECTION_UNDEFINED }, +}; + +static int strtol_strict(const char *str, int *result) { - struct keymap *keymap; + char *end; + long val; - list_for_each_entry(keymap, &dev->keymaps, list) - if (!strcmp(keymap->name, name)) - return keymap; + errno = 0; + val = strtol(str, &end, 10); - return NULL; + if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) + return -EINVAL; + + if (errno != 0 && val == 0) + return -EINVAL; + + if (end == str) + return -EINVAL; + + if (*end != '\0') + return -EINVAL; + + if (val < 1 || val > UINT16_MAX) + return -EINVAL; + + *result = val; + return 0; +} + +static int +keymap_parse(FILE *fp, char **line, size_t *buf_size, struct keymap *keymap, + uint16_t *rows_return, uint16_t *cols_return, + unsigned *keycode_count_return) +{ + unsigned keycode_count = 0; + unsigned layout_count = 0; + int rows = -1; + int cols = -1; + unsigned i; + enum ini_section current_section = INI_SECTION_UNDEFINED; + int r; + + rewind(fp); + + while (getline(line, buf_size, fp) != -1) { + char *p, *tmp; + + p = strchr(*line, '\n'); + if (!p) + continue; + *p = '\0'; + + p = *line; + while (isspace(*p)) + p++; + + if (*p == '\0' || *p == ';' || *p == '#') + continue; + + if (*p == '[') { + p++; + tmp = strchr(p, ']'); + if (!tmp) + continue; + *tmp = '\0'; + + for (i = 0; iniss[i].name; i++) { + if (strcasecmp(iniss[i].name, p)) + continue; + current_section = iniss[i].value; + break; + } + + if (!iniss[i].name) + current_section = INI_SECTION_UNDEFINED; + + continue; + } + + tmp = strchr(p, '='); + if (!tmp) + continue; + + *tmp++ = '\0'; + while (isspace(*tmp)) + tmp++; + + if (*tmp == '\0') + continue; + + /* FIXME: eat RHS whitespace for p */ + switch (current_section) { + case INI_SECTION_DESCRIPTION: + if (!keymap) + continue; + + if (!strcasecmp(p, "Name")) { + keymap->name = strdup(tmp); + if (!keymap->name) + return -ENOMEM; + } else if (!strcasecmp(p, "Description")) { + keymap->desc = strdup(tmp); + if (!keymap->desc) + return -ENOMEM; + } + break; + case INI_SECTION_KEYMAP: + if (!strcasecmp(p, "Map")) { + if (keymap) { + keymap->keycodes[keycode_count].name = "apan"; + keymap->keycodes[keycode_count].value = "papan"; + } + keycode_count++; + } else { + fprintf(stderr, "Unknown parameter: %s\n", p); + } + + break; + case INI_SECTION_LAYOUT: + if (!strcasecmp(p, "Rows")) { + if (rows >= 0) { + fprintf(stderr, "Multiple rows specifications\n"); + return -EINVAL; + } + + r = strtol_strict(tmp, &rows); + if (r < 0) { + fprintf(stderr, "Invalid rows specification\n"); + return -EINVAL; + } + + } else if (!strcasecmp(p, "Cols")) { + if (cols >= 0) { + fprintf(stderr, "Multiple cols specifications\n"); + return -EINVAL; + } + + r = strtol_strict(tmp, &cols); + if (r < 0) { + fprintf(stderr, "Invalid cols specification\n"); + return -EINVAL; + } + + } else if (!strcasecmp(p, "Button")) { + if (keymap) + keymap->layout[layout_count] = &keymap->keycodes[1]; + layout_count++; + + } else { + fprintf(stderr, "Unknown parameter: %s\n", p); + } + break; + case INI_SECTION_UNDEFINED: + break; + default: + printf("Line: %i - %s = %s\n", current_section, p, tmp); + break; + } + } + + if (rows < 0) { + fprintf(stderr, "Rows specification missing\n"); + return -EINVAL; + } + + if (cols < 0) { + fprintf(stderr, "Cols specification missing\n"); + return -EINVAL; + } + + if (keycode_count < 1) { + fprintf(stderr, "No keycodes found\n"); + return -EINVAL; + } + + if (layout_count < 1) { + fprintf(stderr, "No layout found\n"); + return -EINVAL; + } + + if (rows * cols != layout_count) { + fprintf(stderr, "Layout does not match rows x cols\n"); + return -EINVAL; + } + + if (keymap) { + keymap->rows = rows; + keymap->cols = cols; + keymap->keycode_count = keycode_count; + } + + if (rows_return) + *rows_return = rows; + + if (cols_return) + *cols_return = cols; + + if (keycode_count_return) + *keycode_count_return = keycode_count; + + return 0; } static struct keymap * keymap_read(int dfd, const char *name) { - int fd; - struct keymap *keymap; - unsigned i; + _cleanup_close_ int fd = -1; + _cleanup_fclose_ FILE *fp = NULL; + _cleanup_free_ char *line = NULL; + struct keymap *keymap = NULL; + uint16_t cols, rows; + unsigned keycode_count; + size_t buf_size = 0; + unsigned r; if (dfd < 0 || !name) return NULL; @@ -40,36 +256,35 @@ keymap_read(int dfd, const char *name) if (fd < 0) return NULL; - keymap = zmalloc(sizeof(*keymap) + (8 * 3) * sizeof(struct keycode)); - if (!keymap) { - close(fd); + fp = fdopen(fd, "r"); + if (!fp) return NULL; - } + fd = -1; - keymap->name = strdup(name); - if (!keymap->name) { - close(fd); - free(keymap); + r = keymap_parse(fp, &line, &buf_size, NULL, &rows, &cols, &keycode_count); + if (r < 0) { + fprintf(stderr, "Invalid keymap: %s\n", name); return NULL; } - keymap->cols = 3; - keymap->rows = 8; + keymap = zmalloc(sizeof(*keymap) + sizeof(struct keycode) * keycode_count); + if (!keymap) + return NULL; - for (i = 0; i < (keymap->cols * keymap->rows); i++) { - keymap->keycodes[i].name = "key-1-2"; - keymap->keycodes[i].value = "KEY_COFFEE"; + keymap->layout = zmalloc(sizeof(struct keycode *) * cols * rows); + if (!keymap->layout) { + free(keymap); + return NULL; } - /* FIXME: Actually read the keymap :) */ - close(fd); + keymap_parse(fp, &line, &buf_size, keymap, NULL, NULL, NULL); return keymap; } static int keymaps_load_dir(struct device *device, const char *path) { - DIR *dir; + _cleanup_closedir_ DIR *dir = NULL; int dfd; struct dirent *dent; struct keymap *keymap; @@ -79,28 +294,30 @@ keymaps_load_dir(struct device *device, const char *path) return -1; dfd = dirfd(dir); - if (dfd < 0) { - closedir(dir); + if (dfd < 0) return -1; - } while ((dent = readdir(dir))) { switch (dent->d_type) { case DT_REG: case DT_LNK: case DT_UNKNOWN: - keymap = keymap_read(dfd, dent->d_name); + keymap = find_keymap_by_name(device, dent->d_name); if (keymap) + continue; + keymap = keymap_read(dfd, dent->d_name); + if (keymap) { + printf("Keymap added (0x%p)\n", keymap); + printf("\tDescription: %s\n", keymap->desc); + printf("\tName: %s\n", keymap->name); list_add(&keymap->list, &device->keymaps); + } break; default: break; } } - if (closedir(dir) < 0) - return -1; - return 0; } @@ -119,3 +336,24 @@ keymaps_load(struct device *device) return 0; } +void +keymap_free(struct keymap *keymap) +{ + free(keymap->layout); + free(keymap->name); + free(keymap->desc); + free(keymap); +} + +struct keymap * +find_keymap_by_name(struct device *dev, const char *name) +{ + struct keymap *keymap; + + list_for_each_entry(keymap, &dev->keymaps, list) + if (!strcmp(keymap->name, name)) + return keymap; + + return NULL; +} + diff --git a/rcm-server-keymap.h b/rcm-server-keymap.h index f29c0af..6ece017 100644 --- a/rcm-server-keymap.h +++ b/rcm-server-keymap.h @@ -8,9 +8,12 @@ struct keycode { struct keymap { char *name; + char *desc; uint16_t rows; uint16_t cols; struct list_head list; + struct keycode **layout; + unsigned keycode_count; struct keycode keycodes[]; }; @@ -18,5 +21,7 @@ struct keymap *find_keymap_by_name(struct device *dev, const char *name); int keymaps_load(struct device *device); +void keymap_free(struct keymap *keymap); + #endif diff --git a/rcm-server.c b/rcm-server.c index 26620fc..11223b1 100644 --- a/rcm-server.c +++ b/rcm-server.c @@ -154,8 +154,8 @@ method_getkeymap(sd_bus_message *m, void *userdata, sd_bus_error *error) for (i = 0; i < (keymap->rows * keymap->cols); i++) { r = sd_bus_message_append(reply, "a{sv}", 2, - "name", "s", keymap->keycodes[i].name, - "keycode", "s", keymap->keycodes[i].value); + "name", "s", keymap->layout[i]->name, + "keycode", "s", keymap->layout[i]->value); if (r < 0) goto out; } @@ -1,6 +1,8 @@ #ifndef fooutilshfoo #define fooutilshfoo +#include <sys/types.h> +#include <dirent.h> #include <systemd/sd-bus.h> struct list_head { @@ -80,6 +82,17 @@ static inline void sd_bus_close_unrefp(sd_bus **bus) { } #define _cleanup_bus_close_unref_ _cleanup_(sd_bus_close_unrefp) +static inline unsigned +strv_length(char **strv) +{ + unsigned len; + + for (len = 0; strv && *strv; strv++) + len++; + + return len; +} + static inline void strv_free(char **l) { char **k; if (l) { @@ -91,9 +104,20 @@ static inline void strv_free(char **l) { DEFINE_TRIVIAL_CLEANUP_FUNC(char **, strv_free); #define _cleanup_strv_free_ _cleanup_(strv_freep) -DEFINE_TRIVIAL_CLEANUP_FUNC(void *, free); +static inline void freep(void *p) { + free(*(void**) p); +} #define _cleanup_free_ _cleanup_(freep) +DEFINE_TRIVIAL_CLEANUP_FUNC(int, close); +#define _cleanup_close_ _cleanup_(closep) + +DEFINE_TRIVIAL_CLEANUP_FUNC(FILE *, fclose); +#define _cleanup_fclose_ _cleanup_(fclosep) + +DEFINE_TRIVIAL_CLEANUP_FUNC(DIR *, closedir); +#define _cleanup_closedir_ _cleanup_(closedirp) + DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus *, sd_bus_unref); #define _cleanup_bus_unref_ _cleanup_(sd_bus_unrefp) |