emumb.c revision c57823e8
1/* $OpenBSD: emumb.c,v 1.5 2011/07/17 13:08:38 matthieu Exp $ */ 2/* 3 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. 4 * Copyright 1993 by David Dawes <dawes@xfree86.org> 5 * Copyright 2002 by SuSE Linux AG, Author: Egbert Eich 6 * Copyright 1994-2002 by The XFree86 Project, Inc. 7 * Copyright 2002 by Paul Elliott 8 * (Ported from xf86-input-mouse, above copyrights taken from there) 9 * Copyright © 2008 University of South Australia 10 * Copyright © 2009 Matthieu Herrb <matthieu@herrb.eu> 11 * 12 * Permission to use, copy, modify, distribute, and sell this software 13 * and its documentation for any purpose is hereby granted without 14 * fee, provided that the above copyright notice appear in all copies 15 * and that both that copyright notice and this permission notice 16 * appear in supporting documentation, and that the name of the authors 17 * not be used in advertising or publicity pertaining to distribution of the 18 * software without specific, written prior permission. The authors make no 19 * representations about the suitability of this software for any 20 * purpose. It is provided "as is" without express or implied 21 * warranty. 22 * 23 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 24 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN 25 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 26 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 27 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 28 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 29 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 30 * 31 */ 32 33/* Middle mouse button emulation code. */ 34 35#ifdef HAVE_CONFIG_H 36#include "config.h" 37#endif 38 39#include <xorg-server.h> 40#include <X11/Xatom.h> 41#include <xf86.h> 42#include <xf86_OSproc.h> 43#include <X11/extensions/XI.h> 44#include <X11/extensions/XIproto.h> 45#include <xf86Xinput.h> 46#include <exevents.h> 47 48#include <ws-properties.h> 49#include "ws.h" 50 51enum { 52 MBEMU_DISABLED = 0, 53 MBEMU_ENABLED, 54 MBEMU_AUTO 55}; 56 57static Atom prop_mbemu = 0; /* Middle button emulation on/off property */ 58static Atom prop_mbtimeout = 0; /* Middle button timeout property */ 59 60/* 61 * Lets create a simple finite-state machine for 3 button emulation: 62 * 63 * We track buttons 1 and 3 (left and right). There are 11 states: 64 * 0 ground - initial state 65 * 1 delayed left - left pressed, waiting for right 66 * 2 delayed right - right pressed, waiting for left 67 * 3 pressed middle - right and left pressed, emulated middle sent 68 * 4 pressed left - left pressed and sent 69 * 5 pressed right - right pressed and sent 70 * 6 released left - left released after emulated middle 71 * 7 released right - right released after emulated middle 72 * 8 repressed left - left pressed after released left 73 * 9 repressed right - right pressed after released right 74 * 10 pressed both - both pressed, not emulating middle 75 * 76 * At each state, we need handlers for the following events 77 * 0: no buttons down 78 * 1: left button down 79 * 2: right button down 80 * 3: both buttons down 81 * 4: emulate3Timeout passed without a button change 82 * Note that button events are not deltas, they are the set of buttons being 83 * pressed now. It's possible (ie, mouse hardware does it) to go from (eg) 84 * left down to right down without anything in between, so all cases must be 85 * handled. 86 * 87 * a handler consists of three values: 88 * 0: action1 89 * 1: action2 90 * 2: new emulation state 91 * 92 * action > 0: ButtonPress 93 * action = 0: nothing 94 * action < 0: ButtonRelease 95 * 96 * The comment preceeding each section is the current emulation state. 97 * The comments to the right are of the form 98 * <button state> (<events>) -> <new emulation state> 99 * which should be read as 100 * If the buttons are in <button state>, generate <events> then go to 101 * <new emulation state>. 102 */ 103static signed char stateTab[11][5][3] = { 104/* 0 ground */ 105 { 106 { 0, 0, 0 }, /* nothing -> ground (no change) */ 107 { 0, 0, 1 }, /* left -> delayed left */ 108 { 0, 0, 2 }, /* right -> delayed right */ 109 { 2, 0, 3 }, /* left & right (middle press) -> pressed middle */ 110 { 0, 0, -1 } /* timeout N/A */ 111 }, 112/* 1 delayed left */ 113 { 114 { 1, -1, 0 }, /* nothing (left event) -> ground */ 115 { 0, 0, 1 }, /* left -> delayed left (no change) */ 116 { 1, -1, 2 }, /* right (left event) -> delayed right */ 117 { 2, 0, 3 }, /* left & right (middle press) -> pressed middle */ 118 { 1, 0, 4 }, /* timeout (left press) -> pressed left */ 119 }, 120/* 2 delayed right */ 121 { 122 { 3, -3, 0 }, /* nothing (right event) -> ground */ 123 { 3, -3, 1 }, /* left (right event) -> delayed left (no change) */ 124 { 0, 0, 2 }, /* right -> delayed right (no change) */ 125 { 2, 0, 3 }, /* left & right (middle press) -> pressed middle */ 126 { 3, 0, 5 }, /* timeout (right press) -> pressed right */ 127 }, 128/* 3 pressed middle */ 129 { 130 { -2, 0, 0 }, /* nothing (middle release) -> ground */ 131 { 0, 0, 7 }, /* left -> released right */ 132 { 0, 0, 6 }, /* right -> released left */ 133 { 0, 0, 3 }, /* left & right -> pressed middle (no change) */ 134 { 0, 0, -1 }, /* timeout N/A */ 135 }, 136/* 4 pressed left */ 137 { 138 { -1, 0, 0 }, /* nothing (left release) -> ground */ 139 { 0, 0, 4 }, /* left -> pressed left (no change) */ 140 { -1, 0, 2 }, /* right (left release) -> delayed right */ 141 { 3, 0, 10 }, /* left & right (right press) -> pressed both */ 142 { 0, 0, -1 }, /* timeout N/A */ 143 }, 144/* 5 pressed right */ 145 { 146 { -3, 0, 0 }, /* nothing (right release) -> ground */ 147 { -3, 0, 1 }, /* left (right release) -> delayed left */ 148 { 0, 0, 5 }, /* right -> pressed right (no change) */ 149 { 1, 0, 10 }, /* left & right (left press) -> pressed both */ 150 { 0, 0, -1 }, /* timeout N/A */ 151 }, 152/* 6 released left */ 153 { 154 { -2, 0, 0 }, /* nothing (middle release) -> ground */ 155 { -2, 0, 1 }, /* left (middle release) -> delayed left */ 156 { 0, 0, 6 }, /* right -> released left (no change) */ 157 { 1, 0, 8 }, /* left & right (left press) -> repressed left */ 158 { 0, 0, -1 }, /* timeout N/A */ 159 }, 160/* 7 released right */ 161 { 162 { -2, 0, 0 }, /* nothing (middle release) -> ground */ 163 { 0, 0, 7 }, /* left -> released right (no change) */ 164 { -2, 0, 2 }, /* right (middle release) -> delayed right */ 165 { 3, 0, 9 }, /* left & right (right press) -> repressed right */ 166 { 0, 0, -1 }, /* timeout N/A */ 167 }, 168/* 8 repressed left */ 169 { 170 { -2, -1, 0 }, /* nothing (middle release, left release) -> ground */ 171 { -2, 0, 4 }, /* left (middle release) -> pressed left */ 172 { -1, 0, 6 }, /* right (left release) -> released left */ 173 { 0, 0, 8 }, /* left & right -> repressed left (no change) */ 174 { 0, 0, -1 }, /* timeout N/A */ 175 }, 176/* 9 repressed right */ 177 { 178 { -2, -3, 0 }, /* nothing (middle release, right release) -> ground */ 179 { -3, 0, 7 }, /* left (right release) -> released right */ 180 { -2, 0, 5 }, /* right (middle release) -> pressed right */ 181 { 0, 0, 9 }, /* left & right -> repressed right (no change) */ 182 { 0, 0, -1 }, /* timeout N/A */ 183 }, 184/* 10 pressed both */ 185 { 186 { -1, -3, 0 }, /* nothing (left release, right release) -> ground */ 187 { -3, 0, 4 }, /* left (right release) -> pressed left */ 188 { -1, 0, 5 }, /* right (left release) -> pressed right */ 189 { 0, 0, 10 }, /* left & right -> pressed both (no change) */ 190 { 0, 0, -1 }, /* timeout N/A */ 191 }, 192}; 193 194 195int 196wsmbEmuTimer(InputInfoPtr pInfo) 197{ 198 WSDevicePtr priv = pInfo->private; 199 int sigstate; 200 int id; 201 202 sigstate = xf86BlockSIGIO(); 203 204 priv->emulateMB.pending = FALSE; 205 if ((id = stateTab[priv->emulateMB.state][4][0]) != 0) { 206 xf86PostButtonEvent(pInfo->dev, 0, abs(id), (id >= 0), 0, 0); 207 priv->emulateMB.state = 208 stateTab[priv->emulateMB.state][4][2]; 209 } else { 210 ErrorF("Got unexpected buttonTimer in state %d\n", 211 priv->emulateMB.state); 212 } 213 214 xf86UnblockSIGIO(sigstate); 215 return 0; 216} 217 218 219/** 220 * Emulate a middle button on button press. 221 * 222 * @param code button number (1 for left, 3 for right) 223 * @param press TRUE if press, FALSE if release. 224 * 225 * @return TRUE if event was swallowed by middle mouse button emulation, FALSE 226 * otherwise. 227 */ 228BOOL 229wsmbEmuFilterEvent(InputInfoPtr pInfo, int button, BOOL press) 230{ 231 WSDevicePtr priv = pInfo->private; 232 int id; 233 int *btstate; 234 int ret = FALSE; 235 236 if (!priv->emulateMB.enabled) 237 return ret; 238 239 if (button == 2) { 240 wsmbEmuEnable(pInfo, FALSE); 241 return ret; 242 } 243 244 /* don't care about other buttons */ 245 if (button != 1 && button != 3) 246 return ret; 247 248 btstate = &priv->emulateMB.buttonstate; 249 if (press) 250 *btstate |= (button == 1) ? 0x1 : 0x2; 251 else 252 *btstate &= (button == 1) ? ~0x1 : ~0x2; 253 254 if ((id = stateTab[priv->emulateMB.state][*btstate][0]) != 0) { 255 xf86PostButtonEvent(pInfo->dev, 0, abs(id), (id >= 0), 0, 0); 256 ret = TRUE; 257 } 258 if ((id = stateTab[priv->emulateMB.state][*btstate][1]) != 0) { 259 xf86PostButtonEvent(pInfo->dev, 0, abs(id), (id >= 0), 0, 0); 260 ret = TRUE; 261 } 262 263 priv->emulateMB.state = stateTab[priv->emulateMB.state][*btstate][2]; 264 265 if (stateTab[priv->emulateMB.state][4][0] != 0) { 266 priv->emulateMB.expires = GetTimeInMillis() 267 + priv->emulateMB.timeout; 268 priv->emulateMB.pending = TRUE; 269 ret = TRUE; 270 } else { 271 priv->emulateMB.pending = FALSE; 272 } 273 return ret; 274} 275 276 277void 278wsmbEmuWakeupHandler(pointer data, 279 int i, 280 pointer LastSelectMask) 281{ 282 InputInfoPtr pInfo = (InputInfoPtr)data; 283 WSDevicePtr priv = (WSDevicePtr)pInfo->private; 284 int ms; 285 286 if (priv->emulateMB.pending) { 287 ms = priv->emulateMB.expires - GetTimeInMillis(); 288 if (ms <= 0) 289 wsmbEmuTimer(pInfo); 290 } 291} 292 293void 294wsmbEmuBlockHandler(pointer data, 295 struct timeval **waitTime, 296 pointer LastSelectMask) 297{ 298 InputInfoPtr pInfo = (InputInfoPtr) data; 299 WSDevicePtr priv= (WSDevicePtr) pInfo->private; 300 int ms; 301 302 if (priv->emulateMB.pending) { 303 ms = priv->emulateMB.expires - GetTimeInMillis(); 304 if (ms <= 0) 305 ms = 0; 306 AdjustWaitForDelay(waitTime, ms); 307 } 308} 309 310void 311wsmbEmuPreInit(InputInfoPtr pInfo) 312{ 313 WSDevicePtr priv = (WSDevicePtr)pInfo->private; 314 priv->emulateMB.enabled = MBEMU_AUTO; 315 316 DBG(1, ErrorF("wsmbEmuPreInit\n")); 317 if (xf86FindOption(pInfo->options, "Emulate3Buttons")) { 318 priv->emulateMB.enabled = xf86SetBoolOption(pInfo->options, 319 "Emulate3Buttons", 320 MBEMU_ENABLED); 321 xf86Msg(X_INFO, "%s: Forcing middle mouse button " 322 "emulation %s.\n", 323 pInfo->name, (priv->emulateMB.enabled) ? "on" : "off"); 324 } 325 326 priv->emulateMB.timeout = xf86SetIntOption(pInfo->options, 327 "Emulate3Timeout", 50); 328} 329 330void 331wsmbEmuOn(InputInfoPtr pInfo) 332{ 333 RegisterBlockAndWakeupHandlers(wsmbEmuBlockHandler, 334 wsmbEmuWakeupHandler, (pointer)pInfo); 335} 336 337void 338wsmbEmuFinalize(InputInfoPtr pInfo) 339{ 340 RemoveBlockAndWakeupHandlers(wsmbEmuBlockHandler, 341 wsmbEmuWakeupHandler, (pointer)pInfo); 342 343} 344 345/* Enable/disable middle mouse button emulation. */ 346void 347wsmbEmuEnable(InputInfoPtr pInfo, BOOL enable) 348{ 349 WSDevicePtr priv = (WSDevicePtr)pInfo->private; 350 351 if (priv->emulateMB.enabled == MBEMU_AUTO) 352 priv->emulateMB.enabled = enable; 353} 354 355 356static int 357wsmbEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, 358 BOOL checkonly) 359{ 360 InputInfoPtr pInfo = dev->public.devicePrivate; 361 WSDevicePtr priv = pInfo->private; 362 363 DBG(1, ErrorF("wsmbEmuSetProperty %s\n", NameForAtom(atom))); 364 365 if (atom == prop_mbemu) { 366 if (val->format != 8 || val->size != 1 || 367 val->type != XA_INTEGER) 368 return BadMatch; 369 370 if (!checkonly) 371 priv->emulateMB.enabled = *((BOOL*)val->data); 372 } else if (atom == prop_mbtimeout) { 373 if (val->format != 32 || val->size != 1 || 374 val->type != XA_INTEGER) 375 return BadMatch; 376 377 if (!checkonly) 378 priv->emulateMB.timeout = *((CARD32*)val->data); 379 } 380 381 return Success; 382} 383 384/** 385 * Initialise property for MB emulation on/off. 386 */ 387void 388wsmbEmuInitProperty(DeviceIntPtr dev) 389{ 390 InputInfoPtr pInfo = dev->public.devicePrivate; 391 WSDevicePtr priv = pInfo->private; 392 int rc; 393 394 DBG(1, ErrorF("wsmbEmuInitProperty\n")); 395 396 if (!dev->button) /* don't init prop for keyboards */ 397 return; 398 399 prop_mbemu = MakeAtom(WS_PROP_MIDBUTTON, 400 strlen(WS_PROP_MIDBUTTON), TRUE); 401 rc = XIChangeDeviceProperty(dev, prop_mbemu, XA_INTEGER, 8, 402 PropModeReplace, 1, &priv->emulateMB.enabled, FALSE); 403 if (rc != Success) { 404 xf86Msg(X_ERROR, "cannot create device property %s: %d\n", 405 WS_PROP_MIDBUTTON, rc); 406 return; 407 } 408 XISetDevicePropertyDeletable(dev, prop_mbemu, FALSE); 409 410 prop_mbtimeout = MakeAtom(WS_PROP_MIDBUTTON_TIMEOUT, 411 strlen(WS_PROP_MIDBUTTON_TIMEOUT), 412 TRUE); 413 rc = XIChangeDeviceProperty(dev, prop_mbtimeout, 414 XA_INTEGER, 32, PropModeReplace, 1, 415 &priv->emulateMB.timeout, FALSE); 416 417 if (rc != Success) { 418 xf86Msg(X_ERROR, "cannot create device property %s\n", 419 WS_PROP_MIDBUTTON_TIMEOUT); 420 return; 421 } 422 XISetDevicePropertyDeletable(dev, prop_mbtimeout, FALSE); 423 424 XIRegisterPropertyHandler(dev, wsmbEmuSetProperty, NULL, NULL); 425} 426