diff --git a/src/ratbagctl.c b/src/ratbagctl.c index 2e83ee30..b06be8a8 100644 --- a/src/ratbagctl.c +++ b/src/ratbagctl.c @@ -715,6 +715,176 @@ static int show_profile_print_resolutions(struct ratbagctl *ctl, return 0; } +static int show_profile_print_button(struct ratbagctl *ctl, const char *path) +{ + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + unsigned int prop_index = -1, + prop_btnmap = -1; + unsigned int prop_keymap[20] = {0}; + const char *prop_type, + *prop_action_type, + *prop_special; + const char *prop_action_types[20] = {0}; + unsigned int idx = 0; + unsigned int modmap = 0; + int r; + + r = sd_bus_call_method(ctl->bus, + "org.freedesktop.ratbag1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &error, + &reply, + "s", + "org.freedesktop.ratbag1.Button"); + if (r < 0) + goto exit; + + r = sd_bus_message_enter_container(reply, 'a', "{sv}"); + if (r < 0) + goto exit; + + while ((r = sd_bus_message_enter_container(reply, 'e', "sv")) > 0) { + const char *property; + + r = sd_bus_message_read_basic(reply, 's', &property); + if (r < 0) + goto exit; + + if (streq(property, "Index")) { + r = sd_bus_message_read(reply, "v", "u", + &prop_index); + } else if (streq(property, "Type")) { + r = sd_bus_message_read(reply, "v", "s", + &prop_type); + } else if (streq(property, "ButtonMapping")) { + r = sd_bus_message_read(reply, "v", "u", + &prop_btnmap); + } else if (streq(property, "SpecialMapping")) { + r = sd_bus_message_read(reply, "v", "s", + &prop_special); + } else if (streq(property, "KeyMapping")) { + r = sd_bus_message_enter_container(reply, 'v', "au"); + if (r < 0) + goto exit; + + r = sd_bus_message_enter_container(reply, 'a', "u"); + if (r < 0) + goto exit; + + idx = 0; + while ((r = sd_bus_message_read_basic(reply, 'u', + &prop_keymap[idx++])) > 0) + ; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto exit; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto exit; + } else if (streq(property, "ActionType")) { + r = sd_bus_message_read(reply, "v", "s", + &prop_action_type); + } else if (streq(property, "ActionTypes")) { + r = sd_bus_message_enter_container(reply, 'v', "as"); + if (r < 0) + goto exit; + + r = sd_bus_message_enter_container(reply, 'a', "s"); + if (r < 0) + goto exit; + + idx = 0; + while ((r = sd_bus_message_read_basic(reply, 's', + &prop_action_types[idx++])) > 0) + ; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto exit; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto exit; + } else { + r = sd_bus_message_skip(reply, "v"); + } + if (r < 0) + goto exit; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto exit; + } + if (r < 0) + goto exit; + + r = sd_bus_message_rewind(reply, 0); + if (r < 0) + goto exit; + + printf("button-%u\n", prop_index); + + printf("\t Index: %u\n", prop_index); + printf("\t Type: %s\n", prop_type); + printf("\t Action type: %s\n", prop_action_type); + idx = 0; + printf("\t Action types: "); + while ((prop_action_type = prop_action_types[idx++]) != NULL) + printf("%s ", prop_action_type); + printf("\n"); + printf("\t Special mapping: %s\n", prop_special); + printf("\t Button mapping: %d\n", prop_btnmap); + + printf("\t Key mapping: %d (", prop_keymap[0]); + idx = 1; + if (prop_keymap[1] == 0) { + printf("no modifiers"); + } else { + printf("modifiers "); + while ((modmap = prop_keymap[idx++]) != 0) + printf("%d ", modmap); + } + printf(")\n"); +exit: + if (r < 0) + fprintf(stderr, "Cannot show button: %s\n", + error.message ? : "Parser error"); + return r; +} + +static int show_profile_print_buttons(struct ratbagctl *ctl, + sd_bus_message *m) +{ + const char *button; + int r; + + r = sd_bus_message_enter_container(m, 'v', "ao"); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(m, 'a', "o"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read_basic(m, 'o', &button)) > 0) + show_profile_print_button(ctl, button); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + return 0; +} + static int show_profile_print(struct ratbagctl *ctl, const char *device, const char *profile) @@ -795,6 +965,8 @@ static int show_profile_print(struct ratbagctl *ctl, reply, prop_active_resolution, prop_default_resolution); + } else if (streq(property, "Buttons")) { + r = show_profile_print_buttons(ctl, reply); } else { r = sd_bus_message_skip(reply, "v"); } diff --git a/src/ratbagd-button.c b/src/ratbagd-button.c index 3bb2117f..735d7ee0 100644 --- a/src/ratbagd-button.c +++ b/src/ratbagd-button.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "ratbagd.h" #include "shared-macro.h" @@ -40,9 +41,453 @@ struct ratbagd_button { char *path; }; +static int ratbagd_button_get_type(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_button *button = userdata; + const char *type = NULL; + enum ratbag_button_type t; + + t = ratbag_button_get_type(button->lib_button); + + switch(t) { + default: + log_error("Unknown button type %d\n", t); + /* fallthrough */ + case RATBAG_BUTTON_TYPE_UNKNOWN: + type = "unknown"; + break; + case RATBAG_BUTTON_TYPE_LEFT: + type = "left"; + break; + case RATBAG_BUTTON_TYPE_MIDDLE: + type = "middle"; + break; + case RATBAG_BUTTON_TYPE_RIGHT: + type = "right"; + break; + case RATBAG_BUTTON_TYPE_THUMB: + type = "thumb"; + break; + case RATBAG_BUTTON_TYPE_THUMB2: + type = "thumb2"; + break; + case RATBAG_BUTTON_TYPE_THUMB3: + type = "thumb3"; + break; + case RATBAG_BUTTON_TYPE_THUMB4: + type = "thumb4"; + break; + case RATBAG_BUTTON_TYPE_WHEEL_LEFT: + type = "wheel_left"; + break; + case RATBAG_BUTTON_TYPE_WHEEL_RIGHT: + type = "wheel_right"; + break; + case RATBAG_BUTTON_TYPE_WHEEL_CLICK: + type = "wheel_click"; + break; + case RATBAG_BUTTON_TYPE_WHEEL_UP: + type = "wheel_up"; + break; + case RATBAG_BUTTON_TYPE_WHEEL_DOWN: + type = "wheel_down"; + break; + case RATBAG_BUTTON_TYPE_WHEEL_RATCHET_MODE_SHIFT: + type = "wheel_ratchet_mode_shift"; + break; + case RATBAG_BUTTON_TYPE_EXTRA: + type = "extra"; + break; + case RATBAG_BUTTON_TYPE_SIDE: + type = "side"; + break; + case RATBAG_BUTTON_TYPE_PINKIE: + type = "pinkie"; + break; + case RATBAG_BUTTON_TYPE_PINKIE2: + type = "pinkie2"; + break; + case RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP: + type = "resolution_cycle_up"; + break; + case RATBAG_BUTTON_TYPE_RESOLUTION_UP: + type = "resolution_up"; + break; + case RATBAG_BUTTON_TYPE_RESOLUTION_DOWN: + type = "resolution_down"; + break; + case RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP: + type = "profile_cycle_up"; + break; + case RATBAG_BUTTON_TYPE_PROFILE_UP: + type = "profile_up"; + break; + case RATBAG_BUTTON_TYPE_PROFILE_DOWN: + type = "profile_down"; + break; + } + + return sd_bus_message_append(reply, "s", type); +} + +static int ratbagd_button_get_button(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_button *button = userdata; + unsigned int b; + + b = ratbag_button_get_button(button->lib_button); + + return sd_bus_message_append(reply, "u", b); +} + +static int ratbagd_button_set_button(sd_bus_message *m, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_button *button = userdata; + unsigned int map; + int r; + + r = sd_bus_message_read(m, "u", &map); + if (r < 0) + return r; + + if (map == 0 || map > 30) + return 0; + + r = ratbag_button_set_button(button->lib_button, map); + + return sd_bus_reply_method_return(m, "u", r); +} + +static int ratbagd_button_get_special(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_button *button = userdata; + const char *type; + enum ratbag_button_action_special special; + + special = ratbag_button_get_special(button->lib_button); + + switch(special) { + default: + log_error("Unknown special type %d\n", special); + case RATBAG_BUTTON_ACTION_SPECIAL_UNKNOWN: + type = "unknown"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_INVALID: + type = "n/a"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK: + type = "doubleclick"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT: + type = "wheel-left"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT: + type = "wheel-right"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP: + type = "wheel-up"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN: + type = "wheel-down"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH: + type = "ratchet-mode-switch"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP: + type = "resolution-cycle-up"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP: + type = "resolution-up"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN: + type = "resolution-down"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE: + type = "resolution-alternate"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT: + type = "resolution-default"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP: + type = "profile-cycle-up"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP: + type = "profile-up"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN: + type = "profile-down"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE: + type = "second-mode"; + break; + case RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL: + type = "battery-level"; + break; + } + + return sd_bus_message_append(reply, "s", type); +} + +static int ratbagd_button_set_special(sd_bus_message *m, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_button *button = userdata; + const char *s; + enum ratbag_button_action_special special; + int r; + + r = sd_bus_message_read(m, "s", &s); + if (r < 0) + return r; + + if (streq(s, "doubleclick")) + special = RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK; + else if (streq(s, "wheel-left")) + special = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT; + else if (streq(s, "wheel-right")) + special = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT; + else if (streq(s, "wheel-up")) + special = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP; + else if (streq(s, "wheel-down")) + special = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN; + else if (streq(s, "ratchet-mode-switch")) + special = RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH; + else if (streq(s, "resolution-cycle-up")) + special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP; + else if (streq(s, "resolution-up")) + special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP; + else if (streq(s, "resolution-down")) + special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN; + else if (streq(s, "resolution-alternate")) + special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE; + else if (streq(s, "resolution-default")) + special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT; + else if (streq(s, "profile-cycle-up")) + special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP; + else if (streq(s, "profile-up")) + special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP; + else if (streq(s, "profile-down")) + special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN; + else if (streq(s, "second-mode")) + special = RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE; + else if (streq(s, "battery-level")) + special = RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL; + else { + log_error("Unknown button special action %s\n", s); + return 0; + } + + r = ratbag_button_set_special(button->lib_button, special); + + return sd_bus_reply_method_return(m, "u", r); +} + +static int ratbagd_button_get_key(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_button *button = userdata; + unsigned int key; + unsigned int modifiers[10] = {0}; + size_t nmodifiers = ELEMENTSOF(modifiers); + size_t i; + int r; + + /* Return an array with the first element the key, the rest + are modifiers (or 0 if unset). If no key is set, the array is + just [0] */ + + r = sd_bus_message_open_container(reply, 'a', "u"); + if (r < 0) + return r; + + key = ratbag_button_get_key(button->lib_button, + modifiers, + &nmodifiers); + + r = sd_bus_message_append(reply, "u", key); + if (r < 0) + return r; + + if (key == 0) + nmodifiers = 0; + + for (i = 0; i < nmodifiers; i++) { + r = sd_bus_message_append(reply, "u", modifiers[0]); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int ratbagd_button_set_key(sd_bus_message *m, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_button *button = userdata; + unsigned int key; + size_t nmodifiers = 10; + unsigned int modifiers[nmodifiers]; + int r; + + /* Expect an array with the first element the key, the rest + are modifiers (or 0 if unset). */ + + r = sd_bus_message_enter_container(m, 'a', "u"); + if (r < 0) + return r; + + r = sd_bus_message_read(m, "u", &key); + if (r < 0) + return r; + + nmodifiers = 0; + while ((r = sd_bus_message_read_basic(m, 'u', + &modifiers[nmodifiers++])) > 0) + ; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + r = ratbag_button_set_key(button->lib_button, key, modifiers, nmodifiers); + + return sd_bus_reply_method_return(m, "u", r); +} + +static int ratbagd_button_get_action_type(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_button *button = userdata; + const char *type; + + switch (ratbag_button_get_action_type(button->lib_button)) { + case RATBAG_BUTTON_ACTION_TYPE_NONE: + type = "none"; + break; + case RATBAG_BUTTON_ACTION_TYPE_BUTTON: + type = "button"; + break; + case RATBAG_BUTTON_ACTION_TYPE_KEY: + type = "key"; + break; + case RATBAG_BUTTON_ACTION_TYPE_SPECIAL: + type = "special"; + break; + case RATBAG_BUTTON_ACTION_TYPE_MACRO: + type = "macro"; + break; + case RATBAG_BUTTON_ACTION_TYPE_UNKNOWN: + type = "unknown"; + break; + } + + return sd_bus_message_append(reply, "s", type); +} + +static int ratbagd_button_get_action_types(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_button *button = userdata; + const char *types[5] = {NULL}; + const char **t; + int idx = 0; + int r; + + r = sd_bus_message_open_container(reply, 'a', "s"); + if (r < 0) + return r; + + if (ratbag_button_has_action_type(button->lib_button, + RATBAG_BUTTON_ACTION_TYPE_BUTTON)) + types[idx++] = "button"; + if (ratbag_button_has_action_type(button->lib_button, + RATBAG_BUTTON_ACTION_TYPE_KEY)) + types[idx++] = "key"; + if (ratbag_button_has_action_type(button->lib_button, + RATBAG_BUTTON_ACTION_TYPE_SPECIAL)) + types[idx++] = "special"; + if (ratbag_button_has_action_type(button->lib_button, + RATBAG_BUTTON_ACTION_TYPE_MACRO)) + types[idx++] = "macro"; + + t = types; + while (*t) { + r = sd_bus_message_append(reply, "s", *t); + if (r < 0) + return r; + t++; + } + + return sd_bus_message_close_container(reply); +} + +static int ratbagd_button_disable(sd_bus_message *m, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_button *button = userdata; + int r; + + r = sd_bus_message_read(m, ""); + if (r < 0) + return r; + + r = ratbag_button_disable(button->lib_button); + + return sd_bus_reply_method_return(m, "u", r); +} + const sd_bus_vtable ratbagd_button_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_button, index), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Type", "s", ratbagd_button_get_type, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ButtonMapping", "u", ratbagd_button_get_button, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("SpecialMapping", "s", ratbagd_button_get_special, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("KeyMapping", "au", ratbagd_button_get_key, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("ActionType", "s", ratbagd_button_get_action_type, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("ActionTypes", "as", ratbagd_button_get_action_types, 0, SD_BUS_VTABLE_PROPERTY_CONST), + + SD_BUS_METHOD("SetButtonMapping", "u", "u", ratbagd_button_set_button, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetSpecialMapping", "s", "u", ratbagd_button_set_special, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetKeyMapping", "au", "u", ratbagd_button_set_key, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Disable", "", "u", ratbagd_button_disable, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END, }; diff --git a/src/ratbagd-profile.c b/src/ratbagd-profile.c index 3e463b14..77de4746 100644 --- a/src/ratbagd-profile.c +++ b/src/ratbagd-profile.c @@ -114,6 +114,39 @@ static int ratbagd_profile_get_resolutions(sd_bus *bus, return sd_bus_message_close_container(reply); } +static int ratbagd_profile_get_buttons(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_profile *profile = userdata; + struct ratbagd_button *button; + unsigned int i; + int r; + + r = sd_bus_message_open_container(reply, 'a', "o"); + if (r < 0) + return r; + + for (i = 0; i < profile->n_buttons; ++i) { + button = profile->buttons[i]; + if (!button) + continue; + + r = sd_bus_message_append(reply, + "o", + ratbagd_button_get_path(button)); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + + static int ratbagd_profile_get_active_resolution(sd_bus *bus, const char *path, const char *interface, @@ -239,6 +272,7 @@ const sd_bus_vtable ratbagd_profile_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_profile, index), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Resolutions", "ao", ratbagd_profile_get_resolutions, 0, 0), + SD_BUS_PROPERTY("Buttons", "ao", ratbagd_profile_get_buttons, 0, 0), SD_BUS_PROPERTY("ActiveResolution", "u", ratbagd_profile_get_active_resolution, 0, 0), SD_BUS_PROPERTY("DefaultResolution", "u", ratbagd_profile_get_default_resolution, 0, 0), SD_BUS_METHOD("SetActive", "", "u", ratbagd_profile_set_active, SD_BUS_VTABLE_UNPRIVILEGED), @@ -482,7 +516,7 @@ int ratbagd_profile_register_buttons(struct sd_bus *bus, sprintf(index_buffer, "p%u", profile->index); - /* register resolution interfaces */ + /* register button interfaces */ r = sd_bus_path_encode_many(&prefix, "/org/freedesktop/ratbag1/button/%/%", ratbagd_device_get_name(device),