Add tablet tool area ratio property

By default, the X server maps the tablet axes to the available screen area.
When a tablet is mapped to the screen but has a different aspect ratio than
the screen, input data is skewed. Expose an area ratio property to map the
a subsection of the available tablet area into the desired ratio.

Differences to the wacom driver: there the x/y min/max values must be
specified manually and in device coordinates. For this driver we merely
provide the area ratio (e.g. 4:3) and let the driver work out the rest.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Jason Gerecke <jason.gerecke@wacom.com>
This commit is contained in:
Peter Hutterer
2016-10-28 11:20:22 +10:00
parent 5d04707381
commit 974ab6b62b
3 changed files with 221 additions and 6 deletions

View File

@@ -167,6 +167,9 @@ struct xf86libinput {
float rotation_angle;
struct bezier_control_point pressurecurve[4];
struct ratio {
int x, y;
} area;
} options;
struct draglock draglock;
@@ -184,6 +187,10 @@ struct xf86libinput {
int *values;
size_t sz;
} pressurecurve;
struct scale_factor {
double x, y;
} area_scale_factor;
};
enum event_handling {
@@ -418,6 +425,35 @@ xf86libinput_set_pressurecurve(struct xf86libinput *driver_data,
driver_data->pressurecurve.sz);
}
static inline void
xf86libinput_set_area_ratio(struct xf86libinput *driver_data,
const struct ratio *ratio)
{
double f;
double w, h;
if (libinput_device_get_size(driver_data->shared_device->device, &w, &h) != 0)
return;
driver_data->options.area = *ratio;
if (ratio->y == 0) {
driver_data->area_scale_factor.x = 1.0;
driver_data->area_scale_factor.y = 1.0;
return;
}
f = 1.0 * (ratio->x * h)/(ratio->y * w);
if (f <= 1.0) {
driver_data->area_scale_factor.x = 1.0/f;
driver_data->area_scale_factor.y = 1.0;
} else {
driver_data->area_scale_factor.x = 1.0;
driver_data->area_scale_factor.y = f;
}
}
static int
LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
BOOL checkonly);
@@ -1717,6 +1753,26 @@ xf86libinput_handle_tablet_button(InputInfoPtr pInfo,
return EVENT_HANDLED;
}
static void
xf86libinput_apply_area(InputInfoPtr pInfo, double *x, double *y)
{
struct xf86libinput *driver_data = pInfo->private;
const struct scale_factor *f = &driver_data->area_scale_factor;
double sx, sy;
if (driver_data->options.area.x == 0)
return;
/* In left-handed mode, libinput already gives us transformed
* coordinates, so we can clip the same way. */
sx = min(*x * f->x, TABLET_AXIS_MAX);
sy = min(*y * f->y, TABLET_AXIS_MAX);
*x = sx;
*y = sy;
}
static enum event_handling
xf86libinput_handle_tablet_axis(InputInfoPtr pInfo,
struct libinput_event_tablet_tool *event)
@@ -1726,16 +1782,18 @@ xf86libinput_handle_tablet_axis(InputInfoPtr pInfo,
ValuatorMask *mask = driver_data->valuators;
struct libinput_tablet_tool *tool;
double value;
double x, y;
if (xf86libinput_tool_queue_event(event))
return EVENT_QUEUED;
value = libinput_event_tablet_tool_get_x_transformed(event,
TABLET_AXIS_MAX);
valuator_mask_set_double(mask, 0, value);
value = libinput_event_tablet_tool_get_y_transformed(event,
TABLET_AXIS_MAX);
valuator_mask_set_double(mask, 1, value);
x = libinput_event_tablet_tool_get_x_transformed(event,
TABLET_AXIS_MAX);
y = libinput_event_tablet_tool_get_y_transformed(event,
TABLET_AXIS_MAX);
xf86libinput_apply_area(pInfo, &x, &y);
valuator_mask_set_double(mask, 0, x);
valuator_mask_set_double(mask, 1, y);
tool = libinput_event_tablet_tool_get_tool(event);
@@ -2786,6 +2844,48 @@ out:
xf86libinput_set_pressurecurve(driver_data, controls);
}
static inline bool
want_area_handling(struct xf86libinput *driver_data)
{
struct libinput_device *device = driver_data->shared_device->device;
if ((driver_data->capabilities & CAP_TABLET_TOOL) == 0)
return false;
/* If we have a calibration matrix, it's a built-in tablet and we
* don't need to set the area ratio on those */
return !libinput_device_config_calibration_has_matrix(device);
}
static void
xf86libinput_parse_tablet_area_option(InputInfoPtr pInfo,
struct xf86libinput *driver_data,
struct ratio *area_out)
{
char *str;
int rc;
struct ratio area;
if (!want_area_handling(driver_data))
return;
str = xf86SetStrOption(pInfo->options,
"TabletToolAreaRatio",
NULL);
if (!str || strcmp(str, "default") == 0)
goto out;
rc = sscanf(str, "%d:%d", &area.x, &area.y);
if (rc != 2 || area.x <= 0 || area.y <= 0) {
xf86IDrvMsg(pInfo, X_ERROR, "Invalid tablet tool area ratio: %s\n", str);
} else {
*area_out = area;
}
out:
free(str);
}
static void
xf86libinput_parse_options(InputInfoPtr pInfo,
struct xf86libinput *driver_data,
@@ -2823,6 +2923,9 @@ xf86libinput_parse_options(InputInfoPtr pInfo,
xf86libinput_parse_pressurecurve_option(pInfo,
driver_data,
options->pressurecurve);
xf86libinput_parse_tablet_area_option(pInfo,
driver_data,
&options->area);
}
static const char*
@@ -3282,6 +3385,7 @@ static Atom prop_rotation_angle_default;
static Atom prop_draglock;
static Atom prop_horiz_scroll;
static Atom prop_pressurecurve;
static Atom prop_area_ratio;
/* general properties */
static Atom prop_float;
@@ -4078,6 +4182,62 @@ LibinputSetPropertyPressureCurve(DeviceIntPtr dev,
return Success;
}
static inline int
LibinputSetPropertyAreaRatio(DeviceIntPtr dev,
Atom atom,
XIPropertyValuePtr val,
BOOL checkonly)
{
InputInfoPtr pInfo = dev->public.devicePrivate;
struct xf86libinput *driver_data = pInfo->private;
uint32_t *vals;
struct ratio area = { 0, 0 };
if (val->format != 32 || val->size != 2 || val->type != XA_CARDINAL)
return BadMatch;
vals = val->data;
area.x = vals[0];
area.y = vals[1];
if (checkonly) {
if (area.x < 0 || area.y < 0)
return BadValue;
if ((area.x != 0 && area.y == 0) ||
(area.x == 0 && area.y != 0))
return BadValue;
if (!xf86libinput_check_device (dev, atom))
return BadMatch;
} else {
struct xf86libinput *other;
xf86libinput_set_area_ratio(driver_data, &area);
xorg_list_for_each_entry(other,
&driver_data->shared_device->device_list,
shared_device_link) {
DeviceIntPtr other_device = other->pInfo->dev;
if (other->options.area.x == area.x &&
other->options.area.y == area.y)
continue;
XIChangeDeviceProperty(other_device,
atom,
val->type,
val->format,
PropModeReplace,
val->size,
val->data,
TRUE);
}
}
return Success;
}
static int
LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
BOOL checkonly)
@@ -4132,6 +4292,8 @@ LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
rc = LibinputSetPropertyRotationAngle(dev, atom, val, checkonly);
else if (atom == prop_pressurecurve)
rc = LibinputSetPropertyPressureCurve(dev, atom, val, checkonly);
else if (atom == prop_area_ratio)
rc = LibinputSetPropertyAreaRatio(dev, atom, val, checkonly);
else if (atom == prop_device || atom == prop_product_id ||
atom == prop_tap_default ||
atom == prop_tap_drag_default ||
@@ -4985,6 +5147,25 @@ LibinputInitPressureCurveProperty(DeviceIntPtr dev,
8, data);
}
static void
LibinputInitTabletAreaRatioProperty(DeviceIntPtr dev,
struct xf86libinput *driver_data)
{
const struct ratio *ratio = &driver_data->options.area;
uint32_t data[2];
if (!want_area_handling(driver_data))
return;
data[0] = ratio->x;
data[1] = ratio->y;
prop_area_ratio = LibinputMakeProperty(dev,
LIBINPUT_PROP_TABLET_TOOL_AREA_RATIO,
XA_CARDINAL, 32,
2, data);
}
static void
LibinputInitProperty(DeviceIntPtr dev)
{
@@ -5042,4 +5223,5 @@ LibinputInitProperty(DeviceIntPtr dev)
LibinputInitDragLockProperty(dev, driver_data);
LibinputInitHorizScrollProperty(dev, driver_data);
LibinputInitPressureCurveProperty(dev, driver_data);
LibinputInitTabletAreaRatioProperty(dev, driver_data);
}