mieq.c revision 1b5d61b8
1/* 2 * 3Copyright 1990, 1998 The Open Group 4 5Permission to use, copy, modify, distribute, and sell this software and its 6documentation for any purpose is hereby granted without fee, provided that 7the above copyright notice appear in all copies and that both that 8copyright notice and this permission notice appear in supporting 9documentation. 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21Except as contained in this notice, the name of The Open Group shall not be 22used in advertising or otherwise to promote the sale, use or other dealings 23in this Software without prior written authorization from The Open Group. 24 * 25 * Author: Keith Packard, MIT X Consortium 26 */ 27 28/* 29 * mieq.c 30 * 31 * Machine independent event queue 32 * 33 */ 34 35#if HAVE_DIX_CONFIG_H 36#include <dix-config.h> 37#endif 38 39#include <X11/X.h> 40#include <X11/Xmd.h> 41#include <X11/Xproto.h> 42#include "misc.h" 43#include "windowstr.h" 44#include "pixmapstr.h" 45#include "inputstr.h" 46#include "inpututils.h" 47#include "mi.h" 48#include "mipointer.h" 49#include "scrnintstr.h" 50#include <X11/extensions/XI.h> 51#include <X11/extensions/XIproto.h> 52#include <X11/extensions/geproto.h> 53#include "extinit.h" 54#include "exglobals.h" 55#include "eventstr.h" 56 57#ifdef DPMSExtension 58#include "dpmsproc.h" 59#include <X11/extensions/dpmsconst.h> 60#endif 61 62/* Maximum size should be initial size multiplied by a power of 2 */ 63#define QUEUE_INITIAL_SIZE 512 64#define QUEUE_RESERVED_SIZE 64 65#define QUEUE_MAXIMUM_SIZE 4096 66#define QUEUE_DROP_BACKTRACE_FREQUENCY 100 67#define QUEUE_DROP_BACKTRACE_MAX 10 68 69#define EnqueueScreen(dev) dev->spriteInfo->sprite->pEnqueueScreen 70#define DequeueScreen(dev) dev->spriteInfo->sprite->pDequeueScreen 71 72typedef struct _Event { 73 InternalEvent *events; 74 ScreenPtr pScreen; 75 DeviceIntPtr pDev; /* device this event _originated_ from */ 76} EventRec, *EventPtr; 77 78typedef struct _EventQueue { 79 HWEventQueueType head, tail; /* long for SetInputCheck */ 80 CARD32 lastEventTime; /* to avoid time running backwards */ 81 int lastMotion; /* device ID if last event motion? */ 82 EventRec *events; /* our queue as an array */ 83 size_t nevents; /* the number of buckets in our queue */ 84 size_t dropped; /* counter for number of consecutive dropped events */ 85 mieqHandler handlers[128]; /* custom event handler */ 86} EventQueueRec, *EventQueuePtr; 87 88static EventQueueRec miEventQueue; 89 90static size_t 91mieqNumEnqueued(EventQueuePtr eventQueue) 92{ 93 size_t n_enqueued = 0; 94 95 if (eventQueue->nevents) { 96 /* % is not well-defined with negative numbers... sigh */ 97 n_enqueued = eventQueue->tail - eventQueue->head + eventQueue->nevents; 98 if (n_enqueued >= eventQueue->nevents) 99 n_enqueued -= eventQueue->nevents; 100 } 101 return n_enqueued; 102} 103 104/* Pre-condition: Called with input_lock held */ 105static Bool 106mieqGrowQueue(EventQueuePtr eventQueue, size_t new_nevents) 107{ 108 size_t i, n_enqueued, first_hunk; 109 EventRec *new_events; 110 111 if (!eventQueue) { 112 ErrorF("[mi] mieqGrowQueue called with a NULL eventQueue\n"); 113 return FALSE; 114 } 115 116 if (new_nevents <= eventQueue->nevents) 117 return FALSE; 118 119 new_events = calloc(new_nevents, sizeof(EventRec)); 120 if (new_events == NULL) { 121 ErrorF("[mi] mieqGrowQueue memory allocation error.\n"); 122 return FALSE; 123 } 124 125 n_enqueued = mieqNumEnqueued(eventQueue); 126 127 /* First copy the existing events */ 128 first_hunk = eventQueue->nevents - eventQueue->head; 129 memcpy(new_events, 130 &eventQueue->events[eventQueue->head], 131 first_hunk * sizeof(EventRec)); 132 memcpy(&new_events[first_hunk], 133 eventQueue->events, eventQueue->head * sizeof(EventRec)); 134 135 /* Initialize the new portion */ 136 for (i = eventQueue->nevents; i < new_nevents; i++) { 137 InternalEvent *evlist = InitEventList(1); 138 139 if (!evlist) { 140 size_t j; 141 142 for (j = 0; j < i; j++) 143 FreeEventList(new_events[j].events, 1); 144 free(new_events); 145 return FALSE; 146 } 147 new_events[i].events = evlist; 148 } 149 150 /* And update our record */ 151 eventQueue->tail = n_enqueued; 152 eventQueue->head = 0; 153 eventQueue->nevents = new_nevents; 154 free(eventQueue->events); 155 eventQueue->events = new_events; 156 157 return TRUE; 158} 159 160Bool 161mieqInit(void) 162{ 163 memset(&miEventQueue, 0, sizeof(miEventQueue)); 164 miEventQueue.lastEventTime = GetTimeInMillis(); 165 166 input_lock(); 167 if (!mieqGrowQueue(&miEventQueue, QUEUE_INITIAL_SIZE)) 168 FatalError("Could not allocate event queue.\n"); 169 input_unlock(); 170 171 SetInputCheck(&miEventQueue.head, &miEventQueue.tail); 172 return TRUE; 173} 174 175void 176mieqFini(void) 177{ 178 int i; 179 180 for (i = 0; i < miEventQueue.nevents; i++) { 181 if (miEventQueue.events[i].events != NULL) { 182 FreeEventList(miEventQueue.events[i].events, 1); 183 miEventQueue.events[i].events = NULL; 184 } 185 } 186 free(miEventQueue.events); 187} 188 189/* 190 * Must be reentrant with ProcessInputEvents. Assumption: mieqEnqueue 191 * will never be interrupted. Must be called with input_lock held 192 */ 193 194void 195mieqEnqueue(DeviceIntPtr pDev, InternalEvent *e) 196{ 197 unsigned int oldtail = miEventQueue.tail; 198 InternalEvent *evt; 199 int isMotion = 0; 200 int evlen; 201 Time time; 202 size_t n_enqueued; 203 204 verify_internal_event(e); 205 206 n_enqueued = mieqNumEnqueued(&miEventQueue); 207 208 /* avoid merging events from different devices */ 209 if (e->any.type == ET_Motion) 210 isMotion = pDev->id; 211 212 if (isMotion && isMotion == miEventQueue.lastMotion && 213 oldtail != miEventQueue.head) { 214 oldtail = (oldtail - 1) % miEventQueue.nevents; 215 } 216 else if (n_enqueued + 1 == miEventQueue.nevents) { 217 if (!mieqGrowQueue(&miEventQueue, miEventQueue.nevents << 1)) { 218 /* Toss events which come in late. Usually this means your server's 219 * stuck in an infinite loop in the main thread. 220 */ 221 miEventQueue.dropped++; 222 if (miEventQueue.dropped == 1) { 223 ErrorFSigSafe("[mi] EQ overflowing. Additional events will be " 224 "discarded until existing events are processed.\n"); 225 xorg_backtrace(); 226 ErrorFSigSafe("[mi] These backtraces from mieqEnqueue may point to " 227 "a culprit higher up the stack.\n"); 228 ErrorFSigSafe("[mi] mieq is *NOT* the cause. It is a victim.\n"); 229 } 230 else if (miEventQueue.dropped % QUEUE_DROP_BACKTRACE_FREQUENCY == 0 && 231 miEventQueue.dropped / QUEUE_DROP_BACKTRACE_FREQUENCY <= 232 QUEUE_DROP_BACKTRACE_MAX) { 233 ErrorFSigSafe("[mi] EQ overflow continuing. %zu events have been " 234 "dropped.\n", miEventQueue.dropped); 235 if (miEventQueue.dropped / QUEUE_DROP_BACKTRACE_FREQUENCY == 236 QUEUE_DROP_BACKTRACE_MAX) { 237 ErrorFSigSafe("[mi] No further overflow reports will be " 238 "reported until the clog is cleared.\n"); 239 } 240 xorg_backtrace(); 241 } 242 return; 243 } 244 oldtail = miEventQueue.tail; 245 } 246 247 evlen = e->any.length; 248 evt = miEventQueue.events[oldtail].events; 249 memcpy(evt, e, evlen); 250 251 time = e->any.time; 252 /* Make sure that event times don't go backwards - this 253 * is "unnecessary", but very useful. */ 254 if (time < miEventQueue.lastEventTime && 255 miEventQueue.lastEventTime - time < 10000) 256 e->any.time = miEventQueue.lastEventTime; 257 258 miEventQueue.lastEventTime = evt->any.time; 259 miEventQueue.events[oldtail].pScreen = pDev ? EnqueueScreen(pDev) : NULL; 260 miEventQueue.events[oldtail].pDev = pDev; 261 262 miEventQueue.lastMotion = isMotion; 263 miEventQueue.tail = (oldtail + 1) % miEventQueue.nevents; 264} 265 266/** 267 * Changes the screen reference events are being enqueued from. 268 * Input events are enqueued with a screen reference and dequeued and 269 * processed with a (potentially different) screen reference. 270 * This function is called whenever a new event has changed screen but is 271 * still logically on the previous screen as seen by the client. 272 * This usually happens whenever the visible cursor moves across screen 273 * boundaries during event generation, before the same event is processed 274 * and sent down the wire. 275 * 276 * @param pDev The device that triggered a screen change. 277 * @param pScreen The new screen events are being enqueued for. 278 * @param set_dequeue_screen If TRUE, pScreen is set as both enqueue screen 279 * and dequeue screen. 280 */ 281void 282mieqSwitchScreen(DeviceIntPtr pDev, ScreenPtr pScreen, Bool set_dequeue_screen) 283{ 284 EnqueueScreen(pDev) = pScreen; 285 if (set_dequeue_screen) 286 DequeueScreen(pDev) = pScreen; 287} 288 289void 290mieqSetHandler(int event, mieqHandler handler) 291{ 292 if (handler && miEventQueue.handlers[event]) 293 ErrorF("[mi] mieq: warning: overriding existing handler %p with %p for " 294 "event %d\n", miEventQueue.handlers[event], handler, event); 295 296 miEventQueue.handlers[event] = handler; 297} 298 299/** 300 * Change the device id of the given event to the given device's id. 301 */ 302static void 303ChangeDeviceID(DeviceIntPtr dev, InternalEvent *event) 304{ 305 switch (event->any.type) { 306 case ET_Motion: 307 case ET_KeyPress: 308 case ET_KeyRelease: 309 case ET_ButtonPress: 310 case ET_ButtonRelease: 311 case ET_ProximityIn: 312 case ET_ProximityOut: 313 case ET_Hierarchy: 314 case ET_DeviceChanged: 315 case ET_TouchBegin: 316 case ET_TouchUpdate: 317 case ET_TouchEnd: 318 event->device_event.deviceid = dev->id; 319 break; 320 case ET_TouchOwnership: 321 event->touch_ownership_event.deviceid = dev->id; 322 break; 323#ifdef XFreeXDGA 324 case ET_DGAEvent: 325 break; 326#endif 327 case ET_RawKeyPress: 328 case ET_RawKeyRelease: 329 case ET_RawButtonPress: 330 case ET_RawButtonRelease: 331 case ET_RawMotion: 332 case ET_RawTouchBegin: 333 case ET_RawTouchEnd: 334 case ET_RawTouchUpdate: 335 event->raw_event.deviceid = dev->id; 336 break; 337 case ET_BarrierHit: 338 case ET_BarrierLeave: 339 event->barrier_event.deviceid = dev->id; 340 break; 341 default: 342 ErrorF("[mi] Unknown event type (%d), cannot change id.\n", 343 event->any.type); 344 } 345} 346 347static void 348FixUpEventForMaster(DeviceIntPtr mdev, DeviceIntPtr sdev, 349 InternalEvent *original, InternalEvent *master) 350{ 351 verify_internal_event(original); 352 verify_internal_event(master); 353 /* Ensure chained button mappings, i.e. that the detail field is the 354 * value of the mapped button on the SD, not the physical button */ 355 if (original->any.type == ET_ButtonPress || 356 original->any.type == ET_ButtonRelease) { 357 int btn = original->device_event.detail.button; 358 359 if (!sdev->button) 360 return; /* Should never happen */ 361 362 master->device_event.detail.button = sdev->button->map[btn]; 363 } 364} 365 366/** 367 * Copy the given event into master. 368 * @param sdev The slave device the original event comes from 369 * @param original The event as it came from the EQ 370 * @param copy The event after being copied 371 * @return The master device or NULL if the device is a floating slave. 372 */ 373DeviceIntPtr 374CopyGetMasterEvent(DeviceIntPtr sdev, 375 InternalEvent *original, InternalEvent *copy) 376{ 377 DeviceIntPtr mdev; 378 int len = original->any.length; 379 int type = original->any.type; 380 int mtype; /* which master type? */ 381 382 verify_internal_event(original); 383 384 /* ET_XQuartz has sdev == NULL */ 385 if (!sdev || IsMaster(sdev) || IsFloating(sdev)) 386 return NULL; 387 388#ifdef XFreeXDGA 389 if (type == ET_DGAEvent) 390 type = original->dga_event.subtype; 391#endif 392 393 switch (type) { 394 case ET_KeyPress: 395 case ET_KeyRelease: 396 mtype = MASTER_KEYBOARD; 397 break; 398 case ET_ButtonPress: 399 case ET_ButtonRelease: 400 case ET_Motion: 401 case ET_ProximityIn: 402 case ET_ProximityOut: 403 mtype = MASTER_POINTER; 404 break; 405 default: 406 mtype = MASTER_ATTACHED; 407 break; 408 } 409 410 mdev = GetMaster(sdev, mtype); 411 memcpy(copy, original, len); 412 ChangeDeviceID(mdev, copy); 413 FixUpEventForMaster(mdev, sdev, original, copy); 414 415 return mdev; 416} 417 418static void 419mieqMoveToNewScreen(DeviceIntPtr dev, ScreenPtr screen, DeviceEvent *event) 420{ 421 if (dev && screen && screen != DequeueScreen(dev)) { 422 int x = 0, y = 0; 423 424 DequeueScreen(dev) = screen; 425 x = event->root_x; 426 y = event->root_y; 427 NewCurrentScreen(dev, DequeueScreen(dev), x, y); 428 } 429} 430 431/** 432 * Post the given @event through the device hierarchy, as appropriate. 433 * Use this function if an event must be posted for a given device during the 434 * usual event processing cycle. 435 */ 436void 437mieqProcessDeviceEvent(DeviceIntPtr dev, InternalEvent *event, ScreenPtr screen) 438{ 439 mieqHandler handler; 440 DeviceIntPtr master; 441 InternalEvent mevent; /* master event */ 442 443 verify_internal_event(event); 444 445 /* refuse events from disabled devices */ 446 if (dev && !dev->enabled) 447 return; 448 449 /* Custom event handler */ 450 handler = miEventQueue.handlers[event->any.type]; 451 452 switch (event->any.type) { 453 /* Catch events that include valuator information and check if they 454 * are changing the screen */ 455 case ET_Motion: 456 case ET_KeyPress: 457 case ET_KeyRelease: 458 case ET_ButtonPress: 459 case ET_ButtonRelease: 460 if (!handler) 461 mieqMoveToNewScreen(dev, screen, &event->device_event); 462 break; 463 case ET_TouchBegin: 464 case ET_TouchUpdate: 465 case ET_TouchEnd: 466 if (!handler && (event->device_event.flags & TOUCH_POINTER_EMULATED)) 467 mieqMoveToNewScreen(dev, screen, &event->device_event); 468 break; 469 default: 470 break; 471 } 472 master = CopyGetMasterEvent(dev, event, &mevent); 473 474 if (master) 475 master->lastSlave = dev; 476 477 /* If someone's registered a custom event handler, let them 478 * steal it. */ 479 if (handler) { 480 int screenNum = dev && 481 DequeueScreen(dev) ? DequeueScreen(dev)->myNum : (screen ? screen-> 482 myNum : 0); 483 handler(screenNum, event, dev); 484 /* Check for the SD's master in case the device got detached 485 * during event processing */ 486 if (master && !IsFloating(dev)) 487 handler(screenNum, &mevent, master); 488 } 489 else { 490 /* process slave first, then master */ 491 dev->public.processInputProc(event, dev); 492 493 /* Check for the SD's master in case the device got detached 494 * during event processing */ 495 if (master && !IsFloating(dev)) 496 master->public.processInputProc(&mevent, master); 497 } 498} 499 500/* Call this from ProcessInputEvents(). */ 501void 502mieqProcessInputEvents(void) 503{ 504 EventRec *e = NULL; 505 ScreenPtr screen; 506 InternalEvent event; 507 DeviceIntPtr dev = NULL, master = NULL; 508 static Bool inProcessInputEvents = FALSE; 509 510 input_lock(); 511 512 /* 513 * report an error if mieqProcessInputEvents() is called recursively; 514 * this can happen, e.g., if something in the mieqProcessDeviceEvent() 515 * call chain calls UpdateCurrentTime() instead of UpdateCurrentTimeIf() 516 */ 517 BUG_WARN_MSG(inProcessInputEvents, "[mi] mieqProcessInputEvents() called recursively.\n"); 518 inProcessInputEvents = TRUE; 519 520 if (miEventQueue.dropped) { 521 ErrorF("[mi] EQ processing has resumed after %lu dropped events.\n", 522 (unsigned long) miEventQueue.dropped); 523 ErrorF 524 ("[mi] This may be caused by a misbehaving driver monopolizing the server's resources.\n"); 525 miEventQueue.dropped = 0; 526 } 527 528 while (miEventQueue.head != miEventQueue.tail) { 529 e = &miEventQueue.events[miEventQueue.head]; 530 531 event = *e->events; 532 dev = e->pDev; 533 screen = e->pScreen; 534 535 miEventQueue.head = (miEventQueue.head + 1) % miEventQueue.nevents; 536 537 input_unlock(); 538 539 master = (dev) ? GetMaster(dev, MASTER_ATTACHED) : NULL; 540 541 if (screenIsSaved == SCREEN_SAVER_ON) 542 dixSaveScreens(serverClient, SCREEN_SAVER_OFF, ScreenSaverReset); 543#ifdef DPMSExtension 544 else if (DPMSPowerLevel != DPMSModeOn) 545 SetScreenSaverTimer(); 546 547 if (DPMSPowerLevel != DPMSModeOn) 548 DPMSSet(serverClient, DPMSModeOn); 549#endif 550 551 mieqProcessDeviceEvent(dev, &event, screen); 552 553 /* Update the sprite now. Next event may be from different device. */ 554 if (master && 555 (event.any.type == ET_Motion || 556 ((event.any.type == ET_TouchBegin || 557 event.any.type == ET_TouchUpdate) && 558 event.device_event.flags & TOUCH_POINTER_EMULATED))) 559 miPointerUpdateSprite(dev); 560 561 input_lock(); 562 } 563 564 inProcessInputEvents = FALSE; 565 566 input_unlock(); 567} 568