[PATCH v2 xf86-input-libinput] Add tablet tool area ratio property
Peter Hutterer
peter.hutterer at who-t.net
Wed Jan 4 04:38:12 UTC 2017
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 at who-t.net>
---
Changes to v1:
- link up the tools so the ratio applies to all tools
- only init the area property for non-builtin tablets, for the built-in ones
we don't use the area but the calibration matrix in case they're off
include/libinput-properties.h | 3 +
man/libinput.man | 30 +++++++
src/xf86libinput.c | 188 ++++++++++++++++++++++++++++++++++++++++--
3 files changed, 215 insertions(+), 6 deletions(-)
diff --git a/include/libinput-properties.h b/include/libinput-properties.h
index f76500f..a701316 100644
--- a/include/libinput-properties.h
+++ b/include/libinput-properties.h
@@ -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_ */
diff --git a/man/libinput.man b/man/libinput.man
index 88a0428..d717ff7 100644
--- a/man/libinput.man
+++ b/man/libinput.man
@@ -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"
diff --git a/src/xf86libinput.c b/src/xf86libinput.c
index d43f67f..1ff0622 100644
--- a/src/xf86libinput.c
+++ b/src/xf86libinput.c
@@ -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,29 @@ 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;
+
+ 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;
+ }
+
+ driver_data->options.area = *ratio;
+}
+
static int
LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
BOOL checkonly);
@@ -1717,6 +1747,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 +1776,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 +2838,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 +2917,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 +3379,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 +4176,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 +4286,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 ||
@@ -4986,6 +5142,25 @@ LibinputInitPressureCurveProperty(DeviceIntPtr dev,
}
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)
{
InputInfoPtr pInfo = dev->public.devicePrivate;
@@ -5042,4 +5217,5 @@ LibinputInitProperty(DeviceIntPtr dev)
LibinputInitDragLockProperty(dev, driver_data);
LibinputInitHorizScrollProperty(dev, driver_data);
LibinputInitPressureCurveProperty(dev, driver_data);
+ LibinputInitTabletAreaRatioProperty(dev, driver_data);
}
--
2.9.3
More information about the xorg-devel
mailing list