summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2015-07-01 16:59:01 +0200
committerDavid Härdeman <david@hardeman.nu>2015-07-01 16:59:01 +0200
commitb6d82ecee6e38b83776d3a9d4637c51fc5151beb (patch)
tree6045c7440407e215a83df0e4aa37352440aa752b
parent99f8010565df11645582262f03c186036d4211c5 (diff)
Add support for actually reading keymaps
-rw-r--r--rcm-server-keymap.c304
-rw-r--r--rcm-server-keymap.h5
-rw-r--r--rcm-server.c4
-rw-r--r--utils.h26
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;
}
diff --git a/utils.h b/utils.h
index 33cff05..9cb775b 100644
--- a/utils.h
+++ b/utils.h
@@ -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)