From fe923e927a8ddf4d2e82ef4757c885b06d47fa03 Mon Sep 17 00:00:00 2001 From: Greenscreener Date: Sun, 24 Jul 2022 15:02:46 +0200 Subject: [PATCH] Implement smooth panscrolling --- src/WacomInterface.h | 11 +++++++- src/gwacom/wacom-device.h | 5 +++- src/wcmCommon.c | 35 +++++------------------- src/wcmConfig.c | 15 +++++++++-- src/x11/xf86Wacom.c | 25 ++++++++++++++--- src/xf86WacomDefs.h | 5 ++++ test/test_wacom.py | 56 +++++++++++++++++++++++++++++++++++++++ tools/wacom-record.c | 2 ++ 8 files changed, 117 insertions(+), 37 deletions(-) diff --git a/src/WacomInterface.h b/src/WacomInterface.h index 55cde081..a0110a49 100644 --- a/src/WacomInterface.h +++ b/src/WacomInterface.h @@ -63,8 +63,10 @@ enum WacomAxisType { WACOM_AXIS_WHEEL = (1 << 9), /* Artpen rotation or airbrush wheel */ WACOM_AXIS_RING = (1 << 10), WACOM_AXIS_RING2 = (1 << 11), + WACOM_AXIS_SCROLL_X = (1 << 12), + WACOM_AXIS_SCROLL_Y = (1 << 13), - _WACOM_AXIS_LAST = WACOM_AXIS_RING2, + _WACOM_AXIS_LAST = WACOM_AXIS_SCROLL_Y, }; typedef struct { @@ -77,6 +79,7 @@ typedef struct { int throttle; int wheel; int ring, ring2; + int scroll_x, scroll_y; } WacomAxisData; @@ -163,6 +166,8 @@ static inline void wcmAxisSet(WacomAxisData *data, case WACOM_AXIS_WHEEL: data->wheel = value; break; case WACOM_AXIS_RING: data->ring = value; break; case WACOM_AXIS_RING2: data->ring2 = value; break; + case WACOM_AXIS_SCROLL_X: data->scroll_x = value; break; + case WACOM_AXIS_SCROLL_Y: data->scroll_y = value; break; default: abort(); } @@ -187,6 +192,8 @@ static inline bool wcmAxisGet(const WacomAxisData *data, case WACOM_AXIS_WHEEL: *value_out = data->wheel; break; case WACOM_AXIS_RING: *value_out = data->ring; break; case WACOM_AXIS_RING2: *value_out = data->ring2; break; + case WACOM_AXIS_SCROLL_X: *value_out = data->scroll_x; break; + case WACOM_AXIS_SCROLL_Y: *value_out = data->scroll_y; break; default: abort(); } @@ -224,6 +231,8 @@ static inline const char* wcmAxisName(enum WacomAxisType which) case WACOM_AXIS_WHEEL: return "wheel"; case WACOM_AXIS_RING: return "ring"; case WACOM_AXIS_RING2: return "ring2"; + case WACOM_AXIS_SCROLL_X: return "scroll-x"; + case WACOM_AXIS_SCROLL_Y: return "scroll-y"; default: abort(); } diff --git a/src/gwacom/wacom-device.h b/src/gwacom/wacom-device.h index 546fd9fc..659ed5e0 100644 --- a/src/gwacom/wacom-device.h +++ b/src/gwacom/wacom-device.h @@ -108,8 +108,10 @@ typedef enum { WAXIS_WHEEL = (1 << 9), WAXIS_RING = (1 << 10), WAXIS_RING2 = (1 << 11), + WAXIS_SCROLL_X = (1 << 12), + WAXIS_SCROLL_Y = (1 << 13), - _WAXIS_LAST = WAXIS_RING2, + _WAXIS_LAST = WAXIS_SCROLL_Y, } WacomEventAxis; /* The pointer argument to all the event signals. If the mask is set for @@ -124,6 +126,7 @@ typedef struct { int throttle; int wheel; int ring, ring2; + int scroll_x, scroll_y; } WacomEventData; #define WACOM_TYPE_EVENT_DATA (wacom_event_data_get_type()) diff --git a/src/wcmCommon.c b/src/wcmCommon.c index 44bffb5c..20cfaac9 100644 --- a/src/wcmCommon.c +++ b/src/wcmCommon.c @@ -95,38 +95,17 @@ Bool wcmDevSwitchModeCall(WacomDevicePtr priv, Bool absolute) return TRUE; } - -static int wcmButtonPerNotch(WacomDevicePtr priv, int value, int threshold, int btn_positive, int btn_negative) -{ - int mode = is_absolute(priv); - int notches = value / threshold; - int button = (notches > 0) ? btn_positive : btn_negative; - int i; - WacomAxisData axes = {0}; - - for (i = 0; i < abs(notches); i++) { - wcmEmitButton(priv, mode, button, 1, &axes); - wcmEmitButton(priv, mode, button, 0, &axes); - } - - return value % threshold; -} - static void wcmPanscroll(WacomDevicePtr priv, const WacomDeviceState *ds, int x, int y) { WacomCommonPtr common = priv->common; int threshold = common->wcmPanscrollThreshold; - int *accumulated_x, *accumulated_y; int delta_x, delta_y; if (!(priv->flags & SCROLLMODE_FLAG) || !(ds->buttons & 1)) return; - /* Tip has gone down down; store state for dragging */ + /* Tip has gone down down; don't send pan event yet */ if (!(priv->oldState.buttons & 1)) { - priv->wcmPanscrollState = *ds; - priv->wcmPanscrollState.x = 0; - priv->wcmPanscrollState.y = 0; return; } @@ -139,15 +118,13 @@ static void wcmPanscroll(WacomDevicePtr priv, const WacomDeviceState *ds, int x, delta_y = (y - priv->oldState.y); } - accumulated_x = &priv->wcmPanscrollState.x; - accumulated_y = &priv->wcmPanscrollState.y; - *accumulated_x += delta_x; - *accumulated_y += delta_y; - DBG(6, priv, "pan x = %d, pan y = %d\n", *accumulated_x, *accumulated_y); + DBG(6, priv, "pan x = %d, pan y = %d\n", delta_x, delta_y); - *accumulated_x = wcmButtonPerNotch(priv, *accumulated_x, threshold, 6, 7); - *accumulated_y = wcmButtonPerNotch(priv, *accumulated_y, threshold, 4, 5); + WacomAxisData axes = {0}; + wcmAxisSet(&axes, WACOM_AXIS_SCROLL_X, -delta_x * PANSCROLL_INCREMENT/threshold); + wcmAxisSet(&axes, WACOM_AXIS_SCROLL_Y, -delta_y * PANSCROLL_INCREMENT/threshold); + wcmEmitMotion(priv, FALSE, &axes); } void wcmResetButtonAction(WacomDevicePtr priv, int button) diff --git a/src/wcmConfig.c b/src/wcmConfig.c index 757492d3..7c0483f8 100644 --- a/src/wcmConfig.c +++ b/src/wcmConfig.c @@ -99,7 +99,7 @@ WacomDevicePtr wcmAllocate(void *frontend, const char *name) priv->touch_timer = wcmTimerNew(); /* reusable valuator mask */ - priv->valuator_mask = valuator_mask_new(7); + priv->valuator_mask = valuator_mask_new(8); return priv; @@ -1220,6 +1220,14 @@ static int wcmInitAxes(WacomDevicePtr priv) wcmInitAxis(priv, WACOM_AXIS_RING2, min, max, res); } + if (IsPen(priv)) { + /* seventh valuator: scroll_x */ + wcmInitAxis(priv, WACOM_AXIS_SCROLL_X, -1, -1, 0); + + /* eighth valuator: scroll_y */ + wcmInitAxis(priv, WACOM_AXIS_SCROLL_Y, -1, -1, 0); + } + return TRUE; } @@ -1232,7 +1240,7 @@ Bool wcmDevInit(WacomDevicePtr priv) if (priv->common->wcmModel->DetectConfig) priv->common->wcmModel->DetectConfig (priv); - nbaxes = priv->naxes; /* X, Y, Pressure, Tilt-X, Tilt-Y, Wheel */ + nbaxes = priv->naxes; /* X, Y, Pressure, Tilt-X, Tilt-Y, Wheel, Scroll-X, Scroll-Y */ if (!nbaxes || nbaxes > 6) nbaxes = priv->naxes = 6; nbbuttons = priv->nbuttons; /* Use actual number of buttons, if possible */ @@ -1240,6 +1248,9 @@ Bool wcmDevInit(WacomDevicePtr priv) if (IsPad(priv) && TabletHasFeature(priv->common, WCM_DUALRING)) nbaxes = priv->naxes = nbaxes + 1; /* ABS wheel 2 */ + if (IsPen(priv)) + nbaxes = priv->naxes = nbaxes + 2; /* Scroll X and Y */ + /* if more than 3 buttons, offset by the four scroll buttons, * otherwise, alloc 7 buttons for scroll wheel. */ nbbuttons = min(max(nbbuttons + 4, 7), WCM_MAX_BUTTONS); diff --git a/src/x11/xf86Wacom.c b/src/x11/xf86Wacom.c index 4cabe36f..9c718974 100644 --- a/src/x11/xf86Wacom.c +++ b/src/x11/xf86Wacom.c @@ -388,6 +388,8 @@ valuatorNumber(enum WacomAxisType which) case WACOM_AXIS_WHEEL: pos = 5; break; case WACOM_AXIS_RING: pos = 5; break; case WACOM_AXIS_RING2: pos = 6; break; + case WACOM_AXIS_SCROLL_X: pos = 6; break; + case WACOM_AXIS_SCROLL_Y: pos = 7; break; break; default: abort(); @@ -518,15 +520,28 @@ void wcmInitAxis(WacomDevicePtr priv, enum WacomAxisType type, break; case WACOM_AXIS_RING2: break; + case WACOM_AXIS_SCROLL_X: + label = XIGetKnownProperty(AXIS_LABEL_PROP_REL_HSCROLL); + break; + case WACOM_AXIS_SCROLL_Y: + label = XIGetKnownProperty(AXIS_LABEL_PROP_REL_VSCROLL); + break; + default: abort(); } index = valuatorNumber(type); InitValuatorAxisStruct(pInfo->dev, index, - label, - min, max, res, min_res, max_res, - Absolute); + label, + min, max, res, min_res, max_res, + Absolute); + + if (type == WACOM_AXIS_SCROLL_X) + SetScrollValuator(pInfo->dev, index, SCROLL_TYPE_HORIZONTAL, PANSCROLL_INCREMENT, 0); + else if (type == WACOM_AXIS_SCROLL_Y) + SetScrollValuator(pInfo->dev, index, SCROLL_TYPE_VERTICAL, PANSCROLL_INCREMENT, 0); + } bool wcmInitButtons(WacomDevicePtr priv, unsigned int nbuttons) @@ -1119,7 +1134,7 @@ valuator_mask_get(const ValuatorMask *mask, int valuator) TEST_CASE(test_convert_axes) { WacomAxisData axes = {0}; - ValuatorMask *mask = valuator_mask_new(7); + ValuatorMask *mask = valuator_mask_new(8); convertAxes(&axes, mask); assert(valuator_mask_num_valuators(mask) == 0); @@ -1165,6 +1180,7 @@ TEST_CASE(test_convert_axes) assert(valuator_mask_isset(mask, 5)); assert(valuator_mask_get(mask, 5) == 2); assert(!valuator_mask_isset(mask, 6)); + assert(!valuator_mask_isset(mask, 7)); memset(&axes, 0, sizeof(axes)); valuator_mask_zero(mask); @@ -1195,6 +1211,7 @@ TEST_CASE(test_convert_axes) assert(valuator_mask_get(mask, 5) == 2); assert(!valuator_mask_isset(mask, 6)); assert(!valuator_mask_isset(mask, 7)); + assert(!valuator_mask_isset(mask, 8)); free(mask); } diff --git a/src/xf86WacomDefs.h b/src/xf86WacomDefs.h index df218563..92c4d913 100644 --- a/src/xf86WacomDefs.h +++ b/src/xf86WacomDefs.h @@ -68,6 +68,11 @@ #define BTN_STYLUS3 0x149 #endif +/* This value is arbitrary, but low enough values can cause integer division to round + * non-zero numbers to zero. See https://github.com/linuxwacom/xf86-input-wacom/pull/222#discussion_r927920625. + */ +#define PANSCROLL_INCREMENT 0xffff + /****************************************************************************** * Forward Declarations *****************************************************************************/ diff --git a/test/test_wacom.py b/test/test_wacom.py index 26a01aab..19999ac0 100644 --- a/test/test_wacom.py +++ b/test/test_wacom.py @@ -327,4 +327,60 @@ def test_axis_updates_wheel(mainloop, opts, stylus_type): assert first_wheel == current_wheel +def test_scroll(mainloop, opts): + """ + Check panscrolling works correctly + """ + dev = Device.from_name("PTH660", "Pen") + opts["PanScrollThreshold"] = "150" + + prox_in = [ + Sev("ABS_X", 50), + Sev("ABS_Y", 50), + Sev("BTN_TOOL_PEN", 1), + Sev("SYN_REPORT", 0), + ] + + prox_out = [ + Sev("BTN_TOOL_PEN", 0), + Sev("SYN_REPORT", 0), + ] + + press_button2 = [ + Sev("BTN_STYLUS", 1), + Sev("SYN_REPORT", 0), + ] + + touchdown_pen = [ + Sev("BTN_TOUCH", 1), + Sev("ABS_PRESSURE", 20), + Sev("SYN_REPORT", 0), + ] + + move_pen_x = [Sev("ABS_X", 75), Sev("SYN_REPORT", 0)] + + up_pen = [Sev("BTN_TOUCH", 0), Sev("ABS_PRESSURE", 0), Sev("SYN_REPORT", 0)] + + depress_button2 = [Sev("BTN_STYLUS", 0), Sev("SYN_REPORT", 0)] + + monitor = Monitor.new_from_device(dev, opts) + monitor.wacom_device.set_runtime_option("PanButton", "2") + + monitor.write_events(prox_in) + monitor.write_events(press_button2) + monitor.write_events(touchdown_pen) # Pen touchdown + monitor.write_events(move_pen_x) # Move pen 25% towards positive x + monitor.write_events(up_pen) # Pen up + monitor.write_events(depress_button2) # Depress button2 + monitor.write_events(prox_out) + + mainloop.run() + have_we_scrolled = False + for event in monitor.events: + if event.axes.scroll_x != 0: + assert event.axes.scroll_x == -1223320 + have_we_scrolled = True + assert have_we_scrolled + + # vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/tools/wacom-record.c b/tools/wacom-record.c index 395282b0..8b1cf771 100644 --- a/tools/wacom-record.c +++ b/tools/wacom-record.c @@ -233,6 +233,8 @@ static void device_added(WacomDriver *driver, WacomDevice *device) case WAXIS_WHEEL: typestr = "wheel"; break; case WAXIS_RING: typestr = "ring"; break; case WAXIS_RING2: typestr = "ring2"; break; + case WAXIS_SCROLL_X: typestr = "scroll_x"; break; + case WAXIS_SCROLL_Y: typestr = "scroll_y"; break; } printf(" - {type: %-12s, range: [%5d, %5d], resolution: %5d}\n",