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
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
+
diff --git a/src/driver-etekcity.c b/src/driver-etekcity.c
index e9f07a3e..0ec74898 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 {
@@ -464,7 +466,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 +523,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 +565,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 +581,104 @@ 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 {
+ 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,
+ macro->name, macro->length);
+ for (j = 0; j < macro->length; j++) {
+ 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);
+ }
+}
+
+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 (action->type != 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
@@ -622,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
diff --git a/src/libratbag-private.h b/src/libratbag-private.h
index 8ff3a577..45e4a659 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 {
+ char *name;
+ 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..6ce13f4a 100644
--- a/src/libratbag.c
+++ b/src/libratbag.c
@@ -1204,6 +1204,11 @@ static void
ratbag_button_destroy(struct ratbag_button *button)
{
list_remove(&button->link);
+ if (button->action.macro) {
+ if (button->action.macro->name)
+ free(button->action.macro->name);
+ free(button->action.macro);
+ }
free(button);
}
@@ -1284,3 +1289,117 @@ 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, const char *name)
+{
+ struct ratbag_macro *macro;
+
+ if (!button->action.macro)
+ button->action.macro = zalloc(sizeof(*macro));
+ 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;
+}
+
+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 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)
+{
+ 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;
+}
+
+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 4b6116ea..be60309d 100644
--- a/src/libratbag.h
+++ b/src/libratbag.h
@@ -1259,18 +1259,113 @@ 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 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.
*/
-unsigned int
-ratbag_button_set_macro(struct ratbag_button *button);
+const char *
+ratbag_button_get_macro_name(struct ratbag_button *button);
+
+/**
+ * @ingroup button
+ *
+ * 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
+ */
+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, const char *name);
+
+int
+ratbag_button_write_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..2726cc38 100644
--- a/src/libratbag.sym
+++ b/src/libratbag.sym
@@ -8,15 +8,22 @@ 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_macro_name;
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;
+ ratbag_button_write_macro;
ratbag_device_get_name;
ratbag_device_get_num_buttons;
ratbag_device_get_num_profiles;
diff --git a/tools/ratbag-command.c b/tools/ratbag-command.c
index 6652450e..560e0823 100644
--- a/tools/ratbag-command.c
+++ b/tools/ratbag-command.c
@@ -362,6 +362,53 @@ 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;
+
+ 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;
+}
+
static int
ratbag_cmd_change_button(struct ratbag *ratbag, uint32_t flags, int argc, char **argv)
{
@@ -374,6 +421,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 +450,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 +503,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 +548,7 @@ out:
static const struct ratbag_cmd cmd_change_button = {
.name = "change-button",
.cmd = ratbag_cmd_change_button,
- .args = "X