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