From 2eb806af57b499433fe8f21cd100cf4a6f881bf0 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 31 Jul 2024 15:37:19 -0700 Subject: [PATCH 1/3] Recognize the pad in more situations Allow dial and ring events to be recognized as coming from the pad when there are no other better sources of information (for example, BTN_TOOL_* events earlier in the event packet, an ABS_MISC with the information, or a previously-known tool type). This fixes an issue where the pad is not recognized (and events are not generated) if your first interaction is with a relative wheel rather than a button or touchring. The events types matched by this function have an ambiguous source on their own, but other earlier tests would be able to determine the type from other context. If all other context-sensitive tests fail to find a tool type, it is reasonably safe to assume we're looking at a pad. Signed-off-by: Jason Gerecke --- src/wcmUSB.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/wcmUSB.c b/src/wcmUSB.c index 4f40a3db..913e5923 100644 --- a/src/wcmUSB.c +++ b/src/wcmUSB.c @@ -1735,8 +1735,8 @@ static int refreshDeviceType(WacomDevicePtr priv, int fd) return 0; } -static int deriveDeviceTypeFromButtonEvent(WacomDevicePtr priv, - const struct input_event *event_ptr) +static Bool eventCouldBeFromPad(WacomDevicePtr priv, + const struct input_event *event_ptr) { WacomCommonPtr common = priv->common; wcmUSBData *usbdata = common->private; @@ -1752,18 +1752,33 @@ static int deriveDeviceTypeFromButtonEvent(WacomDevicePtr priv, case BTN_BACK: case BTN_EXTRA: case BTN_FORWARD: - return PAD_ID; + return TRUE; default: for (nkeys = 0; nkeys < usbdata->npadkeys; nkeys++) { if (event_ptr->code == usbdata->padkey_code[nkeys]) { - return PAD_ID; + return TRUE; } } break; } } - return 0; + if (event_ptr->type == EV_REL) { + switch (event_ptr->code) { + case REL_WHEEL: + case REL_WHEEL_HI_RES: + return TRUE; + } + } + if (event_ptr->type == EV_ABS) { + switch (event_ptr->code) { + case ABS_WHEEL: + case ABS_THROTTLE: + return TRUE; + } + } + + return FALSE; } /*** @@ -1800,7 +1815,8 @@ static int usbInitToolType(WacomDevicePtr priv, int fd, if (!device_type) /* expresskey pressed at startup or missing type */ for (i = 0; (i < nevents) && !device_type; ++i) - device_type = deriveDeviceTypeFromButtonEvent(priv, &event_ptr[i]); + if (eventCouldBeFromPad(priv, &event_ptr[i])) + device_type = PAD_ID; return device_type; } From 6795d8a7159337bbb74095a491fd24a9b079e0cc Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 30 Jul 2024 14:43:09 -0700 Subject: [PATCH 2/3] Swap the relwheel actions triggered and defaults for vertical scrolling Relative wheel actions are backwards from how the user thinks about them. If you want to get/set the action that occurs when scrolling up, you currently need to work with the REL_WHEEL_DOWN action. This is difficult to explain to users and is also makes the code more difficult to follow. This commit changes the behavior so that programming the REL_WHEEL_UP action actually affects what happens when you physically scroll the wheel up. The behavior is now: physical scroll wheel up -> positive delta from kernel -> REL_WHEEL_UP action -> X11 mouse button 4 -> window scrolls up. Note that this commit will break any xsetwacom scripts that users may have which change the relative wheel actions from their defaults. It would also affect control panels that rely on xsetwacom or configuring the driver directly. We don't expect this to cause significant problems in reality since Wacom has not produced tools with relative wheels in many years and so there are few scripts/control panels to break. Signed-off-by: Jason Gerecke --- src/wcmCommon.c | 17 +++++++++-------- src/wcmConfig.c | 4 ++-- src/wcmUSB.c | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/wcmCommon.c b/src/wcmCommon.c index 14ba53ac..7bd74853 100644 --- a/src/wcmCommon.c +++ b/src/wcmCommon.c @@ -513,17 +513,17 @@ static int getScrollDelta(int current, int old, int wrap, int flags) * the scrolling axis and the possible events that can be * sent. * - * @param delta Amount of change in the scrolling axis - * @param action_up Array index of action to send on scroll up - * @param action_dn Array index of action to send on scroll down - * @return Array index of action that should be performed, or -1 if none. + * @param delta Amount of change in the scrolling axis + * @param action_positive Array index of action to send on a positive delta + * @param action_negative Array index of action to send on negative delta + * @return Array index of action that should be performed, or -1 if none. */ -static int getWheelButton(int delta, int action_up, int action_dn) +static int getWheelButton(int delta, int action_positive, int action_negative) { if (delta > 0) - return action_up; + return action_positive; else if (delta < 0) - return action_dn; + return action_negative; else return -1; } @@ -571,7 +571,8 @@ static void sendWheelStripEvents(WacomDevicePtr priv, const WacomDeviceState* ds sendWheelStripEvent(priv, &priv->strip_actions[idx], ds, axes); } - /* emulate events for relative wheel */ + /* emulate events for relative wheel: + * positive delta = scroll up */ delta = getScrollDelta(ds->relwheel, 0, 0, 0); idx = getWheelButton(delta, WHEEL_REL_UP, WHEEL_REL_DN); if (idx >= 0 && (IsCursor(priv) || IsPad(priv)) && priv->oldState.proximity == ds->proximity) diff --git a/src/wcmConfig.c b/src/wcmConfig.c index 36a06d3d..5b6e84e2 100644 --- a/src/wcmConfig.c +++ b/src/wcmConfig.c @@ -70,8 +70,8 @@ WacomDevicePtr wcmAllocate(void *frontend, const char *name) priv->button_default[i] = (i < 3) ? i + 1 : i + 5; priv->nbuttons = WCM_MAX_BUTTONS; /* Default number of buttons */ - priv->wheel_default[WHEEL_REL_UP] = 5; - priv->wheel_default[WHEEL_REL_DN] = 4; + priv->wheel_default[WHEEL_REL_UP] = 4; /* scroll up */ + priv->wheel_default[WHEEL_REL_DN] = 5; /* scroll down */ /* wheel events are set to 0, but the pad overwrites this default * later in wcmParseOptions, when we have IsPad() available */ priv->wheel_default[WHEEL_ABS_UP] = 0; diff --git a/src/wcmUSB.c b/src/wcmUSB.c index 913e5923..2240a4a0 100644 --- a/src/wcmUSB.c +++ b/src/wcmUSB.c @@ -1935,7 +1935,7 @@ static void usbDispatchEvents(WacomDevicePtr priv) { switch (event->code) { case REL_WHEEL: - ds->relwheel = -event->value; + ds->relwheel = event->value; ds->time = wcmTimeInMillis(); common->wcmChannel[channel].dirty |= TRUE; break; From fc6b57f30fdbbec3b43a8283a9feb6181efcbfa7 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 28 May 2024 13:49:36 -0700 Subject: [PATCH 3/3] Implement support for devices with two relative wheels / dials Just like how tablets may have two absolute wheels, it also is possible for them to have two relative wheels. This commit extends the WACOM_PROP_WHEELBUTTONS property to reference two new actions: RelWheel2Up and RelWheel2Down. These actions will be triggered in response to horizontal scrolling events from the kernel (positive and negative REL_HWHEEL events, respecitively). The default action for these wheels is sending horizontal mouse scroll events (X11 mouse buttons 7 and 6, respectively). Signed-off-by: Jason Gerecke --- include/wacom-properties.h | 10 +++--- man/xsetwacom.man | 12 +++++++ src/wcmCommon.c | 24 +++++++++++--- src/wcmConfig.c | 4 ++- src/wcmUSB.c | 12 +++++++ src/x11/xf86WacomProperties.c | 4 +-- src/xf86WacomDefs.h | 9 +++-- tools/xsetwacom.c | 62 ++++++++++++++++++++++++++++++++++- 8 files changed, 120 insertions(+), 17 deletions(-) diff --git a/include/wacom-properties.h b/include/wacom-properties.h index 0796ab03..9cec2114 100644 --- a/include/wacom-properties.h +++ b/include/wacom-properties.h @@ -42,15 +42,13 @@ /* CARD32, 1 value */ #define WACOM_PROP_SERIAL_BIND "Wacom Serial ID binding" -/* 8 bit, 4 values, left up, left down, right up, right down - OR - Atom, 4 values , left up, left down, right up, right down +/* 8 bit OR Atom, 4 values: + left up, left down, right up, right down */ #define WACOM_PROP_STRIPBUTTONS "Wacom Strip Buttons" -/* 8 bit, 6 values, rel wheel up, rel wheel down, abs wheel up, abs wheel down, abs wheel 2 up, abs wheel 2 down - OR - Atom, 6 values , rel wheel up, rel wheel down, abs wheel up, abs wheel down, abs wheel 2 up, abs wheel 2 down +/* 8 bit OR Atom, 8 values: + rel wheel up, rel wheel down, abs wheel up, abs wheel down, abs wheel 2 up, abs wheel 2 down, rel wheel 2 up, rel wheel 2 down */ #define WACOM_PROP_WHEELBUTTONS "Wacom Wheel Buttons" diff --git a/man/xsetwacom.man b/man/xsetwacom.man index 46040145..fe6807b3 100644 --- a/man/xsetwacom.man +++ b/man/xsetwacom.man @@ -112,9 +112,21 @@ applied. .TQ \fBAbsWheel2Down\fR [mapping] .TQ +\fBDialCW\fR [mapping] +.TQ +\fBDialCCW\fR [mapping] +.TQ +\fBDial2CW\fR [mapping] +.TQ +\fBDial2CCW\fR [mapping] +.TQ \fBRelWheelUp\fR [mapping] .TQ \fBRelWheelDown\fR [mapping] +.TQ +\fBRelWheel2Up\fR [mapping] +.TQ +\fBRelWheel2Down\fR [mapping] Set a mapping for the specified control. Mappings take the form of either a single numeric button or an 'action' to be performed. If no mapping is provided, the default mapping is restored. If a control is mapped diff --git a/src/wcmCommon.c b/src/wcmCommon.c index 7bd74853..06669e8a 100644 --- a/src/wcmCommon.c +++ b/src/wcmCommon.c @@ -581,6 +581,15 @@ static void sendWheelStripEvents(WacomDevicePtr priv, const WacomDeviceState* ds sendWheelStripEvent(priv, &priv->wheel_actions[idx], ds, axes); } + /* emulate events for 2nd relative wheel */ + delta = getScrollDelta(ds->relwheel2, 0, 0, 0); + idx = getWheelButton(delta, WHEEL2_REL_UP, WHEEL2_REL_DN); + if (idx >= 0 && (IsCursor(priv) || IsPad(priv)) && priv->oldState.proximity == ds->proximity) + { + DBG(10, priv, "Relative wheel 2 scroll delta = %d\n", delta); + sendWheelStripEvent(priv, &priv->wheel_actions[idx], ds, axes); + } + /* emulate events for left touch ring */ delta = getScrollDelta(ds->abswheel, priv->oldState.abswheel, common->wcmMaxRing, AXIS_INVERT); idx = getWheelButton(delta, WHEEL_ABS_UP, WHEEL_ABS_DN); @@ -622,7 +631,7 @@ static void sendCommonEvents(WacomDevicePtr priv, const WacomDeviceState* ds, wcmSendButtons(priv, ds, buttons, axes); /* emulate wheel/strip events when defined */ - if ( ds->relwheel || (ds->abswheel != priv->oldState.abswheel) || (ds->abswheel2 != priv->oldState.abswheel2) || + if ( ds->relwheel || ds->relwheel2 || (ds->abswheel != priv->oldState.abswheel) || (ds->abswheel2 != priv->oldState.abswheel2) || ( (ds->stripx - priv->oldState.stripx) && ds->stripx && priv->oldState.stripx) || ((ds->stripy - priv->oldState.stripy) && ds->stripy && priv->oldState.stripy) ) sendWheelStripEvents(priv, ds, axes); @@ -711,7 +720,7 @@ wcmSendPadEvents(WacomDevicePtr priv, const WacomDeviceState* ds, const WacomAxi if (!priv->oldState.proximity && ds->proximity) wcmEmitProximity(priv, TRUE, axes); - if (axes->mask || ds->buttons || ds->relwheel || + if (axes->mask || ds->buttons || ds->relwheel || ds->relwheel2 || (ds->abswheel != priv->oldState.abswheel) || (ds->abswheel2 != priv->oldState.abswheel2)) { sendCommonEvents(priv, ds, axes); @@ -1001,6 +1010,7 @@ wcmCheckSuppress(WacomCommonPtr common, if (abs(dsOrig->abswheel - dsNew->abswheel) > suppress) goto out; if (abs(dsOrig->abswheel2 - dsNew->abswheel2) > suppress) goto out; if (dsNew->relwheel != 0) goto out; + if (dsNew->relwheel2 != 0) goto out; returnV = SUPPRESS_ALL; @@ -1137,7 +1147,7 @@ void wcmEvent(WacomCommonPtr common, unsigned int channel, DBG(10, common, "c=%u i=%d t=%d s=0x%x x=%d y=%d b=%u " - "p=%d rz=%d tx=%d ty=%d aw=%d aw2=%d rw=%d " + "p=%d rz=%d tx=%d ty=%d aw=%d aw2=%d rw=%d rw2=%d " "t=%d px=%d st=%u cs=%d \n", channel, ds.device_id, @@ -1145,7 +1155,7 @@ void wcmEvent(WacomCommonPtr common, unsigned int channel, ds.serial_num, ds.x, ds.y, ds.buttons, ds.pressure, ds.rotation, ds.tiltx, - ds.tilty, ds.abswheel, ds.abswheel2, ds.relwheel, ds.throttle, + ds.tilty, ds.abswheel, ds.abswheel2, ds.relwheel, ds.relwheel2, ds.throttle, ds.proximity, ds.sample, pChannel->nSamples); @@ -1995,6 +2005,12 @@ TEST_CASE(test_suppress) assert(rc == SUPPRESS_NONE); new.relwheel = 0; + /* any movement on relwheel2 counts */ + new.relwheel2 = 1; + rc = wcmCheckSuppress(&common, &old, &new); + assert(rc == SUPPRESS_NONE); + new.relwheel2 = 0; + /* x axis movement */ /* not enough movement */ diff --git a/src/wcmConfig.c b/src/wcmConfig.c index 5b6e84e2..c5492831 100644 --- a/src/wcmConfig.c +++ b/src/wcmConfig.c @@ -72,6 +72,8 @@ WacomDevicePtr wcmAllocate(void *frontend, const char *name) priv->nbuttons = WCM_MAX_BUTTONS; /* Default number of buttons */ priv->wheel_default[WHEEL_REL_UP] = 4; /* scroll up */ priv->wheel_default[WHEEL_REL_DN] = 5; /* scroll down */ + priv->wheel_default[WHEEL2_REL_UP] = 7; /* scroll right */ + priv->wheel_default[WHEEL2_REL_DN] = 6; /* scroll left */ /* wheel events are set to 0, but the pad overwrites this default * later in wcmParseOptions, when we have IsPad() available */ priv->wheel_default[WHEEL_ABS_UP] = 0; @@ -674,7 +676,7 @@ static void wcmInitActions(WacomDevicePtr priv) if (IsPad(priv) || IsCursor(priv)) { - for (i = 0; i < 6; i++) + for (i = 0; i < 8; i++) wcmResetWheelAction(priv, i); } } diff --git a/src/wcmUSB.c b/src/wcmUSB.c index 2240a4a0..a7949292 100644 --- a/src/wcmUSB.c +++ b/src/wcmUSB.c @@ -1766,7 +1766,9 @@ static Bool eventCouldBeFromPad(WacomDevicePtr priv, if (event_ptr->type == EV_REL) { switch (event_ptr->code) { case REL_WHEEL: + case REL_HWHEEL: case REL_WHEEL_HI_RES: + case REL_HWHEEL_HI_RES: return TRUE; } } @@ -1895,6 +1897,7 @@ static void usbDispatchEvents(WacomDevicePtr priv) /* all USB data operates from previous context except relative values*/ ds->relwheel = 0; + ds->relwheel2 = 0; ds->serial_num = private->wcmLastToolSerial; /* loop through all events in group */ @@ -1942,11 +1945,20 @@ static void usbDispatchEvents(WacomDevicePtr priv) case REL_WHEEL_HI_RES: /* unsupported */ break; + case REL_HWHEEL: + ds->relwheel2 = event->value; + ds->time = wcmTimeInMillis(); + common->wcmChannel[channel].dirty |= TRUE; + break; + case REL_HWHEEL_HI_RES: + /* unsupported */ + break; default: wcmLogSafe(priv, W_ERROR, "%s: rel event recv'd (%d)!\n", priv->name, event->code); + break; } } else if (event->type == EV_KEY) diff --git a/src/x11/xf86WacomProperties.c b/src/x11/xf86WacomProperties.c index 6401a3b5..66802b55 100644 --- a/src/x11/xf86WacomProperties.c +++ b/src/x11/xf86WacomProperties.c @@ -269,10 +269,10 @@ void InitWcmDeviceProperties(WacomDevicePtr priv) if (IsPad(priv) || IsCursor(priv)) { - for (i = 0; i < 6; i++) + for (i = 0; i < 8; i++) wcmInitWheelActionProp(priv, i); prop_wheel_buttons = InitWcmAtom(pInfo->dev, WACOM_PROP_WHEELBUTTONS, XA_ATOM, 32, - 6, (int*)priv->wheel_action_props); + 8, (int*)priv->wheel_action_props); } if (IsStylus(priv) || IsEraser(priv)) { diff --git a/src/xf86WacomDefs.h b/src/xf86WacomDefs.h index 1ee84abf..370ea81f 100644 --- a/src/xf86WacomDefs.h +++ b/src/xf86WacomDefs.h @@ -177,6 +177,8 @@ struct _WacomModel #define WHEEL_ABS_DN 3 #define WHEEL2_ABS_UP 4 #define WHEEL2_ABS_DN 5 +#define WHEEL2_REL_UP 6 +#define WHEEL2_REL_DN 7 #define STRIP_LEFT_UP 0 #define STRIP_LEFT_DN 1 #define STRIP_RIGHT_UP 2 @@ -207,6 +209,7 @@ struct _WacomDeviceState int abswheel; int abswheel2; int relwheel; + int relwheel2; int distance; int throttle; int proximity; @@ -272,13 +275,13 @@ struct _WacomDeviceRec */ int button_default[WCM_MAX_BUTTONS]; /* Default mappings set by ourselves (possibly overridden by xorg.conf) */ int strip_default[4]; - int wheel_default[6]; + int wheel_default[8]; WacomAction key_actions[WCM_MAX_BUTTONS]; /* Action codes to perform when the associated event occurs */ WacomAction strip_actions[4]; - WacomAction wheel_actions[6]; + WacomAction wheel_actions[8]; Atom btn_action_props[WCM_MAX_BUTTONS]; /* Action references so we can update the action codes when a client makes a change */ Atom strip_action_props[4]; - Atom wheel_action_props[6]; + Atom wheel_action_props[8]; int nbuttons; /* number of buttons for this subdevice */ int naxes; /* number of axes */ diff --git a/tools/xsetwacom.c b/tools/xsetwacom.c index a4a6d3a0..c8f7a21b 100644 --- a/tools/xsetwacom.c +++ b/tools/xsetwacom.c @@ -300,6 +300,16 @@ static param_t parameters[] = .set_func = map_actions, .get_func = get_map, }, + { + .name = "DialCCW", + .desc = "X11 event to which counter-clockwise motion of the dial should be mapped." , + .prop_name = WACOM_PROP_WHEELBUTTONS, + .prop_format = 8, + .prop_offset = 0, + .arg_count = 0, + .set_func = map_actions, + .get_func = get_map, + }, { .name = "RelWheelDown", .desc = "X11 event to which relative wheel down should be mapped. ", @@ -310,6 +320,16 @@ static param_t parameters[] = .set_func = map_actions, .get_func = get_map, }, + { + .name = "DialCW", + .desc = "X11 event to which clockwise motion of the dial should be mapped." , + .prop_name = WACOM_PROP_WHEELBUTTONS, + .prop_format = 8, + .prop_offset = 1, + .arg_count = 0, + .set_func = map_actions, + .get_func = get_map, + }, { .name = "AbsWheelUp", .desc = "X11 event to which absolute wheel up should be mapped. ", @@ -350,6 +370,46 @@ static param_t parameters[] = .set_func = map_actions, .get_func = get_map, }, + { + .name = "RelWheel2Up", + .desc = "X11 event to which 2nd relative wheel up should be mapped. ", + .prop_name = WACOM_PROP_WHEELBUTTONS, + .prop_format = 8, + .prop_offset = 6, + .arg_count = 0, + .set_func = map_actions, + .get_func = get_map, + }, + { + .name = "Dial2CW", + .desc = "X11 event to which clockwise motion of the 2nd dial should be mapped. ", + .prop_name = WACOM_PROP_WHEELBUTTONS, + .prop_format = 8, + .prop_offset = 6, + .arg_count = 0, + .set_func = map_actions, + .get_func = get_map, + }, + { + .name = "RelWheel2Down", + .desc = "X11 event to which 2nd relative wheel down should be mapped. ", + .prop_name = WACOM_PROP_WHEELBUTTONS, + .prop_format = 8, + .prop_offset = 7, + .arg_count = 0, + .set_func = map_actions, + .get_func = get_map, + }, + { + .name = "Dial2CCW", + .desc = "X11 event to which counter-clockwise motion of the 2nd dial should be mapped. ", + .prop_name = WACOM_PROP_WHEELBUTTONS, + .prop_format = 8, + .prop_offset = 7, + .arg_count = 0, + .set_func = map_actions, + .get_func = get_map, + }, { .name = "StripLeftUp", .desc = "X11 event to which left strip up should be mapped. ", @@ -3079,7 +3139,7 @@ TEST_CASE(test_parameter_number) * deprecated them. * Numbers include trailing NULL entry. */ - assert(ARRAY_SIZE(parameters) == 40); + assert(ARRAY_SIZE(parameters) == 46); assert(ARRAY_SIZE(deprecated_parameters) == 17); }