2 * Copyright © 2011 Red Hat, Inc.
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of the authors
9 * not be used in advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission. The authors make no
11 * representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied
15 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 /* Right mouse button emulation code.
26 * Emulates a right button event if the first button is held down for a
27 * timeout. If the device moves more than a certain amount before the
28 * timeout is over, the emulation is cancelled and a normal button event is
38 #include <X11/Xatom.h>
40 #include <xf86Xinput.h>
43 #include <evdev-properties.h>
45 /* Threshold (in device coordinates) for devices to cancel emulation */
46 #define DEFAULT_MOVE_THRESHOLD 20
48 static Atom prop_3bemu; /* Right button emulation on/off property */
49 static Atom prop_3btimeout; /* Right button timeout property */
50 static Atom prop_3bbutton; /* Right button target physical button */
51 static Atom prop_3bthreshold; /* Right button move cancellation threshold */
53 /* State machine for 3rd button emulation */
55 EM3B_OFF, /* no event */
56 EM3B_PENDING, /* timer pending */
57 EM3B_EMULATING /* in emulation */
61 Evdev3BEmuPostButtonEvent(InputInfoPtr pInfo, int button, enum ButtonAction act)
63 EvdevPtr pEvdev = pInfo->private;
64 struct emulate3B *emu3B = &pEvdev->emulate3B;
65 int absolute = Relative;
67 /* if we cancel, emit the button down event at our start position,
68 * not at the current position. Only for absolute devices though. For
69 * relative events, this may be a bit iffy since pointer accel may shoot
70 * us back more than we moved and confuse the user.
72 if (emu3B->flags & EVDEV_ABSOLUTE_EVENTS)
75 xf86PostButtonEventP(pInfo->dev, absolute, button,
76 (act == BUTTON_PRESS) ? 1 : 0, 0,
77 (absolute ? 2 : 0), emu3B->startpos);
82 * Timer function. Post a button down event to the server.
84 * @param arg The InputInfoPtr for this device.
87 Evdev3BEmuTimer(OsTimerPtr timer, CARD32 time, pointer arg)
89 InputInfoPtr pInfo = (InputInfoPtr)arg;
90 EvdevPtr pEvdev = pInfo->private;
91 struct emulate3B *emu3B = &pEvdev->emulate3B;
94 sigstate = xf86BlockSIGIO ();
95 emu3B->state = EM3B_EMULATING;
96 Evdev3BEmuPostButtonEvent(pInfo, emu3B->button, BUTTON_PRESS);
97 xf86UnblockSIGIO (sigstate);
103 * Cancel all emulation, reset the timer and reset deltas.
106 Evdev3BCancel(InputInfoPtr pInfo)
108 EvdevPtr pEvdev = pInfo->private;
109 struct emulate3B *emu3B = &pEvdev->emulate3B;
111 if (emu3B->state != EM3B_OFF)
113 TimerCancel(emu3B->timer);
114 emu3B->state = EM3B_OFF;
115 memset(emu3B->delta, 0, sizeof(emu3B->delta));
122 * Emulate a third button on button press. Note that emulation only triggers
125 * Return TRUE if event was swallowed by middle mouse button emulation,
129 Evdev3BEmuFilterEvent(InputInfoPtr pInfo, int button, BOOL press)
131 EvdevPtr pEvdev = pInfo->private;
132 struct emulate3B *emu3B = &pEvdev->emulate3B;
139 emu3B->buttonstate |= button;
141 emu3B->buttonstate &= ~button;
143 /* Any other button pressed? Cancel timer */
146 switch (emu3B->state)
149 Evdev3BEmuPostButtonEvent(pInfo, 1, BUTTON_PRESS);
150 Evdev3BCancel(pInfo);
153 /* We're emulating and now the user pressed a different
154 * button. Just release the emulating one, tell the user to
155 * not do that and get on with life */
156 Evdev3BEmuPostButtonEvent(pInfo, emu3B->button, BUTTON_RELEASE);
157 Evdev3BCancel(pInfo);
165 /* Don't emulate if any other button is down */
166 if ((emu3B->buttonstate & ~0x1) != 0)
169 /* Release event → cancel, send press and release now. */
175 Evdev3BEmuPostButtonEvent(pInfo, 1, BUTTON_PRESS);
176 Evdev3BCancel(pInfo);
179 Evdev3BEmuPostButtonEvent(pInfo, emu3B->button, BUTTON_RELEASE);
180 Evdev3BCancel(pInfo);
190 if (press && emu3B->state == EM3B_OFF)
192 emu3B->state = EM3B_PENDING;
193 emu3B->timer = TimerSet(emu3B->timer, 0, emu3B->timeout,
194 Evdev3BEmuTimer, pInfo);
204 * Handle absolute x/y motion. If the motion is above the threshold, cancel
208 Evdev3BEmuProcessAbsMotion(InputInfoPtr pInfo, ValuatorMask *vals)
210 EvdevPtr pEvdev = pInfo->private;
211 struct emulate3B *emu3B = &pEvdev->emulate3B;
215 if (emu3B->state != EM3B_PENDING)
217 if (valuator_mask_isset(vals, 0))
218 emu3B->startpos[0] = valuator_mask_get(vals, 0);
219 if (valuator_mask_isset(vals, 1))
220 emu3B->startpos[1] = valuator_mask_get(vals, 1);
225 if ((emu3B->flags & EVDEV_ABSOLUTE_EVENTS) == 0)
226 emu3B->flags |= EVDEV_ABSOLUTE_EVENTS;
228 while (axis <= 1 && !cancel)
230 if (valuator_mask_isset(vals, axis))
232 int delta = valuator_mask_get(vals, axis) - emu3B->startpos[axis];
233 if (abs(delta) > emu3B->threshold)
241 Evdev3BEmuPostButtonEvent(pInfo, 1, BUTTON_PRESS);
242 Evdev3BCancel(pInfo);
247 * Handle relative x/y motion. If the motion is above the threshold, cancel
251 Evdev3BEmuProcessRelMotion(InputInfoPtr pInfo, int dx, int dy)
253 EvdevPtr pEvdev = pInfo->private;
254 struct emulate3B *emu3B = &pEvdev->emulate3B;
256 if (emu3B->state != EM3B_PENDING)
259 emu3B->delta[0] += dx;
260 emu3B->delta[1] += dy;
261 emu3B->flags |= EVDEV_RELATIVE_EVENTS;
263 if (abs(emu3B->delta[0]) > emu3B->threshold ||
264 abs(emu3B->delta[1]) > emu3B->threshold)
266 Evdev3BEmuPostButtonEvent(pInfo, 1, BUTTON_PRESS);
267 Evdev3BCancel(pInfo);
272 Evdev3BEmuPreInit(InputInfoPtr pInfo)
274 EvdevPtr pEvdev = pInfo->private;
275 struct emulate3B *emu3B = &pEvdev->emulate3B;
277 emu3B->enabled = xf86SetBoolOption(pInfo->options,
278 "EmulateThirdButton",
280 emu3B->timeout = xf86SetIntOption(pInfo->options,
281 "EmulateThirdButtonTimeout",
283 emu3B->button = xf86SetIntOption(pInfo->options,
284 "EmulateThirdButtonButton",
286 /* FIXME: this should be auto-configured based on axis ranges */
287 emu3B->threshold = xf86SetIntOption(pInfo->options,
288 "EmulateThirdButtonMoveThreshold",
289 DEFAULT_MOVE_THRESHOLD);
290 /* allocate now so we don't allocate in the signal handler */
291 emu3B->timer = TimerSet(NULL, 0, 0, NULL, NULL);
295 Evdev3BEmuOn(InputInfoPtr pInfo)
297 /* This function just exists for symmetry in evdev.c */
301 Evdev3BEmuFinalize(InputInfoPtr pInfo)
303 EvdevPtr pEvdev = pInfo->private;
304 struct emulate3B *emu3B = &pEvdev->emulate3B;
306 TimerFree(emu3B->timer);
311 Evdev3BEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
314 InputInfoPtr pInfo = dev->public.devicePrivate;
315 EvdevPtr pEvdev = pInfo->private;
316 struct emulate3B *emu3B = &pEvdev->emulate3B;
318 if (atom == prop_3bemu)
320 if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
324 emu3B->enabled = *((BOOL*)val->data);
326 } else if (atom == prop_3btimeout)
328 if (val->format != 32 || val->size != 1 || val->type != XA_INTEGER)
332 emu3B->timeout = *((CARD32*)val->data);
334 } else if (atom == prop_3bbutton)
336 if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
340 emu3B->button = *((CARD8*)val->data);
341 } else if (atom == prop_3bthreshold)
343 if (val->format != 32 || val->size != 1 || val->type != XA_INTEGER)
347 emu3B->threshold = *((CARD32*)val->data);
355 * Initialise properties for third button emulation
358 Evdev3BEmuInitProperty(DeviceIntPtr dev)
360 InputInfoPtr pInfo = dev->public.devicePrivate;
361 EvdevPtr pEvdev = pInfo->private;
362 struct emulate3B *emu3B = &pEvdev->emulate3B;
365 if (!dev->button) /* don't init prop for keyboards */
368 /* third button emulation on/off */
369 prop_3bemu = MakeAtom(EVDEV_PROP_THIRDBUTTON, strlen(EVDEV_PROP_THIRDBUTTON), TRUE);
370 rc = XIChangeDeviceProperty(dev, prop_3bemu, XA_INTEGER, 8,
377 XISetDevicePropertyDeletable(dev, prop_3bemu, FALSE);
379 /* third button emulation timeout */
380 prop_3btimeout = MakeAtom(EVDEV_PROP_THIRDBUTTON_TIMEOUT,
381 strlen(EVDEV_PROP_THIRDBUTTON_TIMEOUT),
383 rc = XIChangeDeviceProperty(dev, prop_3btimeout, XA_INTEGER, 32, PropModeReplace, 1,
384 &emu3B->timeout, FALSE);
389 XISetDevicePropertyDeletable(dev, prop_3btimeout, FALSE);
391 /* third button emulation button to be triggered */
392 prop_3bbutton = MakeAtom(EVDEV_PROP_THIRDBUTTON_BUTTON,
393 strlen(EVDEV_PROP_THIRDBUTTON_BUTTON),
395 rc = XIChangeDeviceProperty(dev, prop_3bbutton, XA_INTEGER, 8, PropModeReplace, 1,
396 &emu3B->button, FALSE);
401 XISetDevicePropertyDeletable(dev, prop_3bbutton, FALSE);
403 /* third button emulation movement threshold */
404 prop_3bthreshold = MakeAtom(EVDEV_PROP_THIRDBUTTON_THRESHOLD,
405 strlen(EVDEV_PROP_THIRDBUTTON_THRESHOLD),
407 rc = XIChangeDeviceProperty(dev, prop_3bthreshold, XA_INTEGER, 32, PropModeReplace, 1,
408 &emu3B->threshold, FALSE);
413 XISetDevicePropertyDeletable(dev, prop_3bthreshold, FALSE);
415 XIRegisterPropertyHandler(dev, Evdev3BEmuSetProperty, NULL, NULL);