darwinEvents.c revision 35c4bbdf
1/* 2 * Darwin event queue and event handling 3 * 4 * Copyright 2007-2008 Apple Inc. 5 * Copyright 2004 Kaleb S. KEITHLEY. All Rights Reserved. 6 * Copyright (c) 2002-2004 Torrey T. Lyons. All Rights Reserved. 7 * 8 * This file is based on mieq.c by Keith Packard, 9 * which contains the following copyright: 10 * Copyright 1990, 1998 The Open Group 11 * 12 * 13 * Copyright (c) 2002-2012 Apple Inc. All rights reserved. 14 * 15 * Permission is hereby granted, free of charge, to any person 16 * obtaining a copy of this software and associated documentation files 17 * (the "Software"), to deal in the Software without restriction, 18 * including without limitation the rights to use, copy, modify, merge, 19 * publish, distribute, sublicense, and/or sell copies of the Software, 20 * and to permit persons to whom the Software is furnished to do so, 21 * subject to the following conditions: 22 * 23 * The above copyright notice and this permission notice shall be 24 * included in all copies or substantial portions of the Software. 25 * 26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 29 * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT 30 * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 31 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 33 * DEALINGS IN THE SOFTWARE. 34 * 35 * Except as contained in this notice, the name(s) of the above 36 * copyright holders shall not be used in advertising or otherwise to 37 * promote the sale, use or other dealings in this Software without 38 * prior written authorization. 39 */ 40 41#include "sanitizedCarbon.h" 42 43#ifdef HAVE_DIX_CONFIG_H 44#include <dix-config.h> 45#endif 46 47#include <X11/X.h> 48#include <X11/Xmd.h> 49#include <X11/Xproto.h> 50#include "misc.h" 51#include "windowstr.h" 52#include "pixmapstr.h" 53#include "inputstr.h" 54#include "inpututils.h" 55#include "eventstr.h" 56#include "mi.h" 57#include "scrnintstr.h" 58#include "mipointer.h" 59#include "os.h" 60#include "exglobals.h" 61 62#include "darwin.h" 63#include "quartz.h" 64#include "quartzKeyboard.h" 65#include "quartzRandR.h" 66#include "darwinEvents.h" 67 68#include <sys/types.h> 69#include <sys/uio.h> 70#include <unistd.h> 71#include <pthread.h> 72#include <errno.h> 73#include <time.h> 74 75#include <IOKit/hidsystem/IOLLEvent.h> 76 77#include <X11/extensions/applewmconst.h> 78#include "applewmExt.h" 79 80/* FIXME: Abstract this better */ 81extern Bool 82QuartzModeEventHandler(int screenNum, XQuartzEvent *e, DeviceIntPtr dev); 83 84int darwin_all_modifier_flags = 0; // last known modifier state 85int darwin_all_modifier_mask = 0; 86int darwin_x11_modifier_mask = 0; 87 88#define FD_ADD_MAX 128 89static int fd_add[FD_ADD_MAX]; 90int fd_add_count = 0; 91static pthread_mutex_t fd_add_lock = PTHREAD_MUTEX_INITIALIZER; 92static pthread_cond_t fd_add_ready_cond = PTHREAD_COND_INITIALIZER; 93static pthread_t fd_add_tid = NULL; 94 95static InternalEvent* darwinEvents = NULL; 96 97static pthread_mutex_t mieq_lock = PTHREAD_MUTEX_INITIALIZER; 98static pthread_cond_t mieq_ready_cond = PTHREAD_COND_INITIALIZER; 99 100/*** Pthread Magics ***/ 101static pthread_t 102create_thread(void *(*func)(void *), void *arg) 103{ 104 pthread_attr_t attr; 105 pthread_t tid; 106 107 pthread_attr_init(&attr); 108 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); 109 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 110 pthread_create(&tid, &attr, func, arg); 111 pthread_attr_destroy(&attr); 112 113 return tid; 114} 115 116void 117darwinEvents_lock(void); 118void 119darwinEvents_lock(void) 120{ 121 int err; 122 if ((err = pthread_mutex_lock(&mieq_lock))) { 123 ErrorF("%s:%s:%d: Failed to lock mieq_lock: %d\n", 124 __FILE__, __FUNCTION__, __LINE__, err); 125 xorg_backtrace(); 126 } 127 if (darwinEvents == NULL) { 128 pthread_cond_wait(&mieq_ready_cond, &mieq_lock); 129 } 130} 131 132void 133darwinEvents_unlock(void); 134void 135darwinEvents_unlock(void) 136{ 137 int err; 138 if ((err = pthread_mutex_unlock(&mieq_lock))) { 139 ErrorF("%s:%s:%d: Failed to unlock mieq_lock: %d\n", 140 __FILE__, __FUNCTION__, __LINE__, err); 141 xorg_backtrace(); 142 } 143} 144 145/* 146 * DarwinPressModifierKey 147 * Press or release the given modifier key (one of NX_MODIFIERKEY_* constants) 148 */ 149static void 150DarwinPressModifierKey(int pressed, int key) 151{ 152 int keycode = DarwinModifierNXKeyToNXKeycode(key, 0); 153 154 if (keycode == 0) { 155 ErrorF("DarwinPressModifierKey bad keycode: key=%d\n", key); 156 return; 157 } 158 159 DarwinSendKeyboardEvents(pressed, keycode); 160} 161 162/* 163 * DarwinUpdateModifiers 164 * Send events to update the modifier state. 165 */ 166 167static int darwin_x11_modifier_mask_list[] = { 168#ifdef NX_DEVICELCMDKEYMASK 169 NX_DEVICELCTLKEYMASK, NX_DEVICERCTLKEYMASK, 170 NX_DEVICELSHIFTKEYMASK, NX_DEVICERSHIFTKEYMASK, 171 NX_DEVICELCMDKEYMASK, NX_DEVICERCMDKEYMASK, 172 NX_DEVICELALTKEYMASK, NX_DEVICERALTKEYMASK, 173#else 174 NX_CONTROLMASK, NX_SHIFTMASK, NX_COMMANDMASK, 175 NX_ALTERNATEMASK, 176#endif 177 NX_ALPHASHIFTMASK, 178 0 179}; 180 181static int darwin_all_modifier_mask_additions[] = { NX_SECONDARYFNMASK, 0 }; 182 183static void 184DarwinUpdateModifiers(int pressed, // KeyPress or KeyRelease 185 int flags) // modifier flags that have changed 186{ 187 int *f; 188 int key; 189 190 /* Capslock is special. This mask is the state of capslock (on/off), 191 * not the state of the button. Hopefully we can find a better solution. 192 */ 193 if (NX_ALPHASHIFTMASK & flags) { 194 DarwinPressModifierKey(KeyPress, NX_MODIFIERKEY_ALPHALOCK); 195 DarwinPressModifierKey(KeyRelease, NX_MODIFIERKEY_ALPHALOCK); 196 } 197 198 for (f = darwin_x11_modifier_mask_list; *f; f++) 199 if (*f & flags && *f != NX_ALPHASHIFTMASK) { 200 key = DarwinModifierNXMaskToNXKey(*f); 201 if (key == -1) 202 ErrorF("DarwinUpdateModifiers: Unsupported NXMask: 0x%x\n", 203 *f); 204 else 205 DarwinPressModifierKey(pressed, key); 206 } 207} 208 209/* Generic handler for Xquartz-specifc events. When possible, these should 210 be moved into their own individual functions and set as handlers using 211 mieqSetHandler. */ 212 213static void 214DarwinEventHandler(int screenNum, InternalEvent *ie, DeviceIntPtr dev) 215{ 216 XQuartzEvent *e = &(ie->xquartz_event); 217 218 switch (e->subtype) { 219 case kXquartzControllerNotify: 220 DEBUG_LOG("kXquartzControllerNotify\n"); 221 AppleWMSendEvent(AppleWMControllerNotify, 222 AppleWMControllerNotifyMask, 223 e->data[0], 224 e->data[1]); 225 break; 226 227 case kXquartzPasteboardNotify: 228 DEBUG_LOG("kXquartzPasteboardNotify\n"); 229 AppleWMSendEvent(AppleWMPasteboardNotify, 230 AppleWMPasteboardNotifyMask, 231 e->data[0], 232 e->data[1]); 233 break; 234 235 case kXquartzActivate: 236 DEBUG_LOG("kXquartzActivate\n"); 237 QuartzShow(); 238 AppleWMSendEvent(AppleWMActivationNotify, 239 AppleWMActivationNotifyMask, 240 AppleWMIsActive, 0); 241 break; 242 243 case kXquartzDeactivate: 244 DEBUG_LOG("kXquartzDeactivate\n"); 245 AppleWMSendEvent(AppleWMActivationNotify, 246 AppleWMActivationNotifyMask, 247 AppleWMIsInactive, 0); 248 QuartzHide(); 249 break; 250 251 case kXquartzReloadPreferences: 252 DEBUG_LOG("kXquartzReloadPreferences\n"); 253 AppleWMSendEvent(AppleWMActivationNotify, 254 AppleWMActivationNotifyMask, 255 AppleWMReloadPreferences, 0); 256 break; 257 258 case kXquartzToggleFullscreen: 259 DEBUG_LOG("kXquartzToggleFullscreen\n"); 260 if (XQuartzIsRootless) 261 ErrorF( 262 "Ignoring kXquartzToggleFullscreen because of rootless mode."); 263 else 264 QuartzRandRToggleFullscreen(); 265 break; 266 267 case kXquartzSetRootless: 268 DEBUG_LOG("kXquartzSetRootless\n"); 269 if (e->data[0]) { 270 QuartzRandRSetFakeRootless(); 271 } 272 else { 273 QuartzRandRSetFakeFullscreen(FALSE); 274 } 275 break; 276 277 case kXquartzSetRootClip: 278 QuartzSetRootClip(e->data[0]); 279 break; 280 281 case kXquartzQuit: 282 GiveUp(0); 283 break; 284 285 case kXquartzSpaceChanged: 286 DEBUG_LOG("kXquartzSpaceChanged\n"); 287 QuartzSpaceChanged(e->data[0]); 288 break; 289 290 case kXquartzListenOnOpenFD: 291 ErrorF("Calling ListenOnOpenFD() for new fd: %d\n", (int)e->data[0]); 292 ListenOnOpenFD((int)e->data[0], 1); 293 break; 294 295 case kXquartzReloadKeymap: 296 DarwinKeyboardReloadHandler(); 297 break; 298 299 case kXquartzDisplayChanged: 300 DEBUG_LOG("kXquartzDisplayChanged\n"); 301 QuartzUpdateScreens(); 302 303 /* Update our RandR info */ 304 QuartzRandRUpdateFakeModes(TRUE); 305 break; 306 307 default: 308 if (!QuartzModeEventHandler(screenNum, e, dev)) 309 ErrorF("Unknown application defined event type %d.\n", e->subtype); 310 } 311} 312 313void 314DarwinListenOnOpenFD(int fd) 315{ 316 ErrorF("DarwinListenOnOpenFD: %d\n", fd); 317 318 pthread_mutex_lock(&fd_add_lock); 319 if (fd_add_count < FD_ADD_MAX) 320 fd_add[fd_add_count++] = fd; 321 else 322 ErrorF("FD Addition buffer at max. Dropping fd addition request.\n"); 323 324 pthread_cond_broadcast(&fd_add_ready_cond); 325 pthread_mutex_unlock(&fd_add_lock); 326} 327 328static void * 329DarwinProcessFDAdditionQueue_thread(void *args) 330{ 331 /* TODO: Possibly adjust this to no longer be a race... maybe trigger this 332 * once a client connects and claims to be the WM. 333 * 334 * From ajax: 335 * There's already an internal callback chain for setting selection [in 1.5] 336 * ownership. See the CallSelectionCallback at the bottom of 337 * ProcSetSelectionOwner, and xfixes/select.c for an example of how to hook 338 * into it. 339 */ 340 341 struct timespec sleep_for; 342 struct timespec sleep_remaining; 343 344 sleep_for.tv_sec = 3; 345 sleep_for.tv_nsec = 0; 346 347 ErrorF( 348 "X11.app: DarwinProcessFDAdditionQueue_thread: Sleeping to allow xinitrc to catchup.\n"); 349 while (nanosleep(&sleep_for, &sleep_remaining) != 0) { 350 sleep_for = sleep_remaining; 351 } 352 353 pthread_mutex_lock(&fd_add_lock); 354 while (true) { 355 while (fd_add_count) { 356 DarwinSendDDXEvent(kXquartzListenOnOpenFD, 1, 357 fd_add[--fd_add_count]); 358 } 359 pthread_cond_wait(&fd_add_ready_cond, &fd_add_lock); 360 } 361 362 return NULL; 363} 364 365Bool 366DarwinEQInit(void) 367{ 368 int *p; 369 370 for (p = darwin_x11_modifier_mask_list; *p; p++) { 371 darwin_x11_modifier_mask |= *p; 372 } 373 374 darwin_all_modifier_mask = darwin_x11_modifier_mask; 375 for (p = darwin_all_modifier_mask_additions; *p; p++) { 376 darwin_all_modifier_mask |= *p; 377 } 378 379 mieqInit(); 380 mieqSetHandler(ET_XQuartz, DarwinEventHandler); 381 382 /* Note that this *could* cause a potential async issue, since we're checking 383 * darwinEvents without holding the lock, but darwinEvents is only ever set 384 * here, so I don't bother. 385 */ 386 if (!darwinEvents) { 387 darwinEvents = InitEventList(GetMaximumEventsNum()); 388 389 if (!darwinEvents) 390 FatalError("Couldn't allocate event buffer\n"); 391 392 darwinEvents_lock(); 393 pthread_cond_broadcast(&mieq_ready_cond); 394 darwinEvents_unlock(); 395 } 396 397 if (!fd_add_tid) 398 fd_add_tid = create_thread(DarwinProcessFDAdditionQueue_thread, NULL); 399 400 return TRUE; 401} 402 403void 404DarwinEQFini(void) 405{ 406 mieqFini(); 407} 408 409/* 410 * ProcessInputEvents 411 * Read and process events from the event queue until it is empty. 412 */ 413void 414ProcessInputEvents(void) 415{ 416 char nullbyte; 417 int x = sizeof(nullbyte); 418 419 mieqProcessInputEvents(); 420 421 // Empty the signaling pipe 422 while (x == sizeof(nullbyte)) { 423 x = read(darwinEventReadFD, &nullbyte, sizeof(nullbyte)); 424 } 425} 426 427/* Sends a null byte down darwinEventWriteFD, which will cause the 428 Dispatch() event loop to check out event queue */ 429static void 430DarwinPokeEQ(void) 431{ 432 char nullbyte = 0; 433 // <daniels> oh, i ... er ... christ. 434 write(darwinEventWriteFD, &nullbyte, sizeof(nullbyte)); 435} 436 437void 438DarwinInputReleaseButtonsAndKeys(DeviceIntPtr pDev) 439{ 440 darwinEvents_lock(); 441 { 442 int i; 443 if (pDev->button) { 444 for (i = 0; i < pDev->button->numButtons; i++) { 445 if (BitIsOn(pDev->button->down, i)) { 446 QueuePointerEvents(pDev, ButtonRelease, i, 447 POINTER_ABSOLUTE, 448 NULL); 449 } 450 } 451 } 452 453 if (pDev->key) { 454 for (i = 0; i < NUM_KEYCODES; i++) { 455 if (BitIsOn(pDev->key->down, i + MIN_KEYCODE)) { 456 QueueKeyboardEvents(pDev, KeyRelease, i + MIN_KEYCODE); 457 } 458 } 459 } 460 DarwinPokeEQ(); 461 } darwinEvents_unlock(); 462} 463 464void 465DarwinSendTabletEvents(DeviceIntPtr pDev, int ev_type, int ev_button, 466 double pointer_x, double pointer_y, 467 double pressure, double tilt_x, 468 double tilt_y) 469{ 470 ScreenPtr screen; 471 ValuatorMask valuators; 472 473 if (!darwinEvents) { 474 DEBUG_LOG("%s called before darwinEvents was initialized\n", 475 __FUNCTION__); 476 return; 477 } 478 479 screen = miPointerGetScreen(pDev); 480 if (!screen) { 481 DEBUG_LOG("%s called before screen was initialized\n", 482 __FUNCTION__); 483 return; 484 } 485 486 /* Fix offset between darwin and X screens */ 487 pointer_x -= darwinMainScreenX + screen->x; 488 pointer_y -= darwinMainScreenY + screen->y; 489 490 /* Adjust our pointer location to the [0,1] range */ 491 pointer_x = pointer_x / (double)screenInfo.width; 492 pointer_y = pointer_y / (double)screenInfo.height; 493 494 valuator_mask_zero(&valuators); 495 valuator_mask_set_double(&valuators, 0, XQUARTZ_VALUATOR_LIMIT * pointer_x); 496 valuator_mask_set_double(&valuators, 1, XQUARTZ_VALUATOR_LIMIT * pointer_y); 497 valuator_mask_set_double(&valuators, 2, XQUARTZ_VALUATOR_LIMIT * pressure); 498 valuator_mask_set_double(&valuators, 3, XQUARTZ_VALUATOR_LIMIT * tilt_x); 499 valuator_mask_set_double(&valuators, 4, XQUARTZ_VALUATOR_LIMIT * tilt_y); 500 501 darwinEvents_lock(); 502 { 503 if (ev_type == ProximityIn || ev_type == ProximityOut) { 504 QueueProximityEvents(pDev, ev_type, &valuators); 505 } else { 506 QueuePointerEvents(pDev, ev_type, ev_button, POINTER_ABSOLUTE, 507 &valuators); 508 } 509 DarwinPokeEQ(); 510 } darwinEvents_unlock(); 511} 512 513void 514DarwinSendPointerEvents(DeviceIntPtr pDev, int ev_type, int ev_button, 515 double pointer_x, double pointer_y, 516 double pointer_dx, double pointer_dy) 517{ 518 static int darwinFakeMouseButtonDown = 0; 519 ScreenPtr screen; 520 ValuatorMask valuators; 521 522 if (!darwinEvents) { 523 DEBUG_LOG("%s called before darwinEvents was initialized\n", 524 __FUNCTION__); 525 return; 526 } 527 528 screen = miPointerGetScreen(pDev); 529 if (!screen) { 530 DEBUG_LOG("%s called before screen was initialized\n", 531 __FUNCTION__); 532 return; 533 } 534 535 /* Handle fake click */ 536 if (ev_type == ButtonPress && darwinFakeButtons && ev_button == 1) { 537 if (darwinFakeMouseButtonDown != 0) { 538 /* We're currently "down" with another button, so release it first */ 539 DarwinSendPointerEvents(pDev, ButtonRelease, 540 darwinFakeMouseButtonDown, 541 pointer_x, pointer_y, 0.0, 0.0); 542 darwinFakeMouseButtonDown = 0; 543 } 544 if (darwin_all_modifier_flags & darwinFakeMouse2Mask) { 545 ev_button = 2; 546 darwinFakeMouseButtonDown = 2; 547 DarwinUpdateModKeys( 548 darwin_all_modifier_flags & ~darwinFakeMouse2Mask); 549 } 550 else if (darwin_all_modifier_flags & darwinFakeMouse3Mask) { 551 ev_button = 3; 552 darwinFakeMouseButtonDown = 3; 553 DarwinUpdateModKeys( 554 darwin_all_modifier_flags & ~darwinFakeMouse3Mask); 555 } 556 } 557 558 if (ev_type == ButtonRelease && ev_button == 1) { 559 if (darwinFakeMouseButtonDown) { 560 ev_button = darwinFakeMouseButtonDown; 561 } 562 563 if (darwinFakeMouseButtonDown == 2) { 564 DarwinUpdateModKeys( 565 darwin_all_modifier_flags & ~darwinFakeMouse2Mask); 566 } 567 else if (darwinFakeMouseButtonDown == 3) { 568 DarwinUpdateModKeys( 569 darwin_all_modifier_flags & ~darwinFakeMouse3Mask); 570 } 571 572 darwinFakeMouseButtonDown = 0; 573 } 574 575 /* Fix offset between darwin and X screens */ 576 pointer_x -= darwinMainScreenX + screen->x; 577 pointer_y -= darwinMainScreenY + screen->y; 578 579 valuator_mask_zero(&valuators); 580 valuator_mask_set_double(&valuators, 0, pointer_x); 581 valuator_mask_set_double(&valuators, 1, pointer_y); 582 583 if (ev_type == MotionNotify) { 584 if (pointer_dx != 0.0) 585 valuator_mask_set_double(&valuators, 2, pointer_dx); 586 if (pointer_dy != 0.0) 587 valuator_mask_set_double(&valuators, 3, pointer_dy); 588 } 589 590 darwinEvents_lock(); 591 { 592 QueuePointerEvents(pDev, ev_type, ev_button, POINTER_ABSOLUTE, 593 &valuators); 594 DarwinPokeEQ(); 595 } darwinEvents_unlock(); 596} 597 598void 599DarwinSendKeyboardEvents(int ev_type, int keycode) 600{ 601 602 if (!darwinEvents) { 603 DEBUG_LOG( 604 "DarwinSendKeyboardEvents called before darwinEvents was initialized\n"); 605 return; 606 } 607 608 darwinEvents_lock(); 609 { 610 QueueKeyboardEvents(darwinKeyboard, ev_type, keycode + MIN_KEYCODE); 611 DarwinPokeEQ(); 612 } darwinEvents_unlock(); 613} 614 615/* Send the appropriate number of button clicks to emulate scroll wheel */ 616void 617DarwinSendScrollEvents(double scroll_x, double scroll_y) { 618 ScreenPtr screen; 619 ValuatorMask valuators; 620 621 if (!darwinEvents) { 622 DEBUG_LOG( 623 "DarwinSendScrollEvents called before darwinEvents was initialized\n"); 624 return; 625 } 626 627 screen = miPointerGetScreen(darwinPointer); 628 if (!screen) { 629 DEBUG_LOG( 630 "DarwinSendScrollEvents called before screen was initialized\n"); 631 return; 632 } 633 634 valuator_mask_zero(&valuators); 635 valuator_mask_set_double(&valuators, 4, scroll_y); 636 valuator_mask_set_double(&valuators, 5, scroll_x); 637 638 darwinEvents_lock(); 639 { 640 QueuePointerEvents(darwinPointer, MotionNotify, 0, 641 POINTER_RELATIVE, &valuators); 642 DarwinPokeEQ(); 643 } darwinEvents_unlock(); 644} 645 646/* Send the appropriate KeyPress/KeyRelease events to GetKeyboardEvents to 647 reflect changing modifier flags (alt, control, meta, etc) */ 648void 649DarwinUpdateModKeys(int flags) 650{ 651 DarwinUpdateModifiers( 652 KeyRelease, darwin_all_modifier_flags & ~flags & 653 darwin_x11_modifier_mask); 654 DarwinUpdateModifiers( 655 KeyPress, ~darwin_all_modifier_flags & flags & 656 darwin_x11_modifier_mask); 657 darwin_all_modifier_flags = flags; 658} 659 660/* 661 * DarwinSendDDXEvent 662 * Send the X server thread a message by placing it on the event queue. 663 */ 664void 665DarwinSendDDXEvent(int type, int argc, ...) 666{ 667 XQuartzEvent e; 668 int i; 669 va_list args; 670 671 memset(&e, 0, sizeof(e)); 672 e.header = ET_Internal; 673 e.type = ET_XQuartz; 674 e.length = sizeof(e); 675 e.time = GetTimeInMillis(); 676 e.subtype = type; 677 678 if (argc > 0 && argc < XQUARTZ_EVENT_MAXARGS) { 679 va_start(args, argc); 680 for (i = 0; i < argc; i++) 681 e.data[i] = (uint32_t)va_arg(args, uint32_t); 682 va_end(args); 683 } 684 685 darwinEvents_lock(); 686 { 687 mieqEnqueue(NULL, (InternalEvent *)&e); 688 DarwinPokeEQ(); 689 } darwinEvents_unlock(); 690} 691