On 01/03/2017 08:38 PM, Peter Hutterer wrote:
> 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]>
> ---
> 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;
> +}

This function sets my Spidey-sense tingling. I don't like how
ratio={0,0} is treated like any other valid value, causing the function
to store an NaN in area_scale_factor. I want to say that we should
explicitly handle the {0,0} case and set area_scale_factor to {1.0,1.0}
so that other functions don't have to tiptoe around bogus values that
may appear. However, area_scale_factor doesn't always have a valid value
anyway (its initial value is {0,0} until a ratio is set) so maybe it
doesn't really matter.

I'll leave the decision up to you.

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

I was tripped up by this in testing so I might as dig out my newbie hat
and ask... Why XA_CARDINAL? What's the difference between XA_CARDINAL
and XA_INTEGER? Are cardinals related to the CARD{n} types, or is that
just a naming coincidence? Is there any way to set cardinal properties
with xinput set-prop?

> +
> +     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);
>  }
> 

Everything but the NaN looks fine to me.

Reviewed-by: Jason Gerecke <[email protected]>

-- 
Jason
---
Now instead of four in the eights place /
you’ve got three, ‘Cause you added one /
(That is to say, eight) to the two, /
But you can’t take seven from three, /
So you look at the sixty-fours....


_______________________________________________
[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