#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <X11/Xatom.h>
#include <xf86.h>
#include <xf86Xinput.h>
#include <exevents.h>
#include <evdev-properties.h>
#include "evdev.h"
enum {
MBEMU_DISABLED = 0,
MBEMU_ENABLED,
MBEMU_AUTO
};
#ifdef HAVE_PROPERTIES
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) {
EvdevPostButtonEvent(pInfo, abs(id), (id >= 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 == 2) {
EvdevMBEmuEnable(pInfo, FALSE);
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)
{
EvdevQueueButtonEvent(pInfo, abs(id), (id >= 0));
ret = TRUE;
}
if ((id = stateTab[pEvdev->emulateMB.state][*btstate][1]) != 0)
{
EvdevQueueButtonEvent(pInfo, abs(id), (id >= 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 %s.\n",
pInfo->name, (pEvdev->emulateMB.enabled) ? "on" : "off");
}
pEvdev->emulateMB.timeout = xf86SetIntOption(pInfo->options,
"Emulate3Timeout", 50);
}
void
EvdevMBEmuOn(InputInfoPtr pInfo)
{
if (!pInfo->dev->button)
return;
RegisterBlockAndWakeupHandlers (EvdevMBEmuBlockHandler,
EvdevMBEmuWakeupHandler,
(pointer)pInfo);
}
void
EvdevMBEmuFinalize(InputInfoPtr pInfo)
{
if (!pInfo->dev->button)
return;
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;
}
#ifdef HAVE_PROPERTIES
static int
EvdevMBEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
BOOL checkonly)
{
InputInfoPtr pInfo = dev->public.devicePrivate;
EvdevPtr pEvdev = pInfo->private;
if (atom == prop_mbemu)
{
if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
return BadMatch;
if (!checkonly)
pEvdev->emulateMB.enabled = *((BOOL*)val->data);
} else if (atom == prop_mbtimeout)
{
if (val->format != 32 || val->size != 1 || val->type != XA_INTEGER)
return BadMatch;
if (!checkonly)
pEvdev->emulateMB.timeout = *((CARD32*)val->data);
}
return Success;
}
void
EvdevMBEmuInitProperty(DeviceIntPtr dev)
{
InputInfoPtr pInfo = dev->public.devicePrivate;
EvdevPtr pEvdev = pInfo->private;
int rc;
if (!dev->button)
return;
prop_mbemu = MakeAtom(EVDEV_PROP_MIDBUTTON, strlen(EVDEV_PROP_MIDBUTTON), TRUE);
rc = XIChangeDeviceProperty(dev, prop_mbemu, XA_INTEGER, 8,
PropModeReplace, 1,
&pEvdev->emulateMB.enabled,
FALSE);
if (rc != Success)
return;
XISetDevicePropertyDeletable(dev, prop_mbemu, FALSE);
prop_mbtimeout = MakeAtom(EVDEV_PROP_MIDBUTTON_TIMEOUT,
strlen(EVDEV_PROP_MIDBUTTON_TIMEOUT),
TRUE);
rc = XIChangeDeviceProperty(dev, prop_mbtimeout, XA_INTEGER, 32, PropModeReplace, 1,
&pEvdev->emulateMB.timeout, FALSE);
if (rc != Success)
return;
XISetDevicePropertyDeletable(dev, prop_mbtimeout, FALSE);
XIRegisterPropertyHandler(dev, EvdevMBEmuSetProperty, NULL, NULL);
}
#endif