From: Max Schwarz Date: Tue, 6 Jan 2015 15:15:50 +0800 Subject: evdev: smooth scrolling support for wheel emulation Disables the button click generation (EvdevWheelEmuInertia) and generates scroll valuator events directly. Signed-off-by: Max Schwarz --- src/emuWheel.c | 30 ++++++++++++++++++++++++------ src/evdev.c | 44 +++++++++++++++++++++++++++++++++----------- src/evdev.h | 5 +++++ 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/src/emuWheel.c b/src/emuWheel.c index 04487cf..a7e4ff5 100644 --- a/src/emuWheel.c +++ b/src/emuWheel.c @@ -51,7 +51,9 @@ static Atom prop_wheel_timeout = 0; static Atom prop_wheel_button = 0; /* Local Funciton Prototypes */ +#ifndef HAVE_SMOOTH_SCROLLING static int EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value); +#endif /* Filter mouse button events */ BOOL @@ -95,7 +97,8 @@ BOOL EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv) { EvdevPtr pEvdev = (EvdevPtr)pInfo->private; - WheelAxisPtr pAxis = NULL, pOtherAxis = NULL; + WheelAxisPtr pAxis = NULL; + _X_UNUSED WheelAxisPtr pOtherAxis = NULL; int value = pEv->value; /* Has wheel emulation been configured to be enabled? */ @@ -147,11 +150,20 @@ EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv) wheel. Reset the inertia of the other axis when a scroll event was sent to avoid the buildup of erroneous scroll events if the user doesn't move in a perfectly straight line. + If the axis is not configured, simply eat the motion */ - if (pAxis) + if (pAxis && pAxis->up_button) { +#ifndef HAVE_SMOOTH_SCROLLING if (EvdevWheelEmuInertia(pInfo, pAxis, value)) pOtherAxis->traveled_distance = 0; +#else + /* Simply queue scroll valuator events. The Y axis is flipped. */ + if (pAxis->code == REL_WHEEL) + EvdevQueueRelativeMotion(pInfo, pAxis->code, -value); + else + EvdevQueueRelativeMotion(pInfo, pAxis->code, value); +#endif } /* Eat motion events while emulateWheel button pressed. */ @@ -161,6 +173,8 @@ EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv) return FALSE; } + +#ifndef HAVE_SMOOTH_SCROLLING /* Simulate inertia for our emulated mouse wheel. Returns the number of wheel events generated. */ @@ -172,10 +186,6 @@ EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value) int inertia; int rc = 0; - /* if this axis has not been configured, just eat the motion */ - if (!axis->up_button) - return rc; - axis->traveled_distance += value; if (axis->traveled_distance < 0) { @@ -194,6 +204,7 @@ EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value) } return rc; } +#endif /* Handle button mapping here to avoid code duplication, returns true if a button mapping was found. */ @@ -296,6 +307,10 @@ EvdevWheelEmuPreInit(InputInfoPtr pInfo) pEvdev->emulateWheel.timeout = timeout; + /* Default axes */ + pEvdev->emulateWheel.Y.code = REL_WHEEL; + pEvdev->emulateWheel.X.code = REL_HWHEEL; + /* Configure the Y axis or default it */ if (!EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.Y), "YAxisMapping")) { @@ -357,6 +372,9 @@ EvdevWheelEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, 16, PropModeReplace, 1, &pEvdev->emulateWheel.inertia, TRUE); } + + /* We might need to add/remove valuators for the scroll axes */ + EvdevAddRelValuatorClass(dev); } } else if (atom == prop_wheel_button) diff --git a/src/evdev.c b/src/evdev.c index 30f809b..a242fdb 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -364,6 +364,18 @@ EvdevQueueButtonClicks(InputInfoPtr pInfo, int button, int count) } } +void +EvdevQueueRelativeMotion(InputInfoPtr pInfo, int code, int value) +{ + int map; + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + + pEvdev->rel_queued = 1; + pEvdev->delta[code] += value; + map = pEvdev->rel_axis_map[code]; + valuator_mask_set(pEvdev->vals, map, value); +} + static void EvdevSwapAbsValuators(EvdevPtr pEvdev, ValuatorMask *mask) { @@ -673,10 +685,7 @@ EvdevProcessRelativeMotionEvent(InputInfoPtr pInfo, struct input_event *ev) if (EvdevWheelEmuFilterMotion(pInfo, ev)) return; - pEvdev->rel_queued = 1; - pEvdev->delta[ev->code] += value; - map = pEvdev->rel_axis_map[ev->code]; - valuator_mask_set(pEvdev->vals, map, value); + EvdevQueueRelativeMotion(pInfo, ev->code, value); break; } } @@ -1623,7 +1632,8 @@ EvdevSetScrollValuators(DeviceIntPtr device) return Success; } -static int +/* Note: Can be called multiple times if wheelEmulation.enabled changes */ +int EvdevAddRelValuatorClass(DeviceIntPtr device) { InputInfoPtr pInfo; @@ -1655,6 +1665,12 @@ EvdevAddRelValuatorClass(DeviceIntPtr device) if (num_axes <= 0) goto out; +#else + /* If wheel emulation is enabled, we need REL_WHEEL and REL_HWHEEL even + * if the device does not support them */ + if(pEvdev->emulateWheel.enabled) { + num_axes += 2; + } #endif if (num_axes > MAX_VALUATORS) { @@ -1664,6 +1680,9 @@ EvdevAddRelValuatorClass(DeviceIntPtr device) pEvdev->num_vals = num_axes; if (num_axes > 0) { + if(pEvdev->vals) + valuator_mask_free(&pEvdev->vals); + pEvdev->vals = valuator_mask_new(num_axes); if (!pEvdev->vals) goto out; @@ -1678,8 +1697,12 @@ EvdevAddRelValuatorClass(DeviceIntPtr device) if (axis == REL_WHEEL || axis == REL_HWHEEL || axis == REL_DIAL) continue; #endif - if (!libevdev_has_event_code(pEvdev->dev, EV_REL, axis)) + /* If wheel emulation is enabled, we need REL_WHEEL and REL_HWHEEL even + * if the device does not support them */ + if (!libevdev_has_event_code(pEvdev->dev, EV_REL, axis) + && (!pEvdev->emulateWheel.enabled || (axis != REL_WHEEL && axis != REL_HWHEEL))) continue; + pEvdev->rel_axis_map[axis] = map; map++; } @@ -2079,7 +2102,7 @@ EvdevForceXY(InputInfoPtr pInfo, int mode) static int EvdevProbe(InputInfoPtr pInfo) { - int i, has_rel_axes, has_abs_axes, has_keys, num_buttons, has_scroll; + int i, has_rel_axes, has_abs_axes, has_keys, num_buttons; int has_lmr; /* left middle right */ int has_mt; /* multitouch */ int ignore_abs = 0, ignore_rel = 0; @@ -2114,7 +2137,6 @@ EvdevProbe(InputInfoPtr pInfo) has_rel_axes = FALSE; has_abs_axes = FALSE; has_keys = FALSE; - has_scroll = FALSE; has_lmr = FALSE; has_mt = FALSE; num_buttons = 0; @@ -2154,7 +2176,6 @@ EvdevProbe(InputInfoPtr pInfo) libevdev_has_event_code(pEvdev->dev, EV_REL, REL_HWHEEL) || libevdev_has_event_code(pEvdev->dev, EV_REL, REL_DIAL)) { xf86IDrvMsg(pInfo, X_PROBED, "Found scroll wheel(s)\n"); - has_scroll = TRUE; if (!num_buttons) xf86IDrvMsg(pInfo, X_INFO, "Forcing buttons for scroll wheel(s)\n"); @@ -2323,8 +2344,9 @@ EvdevProbe(InputInfoPtr pInfo) rc = 0; } - if (has_scroll && - (has_rel_axes || has_abs_axes || num_buttons || has_keys)) + /* Even if we do not have a scroll wheel, we might provide scroll axes + * through wheel emulation */ + if (has_rel_axes || has_abs_axes || num_buttons || has_keys) { xf86IDrvMsg(pInfo, X_INFO, "Adding scrollwheel support\n"); pEvdev->flags |= EVDEV_BUTTON_EVENTS; diff --git a/src/evdev.h b/src/evdev.h index 520d017..35bb5e9 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -126,6 +126,7 @@ enum ButtonAction { typedef struct { int up_button; int down_button; + int code; int traveled_distance; } WheelAxis, *WheelAxisPtr; @@ -252,10 +253,14 @@ typedef struct { char *type_name; } EvdevRec, *EvdevPtr; +/* (Re-)initialization */ +int EvdevAddRelValuatorClass(DeviceIntPtr device); + /* Event posting functions */ void EvdevQueueKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value); void EvdevQueueButtonEvent(InputInfoPtr pInfo, int button, int value); void EvdevQueueProximityEvent(InputInfoPtr pInfo, int value); +void EvdevQueueRelativeMotion(InputInfoPtr pInfo, int code, int value); #ifdef MULTITOUCH void EvdevQueueTouchEvent(InputInfoPtr pInfo, unsigned int touch, ValuatorMask *mask, uint16_t type);