Edit

IABSD.fr/xenocara/driver/xf86-input-ws/src/emuwheel.c

Branch :

  • Show log

    Commit

  • Author : bru
    Date : 2018-06-18 20:07:30
    Hash : a011f4db
    Message : Improve the wheel emulation logic. Make the axis filtering symmetrical, and less restrictive for vertical scrolling. Thanks to Jake Champlin for help with the issue. ok matthieu@

  • driver/xf86-input-ws/src/emuwheel.c
  • /*	$OpenBSD: emuwheel.c,v 1.4 2018/06/18 20:07:30 bru Exp $	*/
    /*
    * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
    * Copyright 1993 by David Dawes <dawes@xfree86.org>
    * Copyright 2002 by SuSE Linux AG, Author: Egbert Eich
    * Copyright 1994-2002 by The XFree86 Project, Inc.
    * Copyright 2002 by Paul Elliott
    * (Ported from xf86-input-mouse, above copyrights taken from there)
    * Copyright 2008 by Chris Salch
    * Copyright © 2008 Red Hat, Inc.
    *
    * Permission to use, copy, modify, distribute, and sell this software
    * and its documentation for any purpose is hereby granted without
    * fee, provided that the above copyright notice appear in all copies
    * and that both that copyright notice and this permission notice
    * appear in supporting documentation, and that the name of the authors
    * not be used in advertising or publicity pertaining to distribution of the
    * software without specific, written prior permission.  The authors make no
    * representations about the suitability of this software for any
    * purpose.  It is provided "as is" without express or implied
    * warranty.
    *
    * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
    * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
    * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
    * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
    * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
    * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
    * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    *
    */
    
    /* Mouse wheel emulation code. */
    
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include <sys/time.h>
    
    #include <xorg-server.h>
    #include <X11/Xatom.h>
    #include <xf86.h>
    #include <xf86_OSproc.h>
    #include <X11/extensions/XI.h>
    #include <X11/extensions/XIproto.h>
    #include <xf86Xinput.h>
    #include <exevents.h>
    
    #include <ws-properties.h>
    
    #include "ws.h"
    
    static Atom prop_wheel_emu;
    static Atom prop_wheel_axismap;
    static Atom prop_wheel_inertia;
    static Atom prop_wheel_timeout;
    static Atom prop_wheel_button;
    
    static int wsWheelEmuInertia(InputInfoPtr, WheelAxisPtr, int);
    static int wsWheelEmuSetProperty(DeviceIntPtr, Atom, XIPropertyValuePtr, BOOL);
    
    BOOL
    wsWheelEmuFilterButton(InputInfoPtr pInfo, unsigned int button, int press)
    {
    	WSDevicePtr priv = (WSDevicePtr)pInfo->private;
    	int ms;
    
    	if (!priv->emulateWheel.enabled)
    		return FALSE;
    
    	if (priv->emulateWheel.button == button) {
    		priv->emulateWheel.button_state = press;
    
    		if (press) {
    			priv->emulateWheel.expires = GetTimeInMillis() +
    			    priv->emulateWheel.timeout;
    		} else {
    			ms = priv->emulateWheel.expires - GetTimeInMillis();
    
    			if (ms > 0) {
    				/*
    				 * If the button is released early enough emit
    				 * the button press/release events
    				 */
    				wsButtonClicks(pInfo, button, 1);
    			}
    		}
    
    		return TRUE;
    	}
    
    	return FALSE;
    }
    
    BOOL
    wsWheelEmuFilterMotion(InputInfoPtr pInfo, int dx, int dy)
    {
    	WSDevicePtr priv = (WSDevicePtr)pInfo->private;
    	WheelAxisPtr pAxis = NULL, pOtherAxis = NULL;
    	int value;
    
    	if (!priv->emulateWheel.enabled)
    		return FALSE;
    
    	/*
    	 * Handle our motion events if the emuWheel button is pressed.
    	 * Wheel button of 0 means always emulate wheel.
    	 */
    	if (priv->emulateWheel.button_state || priv->emulateWheel.button == 0) {
    		if (priv->emulateWheel.button) {
    			int ms = priv->emulateWheel.expires - GetTimeInMillis();
    
    			if (ms > 0)
    				return TRUE;
    		}
    
    		if (abs(dx) > abs(dy)) {
    			pAxis = &(priv->emulateWheel.X);
    			pOtherAxis = &(priv->emulateWheel.Y);
    			value = dx;
    		} else if (dy) {
    			pAxis = &(priv->emulateWheel.Y);
    			pOtherAxis = &(priv->emulateWheel.X);
    			value = dy;
    		} else
    			return FALSE;
    
    		/*
    		 * 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 (pAxis) {
    			if (wsWheelEmuInertia(pInfo, pAxis, value))
    				pOtherAxis->traveled_distance = 0;
    		}
    
    		return TRUE;
    	}
    
    	return FALSE;
    }
    
    static int
    wsWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value)
    {
    	WSDevicePtr priv = (WSDevicePtr)pInfo->private;
    	int button, inertia;
    	int rc = 0;
    
    	if (axis->negative == WS_NOMAP)
    		return rc;
    
    	axis->traveled_distance += value;
    
    	if (axis->traveled_distance < 0) {
    		button = axis->negative;
    		inertia = -priv->emulateWheel.inertia;
    	} else {
    		button = axis->positive;
    		inertia = priv->emulateWheel.inertia;
    	}
    
    	while (abs(axis->traveled_distance) > priv->emulateWheel.inertia) {
    		axis->traveled_distance -= inertia;
    		wsButtonClicks(pInfo, button, 1);
    		rc++;
    	}
    
    	return rc;
    }
    
    void
    wsWheelEmuPreInit(InputInfoPtr pInfo)
    {
    	WSDevicePtr priv = (WSDevicePtr)pInfo->private;
    	int button, inertia, timeout;
    
    	priv->emulateWheel.enabled = xf86SetBoolOption(pInfo->options,
    	    "EmulateWheel", FALSE);
    
    	button = xf86SetIntOption(pInfo->options, "EmulateWheelButton", 4);
    	if (button < 0 || button > NBUTTONS) {
    		xf86IDrvMsg(pInfo, X_WARNING,
    		    "Invalid EmulateWheelButton value: %d\n", button);
    		xf86IDrvMsg(pInfo, X_WARNING, "Wheel emulation disabled\n");
    		priv->emulateWheel.enabled = FALSE;
    		button = 4;
    	}
    	priv->emulateWheel.button = button;
    
    	inertia = xf86SetIntOption(pInfo->options, "EmulateWheelInertia", 10);
    	if (inertia <= 0) {
    		xf86IDrvMsg(pInfo, X_WARNING,
    		    "Invalid EmulateWheelInertia value: %d\n", inertia);
    		xf86IDrvMsg(pInfo, X_WARNING, "Using built-in inertia value\n");
    		inertia = 10;
    	}
    	priv->emulateWheel.inertia = inertia;
    
    	timeout = xf86SetIntOption(pInfo->options, "EmulateWheelTimeout", 200);
    	if (timeout < 0) {
    		xf86IDrvMsg(pInfo, X_WARNING,
    		    "Invalid EmulateWheelTimeout value: %d\n", timeout);
    		xf86IDrvMsg(pInfo, X_WARNING, "Using built-in timeout value\n");
    		timeout = 200;
    	}
    	priv->emulateWheel.timeout = timeout;
    
    	wsWheelHandleButtonMap(pInfo, &(priv->emulateWheel.Y),
    	    "YAxisMapping", "4 5");
    	wsWheelHandleButtonMap(pInfo, &(priv->emulateWheel.X),
    	    "XAxisMapping", NULL);
    }
    
    static int
    wsWheelEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
        BOOL checkonly)
    {
    	InputInfoPtr pInfo = (InputInfoPtr)dev->public.devicePrivate;
    	WSDevicePtr priv = (WSDevicePtr)pInfo->private;
    
    	if (atom == prop_wheel_emu) {
    		if (val->format != 8 || val->size != 1 ||
    		    val->type != XA_INTEGER)
    			return BadMatch;
    
    		if (!checkonly)
    			priv->emulateWheel.enabled = *((BOOL*)val->data);
    	} else if (atom == prop_wheel_button) {
    		int button;
    
    		if (val->format != 8 || val->size != 1 ||
    		    val->type != XA_INTEGER)
    			return BadMatch;
    
    		button = *((CARD8*)val->data);
    
    		if (button < 0 || button > NBUTTONS)
    			return BadValue;
    
    		if (!checkonly)
    			priv->emulateWheel.button = button;
    	} else if (atom == prop_wheel_axismap) {
    		int x_negative, x_positive;
    		int y_negative, y_positive;
    
    		if (val->format != 8 || val->size != 4 ||
    		    val->type != XA_INTEGER)
    			return BadMatch;
    
    		x_negative = *((CARD8*)val->data);
    		x_positive = *(((CARD8*)val->data) + 1);
    		y_negative = *(((CARD8*)val->data) + 2);
    		y_positive = *(((CARD8*)val->data) + 3);
    
    		if (x_negative < 0 || x_negative > NBUTTONS ||
    		    x_positive < 0 || x_positive > NBUTTONS ||
    		    y_negative < 0 || y_negative > NBUTTONS ||
    		    y_positive < 0 || y_positive > NBUTTONS)
    			return BadValue;
    
    		if ((x_negative == WS_NOMAP && x_positive != WS_NOMAP) ||
    		    (x_negative != WS_NOMAP && x_positive == WS_NOMAP) ||
    		    (y_negative == WS_NOMAP && y_positive != WS_NOMAP) ||
    		    (y_negative != WS_NOMAP && y_positive == WS_NOMAP))
    			return BadValue;
    
    		if (!checkonly) {
    			priv->emulateWheel.X.negative = x_negative;
    			priv->emulateWheel.X.positive = x_positive;
    			priv->emulateWheel.Y.negative = y_negative;
    			priv->emulateWheel.Y.positive = y_positive;
    		}
    	} else if (atom == prop_wheel_inertia) {
    		int inertia;
    
    		if (val->format != 16 || val->size != 1 ||
    		    val->type != XA_INTEGER)
    			return BadMatch;
    
    		inertia = *((CARD16*)val->data);
    
    		if (inertia <= 0)
    			return BadValue;
    
    		if (!checkonly)
    			priv->emulateWheel.inertia = inertia;
    	} else if (atom == prop_wheel_timeout) {
    		int timeout;
    
    		if (val->format != 32 || val->size != 1 ||
    		    val->type != XA_INTEGER)
    			return BadMatch;
    
    		timeout = *((CARD32*)val->data);
    
    		if (timeout < 0)
    			return BadValue;
    
    		if (!checkonly)
    			priv->emulateWheel.timeout = timeout;
    	}
    
    	return Success;
    }
    
    void
    wsWheelEmuInitProperty(DeviceIntPtr dev)
    {
    	InputInfoPtr pInfo = (InputInfoPtr)dev->public.devicePrivate;
    	WSDevicePtr priv = (WSDevicePtr)pInfo->private;
    	char vals[4];
    	int rc;
    
    	prop_wheel_emu = MakeAtom(WS_PROP_WHEEL, strlen(WS_PROP_WHEEL), TRUE);
    	rc = XIChangeDeviceProperty(dev, prop_wheel_emu, XA_INTEGER, 8,
    	    PropModeReplace, 1, &priv->emulateWheel.enabled, FALSE);
    	if (rc != Success) {
    		xf86IDrvMsg(pInfo, X_ERROR,
    		    "cannot create device property %s: %d\n",
    		    WS_PROP_WHEEL, rc);
    		return;
    	}
    	XISetDevicePropertyDeletable(dev, prop_wheel_emu, FALSE);
    
    	vals[0] = priv->emulateWheel.X.negative;
    	vals[1] = priv->emulateWheel.X.positive;
    	vals[2] = priv->emulateWheel.Y.negative;
    	vals[3] = priv->emulateWheel.Y.positive;
    
    	prop_wheel_axismap = MakeAtom(WS_PROP_WHEEL_AXES,
    	    strlen(WS_PROP_WHEEL_AXES), TRUE);
    	rc = XIChangeDeviceProperty(dev, prop_wheel_axismap, XA_INTEGER, 8,
    	    PropModeReplace, 4, vals, FALSE);
    	if (rc != Success) {
    		xf86IDrvMsg(pInfo, X_ERROR,
    		    "cannot create device property %s: %d\n",
    		    WS_PROP_WHEEL_AXES, rc);
    		return;
    	}
    	XISetDevicePropertyDeletable(dev, prop_wheel_axismap, FALSE);
    
    	prop_wheel_inertia = MakeAtom(WS_PROP_WHEEL_INERTIA,
    	    strlen(WS_PROP_WHEEL_INERTIA), TRUE);
    	rc = XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER, 16,
    	    PropModeReplace, 1, &priv->emulateWheel.inertia, FALSE);
    	if (rc != Success) {
    		xf86IDrvMsg(pInfo, X_ERROR,
    		    "cannot create device property %s: %d\n",
    		    WS_PROP_WHEEL_INERTIA, rc);
    		return;
    	}
    	XISetDevicePropertyDeletable(dev, prop_wheel_inertia, FALSE);
    
    	prop_wheel_timeout = MakeAtom(WS_PROP_WHEEL_TIMEOUT,
    	    strlen(WS_PROP_WHEEL_TIMEOUT), TRUE);
    	rc = XIChangeDeviceProperty(dev, prop_wheel_timeout, XA_INTEGER, 32,
    	    PropModeReplace, 1, &priv->emulateWheel.timeout, FALSE);
    	if (rc != Success) {
    		xf86IDrvMsg(pInfo, X_ERROR,
    		    "cannot create device property %s: %d\n",
    		    WS_PROP_WHEEL_TIMEOUT, rc);
    		return;
    	}
    	XISetDevicePropertyDeletable(dev, prop_wheel_timeout, FALSE);
    
    	prop_wheel_button = MakeAtom(WS_PROP_WHEEL_BUTTON,
    	    strlen(WS_PROP_WHEEL_BUTTON), TRUE);
    	rc = XIChangeDeviceProperty(dev, prop_wheel_button, XA_INTEGER, 8,
    	    PropModeReplace, 1, &priv->emulateWheel.button, FALSE);
    	if (rc != Success) {
    		xf86IDrvMsg(pInfo, X_ERROR,
    		    "cannot create device property %s: %d\n",
    		    WS_PROP_WHEEL_BUTTON, rc);
    		return;
    	}
    	XISetDevicePropertyDeletable(dev, prop_wheel_button, FALSE);
    
    	XIRegisterPropertyHandler(dev, wsWheelEmuSetProperty, NULL, NULL);
    }