From ac30a7ae660776f1423e94dd73da649e50376ff4 Mon Sep 17 00:00:00 2001 From: Mika Pi Date: Sun, 1 Dec 2024 19:48:28 -0800 Subject: [PATCH 1/2] Add DeJitter functionality to Wacom driver and update documentation - Implement de-jitter logic to suppress small, erratic movements during click based on defined thresholds. - Introduce three new properties for de-jittering: - `DeJitterEnable`: Enables or disables de-jittering (default: off). - `DeJitterThreshold`: Sets the spatial threshold for movement suppression (default: 150, range: 0-1000). - `DeJitterTimeThreshold`: Sets the time threshold for movement suppression (default: 100ms, range: 0-10000ms). - Update the man page to include detailed documentation for the new properties. --- include/wacom-properties.h | 9 ++++ man/xsetwacom.man | 15 ++++++ src/wcmCommon.c | 97 +++++++++++++++++++++++++++-------- src/x11/xf86WacomProperties.c | 52 ++++++++++++++++++- src/xf86WacomDefs.h | 12 +++++ tools/xsetwacom.c | 25 +++++++++ 6 files changed, 187 insertions(+), 23 deletions(-) diff --git a/include/wacom-properties.h b/include/wacom-properties.h index 0796ab03..c50a9778 100644 --- a/include/wacom-properties.h +++ b/include/wacom-properties.h @@ -124,4 +124,13 @@ #define WACOM_PROP_XI_TYPE_PAD "PAD" #define WACOM_PROP_XI_TYPE_TOUCH "TOUCH" +/* BOOL, 1 value */ +#define WACOM_PROP_DEJITTER_ENABLED "Wacom DeJitter Enabled" + +/* 32 bit, 1 value */ +#define WACOM_PROP_DEJITTER_THRESHOLD "Wacom DeJitter Threshold" + +/* 32 bit, 1 value */ +#define WACOM_PROP_DEJITTER_TIME_THRESHOLD "Wacom DeJitter Time Threshold" + #endif diff --git a/man/xsetwacom.man b/man/xsetwacom.man index 968d4797..ecb5044b 100644 --- a/man/xsetwacom.man +++ b/man/xsetwacom.man @@ -97,6 +97,21 @@ device's native orientation, regardless of the actual rotation currently applied. Input outside of these coordinates will be clipped to the edges of the area defined. Default: 0 0 x2 y2; with x2 and y2 tablet specific. .TP +\fBDeJitterEnable\fR on|off +Enable or disable the de-jittering feature. When enabled, the device will +attempt to suppress small, erratic movements based on the thresholds defined +below. Default: off. +.TP +\fBDeJitterThreshold\fR distance +Set the spatial threshold for de-jittering in device units. Movements below +this distance will be suppressed if de-jittering is enabled. Default: 150, +range: 0 to 1000. +.TP +\fBDeJitterTimeThreshold\fR milliseconds +Set the time threshold for de-jittering in milliseconds. Movements that occur +within this time and below the spatial threshold will be suppressed. Default: +100 ms, range: 0 to 10000 ms. +.TP \fBButton\fR button-number [mapping] Set a mapping for the specified button-number. Mappings take the form of either a single numeric button or an 'action' to be performed. If no mapping diff --git a/src/wcmCommon.c b/src/wcmCommon.c index b39a43a2..17872bca 100644 --- a/src/wcmCommon.c +++ b/src/wcmCommon.c @@ -870,7 +870,59 @@ void wcmSendEvents(WacomDevicePtr priv, const WacomDeviceState* ds) wcmAxisSet(&axes, WACOM_AXIS_PRESSURE, ds->pressure); } - if (type == PAD_ID) + bool supressing = false; + if (priv->wcmDejitterEnabled) + { + CARD32 now = GetTimeInMillis(); + const int Thresh = priv->wcmDejitterThreshold == 0 ? 150 : priv->wcmDejitterThreshold; + const int TimeThresh = priv->wcmDejitterTimeThreshold == 0 ? 100 : priv->wcmDejitterTimeThreshold; + if (ds->buttons > 0) + { + if (priv->dejitterState.is_suppresed) + { + int dx = priv->dejitterState.x - x; + int dy = priv->dejitterState.y - y; + if (now < priv->dejitterState.suppress_start + TimeThresh && + dx * dx + dy * dy < Thresh * Thresh) + { + DBG(10, priv, "suppressing\n"); + x = priv->dejitterState.x; + y = priv->dejitterState.y; + wcmAxisSet(&axes, WACOM_AXIS_X, x); + wcmAxisSet(&axes, WACOM_AXIS_Y, y); + supressing = true; + } + } + else + { + DBG(10, priv, "start suppressing\n"); + priv->dejitterState.is_suppresed = true; + priv->dejitterState.x = x; + priv->dejitterState.y = y; + priv->dejitterState.suppress_start = now; + } + } + else + { + if (priv->dejitterState.is_suppresed) + { + DBG(10, priv, "stop suppressing\n"); + priv->dejitterState.is_suppresed = false; + int dx = priv->dejitterState.x - x; + int dy = priv->dejitterState.y - y; + if (now < priv->dejitterState.suppress_start + TimeThresh && + dx * dx + dy * dy < Thresh * Thresh) + { + x = priv->dejitterState.x; + y = priv->dejitterState.y; + wcmAxisSet(&axes, WACOM_AXIS_X, x); + wcmAxisSet(&axes, WACOM_AXIS_Y, y); + } + } + } + } + + if (type == PAD_ID) { wcmAxisSet(&axes, WACOM_AXIS_STRIP_X, ds->stripx); wcmAxisSet(&axes, WACOM_AXIS_STRIP_Y, ds->stripy); @@ -915,28 +967,31 @@ void wcmSendEvents(WacomDevicePtr priv, const WacomDeviceState* ds) ds->proximity, dump, id, serial, is_button ? "true" : "false", ds->buttons); - /* when entering prox, replace the zeroed-out oldState with a copy of - * the current state to prevent jumps. reset the prox and button state - * to zero to properly detect changes. - */ - if(!priv->oldState.proximity) + if (!supressing) { - int old_key_state = priv->oldState.keys; - - wcmUpdateOldState(priv, ds, x, y); - priv->oldState.proximity = 0; - priv->oldState.buttons = 0; - - /* keys can happen without proximity */ - priv->oldState.keys = old_key_state; - } - - if (type == PAD_ID) - wcmSendPadEvents(priv, ds, &axes); - else { - /* don't move the cursor if in gesture mode (except drag mode) */ - if ((type != TOUCH_ID) || wcmTouchNeedSendEvents(priv->common)) + /* when entering prox, replace the zeroed-out oldState with a copy of + * the current state to prevent jumps. reset the prox and button state + * to zero to properly detect changes. + */ + if (!priv->oldState.proximity) + { + int old_key_state = priv->oldState.keys; + + wcmUpdateOldState(priv, ds, x, y); + priv->oldState.proximity = 0; + priv->oldState.buttons = 0; + + /* keys can happen without proximity */ + priv->oldState.keys = old_key_state; + } + + if (type == PAD_ID) + wcmSendPadEvents(priv, ds, &axes); + else { + /* don't move the cursor if in gesture mode (except drag mode) */ + if ((type != TOUCH_ID) || wcmTouchNeedSendEvents(priv->common)) wcmSendNonPadEvents(priv, ds, &axes); + } } if (ds->proximity) diff --git a/src/x11/xf86WacomProperties.c b/src/x11/xf86WacomProperties.c index a27ce1f7..840b83e3 100644 --- a/src/x11/xf86WacomProperties.c +++ b/src/x11/xf86WacomProperties.c @@ -65,6 +65,9 @@ static Atom prop_panscroll_threshold; #ifdef DEBUG static Atom prop_debuglevels; #endif +static Atom prop_dejitter_enabled; +static Atom prop_dejitter_threshold; +static Atom prop_dejitter_time_threshold; /** * Calculate a user-visible pressure level from a driver-internal pressure @@ -299,6 +302,15 @@ void InitWcmDeviceProperties(WacomDevicePtr priv) prop_debuglevels = InitWcmAtom(pInfo->dev, WACOM_PROP_DEBUGLEVELS, XA_INTEGER, 8, 2, values); #endif + values[0] = priv->wcmDejitterEnabled; + prop_dejitter_enabled = InitWcmAtom(pInfo->dev, WACOM_PROP_DEJITTER_ENABLED, XA_INTEGER, 8, 1, values); + + values[0] = priv->wcmDejitterThreshold; + prop_dejitter_threshold = InitWcmAtom(pInfo->dev, WACOM_PROP_DEJITTER_THRESHOLD, XA_INTEGER, 32, 1, values); + + values[0] = priv->wcmDejitterTimeThreshold; + prop_dejitter_time_threshold = InitWcmAtom(pInfo->dev, WACOM_PROP_DEJITTER_TIME_THRESHOLD, XA_INTEGER, 32, 1, values); + XIRegisterPropertyHandler(pInfo->dev, wcmSetProperty, wcmGetProperty, wcmDeleteProperty); } @@ -949,7 +961,43 @@ static int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr pr if (!checkonly) common->wcmPanscrollThreshold = values[0]; - } else + } else if (property == prop_dejitter_enabled) + { + CARD8 *values = (CARD8*)prop->data; + + if (prop->size != 1 || prop->format != 8) + return BadValue; + + if ((values[0] != 0) && (values[0] != 1)) + return BadValue; + + if (!checkonly && priv->wcmDejitterEnabled != values[0]) + priv->wcmDejitterEnabled = values[0]; + } else if (property == prop_dejitter_threshold) + { + INT32 *values = (INT32*)prop->data; + + if (prop->size != 1 || prop->format != 32) + return BadValue; + + if (values[0] > 1000) + return BadValue; + + if (!checkonly && priv->wcmDejitterThreshold != values[0]) + priv->wcmDejitterThreshold = values[0]; + } else if (property == prop_dejitter_time_threshold) + { + INT32 *values = (INT32*)prop->data; + + if (prop->size != 1 || prop->format != 32) + return BadValue; + + if (values[0] > 10000) + return BadValue; + + if (!checkonly && priv->wcmDejitterTimeThreshold != values[0]) + priv->wcmDejitterTimeThreshold = values[0]; + } else { Atom *handler = NULL; WacomAction *action = NULL; @@ -1021,7 +1069,7 @@ static int wcmGetProperty (DeviceIntPtr dev, Atom property) return XIChangeDeviceProperty(dev, property, XA_ATOM, 32, PropModeReplace, ARRAY_SIZE(priv->wheel_action_props), priv->wheel_action_props, FALSE); - } + } return Success; } diff --git a/src/xf86WacomDefs.h b/src/xf86WacomDefs.h index 886bf77e..bcad1e4a 100644 --- a/src/xf86WacomDefs.h +++ b/src/xf86WacomDefs.h @@ -210,6 +210,14 @@ struct _WacomDeviceState unsigned int keys; /* bitmask for IDX_KEY_CONTROLPANEL, etc. */ }; +struct _WacomDejitterState +{ + int x; + int y; + bool is_suppresed; + CARD32 suppress_start; +}; + static const struct _WacomDeviceState OUTPROX_STATE = { .abswheel = INT_MAX, .abswheel2 = INT_MAX @@ -302,6 +310,10 @@ struct _WacomDeviceRec WacomTimerPtr serial_timer; /* timer used for serial number property update */ WacomTimerPtr tap_timer; /* timer used for tap timing */ WacomTimerPtr touch_timer; /* timer used for touch switch property update */ + struct _WacomDejitterState dejitterState; + int wcmDejitterEnabled; + int wcmDejitterThreshold; + int wcmDejitterTimeThreshold; }; #define MAX_SAMPLES 20 diff --git a/tools/xsetwacom.c b/tools/xsetwacom.c index 9394b960..85e527b8 100644 --- a/tools/xsetwacom.c +++ b/tools/xsetwacom.c @@ -490,6 +490,31 @@ static param_t parameters[] = .arg_count = 1, .prop_flags = PROP_FLAG_WRITEONLY | PROP_FLAG_OUTPUT, }, + { + .name = "DeJitterEnable", + .desc = "Enable or disable de-jittering (default is off).", + .prop_name = WACOM_PROP_DEJITTER_ENABLED, + .prop_format = 8, + .prop_offset = 0, + .arg_count = 1, + .prop_flags = PROP_FLAG_BOOLEAN, + }, + { + .name = "DeJitterThreshold", + .desc = "Set the spatial threshold for de-jittering (default is 150).", + .prop_name = WACOM_PROP_DEJITTER_THRESHOLD, + .prop_format = 32, + .prop_offset = 0, + .arg_count = 1, + }, + { + .name = "DeJitterTimeThreshold", + .desc = "Set the time threshold (in ms) for de-jittering (default is 100).", + .prop_name = WACOM_PROP_DEJITTER_TIME_THRESHOLD, + .prop_format = 32, + .prop_offset = 0, + .arg_count = 1, + }, { .name = "all", .desc = "Get value for all parameters. ", From 6160a5854d826264f67f2815bcc136eda7d5ad4f Mon Sep 17 00:00:00 2001 From: Mika Pi Date: Sun, 1 Dec 2024 19:59:49 -0800 Subject: [PATCH 2/2] use tabs for indentation --- src/wcmCommon.c | 4 ++-- src/x11/xf86WacomProperties.c | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/wcmCommon.c b/src/wcmCommon.c index 0cc8122e..1d4dad9f 100644 --- a/src/wcmCommon.c +++ b/src/wcmCommon.c @@ -932,7 +932,7 @@ void wcmSendEvents(WacomDevicePtr priv, const WacomDeviceState* ds) } } - if (type == PAD_ID) + if (type == PAD_ID) { wcmAxisSet(&axes, WACOM_AXIS_STRIP_X, ds->stripx); wcmAxisSet(&axes, WACOM_AXIS_STRIP_Y, ds->stripy); @@ -977,7 +977,7 @@ void wcmSendEvents(WacomDevicePtr priv, const WacomDeviceState* ds) ds->proximity, dump, id, serial, is_button ? "true" : "false", ds->buttons); - if (!supressing) + if (!supressing) { /* when entering prox, replace the zeroed-out oldState with a copy of * the current state to prevent jumps. reset the prox and button state diff --git a/src/x11/xf86WacomProperties.c b/src/x11/xf86WacomProperties.c index 97c10adc..8107e144 100644 --- a/src/x11/xf86WacomProperties.c +++ b/src/x11/xf86WacomProperties.c @@ -301,11 +301,11 @@ void InitWcmDeviceProperties(WacomDevicePtr priv) values[0] = priv->wcmDejitterEnabled; prop_dejitter_enabled = InitWcmAtom(pInfo->dev, WACOM_PROP_DEJITTER_ENABLED, XA_INTEGER, 8, 1, values); - values[0] = priv->wcmDejitterThreshold; - prop_dejitter_threshold = InitWcmAtom(pInfo->dev, WACOM_PROP_DEJITTER_THRESHOLD, XA_INTEGER, 32, 1, values); + values[0] = priv->wcmDejitterThreshold; + prop_dejitter_threshold = InitWcmAtom(pInfo->dev, WACOM_PROP_DEJITTER_THRESHOLD, XA_INTEGER, 32, 1, values); - values[0] = priv->wcmDejitterTimeThreshold; - prop_dejitter_time_threshold = InitWcmAtom(pInfo->dev, WACOM_PROP_DEJITTER_TIME_THRESHOLD, XA_INTEGER, 32, 1, values); + values[0] = priv->wcmDejitterTimeThreshold; + prop_dejitter_time_threshold = InitWcmAtom(pInfo->dev, WACOM_PROP_DEJITTER_TIME_THRESHOLD, XA_INTEGER, 32, 1, values); XIRegisterPropertyHandler(pInfo->dev, wcmSetProperty, wcmGetProperty, wcmDeleteProperty); } @@ -993,7 +993,7 @@ static int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr pr if (!checkonly && priv->wcmDejitterTimeThreshold != values[0]) priv->wcmDejitterTimeThreshold = values[0]; - } else + } else { Atom *handler = NULL; WacomAction *action = NULL; @@ -1065,7 +1065,7 @@ static int wcmGetProperty (DeviceIntPtr dev, Atom property) return XIChangeDeviceProperty(dev, property, XA_ATOM, 32, PropModeReplace, ARRAY_SIZE(priv->wheel_action_props), priv->wheel_action_props, FALSE); - } + } return Success; }