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