Commit 974ab6b6 authored by Peter Hutterer's avatar Peter Hutterer

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's avatarPeter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Jason Gerecke's avatarJason Gerecke <jason.gerecke@wacom.com>
parent 5d047073
......@@ -190,4 +190,7 @@
*/
#define LIBINPUT_PROP_TABLET_TOOL_PRESSURECURVE "libinput Tablet Tool Pressurecurve"
/* Tablet tool area ratio: CARD32, 2 values, w and h */
#define LIBINPUT_PROP_TABLET_TOOL_AREA_RATIO "libinput Tablet Tool Area Ratio"
#endif /* _LIBINPUT_PROPERTIES_H_ */
......@@ -161,6 +161,14 @@ points. The respective x/y coordinate must be in the [0.0, 1.0] range. For
more information see section
.B TABLET STYLUS PRESSURE CURVE.
.TP 7
.BI "Option \*qTabletToolAreaRatio\*q \*q" "w:h" \*q
Sets the area ratio for a tablet tool. The area always starts at the
origin (0/0) and expands to the largest available area with the specified
aspect ratio. Events outside this area are cropped to the area. The special
value "default" is used for the default mapping (i.e. the device-native
mapping). For more information see section
.B TABLET TOOL AREA RATIO.
.TP 7
.BI "Option \*qTapping\*q \*q" bool \*q
Enables or disables tap-to-click behavior.
.TP 7
......@@ -261,6 +269,11 @@ enabled on this device.
.BI "libinput Tablet Tool Pressurecurve"
4 32-bit float values [0.0 to 1.0]. See section
.B TABLET TOOL PRESSURE CURVE
.TP7
.BI "libinput Tablet Tool Area Ratio"
2 32-bit values, corresponding to width and height. Special value 0, 0
resets to the default ratio. See section
.B TABLET TOOL AREA RATIO
for more information.
.TP 7
.BI "libinput Tapping Enabled"
......@@ -343,6 +356,23 @@ curve (softer) might be "0.0/0.0 0.0/0.05 0.95/1.0 1.0/1.0".
.TP
This feature is provided by this driver, not by libinput.
.SH TABLET TOOL AREA RATIO
By default, a tablet tool can access the whole sensor area and the tablet
area is mapped to the available screen area. For external tablets like
the Wacom Intuos series, the height:width ratio of the tablet may be
different to that of the monitor, causing the skew of input data.
.PP
To avoid this skew of input data, an area ratio may be set to match the
ratio of the screen device. For example, a ratio of 4:3 will reduce the
available area of the tablet to the largest available area with a ratio of
4:3. Events within this area will scale to the tablet's announced axis
range, the area ratio is thus transparent to the X server. Any events
outside this area will send events equal to the maximum value of that axis.
The area always starts at the device's origin in it's current rotation, i.e.
it takes left-handed-ness into account.
.TP
This feature is provided by this driver, not by libinput.
.SH AUTHORS
Peter Hutterer
.SH "SEE ALSO"
......
......@@ -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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment