#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <X11/Xatom.h>
#include <xf86.h>
#include <xf86Xinput.h>
#include <exevents.h>
#include "evdev.h"
enum {
MBEMU_DISABLED = 0,
MBEMU_ENABLED,
MBEMU_AUTO
};
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
static const char *propname_mbemu = "Middle Button Emulation";
static const char *propname_mbtimeout = "Middle Button Timeout";
static Atom prop_mbemu = 0;
static Atom prop_mbtimeout = 0;
#endif
static signed char stateTab[11][5][3] = {
{
{ 0, 0, 0 },
{ 0, 0, 1 },
{ 0, 0, 2 },
{ 2, 0, 3 },
{ 0, 0, -1 }
},
{
{ 1, -1, 0 },
{ 0, 0, 1 },
{ 1, -1, 2 },
{ 2, 0, 3 },
{ 1, 0, 4 },
},
{
{ 3, -3, 0 },
{ 3, -3, 1 },
{ 0, 0, 2 },
{ 2, 0, 3 },
{ 3, 0, 5 },
},
{
{ -2, 0, 0 },
{ 0, 0, 7 },
{ 0, 0, 6 },
{ 0, 0, 3 },
{ 0, 0, -1 },
},
{
{ -1, 0, 0 },
{ 0, 0, 4 },
{ -1, 0, 2 },
{ 3, 0, 10 },
{ 0, 0, -1 },
},
{
{ -3, 0, 0 },
{ -3, 0, 1 },
{ 0, 0, 5 },
{ 1, 0, 10 },
{ 0, 0, -1 },
},
{
{ -2, 0, 0 },
{ -2, 0, 1 },
{ 0, 0, 6 },
{ 1, 0, 8 },
{ 0, 0, -1 },
},
{
{ -2, 0, 0 },
{ 0, 0, 7 },
{ -2, 0, 2 },
{ 3, 0, 9 },
{ 0, 0, -1 },
},
{
{ -2, -1, 0 },
{ -2, 0, 4 },
{ -1, 0, 6 },
{ 0, 0, 8 },
{ 0, 0, -1 },
},
{
{ -2, -3, 0 },
{ -3, 0, 7 },
{ -2, 0, 5 },
{ 0, 0, 9 },
{ 0, 0, -1 },
},
{
{ -1, -3, 0 },
{ -3, 0, 4 },
{ -1, 0, 5 },
{ 0, 0, 10 },
{ 0, 0, -1 },
},
};
int
EvdevMBEmuTimer(InputInfoPtr pInfo)
{
EvdevPtr pEvdev = pInfo->private;
int sigstate;
int id;
sigstate = xf86BlockSIGIO ();
pEvdev->emulateMB.pending = FALSE;
if ((id = stateTab[pEvdev->emulateMB.state][4][0]) != 0) {
xf86PostButtonEvent(pInfo->dev, 0, abs(id), (id >= 0), 0, 0);
pEvdev->emulateMB.state =
stateTab[pEvdev->emulateMB.state][4][2];
} else {
ErrorF("Got unexpected buttonTimer in state %d\n",
pEvdev->emulateMB.state);
}
xf86UnblockSIGIO (sigstate);
return 0;
}
BOOL
EvdevMBEmuFilterEvent(InputInfoPtr pInfo, int button, BOOL press)
{
EvdevPtr pEvdev = pInfo->private;
int id;
int *btstate;
int ret = FALSE;
if (!pEvdev->emulateMB.enabled)
return ret;
if (button != 1 && button != 3)
return ret;
btstate = &pEvdev->emulateMB.buttonstate;
if (press)
*btstate |= (button == 1) ? 0x1 : 0x2;
else
*btstate &= (button == 1) ? ~0x1 : ~0x2;
if ((id = stateTab[pEvdev->emulateMB.state][*btstate][0]) != 0)
{
xf86PostButtonEvent(pInfo->dev, 0, abs(id), (id >= 0), 0, 0);
ret = TRUE;
}
if ((id = stateTab[pEvdev->emulateMB.state][*btstate][1]) != 0)
{
xf86PostButtonEvent(pInfo->dev, 0, abs(id), (id >= 0), 0, 0);
ret = TRUE;
}
pEvdev->emulateMB.state =
stateTab[pEvdev->emulateMB.state][*btstate][2];
if (stateTab[pEvdev->emulateMB.state][4][0] != 0) {
pEvdev->emulateMB.expires = GetTimeInMillis () + pEvdev->emulateMB.timeout;
pEvdev->emulateMB.pending = TRUE;
ret = TRUE;
} else {
pEvdev->emulateMB.pending = FALSE;
}
return ret;
}
void EvdevMBEmuWakeupHandler(pointer data,
int i,
pointer LastSelectMask)
{
InputInfoPtr pInfo = (InputInfoPtr)data;
EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
int ms;
if (pEvdev->emulateMB.pending)
{
ms = pEvdev->emulateMB.expires - GetTimeInMillis();
if (ms <= 0)
EvdevMBEmuTimer(pInfo);
}
}
void EvdevMBEmuBlockHandler(pointer data,
struct timeval **waitTime,
pointer LastSelectMask)
{
InputInfoPtr pInfo = (InputInfoPtr) data;
EvdevPtr pEvdev= (EvdevPtr) pInfo->private;
int ms;
if (pEvdev->emulateMB.pending)
{
ms = pEvdev->emulateMB.expires - GetTimeInMillis ();
if (ms <= 0)
ms = 0;
AdjustWaitForDelay (waitTime, ms);
}
}
void
EvdevMBEmuPreInit(InputInfoPtr pInfo)
{
EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
pEvdev->emulateMB.enabled = MBEMU_AUTO;
if (xf86FindOption(pInfo->options, "Emulate3Buttons"))
{
pEvdev->emulateMB.enabled = xf86SetBoolOption(pInfo->options,
"Emulate3Buttons",
MBEMU_ENABLED);
xf86Msg(X_INFO, "%s: Forcing middle mouse button emulation.\n",
pInfo->name);
}
pEvdev->emulateMB.timeout = xf86SetIntOption(pInfo->options,
"Emulate3Timeout", 50);
RegisterBlockAndWakeupHandlers (EvdevMBEmuBlockHandler,
EvdevMBEmuWakeupHandler,
(pointer)pInfo);
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
XIChangeDeviceProperty(pInfo->dev, prop_mbemu, XA_INTEGER, 8,
PropModeReplace, 1, &pEvdev->emulateMB.enabled,
TRUE, FALSE, FALSE);
XIChangeDeviceProperty(pInfo->dev, prop_mbtimeout, XA_INTEGER, 16,
PropModeReplace, 1, &pEvdev->emulateMB.timeout,
TRUE, FALSE, FALSE);
#endif
}
void
EvdevMBEmuFinalize(InputInfoPtr pInfo)
{
RemoveBlockAndWakeupHandlers (EvdevMBEmuBlockHandler,
EvdevMBEmuWakeupHandler,
(pointer)pInfo);
}
void
EvdevMBEmuEnable(InputInfoPtr pInfo, BOOL enable)
{
EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
if (pEvdev->emulateMB.enabled == MBEMU_AUTO)
pEvdev->emulateMB.enabled = enable;
}
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
void
EvdevMBEmuInitProperty(DeviceIntPtr dev)
{
InputInfoPtr pInfo = dev->public.devicePrivate;
EvdevPtr pEvdev = pInfo->private;
int rc = TRUE;
INT32 valid_vals[] = { MBEMU_DISABLED, MBEMU_ENABLED, MBEMU_AUTO };
if (!dev->button)
return;
prop_mbemu = MakeAtom((char*)propname_mbemu, strlen(propname_mbemu), TRUE);
rc = XIChangeDeviceProperty(dev, prop_mbemu, XA_INTEGER, 8,
PropModeReplace, 1,
&pEvdev->emulateMB.enabled,
FALSE, FALSE, FALSE);
if (rc != Success)
return;
rc = XIConfigureDeviceProperty(dev, prop_mbemu, FALSE, FALSE, FALSE, 3, valid_vals);
if (rc != Success)
return;
prop_mbtimeout = MakeAtom((char*)propname_mbtimeout,
strlen(propname_mbtimeout),
TRUE);
rc = XIChangeDeviceProperty(dev, prop_mbtimeout, XA_INTEGER, 16, PropModeReplace, 1,
&pEvdev->emulateMB.timeout, FALSE, FALSE,
FALSE);
if (rc != Success)
return;
}
BOOL
EvdevMBEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val)
{
InputInfoPtr pInfo = dev->public.devicePrivate;
EvdevPtr pEvdev = pInfo->private;
if (atom == prop_mbemu)
pEvdev->emulateMB.enabled = *((BOOL*)val->data);
else if (atom == prop_mbtimeout)
pEvdev->emulateMB.timeout = *((INT16*)val->data);
return TRUE;
}
#endif