autogen.sh | 11 configure.ac | 2 include/libinput-properties.h | 10 man/libinput.man | 60 ++++ src/Makefile.am | 5 src/bezier.c | 177 ++++++++++++ src/bezier.h | 69 ++++ src/xf86libinput.c | 616 ++++++++++++++++++++++++++++++++++++++---- test/Makefile.am | 5 test/test-bezier.c | 206 ++++++++++++++ 10 files changed, 1095 insertions(+), 66 deletions(-)
New commits: commit 153a7fc62fa87a2cc2516826b3eae16fa8cc861d Author: Peter Hutterer <peter.hutte...@who-t.net> Date: Thu Mar 9 15:58:39 2017 +1000 xf86-input-libinput 0.25.0 Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> diff --git a/configure.ac b/configure.ac index d6d7d34..268fda3 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ # Initialize Autoconf AC_PREREQ([2.60]) AC_INIT([xf86-input-libinput], - [0.24.0], + [0.25.0], [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg], [xf86-input-libinput]) AC_CONFIG_SRCDIR([Makefile.am]) commit 72fb6d304eec6eeeac6b42963c2729134d56de57 Author: Peter Hutterer <peter.hutte...@who-t.net> Date: Tue Feb 28 14:45:29 2017 +1000 test: fix a test failure on ppc64(le) and aarch64 Caused by different results in -O0 vs -O2. The resulting array differs only slightly but the initial sequence has one extra zero. That triggers our assert, no other compiler flag seem to be affecting this. Compiled with -O0: Breakpoint 1, test_nonzero_x_linear () at test-bezier.c:157 157 assert(bezier[x] > bezier[x-1]); (gdb) p bezier $6 = {0 <repeats 409 times>, 1, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 21, 22, Compiled with -O2: (gdb) p bezier $1 = {0 <repeats 410 times>, 1, 3, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, Printing of the temporary numbers in the decasteljau function shows that a few of them are off by one, e.g. 408.530612/0.836735 with O0, but 409.510204/0.836735 with O2 Note: these are not rounding errors caused by the code, the cast to int happens afterwards. Hack around this by allowing for one extra zero before we check that the rest of the curve is ascending again. https://bugs.freedesktop.org/show_bug.cgi?id=99992 Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> diff --git a/test/test-bezier.c b/test/test-bezier.c index 1b290a4..9a6e59e 100644 --- a/test/test-bezier.c +++ b/test/test-bezier.c @@ -153,6 +153,13 @@ test_nonzero_x_linear(void) assert(bezier[x] == 0); } while (++x < size * 0.2 - 1); + /* ppc64le, ppc64, aarch64 have different math results at -O2, + resulting in one extra zero at the beginning of the array. + some other numbers are different too but within the error + margin (#99992) */ + if (bezier[x] == 0) + x++; + do { assert(bezier[x] > bezier[x-1]); } while (++x < size * 0.8 - 1); commit aae2c8ad9a9f1712149c93d50284ddb5f37e4cbd Author: Peter Hutterer <peter.hutte...@who-t.net> Date: Fri Feb 24 12:56:41 2017 +1000 Open sysfs files directly instead of going through the server Only use-case here are pad mode LEDs that now live in /sys/class/leds. Asking the server to open them is pointless, the server only knows how to open Option "Device". And since the LEDs are in sysfs we should have access to them anyway, so no need for jumping through or hula-ing hoops. xf86CloseSerial() works as intended as it's a slim wrapper around close(), so we only have to worry about the open() path here. Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> diff --git a/src/xf86libinput.c b/src/xf86libinput.c index ef03d3e..888c8f2 100644 --- a/src/xf86libinput.c +++ b/src/xf86libinput.c @@ -2187,6 +2187,12 @@ open_restricted(const char *path, int flags, void *data) InputInfoPtr pInfo; int fd = -1; + /* Special handling for sysfs files (used for pad LEDs) */ + if (strneq(path, "/sys/", 5)) { + fd = open(path, flags); + return fd < 0 ? -errno : fd; + } + nt_list_for_each_entry(pInfo, xf86FirstLocalDevice(), next) { char *device = xf86CheckStrOption(pInfo->options, "Device", NULL); commit dafc296f2df587a1bb5feb37697c50608db4f246 Author: Peter Hutterer <peter.hutte...@who-t.net> Date: Fri Feb 24 12:34:10 2017 +1000 Add streq() macro, replace strcmp instances with it And why isn't this a thing in glibc yet Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> Reviewed-by: Eric Engestrom <eric.engest...@imgtec.com> diff --git a/src/xf86libinput.c b/src/xf86libinput.c index e6b9921..ef03d3e 100644 --- a/src/xf86libinput.c +++ b/src/xf86libinput.c @@ -65,6 +65,9 @@ #define TOUCH_MAX_SLOTS 15 #define XORG_KEYCODE_OFFSET 8 +#define streq(a, b) (strcmp(a, b) == 0) +#define strneq(a, b, n) (strncmp(a, b, n) == 0) + /* libinput does not provide axis information for absolute devices, instead it scales into the screen dimensions provided. So we set up the axes with @@ -259,7 +262,7 @@ xf86libinput_is_subdevice(InputInfoPtr pInfo) BOOL is_subdevice; source = xf86SetStrOption(pInfo->options, "_source", ""); - is_subdevice = strcmp(source, "_driver/libinput") == 0; + is_subdevice = streq(source, "_driver/libinput"); free(source); return is_subdevice; @@ -1213,7 +1216,7 @@ is_libinput_device(InputInfoPtr pInfo) BOOL rc; driver = xf86CheckStrOption(pInfo->options, "driver", ""); - rc = strcmp(driver, "libinput") == 0; + rc = streq(driver, "libinput"); free(driver); return rc; @@ -2187,7 +2190,7 @@ open_restricted(const char *path, int flags, void *data) nt_list_for_each_entry(pInfo, xf86FirstLocalDevice(), next) { char *device = xf86CheckStrOption(pInfo->options, "Device", NULL); - if (device != NULL && strcmp(path, device) == 0) { + if (device != NULL && streq(path, device)) { free(device); break; } @@ -2353,9 +2356,9 @@ xf86libinput_parse_tap_buttonmap_option(InputInfoPtr pInfo, "TappingButtonMap", NULL); if (str) { - if (strcmp(str, "lmr") == 0) + if (streq(str, "lmr")) map = LIBINPUT_CONFIG_TAP_MAP_LMR; - else if (strcmp(str, "lrm") == 0) + else if (streq(str, "lrm")) map = LIBINPUT_CONFIG_TAP_MAP_LRM; else xf86IDrvMsg(pInfo, X_ERROR, @@ -2468,11 +2471,11 @@ xf86libinput_parse_sendevents_option(InputInfoPtr pInfo, "SendEventsMode", NULL); if (modestr) { - if (strcmp(modestr, "enabled") == 0) + if (streq(modestr, "enabled")) mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; - else if (strcmp(modestr, "disabled") == 0) + else if (streq(modestr, "disabled")) mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; - else if (strcmp(modestr, "disabled-on-external-mouse") == 0) + else if (streq(modestr, "disabled-on-external-mouse")) mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; else xf86IDrvMsg(pInfo, X_ERROR, @@ -2866,7 +2869,7 @@ xf86libinput_parse_tablet_area_option(InputInfoPtr pInfo, str = xf86SetStrOption(pInfo->options, "TabletToolAreaRatio", NULL); - if (!str || strcmp(str, "default") == 0) + if (!str || streq(str, "default")) goto out; rc = sscanf(str, "%d:%d", &area.x, &area.y); commit 7c90f06d569b1b14d84075e7cea22bce06b925e6 Author: Peter Hutterer <peter.hutte...@who-t.net> Date: Fri Feb 24 12:27:37 2017 +1000 Update pad modes in a workproc, not during the input thread Updating the property directly causes us to send events from the input thread which has some "interesting" side effects like messing up the reply order or just crashing the server. Schedule a work proc instead and update it whenever the server is back in the main thread. Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> diff --git a/src/xf86libinput.c b/src/xf86libinput.c index 703d872..e6b9921 100644 --- a/src/xf86libinput.c +++ b/src/xf86libinput.c @@ -3386,17 +3386,43 @@ static Atom prop_float; static Atom prop_device; static Atom prop_product_id; -static inline void -update_mode_prop(InputInfoPtr pInfo, - struct libinput_event_tablet_pad *event) -{ - struct xf86libinput *driver_data = pInfo->private; +struct mode_prop_state { + int deviceid; + InputInfoPtr pInfo; + struct libinput_tablet_pad_mode_group *group; unsigned int mode; unsigned int idx; +}; + +static Bool +update_mode_prop_cb(ClientPtr client, pointer closure) +{ + struct mode_prop_state *state = closure; + InputInfoPtr pInfo = state->pInfo, tmp; + struct xf86libinput *driver_data = pInfo->private; + BOOL found = FALSE; XIPropertyValuePtr val; int rc; unsigned char groups[4] = {0}; + struct libinput_tablet_pad_mode_group *group = state->group; + unsigned int mode = state->mode; + unsigned int idx = state->idx; + + if (idx >= ARRAY_SIZE(groups)) + goto out; + + /* The device may have gotten removed before the WorkProc was + * scheduled. X reuses deviceids, but if the pointer value and + * device ID are what we had before, we're good */ + nt_list_for_each_entry(tmp, xf86FirstLocalDevice(), next) { + if (tmp->dev->id == state->deviceid && tmp == pInfo) { + found = TRUE; + break; + } + } + if (!found) + goto out; rc = XIGetDeviceProperty(pInfo->dev, prop_mode_groups, @@ -3404,18 +3430,12 @@ update_mode_prop(InputInfoPtr pInfo, if (rc != Success || val->format != 8 || val->size <= 0) - return; + goto out; memcpy(groups, (unsigned char*)val->data, val->size); - group = libinput_event_tablet_pad_get_mode_group(event); - mode = libinput_event_tablet_pad_get_mode(event); - idx = libinput_tablet_pad_mode_group_get_index(group); - if (idx >= ARRAY_SIZE(groups)) - return; - if (groups[idx] == mode) - return; + goto out; groups[idx] = mode; @@ -3428,8 +3448,36 @@ update_mode_prop(InputInfoPtr pInfo, groups, TRUE); driver_data->allow_mode_group_updates = false; - if (rc != Success) + +out: + libinput_tablet_pad_mode_group_unref(group); + free(state); + return TRUE; +} + +static inline void +update_mode_prop(InputInfoPtr pInfo, + struct libinput_event_tablet_pad *event) +{ + struct libinput_tablet_pad_mode_group *group; + struct mode_prop_state *state; + + state = calloc(1, sizeof(*state)); + if (!state) return; + + state->deviceid = pInfo->dev->id; + state->pInfo = pInfo; + + group = libinput_event_tablet_pad_get_mode_group(event); + + state->group = libinput_tablet_pad_mode_group_ref(group); + state->mode = libinput_event_tablet_pad_get_mode(event); + state->idx = libinput_tablet_pad_mode_group_get_index(group); + + /* Schedule a WorkProc so we don't update from within the input + thread */ + QueueWorkProc(update_mode_prop_cb, serverClient, state); } static inline BOOL commit 2eb5a2f0c08747df44eba6faff95cc9ce24b78ed Author: Peter Hutterer <peter.hutte...@who-t.net> Date: Thu Feb 9 16:16:34 2017 +1000 xf86-input-libinput 0.24.0 Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> diff --git a/configure.ac b/configure.ac index 7a1918b..d6d7d34 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ # Initialize Autoconf AC_PREREQ([2.60]) AC_INIT([xf86-input-libinput], - [0.23.0], + [0.24.0], [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg], [xf86-input-libinput]) AC_CONFIG_SRCDIR([Makefile.am]) commit 19ceef972e76bc491438198659748786d9457668 Author: Peter Hutterer <peter.hutte...@who-t.net> Date: Fri Jan 27 10:24:08 2017 +1000 Drop unnecessary function declaration Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> diff --git a/src/xf86libinput.c b/src/xf86libinput.c index 55cfc0c..703d872 100644 --- a/src/xf86libinput.c +++ b/src/xf86libinput.c @@ -2233,13 +2233,7 @@ const struct libinput_interface interface = { .close_restricted = close_restricted, }; -static void -xf86libinput_log_handler(struct libinput *libinput, - enum libinput_log_priority priority, - const char *format, - va_list args) - _X_ATTRIBUTE_PRINTF(3, 0); - +_X_ATTRIBUTE_PRINTF(3, 0) static void xf86libinput_log_handler(struct libinput *libinput, enum libinput_log_priority priority, commit 07f30ea049303739bf6006d23ac924971a19d778 Author: Mihail Konev <k....@ya.ru> Date: Thu Jan 26 14:00:21 2017 +1000 autogen: add default patch prefix Signed-off-by: Mihail Konev <k....@ya.ru> diff --git a/autogen.sh b/autogen.sh index 0006de8..421528e 100755 --- a/autogen.sh +++ b/autogen.sh @@ -9,6 +9,9 @@ cd "$srcdir" autoreconf -v --install || exit 1 cd "$ORIGDIR" || exit $? +git config --local --get format.subjectPrefix >/dev/null 2>&1 || + git config --local format.subjectPrefix "PATCH xf86-input-libinput" + if test -z "$NOCONFIGURE"; then exec "$srcdir"/configure "$@" fi commit 6187ed0450e68aaf727779ad61b50b0b70a1122e Author: Emil Velikov <emil.l.veli...@gmail.com> Date: Mon Mar 9 12:00:52 2015 +0000 autogen.sh: use quoted string variables Place quotes around the $srcdir, $ORIGDIR and $0 variables to prevent fall-outs, when they contain space. Signed-off-by: Emil Velikov <emil.l.veli...@gmail.com> Reviewed-by: Peter Hutterer <peter.hutte...@who-t.net> Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> diff --git a/autogen.sh b/autogen.sh index fd9c59a..0006de8 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,14 +1,14 @@ #! /bin/sh -srcdir=`dirname $0` +srcdir=`dirname "$0"` test -z "$srcdir" && srcdir=. ORIGDIR=`pwd` -cd $srcdir +cd "$srcdir" autoreconf -v --install || exit 1 -cd $ORIGDIR || exit $? +cd "$ORIGDIR" || exit $? if test -z "$NOCONFIGURE"; then - exec $srcdir/configure "$@" + exec "$srcdir"/configure "$@" fi commit 974ab6b62bd2af97e1556314df28fe9f3b816e54 Author: Peter Hutterer <peter.hutte...@who-t.net> Date: Fri Oct 28 11:20:22 2016 +1000 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.hutte...@who-t.net> Reviewed-by: Jason Gerecke <jason.gere...@wacom.com> 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..55cfc0c 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,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 || @@ -4986,6 +5148,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 +5223,5 @@ LibinputInitProperty(DeviceIntPtr dev) LibinputInitDragLockProperty(dev, driver_data); LibinputInitHorizScrollProperty(dev, driver_data); LibinputInitPressureCurveProperty(dev, driver_data); + LibinputInitTabletAreaRatioProperty(dev, driver_data); } commit 5d0470738125243c98f7a8cc40d62f53604a8051 Author: Peter Hutterer <peter.hutte...@who-t.net> Date: Mon Oct 24 14:41:51 2016 +1000 Implement stylus pressure curve support Takes a 4-point cubic bezier curve as input and maps the pressure coordinates to the values outlined by this curve. This is an extension of the current implementation in the xf86-input-wacom driver which only allows the two center control points to be modified. Over the years a few users have noted that the wacom driver's pressure curve makes it impossible to cap the pressure at a given value. Given our bezier implementation here, it's effectively a freebie to add configurability of the first and last control points. We do require all control points' x coordinates to be in ascending order. Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> diff --git a/include/libinput-properties.h b/include/libinput-properties.h index 8c6942d..f76500f 100644 --- a/include/libinput-properties.h +++ b/include/libinput-properties.h @@ -183,4 +183,11 @@ /* Device rotation: FLOAT, 1 value, 32 bit, read-only */ #define LIBINPUT_PROP_ROTATION_ANGLE_DEFAULT "libinput Rotation Angle Default" +/* Tablet tool pressure curve: float, 8 values, 32 bit + * Value range is [0.0, 1.0], the values specify the x/y of the four + * control points for a cubic bezier curve. + * Default value: 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0 + */ +#define LIBINPUT_PROP_TABLET_TOOL_PRESSURECURVE "libinput Tablet Tool Pressurecurve" + #endif /* _LIBINPUT_PROPERTIES_H_ */ diff --git a/man/libinput.man b/man/libinput.man index 36775e6..88a0428 100644 --- a/man/libinput.man +++ b/man/libinput.man @@ -155,6 +155,12 @@ default scroll option for this device is used. Sets the send events mode to disabled, enabled, or "disable when an external mouse is connected". .TP 7 +.BI "Option \*qTabletToolPressureCurve\*q \*q" "x0/y0 x1/y1 x2/y2 x3/y3" \*q +Set the pressure curve for a tablet stylus to the bezier formed by the four +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 \*qTapping\*q \*q" bool \*q Enables or disables tap-to-click behavior. .TP 7 @@ -252,6 +258,11 @@ on this device. "disabled-on-external-mouse". Indicates which send-event modes is currently enabled on this device. .TP 7 +.BI "libinput Tablet Tool Pressurecurve" +4 32-bit float values [0.0 to 1.0]. See section +.B TABLET TOOL PRESSURE CURVE +for more information. +.TP 7 .BI "libinput Tapping Enabled" 1 boolean value (8 bit, 0 or 1). 1 enables tapping .TP 7 @@ -313,6 +324,25 @@ and only the target button events are sent. .TP This feature is provided by this driver, not by libinput. +.SH TABLET TOOL PRESSURECURVE +The pressure curve affects how stylus pressure is reported. By default, the +hardware pressure is reported as-is. By setting a pressure curve, the feel +of the stylus can be adjusted to be more like e.g. a pencil or a brush. +.PP +The pressure curve is a cubic Bezier curve, drawn within a normalized range +of 0.0 to 1.0 between the four points provided. This normalized range is +applied to the tablet's pressure input so that the highest pressure maps to +1.0. The points must have increasing x coordinates, if x0 is larger than 0.0 +all pressure values lower than x0 are equivalent to y0. If x3 is less than +1.0, all pressure values higher than x3 are equivalent to y3. + +The input for a linear curve (default) is "0.0/0.0 0.0/0.0 1.0/1.0 1.0/1.0"; +a slightly +depressed curve (firmer) might be "0.0/0.0 0.05/0.0 1.0/0.95 1.0/1.0"; a slightly raised +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 AUTHORS Peter Hutterer .SH "SEE ALSO" diff --git a/src/Makefile.am b/src/Makefile.am index 8634b69..5dcb55e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -30,7 +30,7 @@ AM_CPPFLAGS =-I$(top_srcdir)/include $(LIBINPUT_CFLAGS) @DRIVER_NAME@_drv_la_LTLIBRARIES = @DRIVER_NAME@_drv.la @DRIVER_NAME@_drv_la_LDFLAGS = -module -avoid-version -@DRIVER_NAME@_drv_la_LIBADD = $(LIBINPUT_LIBS) libdraglock.la -lm +@DRIVER_NAME@_drv_la_LIBADD = $(LIBINPUT_LIBS) libdraglock.la libbezier.la -lm @DRIVER_NAME@_drv_ladir = @inputdir@ @DRIVER_NAME@_drv_la_SOURCES = xf86libinput.c diff --git a/src/bezier.c b/src/bezier.c index e571a3c..95af16a 100644 --- a/src/bezier.c +++ b/src/bezier.c @@ -31,6 +31,13 @@ #include "bezier.h" +const struct bezier_control_point bezier_defaults[4] = { + { 0.0, 0.0 }, + { 0.0, 0.0 }, + { 1.0, 1.0 }, + { 1.0, 1.0 }, +}; + struct point { int x, y; }; diff --git a/src/bezier.h b/src/bezier.h index c7f3b27..7406565 100644 --- a/src/bezier.h +++ b/src/bezier.h @@ -35,6 +35,8 @@ struct bezier_control_point { double x, y; }; +extern const struct bezier_control_point bezier_defaults[4]; + /** * Given four control points in the range [(0.0/0.0), (1.0/1.0)] * construct a Bézier curve. diff --git a/src/xf86libinput.c b/src/xf86libinput.c index d10f38d..d43f67f 100644 --- a/src/xf86libinput.c +++ b/src/xf86libinput.c @@ -42,6 +42,7 @@ #include <X11/Xatom.h> +#include "bezier.h" #include "draglock.h" #include "libinput-properties.h" @@ -165,6 +166,7 @@ struct xf86libinput { BOOL horiz_scrolling_enabled; float rotation_angle; + struct bezier_control_point pressurecurve[4]; } options; struct draglock draglock; @@ -175,6 +177,13 @@ struct xf86libinput { struct libinput_tablet_tool *tablet_tool; bool allow_mode_group_updates; + + /* Pre-calculated pressure curve. + In the 0...TABLET_AXIS_MAX range */ + struct { + int *values; + size_t sz; + } pressurecurve; }; enum event_handling { @@ -385,6 +394,30 @@ xf86libinput_shared_is_enabled(struct xf86libinput_device *shared_device) return shared_device->enabled_count > 0; } +static inline bool +xf86libinput_set_pressurecurve(struct xf86libinput *driver_data, + const struct bezier_control_point controls[4]) +{ + if (memcmp(controls, bezier_defaults, sizeof(bezier_defaults)) == 0) { + free(driver_data->pressurecurve.values); + driver_data->pressurecurve.values = NULL; + return true; + }