diff --git a/src/driver-gskill.c b/src/driver-gskill.c index 5974e89c..4824c297 100644 --- a/src/driver-gskill.c +++ b/src/driver-gskill.c @@ -310,6 +310,7 @@ struct gskill_profile_data { }; struct gskill_data { + uint8_t profile_count; struct gskill_profile_data profile_data[GSKILL_PROFILE_MAX]; }; @@ -499,6 +500,8 @@ gskill_get_profile_count(struct ratbag_device *device) return rc; } + log_debug(device->ratbag, "Profile count: %d\n", buf[3]); + return buf[3]; } @@ -509,6 +512,8 @@ gskill_set_profile_count(struct ratbag_device *device, unsigned int count) count, 0x0 }; int rc; + log_debug(device->ratbag, "Setting profile count to %d\n", count); + rc = gskill_general_cmd(device, buf); if (rc) { log_error(device->ratbag, @@ -613,10 +618,6 @@ gskill_write_profile(struct ratbag_device *device, return rc < 0 ? rc : -EPROTO; } - rc = gskill_reload_profile_data(device); - if (rc) - return rc; - return 0; } @@ -960,30 +961,6 @@ gskill_write_button_macro(struct ratbag_device *device, return 0; } -/* - * FIXME: Unfortunately if we ever try to get a profile that's above the - * current profile limit of the mouse, we end up making it cry and barf up - * garbage. No one likes barfing up garbage, so we unfortunately have to work - * around this until we have the ability to enable/disable profiles in ratbag. - */ -static int -gskill_workaround_profile_count(struct ratbag_device *device) -{ - int ret; - - ret = gskill_get_profile_count(device); - if (ret == GSKILL_PROFILE_MAX) - return 0; - if (ret < 0) - return ret; - - log_info(device->ratbag, - "We don't support dynamically enabling/disabling profiles yet, sorry! Setting profile count of mouse to 5\n"); - - ret = gskill_set_profile_count(device, GSKILL_PROFILE_MAX); - return ret; -} - static int gskill_probe(struct ratbag_device *device) { @@ -999,10 +976,6 @@ gskill_probe(struct ratbag_device *device) drv_data = zalloc(sizeof(*drv_data)); ratbag_set_drv_data(device, drv_data); - ret = gskill_workaround_profile_count(device); - if (ret) - goto err; - ret = gskill_get_firmware_version(device); if (ret < 0) goto err; @@ -1010,12 +983,18 @@ gskill_probe(struct ratbag_device *device) log_debug(device->ratbag, "Firmware version: %d\n", ret); + ret = gskill_get_profile_count(device); + if (ret < 0) + goto err; + drv_data->profile_count = ret; + ratbag_device_init_profiles(device, GSKILL_PROFILE_MAX, GSKILL_NUM_DPI, GSKILL_BUTTON_MAX); ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_QUERY_CONFIGURATION); ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_KEY); ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS); + ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_DISABLE_PROFILE); ret = gskill_get_active_profile_idx(device); if (ret < 0) @@ -1100,6 +1079,11 @@ gskill_read_profile(struct ratbag_profile *profile, unsigned int index) uint8_t checksum; int rc, retries; + if (index >= drv_data->profile_count) { + profile->is_enabled = false; + return; + } + /* * There's a couple of situations where after various commands, the * mouse will get confused and send the wrong profile. Keep trying @@ -1215,17 +1199,25 @@ gskill_read_button(struct ratbag_button *button) &profile_to_pdata(profile)->report; struct gskill_macro_report *macro_report; struct ratbag_button_macro *macro; - struct gskill_button_cfg *bcfg; + struct gskill_button_cfg *bcfg = &report->btn_cfgs[button->index]; struct ratbag_button_action *act = &button->action; button->type = gskill_button_type_mapping[button->index]; - bcfg = &report->btn_cfgs[button->index]; ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON); ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL); ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY); ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO); + /* + * G.Skill mice can't save disabled profiles, so buttons from disabled + * profiles shouldn't be set to anything + */ + if (!profile->is_enabled) { + act->type = RATBAG_BUTTON_ACTION_TYPE_NONE; + return; + } + /* Parse any parameters that might accompany the action type */ switch (bcfg->type) { case GSKILL_BUTTON_FUNCTION_WHEEL: @@ -1428,17 +1420,52 @@ static int gskill_commit(struct ratbag_device *device) { struct ratbag_profile *profile; + struct gskill_data *drv_data = ratbag_get_drv_data(device); + struct gskill_profile_report *report; + uint8_t profile_count = 0, new_idx, i; bool reload = false; int rc; - list_for_each(profile, &device->profiles, link) { - if (!profile->dirty) + /* + * G.Skill mice only have a concept of how many profiles are enabled, + * not which ones are and aren't enabled. So in order to provide the + * ability to disable individual profiles we need to only write the + * enabled profiles and make sure no holes are left inbetween profiles + */ + for (i = 0; i < GSKILL_PROFILE_MAX; i++) { + profile = ratbag_device_get_profile(device, i); + + if (!profile->is_enabled) continue; - reload = true; + report = &drv_data->profile_data[profile->index].report; + new_idx = profile_count++; + + if (new_idx == report->profile_num) + continue; + + log_debug(device->ratbag, + "Profile %d remapped to %d\n", + profile->index, new_idx); + + profile->dirty = true; + report->profile_num = new_idx; + } + + if (profile_count != drv_data->profile_count) { + rc = gskill_set_profile_count(device, profile_count); + if (rc < 0) + return rc; + drv_data->profile_count = profile_count; + } + + list_for_each(profile, &device->profiles, link) { + if (!profile->is_enabled || !profile->dirty) + continue; log_debug(device->ratbag, "Profile %d changed, rewriting\n", profile->index); + reload = true; rc = gskill_update_profile(profile); if (rc) diff --git a/src/libratbag-private.h b/src/libratbag-private.h index 668ffe66..224dd07e 100644 --- a/src/libratbag-private.h +++ b/src/libratbag-private.h @@ -231,6 +231,7 @@ struct ratbag_profile { } resolution; bool is_active; /**< profile is the currently active one */ + bool is_enabled; bool dirty; /**< profile changed since last commit */ }; diff --git a/src/libratbag.c b/src/libratbag.c index 4e93f070..8965b5b5 100644 --- a/src/libratbag.c +++ b/src/libratbag.c @@ -610,6 +610,7 @@ ratbag_create_profile(struct ratbag_device *device, profile->resolution.modes = zalloc(num_resolutions * sizeof(*profile->resolution.modes)); profile->resolution.num_modes = num_resolutions; + profile->is_enabled = true; list_insert(&device->profiles, &profile->link); list_init(&profile->buttons); @@ -714,12 +715,31 @@ ratbag_device_get_profile(struct ratbag_device *device, unsigned int index) return NULL; } +LIBRATBAG_EXPORT enum ratbag_error_code +ratbag_profile_set_enabled(struct ratbag_profile *profile, bool enabled) +{ + if (!ratbag_device_has_capability(profile->device, + RATBAG_DEVICE_CAP_DISABLE_PROFILE)) + return RATBAG_ERROR_CAPABILITY; + + profile->is_enabled = enabled; + profile->dirty = true; + + return RATBAG_SUCCESS; +} + LIBRATBAG_EXPORT int ratbag_profile_is_active(struct ratbag_profile *profile) { return profile->is_active; } +LIBRATBAG_EXPORT bool +ratbag_profile_is_enabled(const struct ratbag_profile *profile) +{ + return profile->is_enabled; +} + LIBRATBAG_EXPORT unsigned int ratbag_device_get_num_profiles(struct ratbag_device *device) { diff --git a/src/libratbag.h b/src/libratbag.h index 0bdc4dc3..e213b0ef 100644 --- a/src/libratbag.h +++ b/src/libratbag.h @@ -30,6 +30,7 @@ extern "C" { #include #include +#include #include #define LIBRATBAG_ATTRIBUTE_PRINTF(_format, _args) \ @@ -519,6 +520,15 @@ enum ratbag_device_capability { * libratbag can be used to query the device as normal. */ RATBAG_DEVICE_CAP_QUERY_CONFIGURATION, + + /** + * The device has the capability to disable and enable profiles. While + * profiles are not immediately deleted after being disabled, it is not + * guaranteed that the device will remember any disabled profiles the + * next time ratbag runs. Furthermore, the order of profiles may get + * changed the next time ratbag runs if profiles are disabled. + */ + RATBAG_DEVICE_CAP_DISABLE_PROFILE, }; /** @@ -603,6 +613,34 @@ ratbag_profile_ref(struct ratbag_profile *profile); struct ratbag_profile * ratbag_profile_unref(struct ratbag_profile *profile); +/** + * @ingroup profile + * + * Enable/disable the ratbag profile. For this to work, the device must support + * @ref RATBAG_DEVICE_CAP_DISABLE_PROFILE. + * + * @param profile A previously initialized ratbag profile + * @param enable Whether to enable or disable the profile + * + * @return 0 on success or an error code otherwise + */ +enum ratbag_error_code +ratbag_profile_set_enabled(struct ratbag_profile *profile, bool enabled); + +/** + * @ingroup profile + * + * Check whether the ratbag profile is enabled or not. For devices that don't + * support @ref RATBAG_DEVICE_CAP_DISABLE_PROFILE the profile will always be + * set to enabled. + * + * @param profile A previously initialized ratbag profile + * + * @return Whether the profile is enabled or not. + */ +bool +ratbag_profile_is_enabled(const struct ratbag_profile *profile); + /** * @ingroup profile * diff --git a/src/libratbag.sym b/src/libratbag.sym index 8fd781a8..64307ef6 100644 --- a/src/libratbag.sym +++ b/src/libratbag.sym @@ -50,9 +50,11 @@ global: ratbag_profile_get_resolution; ratbag_profile_get_user_data; ratbag_profile_is_active; + ratbag_profile_is_enabled; ratbag_profile_ref; ratbag_profile_set_user_data; ratbag_profile_set_active; + ratbag_profile_set_enabled; ratbag_profile_unref; ratbag_resolution_get_dpi; ratbag_resolution_get_dpi_x; diff --git a/tools/ratbag-command.c b/tools/ratbag-command.c index 25d9b988..f79a70ed 100644 --- a/tools/ratbag-command.c +++ b/tools/ratbag-command.c @@ -101,6 +101,8 @@ usage(void) "Profile Commands:\n" " profile active get Print the currently active profile\n" " profile active set N Set profile N as to the active profile\n" + " profile enable N Enable profile N\n" + " profile disable N Disable profile N\n" " profile N {COMMAND} Use profile N for COMMAND\n" "\n" "Resolution Commands\n" @@ -351,7 +353,8 @@ ratbag_cmd_info(const struct ratbag_cmd *cmd, if (!profile) continue; - printf(" Profile %d%s\n", i, + printf(" Profile %d (%s)%s\n", i, + ratbag_profile_is_enabled(profile) ? "enabled" : "disabled", ratbag_profile_is_active(profile) ? " (active)" : ""); printf(" Resolutions:\n"); for (j = 0; j < ratbag_profile_get_num_resolutions(profile); j++) { @@ -1562,6 +1565,60 @@ static const struct ratbag_cmd cmd_profile_active = { }, }; +static int +ratbag_cmd_profile_enable(const struct ratbag_cmd *cmd, + struct ratbag *ratbag, + struct ratbag_cmd_options *options, + int argc, char **argv) +{ + int rc; + + if (!ratbag_device_has_capability(options->device, + RATBAG_DEVICE_CAP_DISABLE_PROFILE)) + return ERR_UNSUPPORTED; + + ratbag_profile_set_enabled(options->profile, true); + rc = ratbag_device_commit(options->device); + + return rc ? ERR_DEVICE : SUCCESS; +} + +static const struct ratbag_cmd cmd_profile_enable = { + .name = "enable", + .cmd = ratbag_cmd_profile_enable, + .flags = FLAG_NEED_DEVICE | FLAG_NEED_PROFILE, + .subcommands = { + NULL, + }, +}; + +static int +ratbag_cmd_profile_disable(const struct ratbag_cmd *cmd, + struct ratbag *ratbag, + struct ratbag_cmd_options *options, + int argc, char **argv) +{ + int rc; + + if (!ratbag_device_has_capability(options->device, + RATBAG_DEVICE_CAP_DISABLE_PROFILE)) + return ERR_UNSUPPORTED; + + ratbag_profile_set_enabled(options->profile, false); + rc = ratbag_device_commit(options->device); + + return rc ? ERR_DEVICE : SUCCESS; +} + +static const struct ratbag_cmd cmd_profile_disable = { + .name = "disable", + .cmd = ratbag_cmd_profile_disable, + .flags = FLAG_NEED_DEVICE | FLAG_NEED_PROFILE, + .subcommands = { + NULL, + }, +}; + static int ratbag_cmd_profile(const struct ratbag_cmd *cmd, struct ratbag *ratbag, @@ -1613,6 +1670,8 @@ static const struct ratbag_cmd cmd_profile = { .flags = FLAG_NEED_DEVICE, .subcommands = { &cmd_profile_active, + &cmd_profile_enable, + &cmd_profile_disable, &cmd_resolution, &cmd_button, NULL,