diff --git a/src/driver-etekcity.c b/src/driver-etekcity.c index 32641f30..48e71b45 100644 --- a/src/driver-etekcity.c +++ b/src/driver-etekcity.c @@ -354,6 +354,12 @@ etekcity_write_button(struct ratbag_button *button) return 0; } +static int +etekcity_write_resolution_dpi(struct ratbag_profile *profile, int dpi) +{ + return -ENOTSUP; +} + static int etekcity_probe(struct ratbag_device *device, const struct ratbag_id id) { @@ -439,4 +445,5 @@ struct ratbag_driver etekcity_driver = { .has_capability = etekcity_has_capability, .read_button = etekcity_read_button, .write_button = etekcity_write_button, + .write_resolution_dpi = etekcity_write_resolution_dpi, }; diff --git a/src/driver-hidpp20.c b/src/driver-hidpp20.c index 2e201f26..4cedb46a 100644 --- a/src/driver-hidpp20.c +++ b/src/driver-hidpp20.c @@ -73,6 +73,18 @@ hidpp20drv_write_button(struct ratbag_button *button) static int hidpp20drv_has_capability(const struct ratbag_device *device, enum ratbag_capability cap) { + /* + * We need to force the non const, but we will not change anything, + * promise! + */ + struct hidpp20drv_data *drv_data = ratbag_get_drv_data((struct ratbag_device *)device); + + switch (cap) { + case RATBAG_CAP_SWITCHABLE_RESOLUTION: + return !!(drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201); + default: + return 0; + } return 0; } @@ -85,7 +97,7 @@ hidpp20drv_current_profile(struct ratbag_device *device) static int hidpp20drv_set_current_profile(struct ratbag_device *device, unsigned int index) { - return -1; + return -ENOTSUP; } static int @@ -136,6 +148,58 @@ hidpp20drv_read_resolution_dpi(struct ratbag_device *device) return 0; } +static int +hidpp20drv_write_resolution_dpi(struct ratbag_profile *profile, int dpi) +{ + struct ratbag_device *device = profile->device; + struct ratbag *ratbag = device->ratbag; + struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); + struct hidpp20_sensor *sensors, *sensor; + int rc, i; + + if (!(drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201)) + return -ENOTSUP; + + rc = hidpp20_adjustable_dpi_get_sensors(device, &sensors); + if (rc < 0) { + log_error(ratbag, + "Error while requesting resolution: %s (%d)\n", + strerror(-rc), rc); + return 0; + } else if (rc == 0) { + log_error(ratbag, "Error, no compatible sensors found.\n"); + return 0; + } + + /* just for clarity, we use the first available sensor only */ + sensor = &sensors[0]; + + /* validate that the sensor accepts the given DPI */ + rc = -EINVAL; + if (dpi < sensor->dpi_min || dpi > sensor->dpi_max) + goto out; + if (sensor->dpi_steps) { + for (i = sensor->dpi_min; i < dpi; i += sensor->dpi_steps) { + } + if (i != dpi) + goto out; + } else { + i = 0; + while (sensor->dpi_list[i]) { + if (sensor->dpi_list[i] == dpi) + break; + } + if (sensor->dpi_list[i] != dpi) + goto out; + } + + rc = hidpp20_adjustable_dpi_set_sensor_dpi(device, sensor, dpi); + +out: + free(sensors); + return rc; +} + static void hidpp20drv_read_profile(struct ratbag_profile *profile, unsigned int index) { @@ -334,4 +398,5 @@ struct ratbag_driver hidpp20_driver = { .has_capability = hidpp20drv_has_capability, .read_button = hidpp20drv_read_button, .write_button = hidpp20drv_write_button, + .write_resolution_dpi = hidpp20drv_write_resolution_dpi, }; diff --git a/src/hidpp20.c b/src/hidpp20.c index 4870a284..0e2500ac 100644 --- a/src/hidpp20.c +++ b/src/hidpp20.c @@ -578,6 +578,40 @@ err: return rc; } +int hidpp20_adjustable_dpi_set_sensor_dpi(struct ratbag_device *device, + struct hidpp20_sensor *sensor, uint16_t dpi) +{ + uint8_t feature_index, feature_type, feature_version; + int rc; + union hidpp20_message msg = { + .msg.report_id = REPORT_ID_LONG, + .msg.device_idx = 0xff, + .msg.address = CMD_ADJUSTABLE_DPI_SET_SENSOR_DPI, + .msg.parameters[0] = sensor->index, + .msg.parameters[1] = dpi >> 8, + .msg.parameters[2] = dpi & 0xff, + }; + + rc = hidpp_root_get_feature(device, + HIDPP_PAGE_ADJUSTABLE_DPI, + &feature_index, + &feature_type, + &feature_version); + if (rc) + return rc; + + msg.msg.sub_id = feature_index; + + rc = hidpp20_request_command(device, &msg); + if (rc) + return rc; + + if (hidpp20_get_unaligned_u16(&msg.msg.parameters[1]) != dpi) + return -EIO; + + return 0; +} + /* -------------------------------------------------------------------------- */ /* 0x1b04: Special keys and mouse buttons */ /* -------------------------------------------------------------------------- */ diff --git a/src/hidpp20.h b/src/hidpp20.h index cda0b40a..260a3ebd 100644 --- a/src/hidpp20.h +++ b/src/hidpp20.h @@ -130,6 +130,13 @@ struct hidpp20_sensor { int hidpp20_adjustable_dpi_get_sensors(struct ratbag_device *device, struct hidpp20_sensor **sensors_list); +/** + * set the current dpi of the provided sensor. sensor must have been + * allocated by hidpp20_adjustable_dpi_get_sensors() + */ +int hidpp20_adjustable_dpi_set_sensor_dpi(struct ratbag_device *device, + struct hidpp20_sensor *sensor, uint16_t dpi); + /* -------------------------------------------------------------------------- */ /* 0x1b04: Special keys and mouse buttons */ /* -------------------------------------------------------------------------- */ diff --git a/src/libratbag-private.h b/src/libratbag-private.h index 503b1526..af3ce74e 100644 --- a/src/libratbag-private.h +++ b/src/libratbag-private.h @@ -163,6 +163,13 @@ struct ratbag_driver { */ int (*write_button)(struct ratbag_button *button); + /** For the given profile, overwrite the current resolution + * of the sensor expressed in DPI, and commit it to the hardware. + * + * Mandatory if the driver exports RATBAG_CAP_SWITCHABLE_RESOLUTION. + */ + int (*write_resolution_dpi)(struct ratbag_profile *profile, int dpi); + /* private */ struct list link; }; diff --git a/src/libratbag.c b/src/libratbag.c index 85cacf0c..146df1ee 100644 --- a/src/libratbag.c +++ b/src/libratbag.c @@ -607,6 +607,17 @@ ratbag_profile_get_resolution_dpi(const struct ratbag_profile *profile) return profile->current_dpi; } +LIBRATBAG_EXPORT int +ratbag_profile_set_resolution_dpi(struct ratbag_profile *profile, int dpi) +{ + if (!ratbag_device_has_capability(profile->device, + RATBAG_CAP_SWITCHABLE_RESOLUTION)) + return -ENOTSUP; + + assert(profile->device->driver->write_resolution_dpi); + return profile->device->driver->write_resolution_dpi(profile, dpi); +} + LIBRATBAG_EXPORT int ratbag_profile_get_report_rate_hz(const struct ratbag_profile *profile) { diff --git a/src/libratbag.sym b/src/libratbag.sym index 69428bf6..88d08261 100644 --- a/src/libratbag.sym +++ b/src/libratbag.sym @@ -23,6 +23,7 @@ global: ratbag_profile_get_button_by_index; ratbag_profile_get_report_rate_hz; ratbag_profile_get_resolution_dpi; + ratbag_profile_set_resolution_dpi; ratbag_profile_ref; ratbag_profile_unref; ratbag_ref; diff --git a/tools/ratbag-command.c b/tools/ratbag-command.c index e56bcf49..23456246 100644 --- a/tools/ratbag-command.c +++ b/tools/ratbag-command.c @@ -488,10 +488,71 @@ static const struct ratbag_cmd cmd_list = { .help = "List the available devices", }; +static int +ratbag_cmd_switch_dpi(struct ratbag *ratbag, uint32_t flags, int argc, char **argv) +{ + const char *path; + struct ratbag_device *device; + struct ratbag_button *button_6, *button_7; + struct ratbag_profile *profile = NULL; + int rc = 1, commit = 0; + int dpi; + + if (argc != 2) { + usage(); + return 1; + } + + dpi = atoi(argv[0]); + path = argv[1]; + + device = ratbag_cmd_open_device(ratbag, path); + if (!device) { + error("Looks like '%s' is not supported\n", path); + return 1; + } + + if (!ratbag_device_has_capability(device, + RATBAG_CAP_SWITCHABLE_RESOLUTION)) { + error("Looks like '%s' has no switchable resolution\n", path); + goto out; + } + + profile = ratbag_device_get_active_profile(device); + if (!profile) { + error("Huh hoh, something bad happened, unable to retrieve the active profile\n"); + goto out; + } + + rc = ratbag_profile_set_resolution_dpi(profile, dpi); + if (!rc) + printf("Switched the current resolution profile of '%s' to %d dpi\n", + ratbag_device_get_name(device), + dpi); + else + error("can't seem to be able to change the dpi: %s (%d)\n", + strerror(-rc), + rc); + +out: + profile = ratbag_profile_unref(profile); + + device = ratbag_device_unref(device); + return rc; +} + +static const struct ratbag_cmd cmd_switch_dpi = { + .name = "switch-dpi", + .cmd = ratbag_cmd_switch_dpi, + .args = "N", + .help = "Switch the resolution of the mouse in the active profile", +}; + static const struct ratbag_cmd *ratbag_commands[] = { &cmd_info, - &cmd_switch_etekcity, &cmd_list, + &cmd_switch_etekcity, + &cmd_switch_dpi, &cmd_switch_profile, NULL, };