Implement set profile resolution dpi

Works only in Logitech with adjustable DPI for now (MX Master).

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
This commit is contained in:
Benjamin Tissoires
2015-08-21 12:43:21 -04:00
parent 64cfad665d
commit cf4a932d34
8 changed files with 195 additions and 2 deletions

View File

@@ -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,
};

View File

@@ -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,
};

View File

@@ -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 */
/* -------------------------------------------------------------------------- */

View File

@@ -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 */
/* -------------------------------------------------------------------------- */

View File

@@ -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;
};

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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,
};