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 <[email protected]>
---
 include/libinput-properties.h |   3 +
 man/libinput.man              |  30 ++++++++
 src/xf86libinput.c            | 154 ++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 181 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 c1655a7..b75e204 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"
@@ -344,6 +357,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 470b1ff..6b0ed20 100644
--- a/src/xf86libinput.c
+++ b/src/xf86libinput.c
@@ -164,6 +164,9 @@ struct xf86libinput {
 
                float rotation_angle;
                struct bezier_control_point pressurecurve[4];
+               struct ratio {
+                       int x, y;
+               } area;
        } options;
 
        struct draglock draglock;
@@ -181,6 +184,10 @@ struct xf86libinput {
                int *values;
                size_t sz;
        } pressurecurve;
+
+       struct scale_factor {
+               double x, y;
+       } area_scale_factor;
 };
 
 enum event_handling {
@@ -415,6 +422,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);
@@ -1636,6 +1666,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)
@@ -1645,16 +1695,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);
 
@@ -2703,6 +2755,35 @@ out:
 }
 
 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 ((driver_data->capabilities & CAP_TABLET_TOOL) == 0)
+               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,
                           struct libinput_device *device)
@@ -2739,6 +2820,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*
@@ -3188,6 +3272,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;
@@ -3963,6 +4048,41 @@ 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 {
+               xf86libinput_set_area_ratio(driver_data, &area);
+       }
+
+       return Success;
+}
+
 static int
 LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
                  BOOL checkonly)
@@ -4017,6 +4137,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 ||
@@ -4870,6 +4992,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 ((driver_data->capabilities & CAP_TABLET_TOOL) == 0)
+               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;
@@ -4926,4 +5067,5 @@ LibinputInitProperty(DeviceIntPtr dev)
        LibinputInitDragLockProperty(dev, driver_data);
        LibinputInitHorizScrollProperty(dev, driver_data);
        LibinputInitPressureCurveProperty(dev, driver_data);
+       LibinputInitTabletAreaRatioProperty(dev, driver_data);
 }
-- 
2.9.3

_______________________________________________
[email protected]: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: https://lists.x.org/mailman/listinfo/xorg-devel

Reply via email to