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