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