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