darwinEvents.c revision 6747b715
1/* 2Darwin event queue and event handling 3 4Copyright 2007-2008 Apple Inc. 5Copyright 2004 Kaleb S. KEITHLEY. All Rights Reserved. 6Copyright (c) 2002-2004 Torrey T. Lyons. All Rights Reserved. 7 8This file is based on mieq.c by Keith Packard, 9which contains the following copyright: 10Copyright 1990, 1998 The Open Group 11 12Permission to use, copy, modify, distribute, and sell this software and its 13documentation for any purpose is hereby granted without fee, provided that 14the above copyright notice appear in all copies and that both that 15copyright notice and this permission notice appear in supporting 16documentation. 17 18The above copyright notice and this permission notice shall be included in 19all copies or substantial portions of the Software. 20 21THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 25AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 28Except as contained in this notice, the name of The Open Group shall not be 29used in advertising or otherwise to promote the sale, use or other dealings 30in this Software without prior written authorization from The Open Group. 31 */ 32 33#include "sanitizedCarbon.h" 34 35#ifdef 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 "eventstr.h" 47#include "mi.h" 48#include "scrnintstr.h" 49#include "mipointer.h" 50#include "os.h" 51 52#include "darwin.h" 53#include "quartz.h" 54#include "quartzKeyboard.h" 55#include "quartzRandR.h" 56#include "darwinEvents.h" 57 58#include <sys/types.h> 59#include <sys/uio.h> 60#include <unistd.h> 61#include <pthread.h> 62#include <errno.h> 63 64#include <IOKit/hidsystem/IOLLEvent.h> 65 66/* Fake button press/release for scroll wheel move. */ 67#define SCROLLWHEELUPFAKE 4 68#define SCROLLWHEELDOWNFAKE 5 69#define SCROLLWHEELLEFTFAKE 6 70#define SCROLLWHEELRIGHTFAKE 7 71 72#include <X11/extensions/applewmconst.h> 73#include "applewmExt.h" 74 75/* FIXME: Abstract this better */ 76extern Bool QuartzModeEventHandler(int screenNum, XQuartzEvent *e, DeviceIntPtr dev); 77 78int darwin_all_modifier_flags = 0; // last known modifier state 79int darwin_all_modifier_mask = 0; 80int darwin_x11_modifier_mask = 0; 81 82#define FD_ADD_MAX 128 83static int fd_add[FD_ADD_MAX]; 84int fd_add_count = 0; 85static pthread_mutex_t fd_add_lock = PTHREAD_MUTEX_INITIALIZER; 86static pthread_cond_t fd_add_ready_cond = PTHREAD_COND_INITIALIZER; 87static pthread_t fd_add_tid = NULL; 88 89static EventListPtr darwinEvents = NULL; 90 91static pthread_mutex_t mieq_lock = PTHREAD_MUTEX_INITIALIZER; 92static pthread_cond_t mieq_ready_cond = PTHREAD_COND_INITIALIZER; 93 94/*** Pthread Magics ***/ 95static pthread_t create_thread(void *func, void *arg) { 96 pthread_attr_t attr; 97 pthread_t tid; 98 99 pthread_attr_init (&attr); 100 pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); 101 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); 102 pthread_create (&tid, &attr, func, arg); 103 pthread_attr_destroy (&attr); 104 105 return tid; 106} 107 108void darwinEvents_lock(void); 109void darwinEvents_lock(void) { 110 int err; 111 if((err = pthread_mutex_lock(&mieq_lock))) { 112 ErrorF("%s:%s:%d: Failed to lock mieq_lock: %d\n", 113 __FILE__, __FUNCTION__, __LINE__, err); 114 spewCallStack(); 115 } 116 if(darwinEvents == NULL) { 117 pthread_cond_wait(&mieq_ready_cond, &mieq_lock); 118 } 119} 120 121void darwinEvents_unlock(void); 122void darwinEvents_unlock(void) { 123 int err; 124 if((err = pthread_mutex_unlock(&mieq_lock))) { 125 ErrorF("%s:%s:%d: Failed to unlock mieq_lock: %d\n", 126 __FILE__, __FUNCTION__, __LINE__, err); 127 spewCallStack(); 128 } 129} 130 131/* 132 * DarwinPressModifierKey 133 * Press or release the given modifier key (one of NX_MODIFIERKEY_* constants) 134 */ 135static void DarwinPressModifierKey(int pressed, int key) { 136 int keycode = DarwinModifierNXKeyToNXKeycode(key, 0); 137 138 if (keycode == 0) { 139 ErrorF("DarwinPressModifierKey bad keycode: key=%d\n", key); 140 return; 141 } 142 143 DarwinSendKeyboardEvents(pressed, keycode); 144} 145 146/* 147 * DarwinUpdateModifiers 148 * Send events to update the modifier state. 149 */ 150 151static int darwin_x11_modifier_mask_list[] = { 152#ifdef NX_DEVICELCMDKEYMASK 153 NX_DEVICELCTLKEYMASK, NX_DEVICERCTLKEYMASK, 154 NX_DEVICELSHIFTKEYMASK, NX_DEVICERSHIFTKEYMASK, 155 NX_DEVICELCMDKEYMASK, NX_DEVICERCMDKEYMASK, 156 NX_DEVICELALTKEYMASK, NX_DEVICERALTKEYMASK, 157#else 158 NX_CONTROLMASK, NX_SHIFTMASK, NX_COMMANDMASK, NX_ALTERNATEMASK, 159#endif 160 NX_ALPHASHIFTMASK, 161 0 162}; 163 164static int darwin_all_modifier_mask_additions[] = { NX_SECONDARYFNMASK, }; 165 166static void DarwinUpdateModifiers( 167 int pressed, // KeyPress or KeyRelease 168 int flags ) // modifier flags that have changed 169{ 170 int *f; 171 int key; 172 173 /* Capslock is special. This mask is the state of capslock (on/off), 174 * not the state of the button. Hopefully we can find a better solution. 175 */ 176 if(NX_ALPHASHIFTMASK & flags) { 177 DarwinPressModifierKey(KeyPress, NX_MODIFIERKEY_ALPHALOCK); 178 DarwinPressModifierKey(KeyRelease, NX_MODIFIERKEY_ALPHALOCK); 179 } 180 181 for(f=darwin_x11_modifier_mask_list; *f; f++) 182 if(*f & flags && *f != NX_ALPHASHIFTMASK) { 183 key = DarwinModifierNXMaskToNXKey(*f); 184 if(key == -1) 185 ErrorF("DarwinUpdateModifiers: Unsupported NXMask: 0x%x\n", *f); 186 else 187 DarwinPressModifierKey(pressed, key); 188 } 189} 190 191/* Generic handler for Xquartz-specifc events. When possible, these should 192 be moved into their own individual functions and set as handlers using 193 mieqSetHandler. */ 194 195static void DarwinEventHandler(int screenNum, InternalEvent *ie, DeviceIntPtr dev) { 196 XQuartzEvent *e = &(ie->xquartz_event); 197 198 TA_SERVER(); 199 200 switch(e->subtype) { 201 case kXquartzControllerNotify: 202 DEBUG_LOG("kXquartzControllerNotify\n"); 203 AppleWMSendEvent(AppleWMControllerNotify, 204 AppleWMControllerNotifyMask, 205 e->data[0], 206 e->data[1]); 207 break; 208 209 case kXquartzPasteboardNotify: 210 DEBUG_LOG("kXquartzPasteboardNotify\n"); 211 AppleWMSendEvent(AppleWMPasteboardNotify, 212 AppleWMPasteboardNotifyMask, 213 e->data[0], 214 e->data[1]); 215 break; 216 217 case kXquartzActivate: 218 DEBUG_LOG("kXquartzActivate\n"); 219 QuartzShow(); 220 AppleWMSendEvent(AppleWMActivationNotify, 221 AppleWMActivationNotifyMask, 222 AppleWMIsActive, 0); 223 break; 224 225 case kXquartzDeactivate: 226 DEBUG_LOG("kXquartzDeactivate\n"); 227 AppleWMSendEvent(AppleWMActivationNotify, 228 AppleWMActivationNotifyMask, 229 AppleWMIsInactive, 0); 230 QuartzHide(); 231 break; 232 233 case kXquartzReloadPreferences: 234 DEBUG_LOG("kXquartzReloadPreferences\n"); 235 AppleWMSendEvent(AppleWMActivationNotify, 236 AppleWMActivationNotifyMask, 237 AppleWMReloadPreferences, 0); 238 break; 239 240 case kXquartzToggleFullscreen: 241 DEBUG_LOG("kXquartzToggleFullscreen\n"); 242 if(XQuartzIsRootless) 243 ErrorF("Ignoring kXquartzToggleFullscreen because of rootless mode."); 244 else 245 QuartzRandRToggleFullscreen(); 246 break; 247 248 case kXquartzSetRootless: 249 DEBUG_LOG("kXquartzSetRootless\n"); 250 if(e->data[0]) { 251 QuartzRandRSetFakeRootless(); 252 } else { 253 QuartzRandRSetFakeFullscreen(FALSE); 254 } 255 break; 256 257 case kXquartzSetRootClip: 258 QuartzSetRootClip((Bool)e->data[0]); 259 break; 260 261 case kXquartzQuit: 262 GiveUp(0); 263 break; 264 265 case kXquartzSpaceChanged: 266 DEBUG_LOG("kXquartzSpaceChanged\n"); 267 QuartzSpaceChanged(e->data[0]); 268 break; 269 270 case kXquartzListenOnOpenFD: 271 ErrorF("Calling ListenOnOpenFD() for new fd: %d\n", (int)e->data[0]); 272 ListenOnOpenFD((int)e->data[0], 1); 273 break; 274 275 case kXquartzReloadKeymap: 276 DarwinKeyboardReloadHandler(); 277 break; 278 279 case kXquartzDisplayChanged: 280 DEBUG_LOG("kXquartzDisplayChanged\n"); 281 QuartzUpdateScreens(); 282#ifdef RANDR 283 /* Update our RandR info */ 284 QuartzRandRUpdateFakeModes(TRUE); 285#endif 286 break; 287 288 default: 289 if(!QuartzModeEventHandler(screenNum, e, dev)) 290 ErrorF("Unknown application defined event type %d.\n", e->subtype); 291 } 292} 293 294void DarwinListenOnOpenFD(int fd) { 295 ErrorF("DarwinListenOnOpenFD: %d\n", fd); 296 297 pthread_mutex_lock(&fd_add_lock); 298 if(fd_add_count < FD_ADD_MAX) 299 fd_add[fd_add_count++] = fd; 300 else 301 ErrorF("FD Addition buffer at max. Dropping fd addition request.\n"); 302 303 pthread_cond_broadcast(&fd_add_ready_cond); 304 pthread_mutex_unlock(&fd_add_lock); 305} 306 307static void DarwinProcessFDAdditionQueue_thread(void *args) { 308 pthread_mutex_lock(&fd_add_lock); 309 while(true) { 310 while(fd_add_count) { 311 DarwinSendDDXEvent(kXquartzListenOnOpenFD, 1, fd_add[--fd_add_count]); 312 } 313 pthread_cond_wait(&fd_add_ready_cond, &fd_add_lock); 314 } 315} 316 317Bool DarwinEQInit(void) { 318 int *p; 319 320 for(p=darwin_x11_modifier_mask_list, darwin_all_modifier_mask=0; *p; p++) { 321 darwin_x11_modifier_mask |= *p; 322 } 323 324 for(p=darwin_all_modifier_mask_additions, darwin_all_modifier_mask= darwin_x11_modifier_mask; *p; p++) { 325 darwin_all_modifier_mask |= *p; 326 } 327 328 mieqInit(); 329 mieqSetHandler(ET_XQuartz, DarwinEventHandler); 330 331 /* Note that this *could* cause a potential async issue, since we're checking 332 * darwinEvents without holding the lock, but darwinEvents is only ever set 333 * here, so I don't bother. 334 */ 335 if (!darwinEvents) { 336 darwinEvents = InitEventList(GetMaximumEventsNum());; 337 338 if (!darwinEvents) 339 FatalError("Couldn't allocate event buffer\n"); 340 341 darwinEvents_lock(); 342 pthread_cond_broadcast(&mieq_ready_cond); 343 darwinEvents_unlock(); 344 } 345 346 if(!fd_add_tid) 347 fd_add_tid = create_thread(DarwinProcessFDAdditionQueue_thread, NULL); 348 349 return TRUE; 350} 351 352/* 353 * ProcessInputEvents 354 * Read and process events from the event queue until it is empty. 355 */ 356void ProcessInputEvents(void) { 357 char nullbyte; 358 int x = sizeof(nullbyte); 359 360 TA_SERVER(); 361 362 mieqProcessInputEvents(); 363 364 // Empty the signaling pipe 365 while (x == sizeof(nullbyte)) { 366 x = read(darwinEventReadFD, &nullbyte, sizeof(nullbyte)); 367 } 368} 369 370/* Sends a null byte down darwinEventWriteFD, which will cause the 371 Dispatch() event loop to check out event queue */ 372static void DarwinPokeEQ(void) { 373 char nullbyte=0; 374 // <daniels> oh, i ... er ... christ. 375 write(darwinEventWriteFD, &nullbyte, sizeof(nullbyte)); 376} 377 378/* Convert from Appkit pointer input values to X input values: 379 * Note: pointer_x and pointer_y are relative to the upper-left of primary 380 * display. 381 */ 382static void DarwinPrepareValuators(DeviceIntPtr pDev, int *valuators, ScreenPtr screen, 383 float pointer_x, float pointer_y, 384 float pressure, float tilt_x, float tilt_y) { 385 /* Fix offset between darwin and X screens */ 386 pointer_x -= darwinMainScreenX + screen->x; 387 pointer_y -= darwinMainScreenY + screen->y; 388 389 if(pointer_x < 0.0) 390 pointer_x = 0.0; 391 392 if(pointer_y < 0.0) 393 pointer_y = 0.0; 394 395 if(pDev == darwinPointer) { 396 valuators[0] = pointer_x; 397 valuators[1] = pointer_y; 398 valuators[2] = 0; 399 valuators[3] = 0; 400 valuators[4] = 0; 401 } else { 402 /* Setup our array of values */ 403 valuators[0] = XQUARTZ_VALUATOR_LIMIT * (pointer_x / (float)screenInfo.screens[0]->width); 404 valuators[1] = XQUARTZ_VALUATOR_LIMIT * (pointer_y / (float)screenInfo.screens[0]->height); 405 valuators[2] = XQUARTZ_VALUATOR_LIMIT * pressure; 406 valuators[3] = XQUARTZ_VALUATOR_LIMIT * tilt_x; 407 valuators[4] = XQUARTZ_VALUATOR_LIMIT * tilt_y; 408 } 409 //DEBUG_LOG("Pointer (%f, %f), Valuators: {%d,%d,%d,%d,%d}\n", pointer_x, pointer_y, 410 // valuators[0], valuators[1], valuators[2], valuators[3], valuators[4]); 411} 412 413void DarwinSendPointerEvents(DeviceIntPtr pDev, int ev_type, int ev_button, float pointer_x, float pointer_y, 414 float pressure, float tilt_x, float tilt_y) { 415 static int darwinFakeMouseButtonDown = 0; 416 int i, num_events; 417 ScreenPtr screen; 418 int valuators[5]; 419 420 //DEBUG_LOG("x=%f, y=%f, p=%f, tx=%f, ty=%f\n", pointer_x, pointer_y, pressure, tilt_x, tilt_y); 421 422 if(!darwinEvents) { 423 DEBUG_LOG("DarwinSendPointerEvents called before darwinEvents was initialized\n"); 424 return; 425 } 426 427 screen = miPointerGetScreen(pDev); 428 if(!screen) { 429 DEBUG_LOG("DarwinSendPointerEvents called before screen was initialized\n"); 430 return; 431 } 432 433 /* Handle fake click */ 434 if (ev_type == ButtonPress && darwinFakeButtons && ev_button == 1) { 435 if(darwinFakeMouseButtonDown != 0) { 436 /* We're currently "down" with another button, so release it first */ 437 DarwinSendPointerEvents(pDev, ButtonRelease, darwinFakeMouseButtonDown, pointer_x, pointer_y, pressure, tilt_x, tilt_y); 438 darwinFakeMouseButtonDown=0; 439 } 440 if (darwin_all_modifier_flags & darwinFakeMouse2Mask) { 441 ev_button = 2; 442 darwinFakeMouseButtonDown = 2; 443 DarwinUpdateModKeys(darwin_all_modifier_flags & ~darwinFakeMouse2Mask); 444 } else if (darwin_all_modifier_flags & darwinFakeMouse3Mask) { 445 ev_button = 3; 446 darwinFakeMouseButtonDown = 3; 447 DarwinUpdateModKeys(darwin_all_modifier_flags & ~darwinFakeMouse3Mask); 448 } 449 } 450 451 if (ev_type == ButtonRelease && ev_button == 1) { 452 if(darwinFakeMouseButtonDown) { 453 ev_button = darwinFakeMouseButtonDown; 454 } 455 456 if(darwinFakeMouseButtonDown == 2) { 457 DarwinUpdateModKeys(darwin_all_modifier_flags & ~darwinFakeMouse2Mask); 458 } else if(darwinFakeMouseButtonDown == 3) { 459 DarwinUpdateModKeys(darwin_all_modifier_flags & ~darwinFakeMouse3Mask); 460 } 461 462 darwinFakeMouseButtonDown = 0; 463 } 464 465 DarwinPrepareValuators(pDev, valuators, screen, pointer_x, pointer_y, pressure, tilt_x, tilt_y); 466 darwinEvents_lock(); { 467 num_events = GetPointerEvents(darwinEvents, pDev, ev_type, ev_button, 468 POINTER_ABSOLUTE, 0, pDev==darwinTabletCurrent?5:2, valuators); 469 for(i=0; i<num_events; i++) mieqEnqueue (pDev, (InternalEvent*)darwinEvents[i].event); 470 if(num_events > 0) DarwinPokeEQ(); 471 } darwinEvents_unlock(); 472} 473 474void DarwinSendKeyboardEvents(int ev_type, int keycode) { 475 int i, num_events; 476 477 if(!darwinEvents) { 478 DEBUG_LOG("DarwinSendKeyboardEvents called before darwinEvents was initialized\n"); 479 return; 480 } 481 482 darwinEvents_lock(); { 483 num_events = GetKeyboardEvents(darwinEvents, darwinKeyboard, ev_type, keycode + MIN_KEYCODE); 484 for(i=0; i<num_events; i++) mieqEnqueue(darwinKeyboard, (InternalEvent*)darwinEvents[i].event); 485 if(num_events > 0) DarwinPokeEQ(); 486 } darwinEvents_unlock(); 487} 488 489void DarwinSendProximityEvents(int ev_type, float pointer_x, float pointer_y) { 490 int i, num_events; 491 ScreenPtr screen; 492 DeviceIntPtr pDev = darwinTabletCurrent; 493 int valuators[5]; 494 495 DEBUG_LOG("DarwinSendProximityEvents(%d, %f, %f)\n", ev_type, pointer_x, pointer_y); 496 497 if(!darwinEvents) { 498 DEBUG_LOG("DarwinSendProximityEvents called before darwinEvents was initialized\n"); 499 return; 500 } 501 502 screen = miPointerGetScreen(pDev); 503 if(!screen) { 504 DEBUG_LOG("DarwinSendPointerEvents called before screen was initialized\n"); 505 return; 506 } 507 508 DarwinPrepareValuators(pDev, valuators, screen, pointer_x, pointer_y, 0.0f, 0.0f, 0.0f); 509 darwinEvents_lock(); { 510 num_events = GetProximityEvents(darwinEvents, pDev, ev_type, 511 0, 5, valuators); 512 for(i=0; i<num_events; i++) mieqEnqueue (pDev, (InternalEvent*)darwinEvents[i].event); 513 if(num_events > 0) DarwinPokeEQ(); 514 } darwinEvents_unlock(); 515} 516 517 518/* Send the appropriate number of button clicks to emulate scroll wheel */ 519void DarwinSendScrollEvents(float count_x, float count_y, 520 float pointer_x, float pointer_y, 521 float pressure, float tilt_x, float tilt_y) { 522 int sign_x, sign_y; 523 if(!darwinEvents) { 524 DEBUG_LOG("DarwinSendScrollEvents called before darwinEvents was initialized\n"); 525 return; 526 } 527 528 sign_x = count_x > 0.0f ? SCROLLWHEELLEFTFAKE : SCROLLWHEELRIGHTFAKE; 529 sign_y = count_y > 0.0f ? SCROLLWHEELUPFAKE : SCROLLWHEELDOWNFAKE; 530 count_x = fabs(count_x); 531 count_y = fabs(count_y); 532 533 while ((count_x > 0.0f) || (count_y > 0.0f)) { 534 if (count_x > 0.0f) { 535 DarwinSendPointerEvents(darwinPointer, ButtonPress, sign_x, pointer_x, pointer_y, pressure, tilt_x, tilt_y); 536 DarwinSendPointerEvents(darwinPointer, ButtonRelease, sign_x, pointer_x, pointer_y, pressure, tilt_x, tilt_y); 537 count_x = count_x - 1.0f; 538 } 539 if (count_y > 0.0f) { 540 DarwinSendPointerEvents(darwinPointer, ButtonPress, sign_y, pointer_x, pointer_y, pressure, tilt_x, tilt_y); 541 DarwinSendPointerEvents(darwinPointer, ButtonRelease, sign_y, pointer_x, pointer_y, pressure, tilt_x, tilt_y); 542 count_y = count_y - 1.0f; 543 } 544 } 545} 546 547/* Send the appropriate KeyPress/KeyRelease events to GetKeyboardEvents to 548 reflect changing modifier flags (alt, control, meta, etc) */ 549void DarwinUpdateModKeys(int flags) { 550 DarwinUpdateModifiers(KeyRelease, darwin_all_modifier_flags & ~flags & darwin_x11_modifier_mask); 551 DarwinUpdateModifiers(KeyPress, ~darwin_all_modifier_flags & flags & darwin_x11_modifier_mask); 552 darwin_all_modifier_flags = flags; 553} 554 555/* 556 * DarwinSendDDXEvent 557 * Send the X server thread a message by placing it on the event queue. 558 */ 559void DarwinSendDDXEvent(int type, int argc, ...) { 560 XQuartzEvent e; 561 int i; 562 va_list args; 563 564 memset(&e, 0, sizeof(e)); 565 e.header = ET_Internal; 566 e.type = ET_XQuartz; 567 e.length = sizeof(e); 568 e.time = GetTimeInMillis(); 569 e.subtype = type; 570 571 if (argc > 0 && argc < XQUARTZ_EVENT_MAXARGS) { 572 va_start (args, argc); 573 for (i = 0; i < argc; i++) 574 e.data[i] = (uint32_t) va_arg (args, uint32_t); 575 va_end (args); 576 } 577 578 darwinEvents_lock(); { 579 mieqEnqueue(NULL, (InternalEvent*)&e); 580 DarwinPokeEQ(); 581 } darwinEvents_unlock(); 582} 583