From a7a1f1a2a4a05bcdc8a06d0a266857dee5be0094 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 8 Sep 2015 11:34:56 -0400 Subject: [PATCH 01/10] etekcity: retrieve the macros while reading the button Avoids one for loop and will allow to store the macro in the button. Signed-off-by: Benjamin Tissoires --- src/driver-etekcity.c | 69 ++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/src/driver-etekcity.c b/src/driver-etekcity.c index bd8a015b..4676a47d 100644 --- a/src/driver-etekcity.c +++ b/src/driver-etekcity.c @@ -464,7 +464,7 @@ etekcity_read_profile(struct ratbag_profile *profile, unsigned int index) struct etekcity_settings_report *setting_report; uint8_t *buf; unsigned int report_rate; - unsigned int i, j; + unsigned int i; int dpi_x, dpi_y, hz; int rc; @@ -521,35 +521,6 @@ etekcity_read_profile(struct ratbag_profile *profile, unsigned int index) msleep(10); - for (i = 0; i < ETEKCITY_BUTTON_MAX; i++) { - const struct ratbag_button_action *action; - struct etekcity_macro *macro; - - action = etekcity_button_to_action(profile, i); - if (!action || action->type != RATBAG_BUTTON_ACTION_TYPE_MACRO) - continue; - - etekcity_set_config_profile(device, index, i); - macro = &drv_data->macros[index][i]; - buf = (uint8_t*)macro; - rc = ratbag_hidraw_raw_request(device, ETEKCITY_REPORT_ID_MACRO, - buf, ETEKCITY_REPORT_SIZE_MACRO, - HID_FEATURE_REPORT, HID_REQ_GET_REPORT); - log_info(device->ratbag, - "macro on button %d of profile %d is named '%s', and contains %d events:\n", - i, profile->index, - macro->name, macro->length); - for (j = 0; j < macro->length; j++) { - log_info(device->ratbag, - " - %s %s\n", - libevdev_event_code_get_name(EV_KEY, macro_mapping[macro->keys[j].keycode]), - macro->keys[j].flag & 0x80 ? "released" : "pressed"); - } - - } - - msleep(10); - if (rc < ETEKCITY_REPORT_SIZE_PROFILE) return; @@ -592,6 +563,12 @@ static void etekcity_read_button(struct ratbag_button *button) { const struct ratbag_button_action *action; + struct ratbag_device *device; + struct etekcity_macro *macro; + struct etekcity_data *drv_data; + uint8_t *buf; + unsigned j; + int rc; action = etekcity_button_to_action(button->profile, button->index); if (action) @@ -602,6 +579,38 @@ etekcity_read_button(struct ratbag_button *button) ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY); ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL); ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO); + + if (action && action->type == RATBAG_BUTTON_ACTION_TYPE_MACRO) { + device = button->profile->device; + drv_data = ratbag_get_drv_data(device); + + etekcity_set_config_profile(device, + button->profile->index, + button->index); + macro = &drv_data->macros[button->profile->index][button->index]; + buf = (uint8_t*)macro; + rc = ratbag_hidraw_raw_request(device, ETEKCITY_REPORT_ID_MACRO, + buf, ETEKCITY_REPORT_SIZE_MACRO, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (rc != ETEKCITY_REPORT_SIZE_MACRO) { + log_error(device->ratbag, + "Unable to retrieve the macro for button %d of profile %d: %s (%d)\n", + button->index, button->profile->index, + rc < 0 ? strerror(-rc) : "not read enough", rc); + } else { + log_info(device->ratbag, + "macro on button %d of profile %d is named '%s', and contains %d events:\n", + button->index, button->profile->index, + macro->name, macro->length); + for (j = 0; j < macro->length; j++) { + log_info(device->ratbag, + " - %s %s\n", + libevdev_event_code_get_name(EV_KEY, macro_mapping[macro->keys[j].keycode]), + macro->keys[j].flag & 0x80 ? "released" : "pressed"); + } + } + msleep(10); + } } static int From ce10bb1e84fe1fe679af23b351a1e7045c81dc39 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 8 Sep 2015 12:44:55 -0400 Subject: [PATCH 02/10] macro: add first implementation The doctext needs fixes and the API might not be final. This however works and allow to read the macro. Signed-off-by: Benjamin Tissoires --- src/driver-etekcity.c | 21 +++++---- src/libratbag-private.h | 19 +++++++- src/libratbag.c | 96 +++++++++++++++++++++++++++++++++++++++++ src/libratbag.h | 86 +++++++++++++++++++++++++++++++++--- src/libratbag.sym | 5 +++ tools/shared.c | 44 ++++++++++++++++++- tools/shared.h | 2 + 7 files changed, 257 insertions(+), 16 deletions(-) diff --git a/src/driver-etekcity.c b/src/driver-etekcity.c index 4676a47d..06260ca0 100644 --- a/src/driver-etekcity.c +++ b/src/driver-etekcity.c @@ -598,15 +598,20 @@ etekcity_read_button(struct ratbag_button *button) button->index, button->profile->index, rc < 0 ? strerror(-rc) : "not read enough", rc); } else { - log_info(device->ratbag, - "macro on button %d of profile %d is named '%s', and contains %d events:\n", - button->index, button->profile->index, - macro->name, macro->length); + ratbag_button_set_macro(button); + log_raw(device->ratbag, + "macro on button %d of profile %d is named '%s', and contains %d events:\n", + button->index, button->profile->index, + macro->name, macro->length); for (j = 0; j < macro->length; j++) { - log_info(device->ratbag, - " - %s %s\n", - libevdev_event_code_get_name(EV_KEY, macro_mapping[macro->keys[j].keycode]), - macro->keys[j].flag & 0x80 ? "released" : "pressed"); + ratbag_button_set_macro_event(button, + j, + macro->keys[j].flag & 0x80 ? RATBAG_MACRO_EVENT_KEY_RELEASED : RATBAG_MACRO_EVENT_KEY_PRESSED, + macro_mapping[macro->keys[j].keycode]); + log_raw(device->ratbag, + " - %s %s\n", + libevdev_event_code_get_name(EV_KEY, macro_mapping[macro->keys[j].keycode]), + macro->keys[j].flag & 0x80 ? "released" : "pressed"); } } msleep(10); diff --git a/src/libratbag-private.h b/src/libratbag-private.h index 8ff3a577..8dd263a0 100644 --- a/src/libratbag-private.h +++ b/src/libratbag-private.h @@ -247,6 +247,21 @@ struct ratbag_profile { { .type = RATBAG_BUTTON_ACTION_TYPE_MACRO, \ /* FIXME: add the macro keys */ } +struct ratbag_macro_event { + enum ratbag_macro_event_type type; + union ratbag_macro_evnt { + unsigned int key; + unsigned int timeout; + } event; +}; + +#define MAX_MACRO_EVENTS 64 +struct ratbag_macro { + const char *name; + const char *group; + struct ratbag_macro_event events[MAX_MACRO_EVENTS]; +}; + struct ratbag_button_action { enum ratbag_button_action_type type; union ratbag_btn_action { @@ -256,8 +271,8 @@ struct ratbag_button_action { unsigned int key; /* action_type == key */ /* FIXME: modifiers */ } key; - /* FIXME: MACRO */ } action; + struct ratbag_macro *macro; /* dynamically allocated, so kept aside */ }; struct ratbag_button { @@ -338,7 +353,7 @@ ratbag_button_action_match(const struct ratbag_button_action *action, case RATBAG_BUTTON_ACTION_TYPE_SPECIAL: return match->action.special == action->action.special; case RATBAG_BUTTON_ACTION_TYPE_MACRO: - /* FIXME: currently, do nothing */ + return 1; default: break; } diff --git a/src/libratbag.c b/src/libratbag.c index fc38ec5e..0c240865 100644 --- a/src/libratbag.c +++ b/src/libratbag.c @@ -1204,6 +1204,8 @@ static void ratbag_button_destroy(struct ratbag_button *button) { list_remove(&button->link); + if (button->action.macro) + free(button->action.macro); free(button); } @@ -1284,3 +1286,97 @@ ratbag_resolution_get_user_data(const struct ratbag_resolution *ratbag_resolutio { return ratbag_resolution->userdata; } + +LIBRATBAG_EXPORT int +ratbag_button_set_macro(struct ratbag_button *button) +{ + struct ratbag_macro *macro; + + if (!button->action.macro) + button->action.macro = zalloc(sizeof(*macro)); + else + memset(button->action.macro, 0, sizeof(*macro)); + + button->action.type = RATBAG_BUTTON_ACTION_TYPE_MACRO; + + return 0; +} + +LIBRATBAG_EXPORT int +ratbag_button_set_macro_event(struct ratbag_button *button, + unsigned int index, + enum ratbag_macro_event_type type, + unsigned int data) +{ + struct ratbag_macro *macro; + + if (button->action.type != RATBAG_BUTTON_ACTION_TYPE_MACRO || + index >= MAX_MACRO_EVENTS) + return -EINVAL; + + macro = button->action.macro; + + switch (type) { + case RATBAG_MACRO_EVENT_KEY_PRESSED: + case RATBAG_MACRO_EVENT_KEY_RELEASED: + macro->events[index].type = type; + macro->events[index].event.key = data; + break; + case RATBAG_MACRO_EVENT_WAIT: + macro->events[index].type = type; + macro->events[index].event.timeout = data; + break; + case RATBAG_MACRO_EVENT_NONE: + macro->events[index].type = type; + break; + default: + return -EINVAL; + } + + return 0; +} + +LIBRATBAG_EXPORT enum ratbag_macro_event_type +ratbag_button_get_macro_event_type(struct ratbag_button *button, unsigned int index) +{ + if (button->action.type != RATBAG_BUTTON_ACTION_TYPE_MACRO || + index >= MAX_MACRO_EVENTS) + return RATBAG_MACRO_EVENT_INVALID; + + return button->action.macro->events[index].type; +} + +LIBRATBAG_EXPORT int +ratbag_button_get_macro_event_key(struct ratbag_button *button, unsigned int index) +{ + struct ratbag_macro *macro; + + if (button->action.type != RATBAG_BUTTON_ACTION_TYPE_MACRO || + index >= MAX_MACRO_EVENTS) + return -EINVAL; + + macro = button->action.macro; + + if (macro->events[index].type != RATBAG_MACRO_EVENT_KEY_PRESSED && + macro->events[index].type != RATBAG_MACRO_EVENT_KEY_RELEASED) + return -EINVAL; + + return macro->events[index].event.key; +} + +LIBRATBAG_EXPORT int +ratbag_button_get_macro_event_timeout(struct ratbag_button *button, unsigned int index) +{ + struct ratbag_macro *macro; + + if (button->action.type != RATBAG_BUTTON_ACTION_TYPE_MACRO || + index >= MAX_MACRO_EVENTS) + return -EINVAL; + + macro = button->action.macro; + + if (macro->events[index].type != RATBAG_MACRO_EVENT_WAIT) + return -EINVAL; + + return macro->events[index].event.timeout; +} diff --git a/src/libratbag.h b/src/libratbag.h index 4b6116ea..11d50b38 100644 --- a/src/libratbag.h +++ b/src/libratbag.h @@ -1259,19 +1259,95 @@ ratbag_button_disable(struct ratbag_button *button); /** * @ingroup button * - * FIXME: no idea at this point + * Macro event types describing the event. */ -unsigned int -ratbag_button_get_macro(struct ratbag_button *button); +enum ratbag_macro_event_type { + RATBAG_MACRO_EVENT_INVALID = -1, + RATBAG_MACRO_EVENT_NONE = 0, + RATBAG_MACRO_EVENT_KEY_PRESSED, + RATBAG_MACRO_EVENT_KEY_RELEASED, + RATBAG_MACRO_EVENT_WAIT, +}; /** * @ingroup button * - * FIXME: no idea at this point + * If a button's action is @ref RATBAG_BUTTON_ACTION_TYPE_MACRO, + * this function returns the macro event type configured for the event at the + * given index. + * + * If the button's action type is not @ref RATBAG_BUTTON_ACTION_TYPE_MACRO, + * this function returns @ref RATBAG_MACRO_EVENT_INVALID. + * + * @param button A previously initialized ratbag button + * @param index An index of the event within the macro we are interested in. + * + * @return The type of the event at the given index */ -unsigned int +enum ratbag_macro_event_type +ratbag_button_get_macro_event_type(struct ratbag_button *button, unsigned int index); + +/** + * @ingroup button + * + * If a button's action is @ref RATBAG_BUTTON_ACTION_TYPE_MACRO, and if the + * event stored at the given index is @ref RATBAG_MACRO_EVENT_KEY_PRESSED or + * @ref RATBAG_MACRO_EVENT_KEY_RELEASED, this function returns the key code + * configured for the event at the given index. + * + * If the button's action type is not @ref RATBAG_BUTTON_ACTION_TYPE_MACRO, + * or if the event stored at the given index is not + * @ref RATBAG_MACRO_EVENT_KEY_PRESSED or @ref RATBAG_MACRO_EVENT_KEY_RELEASED, + * this function returns -EINVAL. + * + * @param button A previously initialized ratbag button + * @param index An index of the event within the macro we are interested in. + * + * @return The key of the event at the given index + */ +int +ratbag_button_get_macro_event_key(struct ratbag_button *button, unsigned int index); + +/** + * @ingroup button + * + * If a button's action is @ref RATBAG_BUTTON_ACTION_TYPE_MACRO, and if the + * event stored at the given index is @ref RATBAG_MACRO_EVENT_WAIT, this + * function returns the timeout configured for the event at the given index. + * + * If the button's action type is not @ref RATBAG_BUTTON_ACTION_TYPE_MACRO, + * or if the event stored at the given index is not + * @ref RATBAG_MACRO_EVENT_WAIT, this function returns -EINVAL. + * + * @param button A previously initialized ratbag button + * @param index An index of the event within the macro we are interested in. + * + * @return The tiemout of the event at the given index + */ +int +ratbag_button_get_macro_event_timeout(struct ratbag_button *button, unsigned int index); + +/** + * @ingroup button + * + * Sets the button's action to @ref RATBAG_BUTTON_ACTION_TYPE_MACRO and + * allocates the required memory to store the macro. + */ +int ratbag_button_set_macro(struct ratbag_button *button); +/** + * @ingroup button + * + * Sets the macro's event at the given index to the given type with the + * key code or timeout given. + */ +int +ratbag_button_set_macro_event(struct ratbag_button *button, + unsigned int index, + enum ratbag_macro_event_type type, + unsigned int data); + /** * @ingroup button * diff --git a/src/libratbag.sym b/src/libratbag.sym index bd43ce83..1b1cda5d 100644 --- a/src/libratbag.sym +++ b/src/libratbag.sym @@ -8,12 +8,17 @@ global: ratbag_button_get_type; ratbag_button_get_button; ratbag_button_get_key; + ratbag_button_get_macro_event_type; + ratbag_button_get_macro_event_key; + ratbag_button_get_macro_event_timeout; ratbag_button_get_special; ratbag_button_get_user_data; ratbag_button_has_action_type; ratbag_button_ref; ratbag_button_set_button; ratbag_button_set_key; + ratbag_button_set_macro; + ratbag_button_set_macro_event; ratbag_button_set_special; ratbag_button_set_user_data; ratbag_button_unref; diff --git a/tools/shared.c b/tools/shared.c index b80b518f..56273f42 100644 --- a/tools/shared.c +++ b/tools/shared.c @@ -145,6 +145,48 @@ button_action_key_to_str(struct ratbag_button *button) return strdup(str); } +static const char *strip_ev_key(int key) +{ + const char *str = libevdev_event_code_get_name(EV_KEY, key); + + if (!strncmp(str, "KEY_", 4)) + return str + 4; + return str; +}; + +char * +button_action_macro_to_str(struct ratbag_button *button) +{ + char str[4096] = {0}; + int offset; + unsigned int i; + + offset = snprintf(str, sizeof(str), "macro:"); + for (i = 0; i < MAX_MACRO_EVENTS; i++) { + enum ratbag_macro_event_type type = ratbag_button_get_macro_event_type(button, i); + int key = ratbag_button_get_macro_event_key(button, i); + int timeout = ratbag_button_get_macro_event_timeout(button, i); + + if (type == RATBAG_MACRO_EVENT_NONE) + break; + + switch (type) { + case RATBAG_MACRO_EVENT_KEY_PRESSED: + offset += snprintf(str + offset, sizeof(str) - offset, " %s↓", strip_ev_key(key)); + break; + case RATBAG_MACRO_EVENT_KEY_RELEASED: + offset += snprintf(str + offset, sizeof(str) - offset, " %s↑", strip_ev_key(key)); + break; + case RATBAG_MACRO_EVENT_WAIT: + offset += snprintf(str + offset, sizeof(str) - offset, " %d.%d⏱", timeout / 1000, timeout % 1000); + default: + offset += snprintf(str + offset, sizeof(str) - offset, " ###"); + } + } + + return strdup(str); +} + char * button_action_to_str(struct ratbag_button *button) { @@ -157,7 +199,7 @@ button_action_to_str(struct ratbag_button *button) case RATBAG_BUTTON_ACTION_TYPE_BUTTON: str = button_action_button_to_str(button); break; case RATBAG_BUTTON_ACTION_TYPE_KEY: str = button_action_key_to_str(button); break; case RATBAG_BUTTON_ACTION_TYPE_SPECIAL: str = strdup(button_action_special_to_str(button)); break; - case RATBAG_BUTTON_ACTION_TYPE_MACRO: str = strdup("macro"); break; + case RATBAG_BUTTON_ACTION_TYPE_MACRO: str = button_action_macro_to_str(button); break; case RATBAG_BUTTON_ACTION_TYPE_NONE: str = strdup("none"); break; default: error("type %d unknown\n", type); diff --git a/tools/shared.h b/tools/shared.h index c3c6fc8d..968131f4 100644 --- a/tools/shared.h +++ b/tools/shared.h @@ -37,6 +37,8 @@ #include #include +#define MAX_MACRO_EVENTS 64 + LIBRATBAG_ATTRIBUTE_PRINTF(1, 2) static inline void error(const char *format, ...) From e4f2f295c059d43b8409dc5056bc9350155a1d92 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 8 Sep 2015 12:48:25 -0400 Subject: [PATCH 03/10] TODO: add macro todo list Signed-off-by: Benjamin Tissoires --- TODO | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/TODO b/TODO index a276507e..ddb2ed55 100644 --- a/TODO +++ b/TODO @@ -17,3 +17,9 @@ require HID parsing in libratbag for relatively little benefit. The only advantage we really get out of it is that a configuration UI would be able to update itself if a user presses a button to e.g. change the profile while the UI is running. + +Macro support: +- enhance doctext +- add API support for name/groups +- revamp the API? +- add command to set a macro in ratbag-command From c7b7996f09d2d6a0d4425bf2d35fb0bcbfaa40e7 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 8 Sep 2015 20:17:12 -0400 Subject: [PATCH 04/10] etekcity: actually write the macro to the device Signed-off-by: Benjamin Tissoires --- src/driver-etekcity.c | 75 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/src/driver-etekcity.c b/src/driver-etekcity.c index 06260ca0..831c5b56 100644 --- a/src/driver-etekcity.c +++ b/src/driver-etekcity.c @@ -52,6 +52,8 @@ #define ETEKCITY_CONFIG_SETTINGS 0x10 #define ETEKCITY_CONFIG_KEY_MAPPING 0x20 +#define ETEKCITY_MAX_MACRO_LENGTH 50 + struct etekcity_settings_report { uint8_t reportID; uint8_t twentyHeight; @@ -75,13 +77,13 @@ struct etekcity_macro { uint8_t heightytwo; uint8_t profile; uint8_t button_index; - uint8_t one; + uint8_t active; char name[24]; uint8_t length; struct { uint8_t keycode; uint8_t flag; - } keys[50]; + } keys[ETEKCITY_MAX_MACRO_LENGTH]; } __attribute__((packed)); struct etekcity_data { @@ -618,6 +620,67 @@ etekcity_read_button(struct ratbag_button *button) } } +static int +etekcity_write_macro(struct ratbag_button *button, + const struct ratbag_button_action *action) +{ + struct ratbag_device *device; + struct etekcity_macro *macro; + struct etekcity_data *drv_data; + uint8_t *buf; + unsigned i, j, count = 0; + int rc; + + if (ratbag_button_get_action_type(button) != RATBAG_BUTTON_ACTION_TYPE_MACRO) + return 0; + + device = button->profile->device; + drv_data = ratbag_get_drv_data(device); + macro = &drv_data->macros[button->profile->index][button->index]; + buf = (uint8_t*)macro; + + for (i = 0; i < MAX_MACRO_EVENTS && count < ETEKCITY_MAX_MACRO_LENGTH; i++) { + if (action->macro->events[i].type == RATBAG_MACRO_EVENT_INVALID) + return -EINVAL; /* should not happen, ever */ + + if (action->macro->events[i].type == RATBAG_MACRO_EVENT_NONE) + break; + + /* ignore timeout events */ + if (action->macro->events[i].type == RATBAG_MACRO_EVENT_WAIT) + continue; + + for (j = 0; j < ARRAY_LENGTH(macro_mapping); j++) { + if (macro_mapping[j] == action->macro->events[i].event.key) + macro->keys[count].keycode = j; + } + if (action->macro->events[i].type == RATBAG_MACRO_EVENT_KEY_PRESSED) + macro->keys[count].flag = 0x00; + else + macro->keys[count].flag = 0x80; + count++; + } + + macro->reportID = ETEKCITY_REPORT_ID_MACRO; + macro->heightytwo = 0x82; + macro->profile = button->profile->index; + macro->button_index = button->index; + macro->active = 0x01; + strncpy(macro->name, action->macro->name, 23); + macro->length = count; + + etekcity_set_config_profile(device, + button->profile->index, + button->index); + rc = ratbag_hidraw_raw_request(device, ETEKCITY_REPORT_ID_MACRO, + buf, ETEKCITY_REPORT_SIZE_MACRO, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (rc < 0) + return rc; + + return rc == ETEKCITY_REPORT_SIZE_MACRO ? 0 : -EIO; +} + static int etekcity_write_button(struct ratbag_button *button, const struct ratbag_button_action *action) @@ -636,7 +699,13 @@ etekcity_write_button(struct ratbag_button *button, *data = rc; - return 0; + rc = etekcity_write_macro(button, action); + if (rc) + log_error(device->ratbag, + "unable to write the macro to the device: '%s' (%d)\n", + strerror(-rc), rc); + + return rc; } static int From cdf710d8076008b6ddc0b119057f013783d29785 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 8 Sep 2015 14:49:40 -0400 Subject: [PATCH 05/10] add support for getting/setting the macro name Signed-off-by: Benjamin Tissoires --- src/driver-etekcity.c | 2 +- src/libratbag-private.h | 4 ++-- src/libratbag.c | 22 +++++++++++++++++++--- src/libratbag.h | 18 +++++++++++++++++- src/libratbag.sym | 1 + tools/shared.c | 5 ++++- 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/driver-etekcity.c b/src/driver-etekcity.c index 831c5b56..67f099b5 100644 --- a/src/driver-etekcity.c +++ b/src/driver-etekcity.c @@ -600,7 +600,7 @@ etekcity_read_button(struct ratbag_button *button) button->index, button->profile->index, rc < 0 ? strerror(-rc) : "not read enough", rc); } else { - ratbag_button_set_macro(button); + ratbag_button_set_macro(button, macro->name); log_raw(device->ratbag, "macro on button %d of profile %d is named '%s', and contains %d events:\n", button->index, button->profile->index, diff --git a/src/libratbag-private.h b/src/libratbag-private.h index 8dd263a0..45e4a659 100644 --- a/src/libratbag-private.h +++ b/src/libratbag-private.h @@ -257,8 +257,8 @@ struct ratbag_macro_event { #define MAX_MACRO_EVENTS 64 struct ratbag_macro { - const char *name; - const char *group; + char *name; + char *group; struct ratbag_macro_event events[MAX_MACRO_EVENTS]; }; diff --git a/src/libratbag.c b/src/libratbag.c index 0c240865..982a9914 100644 --- a/src/libratbag.c +++ b/src/libratbag.c @@ -1204,8 +1204,11 @@ static void ratbag_button_destroy(struct ratbag_button *button) { list_remove(&button->link); - if (button->action.macro) + if (button->action.macro) { + if (button->action.macro->name) + free(button->action.macro->name); free(button->action.macro); + } free(button); } @@ -1288,16 +1291,20 @@ ratbag_resolution_get_user_data(const struct ratbag_resolution *ratbag_resolutio } LIBRATBAG_EXPORT int -ratbag_button_set_macro(struct ratbag_button *button) +ratbag_button_set_macro(struct ratbag_button *button, const char *name) { struct ratbag_macro *macro; if (!button->action.macro) button->action.macro = zalloc(sizeof(*macro)); - else + else { + if (button->action.macro->name) + free(button->action.macro->name); memset(button->action.macro, 0, sizeof(*macro)); + } button->action.type = RATBAG_BUTTON_ACTION_TYPE_MACRO; + button->action.macro->name = name ? strdup(name) : NULL; return 0; } @@ -1380,3 +1387,12 @@ ratbag_button_get_macro_event_timeout(struct ratbag_button *button, unsigned int return macro->events[index].event.timeout; } + +LIBRATBAG_EXPORT const char * +ratbag_button_get_macro_name(struct ratbag_button *button) +{ + if (button->action.type != RATBAG_BUTTON_ACTION_TYPE_MACRO) + return NULL; + + return button->action.macro->name; +} diff --git a/src/libratbag.h b/src/libratbag.h index 11d50b38..f15c90f6 100644 --- a/src/libratbag.h +++ b/src/libratbag.h @@ -1269,6 +1269,22 @@ enum ratbag_macro_event_type { RATBAG_MACRO_EVENT_WAIT, }; +/** + * @ingroup button + * + * If a button's action is @ref RATBAG_BUTTON_ACTION_TYPE_MACRO, + * this function returns the macro name. + * + * If the button's action type is not @ref RATBAG_BUTTON_ACTION_TYPE_MACRO, + * this function returns NULL. + * + * @param button A previously initialized ratbag button + * + * @return The name of the macro associated to the button. + */ +const char * +ratbag_button_get_macro_name(struct ratbag_button *button); + /** * @ingroup button * @@ -1334,7 +1350,7 @@ ratbag_button_get_macro_event_timeout(struct ratbag_button *button, unsigned int * allocates the required memory to store the macro. */ int -ratbag_button_set_macro(struct ratbag_button *button); +ratbag_button_set_macro(struct ratbag_button *button, const char *name); /** * @ingroup button diff --git a/src/libratbag.sym b/src/libratbag.sym index 1b1cda5d..291f51b2 100644 --- a/src/libratbag.sym +++ b/src/libratbag.sym @@ -11,6 +11,7 @@ global: ratbag_button_get_macro_event_type; ratbag_button_get_macro_event_key; ratbag_button_get_macro_event_timeout; + ratbag_button_get_macro_name; ratbag_button_get_special; ratbag_button_get_user_data; ratbag_button_has_action_type; diff --git a/tools/shared.c b/tools/shared.c index 56273f42..280975fc 100644 --- a/tools/shared.c +++ b/tools/shared.c @@ -158,10 +158,13 @@ char * button_action_macro_to_str(struct ratbag_button *button) { char str[4096] = {0}; + const char *name; int offset; unsigned int i; - offset = snprintf(str, sizeof(str), "macro:"); + name = ratbag_button_get_macro_name(button); + offset = snprintf(str, sizeof(str), "macro \"%s\":", + name ? name : "UNKNOWN"); for (i = 0; i < MAX_MACRO_EVENTS; i++) { enum ratbag_macro_event_type type = ratbag_button_get_macro_event_type(button, i); int key = ratbag_button_get_macro_event_key(button, i); From 6910eabaccb93376c5ac77acdceb2b6e3396aad6 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 8 Sep 2015 20:19:20 -0400 Subject: [PATCH 06/10] Add an additional macro API to commit the macro Not very glorious Signed-off-by: Benjamin Tissoires --- src/libratbag.c | 7 +++++++ src/libratbag.h | 3 +++ src/libratbag.sym | 1 + 3 files changed, 11 insertions(+) diff --git a/src/libratbag.c b/src/libratbag.c index 982a9914..6ce13f4a 100644 --- a/src/libratbag.c +++ b/src/libratbag.c @@ -1343,6 +1343,13 @@ ratbag_button_set_macro_event(struct ratbag_button *button, return 0; } +LIBRATBAG_EXPORT int +ratbag_button_write_macro(struct ratbag_button *button) +{ + return button->profile->device->driver->write_button(button, &button->action); + +} + LIBRATBAG_EXPORT enum ratbag_macro_event_type ratbag_button_get_macro_event_type(struct ratbag_button *button, unsigned int index) { diff --git a/src/libratbag.h b/src/libratbag.h index f15c90f6..be60309d 100644 --- a/src/libratbag.h +++ b/src/libratbag.h @@ -1352,6 +1352,9 @@ ratbag_button_get_macro_event_timeout(struct ratbag_button *button, unsigned int int ratbag_button_set_macro(struct ratbag_button *button, const char *name); +int +ratbag_button_write_macro(struct ratbag_button *button); + /** * @ingroup button * diff --git a/src/libratbag.sym b/src/libratbag.sym index 291f51b2..2726cc38 100644 --- a/src/libratbag.sym +++ b/src/libratbag.sym @@ -23,6 +23,7 @@ global: ratbag_button_set_special; ratbag_button_set_user_data; ratbag_button_unref; + ratbag_button_write_macro; ratbag_device_get_name; ratbag_device_get_num_buttons; ratbag_device_get_num_profiles; From 286f39876359c6c65db5f21432b4fc204572d8c9 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 8 Sep 2015 20:13:50 -0400 Subject: [PATCH 07/10] tools: add a (currently fake) way of addressing a macro change We need to implement a parsing of the given macro... Signed-off-by: Benjamin Tissoires --- tools/ratbag-command.c | 53 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/tools/ratbag-command.c b/tools/ratbag-command.c index 6652450e..85f4a6e9 100644 --- a/tools/ratbag-command.c +++ b/tools/ratbag-command.c @@ -362,6 +362,37 @@ static const struct ratbag_cmd cmd_switch_etekcity = { .help = "Switch the Etekcity mouse active profile", }; +struct macro { + const char *name; + struct { + enum ratbag_macro_event_type type; + unsigned data; + } events[64]; +}; + +static int +str_to_macro(const char *action_arg, struct macro *m) +{ + if (!action_arg) + return -EINVAL; + + m->name = "foo"; + m->events[0].type = RATBAG_MACRO_EVENT_KEY_PRESSED; + m->events[0].data = KEY_F; + m->events[1].type = RATBAG_MACRO_EVENT_KEY_RELEASED; + m->events[1].data = KEY_F; + m->events[2].type = RATBAG_MACRO_EVENT_KEY_PRESSED; + m->events[2].data = KEY_O; + m->events[3].type = RATBAG_MACRO_EVENT_KEY_RELEASED; + m->events[3].data = KEY_O; + m->events[4].type = RATBAG_MACRO_EVENT_KEY_PRESSED; + m->events[4].data = KEY_O; + m->events[5].type = RATBAG_MACRO_EVENT_KEY_RELEASED; + m->events[5].data = KEY_O; + + return 0; +} + static int ratbag_cmd_change_button(struct ratbag *ratbag, uint32_t flags, int argc, char **argv) { @@ -374,6 +405,7 @@ ratbag_cmd_change_button(struct ratbag *ratbag, uint32_t flags, int argc, char * int rc = 1; unsigned int btnkey; enum ratbag_button_action_special special; + struct macro macro = {0}; int i; if (argc != 4) { @@ -402,6 +434,12 @@ ratbag_cmd_change_button(struct ratbag *ratbag, uint32_t flags, int argc, char * error("Invalid special command '%s'\n", action_arg); return 1; } + } else if (streq(action_str, "macro")) { + action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO; + if (str_to_macro(action_arg, ¯o)) { + error("Invalid special command '%s'\n", action_arg); + return 1; + } } else { usage(); return 1; @@ -449,6 +487,19 @@ ratbag_cmd_change_button(struct ratbag *ratbag, uint32_t flags, int argc, char * case RATBAG_BUTTON_ACTION_TYPE_SPECIAL: rc = ratbag_button_set_special(button, special); break; + case RATBAG_BUTTON_ACTION_TYPE_MACRO: + rc = ratbag_button_set_macro(button, macro.name); + for (i = 0; i < ARRAY_LENGTH(macro.events); i++) { + if (macro.events[i].type == RATBAG_MACRO_EVENT_NONE) + break; + + ratbag_button_set_macro_event(button, + i, + macro.events[i].type, + macro.events[i].data); + } + rc = ratbag_button_write_macro(button); + break; default: error("well, that shouldn't have happened\n"); abort(); @@ -481,7 +532,7 @@ out: static const struct ratbag_cmd cmd_change_button = { .name = "change-button", .cmd = ratbag_cmd_change_button, - .args = "X ", + .args = "X ", .help = "Remap button X to the given action in the active profile", }; From 1e40cda292921bca49ce003830b7a641f939022b Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 9 Sep 2015 11:18:29 -0400 Subject: [PATCH 08/10] data: minor adjustments to the SVGs Signed-off-by: Benjamin Tissoires --- data/etekcity.svg | 208 ++++++++++++++++++++---------------- data/logitech-mx_master.svg | 24 ++++- 2 files changed, 136 insertions(+), 96 deletions(-) diff --git a/data/etekcity.svg b/data/etekcity.svg index e467d19c..43867441 100644 --- a/data/etekcity.svg +++ b/data/etekcity.svg @@ -48,7 +48,17 @@ xlink:href="#linearGradient4449" id="linearGradient4562" gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.698701,0,0,1.698701,6.5113742,644.61147)" + gradientTransform="matrix(1.698701,0,0,1.698701,340.78058,475.26804)" + x1="31.968746" + y1="18.546629" + x2="31.968746" + y2="29.577879" /> + - + y2="200.83122" + gradientTransform="translate(0,85.407996)" /> + style="display:inline"> + style="fill:#000000;fill-opacity:1;stroke:none" + transform="translate(0,85.407996)"> @@ -192,7 +193,7 @@ width="31.607143" height="75.178574" x="274.67535" - y="179.50507" + y="264.91306" rx="3.25" ry="3.25" /> @@ -201,11 +202,10 @@ id="layer3" inkscape:label="Actions" style="display:inline" - transform="translate(647.36582,205.64997)" - sodipodi:insensitive="true"> + transform="translate(647.36582,205.64997)"> @@ -270,65 +270,58 @@ + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> @@ -342,21 +335,21 @@ xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="27.097548" - y="-68.740425" + y="16.66757" id="text4473" sodipodi:linespacing="125%">Button0 Button1 @@ -364,21 +357,21 @@ xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="196.44098" - y="-136.41295" + y="-51.004951" id="text4481" sodipodi:linespacing="125%">Button2 + y="-51.004951">Button2 Button3 @@ -386,71 +379,71 @@ xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="-177.07159" - y="440.89215" + y="526.30017" id="text4489" sodipodi:linespacing="125%">Button4 + y="526.30017">Button4 Button5 Button6 + x="375.72412" + y="288.81161">Button6 Button7 Button8 + x="417.32373" + y="526.42987">Button8 Button9 + transform="translate(-140.12576,-153.50496)"> + Current Profile + + + Etekcity Scroll Alpha diff --git a/data/logitech-mx_master.svg b/data/logitech-mx_master.svg index 560f6f60..ae3d9091 100644 --- a/data/logitech-mx_master.svg +++ b/data/logitech-mx_master.svg @@ -71,10 +71,10 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.70710678" - inkscape:cx="705.88437" - inkscape:cy="617.7106" + inkscape:cx="672.09896" + inkscape:cy="504.57351" inkscape:document-units="px" - inkscape:current-layer="layer5" + inkscape:current-layer="layer2" showgrid="false" showguides="true" inkscape:guide-bbox="true" @@ -368,4 +368,22 @@ rx="10.321068" ry="10.321068" /> + + Logitech MX Master + From d7ef078c027d717c85d47909eb121601f9fa9ab1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 10 Sep 2015 08:49:51 -0400 Subject: [PATCH 09/10] tools: Now, we have foo and bar as macro Still not a parsing of the user input... Signed-off-by: Benjamin Tissoires --- tools/ratbag-command.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/tools/ratbag-command.c b/tools/ratbag-command.c index 85f4a6e9..560e0823 100644 --- a/tools/ratbag-command.c +++ b/tools/ratbag-command.c @@ -376,19 +376,35 @@ str_to_macro(const char *action_arg, struct macro *m) if (!action_arg) return -EINVAL; - m->name = "foo"; - m->events[0].type = RATBAG_MACRO_EVENT_KEY_PRESSED; - m->events[0].data = KEY_F; - m->events[1].type = RATBAG_MACRO_EVENT_KEY_RELEASED; - m->events[1].data = KEY_F; - m->events[2].type = RATBAG_MACRO_EVENT_KEY_PRESSED; - m->events[2].data = KEY_O; - m->events[3].type = RATBAG_MACRO_EVENT_KEY_RELEASED; - m->events[3].data = KEY_O; - m->events[4].type = RATBAG_MACRO_EVENT_KEY_PRESSED; - m->events[4].data = KEY_O; - m->events[5].type = RATBAG_MACRO_EVENT_KEY_RELEASED; - m->events[5].data = KEY_O; + if (action_arg[0] == 'f') { + m->name = "foo"; + m->events[0].type = RATBAG_MACRO_EVENT_KEY_PRESSED; + m->events[0].data = KEY_F; + m->events[1].type = RATBAG_MACRO_EVENT_KEY_RELEASED; + m->events[1].data = KEY_F; + m->events[2].type = RATBAG_MACRO_EVENT_KEY_PRESSED; + m->events[2].data = KEY_O; + m->events[3].type = RATBAG_MACRO_EVENT_KEY_RELEASED; + m->events[3].data = KEY_O; + m->events[4].type = RATBAG_MACRO_EVENT_KEY_PRESSED; + m->events[4].data = KEY_O; + m->events[5].type = RATBAG_MACRO_EVENT_KEY_RELEASED; + m->events[5].data = KEY_O; + } else if (action_arg[0] == 'b') { + m->name = "bar"; + m->events[0].type = RATBAG_MACRO_EVENT_KEY_PRESSED; + m->events[0].data = KEY_B; + m->events[1].type = RATBAG_MACRO_EVENT_KEY_RELEASED; + m->events[1].data = KEY_B; + m->events[2].type = RATBAG_MACRO_EVENT_KEY_PRESSED; + m->events[2].data = KEY_A; + m->events[3].type = RATBAG_MACRO_EVENT_KEY_RELEASED; + m->events[3].data = KEY_A; + m->events[4].type = RATBAG_MACRO_EVENT_KEY_PRESSED; + m->events[4].data = KEY_R; + m->events[5].type = RATBAG_MACRO_EVENT_KEY_RELEASED; + m->events[5].data = KEY_R; + } return 0; } From f44f07c6a9b2cd4548d473b697b632c748b79f3f Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 10 Sep 2015 09:06:38 -0400 Subject: [PATCH 10/10] etekcity: fix the check for macro The current code was checking if the previous action assigned to a button was a macro, not that the action we are assigning will be a macro. Signed-off-by: Benjamin Tissoires --- src/driver-etekcity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/driver-etekcity.c b/src/driver-etekcity.c index 67f099b5..d2be7601 100644 --- a/src/driver-etekcity.c +++ b/src/driver-etekcity.c @@ -631,7 +631,7 @@ etekcity_write_macro(struct ratbag_button *button, unsigned i, j, count = 0; int rc; - if (ratbag_button_get_action_type(button) != RATBAG_BUTTON_ACTION_TYPE_MACRO) + if (action->type != RATBAG_BUTTON_ACTION_TYPE_MACRO) return 0; device = button->profile->device;