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) | 
