darwinEvents.c revision 8223e2f2
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
283            /* Update our RandR info */
284            QuartzRandRUpdateFakeModes(TRUE);
285            break;
286
287        default:
288            if(!QuartzModeEventHandler(screenNum, e, dev))
289                ErrorF("Unknown application defined event type %d.\n", e->subtype);
290    }
291}
292
293void DarwinListenOnOpenFD(int fd) {
294    ErrorF("DarwinListenOnOpenFD: %d\n", fd);
295
296    pthread_mutex_lock(&fd_add_lock);
297    if(fd_add_count < FD_ADD_MAX)
298        fd_add[fd_add_count++] = fd;
299    else
300        ErrorF("FD Addition buffer at max.  Dropping fd addition request.\n");
301
302    pthread_cond_broadcast(&fd_add_ready_cond);
303    pthread_mutex_unlock(&fd_add_lock);
304}
305
306static void DarwinProcessFDAdditionQueue_thread(void *args) {
307    pthread_mutex_lock(&fd_add_lock);
308    while(true) {
309        while(fd_add_count) {
310            DarwinSendDDXEvent(kXquartzListenOnOpenFD, 1, fd_add[--fd_add_count]);
311        }
312        pthread_cond_wait(&fd_add_ready_cond, &fd_add_lock);
313    }
314}
315
316Bool DarwinEQInit(void) {
317    int *p;
318
319    for(p=darwin_x11_modifier_mask_list, darwin_all_modifier_mask=0; *p; p++) {
320        darwin_x11_modifier_mask |= *p;
321    }
322
323    for(p=darwin_all_modifier_mask_additions, darwin_all_modifier_mask= darwin_x11_modifier_mask; *p; p++) {
324        darwin_all_modifier_mask |= *p;
325    }
326
327    mieqInit();
328    mieqSetHandler(ET_XQuartz, DarwinEventHandler);
329
330    /* Note that this *could* cause a potential async issue, since we're checking
331     * darwinEvents without holding the lock, but darwinEvents is only ever set
332     * here, so I don't bother.
333     */
334    if (!darwinEvents) {
335        darwinEvents = InitEventList(GetMaximumEventsNum());;
336
337        if (!darwinEvents)
338            FatalError("Couldn't allocate event buffer\n");
339
340        darwinEvents_lock();
341        pthread_cond_broadcast(&mieq_ready_cond);
342        darwinEvents_unlock();
343    }
344
345    if(!fd_add_tid)
346        fd_add_tid = create_thread(DarwinProcessFDAdditionQueue_thread, NULL);
347
348    return TRUE;
349}
350
351/*
352 * ProcessInputEvents
353 *  Read and process events from the event queue until it is empty.
354 */
355void ProcessInputEvents(void) {
356    char nullbyte;
357	int x = sizeof(nullbyte);
358
359    TA_SERVER();
360
361    mieqProcessInputEvents();
362
363    // Empty the signaling pipe
364    while (x == sizeof(nullbyte)) {
365      x = read(darwinEventReadFD, &nullbyte, sizeof(nullbyte));
366    }
367}
368
369/* Sends a null byte down darwinEventWriteFD, which will cause the
370   Dispatch() event loop to check out event queue */
371static void DarwinPokeEQ(void) {
372	char nullbyte=0;
373	//  <daniels> oh, i ... er ... christ.
374	write(darwinEventWriteFD, &nullbyte, sizeof(nullbyte));
375}
376
377/* Convert from Appkit pointer input values to X input values:
378 * Note: pointer_x and pointer_y are relative to the upper-left of primary
379 *       display.
380 */
381static void DarwinPrepareValuators(DeviceIntPtr pDev, int *valuators, ScreenPtr screen,
382                                   float pointer_x, float pointer_y,
383                                   float pressure, float tilt_x, float tilt_y) {
384    /* Fix offset between darwin and X screens */
385    pointer_x -= darwinMainScreenX + screen->x;
386    pointer_y -= darwinMainScreenY + screen->y;
387
388    if(pointer_x < 0.0)
389        pointer_x = 0.0;
390
391    if(pointer_y < 0.0)
392        pointer_y = 0.0;
393
394    if(pDev == darwinPointer) {
395        valuators[0] = pointer_x;
396        valuators[1] = pointer_y;
397        valuators[2] = 0;
398        valuators[3] = 0;
399        valuators[4] = 0;
400    } else {
401        /* Setup our array of values */
402        valuators[0] = XQUARTZ_VALUATOR_LIMIT * (pointer_x / (float)screenInfo.screens[0]->width);
403        valuators[1] = XQUARTZ_VALUATOR_LIMIT * (pointer_y / (float)screenInfo.screens[0]->height);
404        valuators[2] = XQUARTZ_VALUATOR_LIMIT * pressure;
405        valuators[3] = XQUARTZ_VALUATOR_LIMIT * tilt_x;
406        valuators[4] = XQUARTZ_VALUATOR_LIMIT * tilt_y;
407    }
408    //DEBUG_LOG("Pointer (%f, %f), Valuators: {%d,%d,%d,%d,%d}\n", pointer_x, pointer_y,
409    //          valuators[0], valuators[1], valuators[2], valuators[3], valuators[4]);
410}
411
412void DarwinSendPointerEvents(DeviceIntPtr pDev, int ev_type, int ev_button, float pointer_x, float pointer_y,
413			     float pressure, float tilt_x, float tilt_y) {
414	static int darwinFakeMouseButtonDown = 0;
415	int i, num_events;
416    ScreenPtr screen;
417    int valuators[5];
418
419    //DEBUG_LOG("x=%f, y=%f, p=%f, tx=%f, ty=%f\n", pointer_x, pointer_y, pressure, tilt_x, tilt_y);
420
421	if(!darwinEvents) {
422		DEBUG_LOG("DarwinSendPointerEvents called before darwinEvents was initialized\n");
423		return;
424	}
425
426    screen = miPointerGetScreen(pDev);
427    if(!screen) {
428        DEBUG_LOG("DarwinSendPointerEvents called before screen was initialized\n");
429        return;
430    }
431
432    /* Handle fake click */
433	if (ev_type == ButtonPress && darwinFakeButtons && ev_button == 1) {
434        if(darwinFakeMouseButtonDown != 0) {
435            /* We're currently "down" with another button, so release it first */
436            DarwinSendPointerEvents(pDev, ButtonRelease, darwinFakeMouseButtonDown, pointer_x, pointer_y, pressure, tilt_x, tilt_y);
437            darwinFakeMouseButtonDown=0;
438        }
439		if (darwin_all_modifier_flags & darwinFakeMouse2Mask) {
440            ev_button = 2;
441			darwinFakeMouseButtonDown = 2;
442            DarwinUpdateModKeys(darwin_all_modifier_flags & ~darwinFakeMouse2Mask);
443		} else if (darwin_all_modifier_flags & darwinFakeMouse3Mask) {
444            ev_button = 3;
445			darwinFakeMouseButtonDown = 3;
446            DarwinUpdateModKeys(darwin_all_modifier_flags & ~darwinFakeMouse3Mask);
447		}
448	}
449
450	if (ev_type == ButtonRelease && ev_button == 1) {
451        if(darwinFakeMouseButtonDown) {
452            ev_button = darwinFakeMouseButtonDown;
453        }
454
455        if(darwinFakeMouseButtonDown == 2) {
456            DarwinUpdateModKeys(darwin_all_modifier_flags & ~darwinFakeMouse2Mask);
457        } else if(darwinFakeMouseButtonDown == 3) {
458            DarwinUpdateModKeys(darwin_all_modifier_flags & ~darwinFakeMouse3Mask);
459        }
460
461        darwinFakeMouseButtonDown = 0;
462	}
463
464    DarwinPrepareValuators(pDev, valuators, screen, pointer_x, pointer_y, pressure, tilt_x, tilt_y);
465    darwinEvents_lock(); {
466        num_events = GetPointerEvents(darwinEvents, pDev, ev_type, ev_button,
467                                      POINTER_ABSOLUTE, 0, pDev==darwinTabletCurrent?5:2, valuators);
468        for(i=0; i<num_events; i++) mieqEnqueue (pDev, (InternalEvent*)darwinEvents[i].event);
469        if(num_events > 0) DarwinPokeEQ();
470    } darwinEvents_unlock();
471}
472
473void DarwinSendKeyboardEvents(int ev_type, int keycode) {
474	int i, num_events;
475
476	if(!darwinEvents) {
477		DEBUG_LOG("DarwinSendKeyboardEvents called before darwinEvents was initialized\n");
478		return;
479	}
480
481    darwinEvents_lock(); {
482        num_events = GetKeyboardEvents(darwinEvents, darwinKeyboard, ev_type, keycode + MIN_KEYCODE);
483        for(i=0; i<num_events; i++) mieqEnqueue(darwinKeyboard, (InternalEvent*)darwinEvents[i].event);
484        if(num_events > 0) DarwinPokeEQ();
485    } darwinEvents_unlock();
486}
487
488void DarwinSendProximityEvents(int ev_type, float pointer_x, float pointer_y) {
489	int i, num_events;
490    ScreenPtr screen;
491    DeviceIntPtr pDev = darwinTabletCurrent;
492    int valuators[5];
493
494	DEBUG_LOG("DarwinSendProximityEvents(%d, %f, %f)\n", ev_type, pointer_x, pointer_y);
495
496	if(!darwinEvents) {
497		DEBUG_LOG("DarwinSendProximityEvents called before darwinEvents was initialized\n");
498		return;
499	}
500
501    screen = miPointerGetScreen(pDev);
502    if(!screen) {
503        DEBUG_LOG("DarwinSendPointerEvents called before screen was initialized\n");
504        return;
505    }
506
507    DarwinPrepareValuators(pDev, valuators, screen, pointer_x, pointer_y, 0.0f, 0.0f, 0.0f);
508    darwinEvents_lock(); {
509        num_events = GetProximityEvents(darwinEvents, pDev, ev_type,
510                                        0, 5, valuators);
511        for(i=0; i<num_events; i++) mieqEnqueue (pDev, (InternalEvent*)darwinEvents[i].event);
512        if(num_events > 0) DarwinPokeEQ();
513    } darwinEvents_unlock();
514}
515
516
517/* Send the appropriate number of button clicks to emulate scroll wheel */
518void DarwinSendScrollEvents(float count_x, float count_y,
519							float pointer_x, float pointer_y,
520			    			float pressure, float tilt_x, float tilt_y) {
521	int sign_x, sign_y;
522	if(!darwinEvents) {
523		DEBUG_LOG("DarwinSendScrollEvents called before darwinEvents was initialized\n");
524		return;
525	}
526
527	sign_x = count_x > 0.0f ? SCROLLWHEELLEFTFAKE : SCROLLWHEELRIGHTFAKE;
528	sign_y = count_y > 0.0f ? SCROLLWHEELUPFAKE : SCROLLWHEELDOWNFAKE;
529	count_x = fabs(count_x);
530	count_y = fabs(count_y);
531
532	while ((count_x > 0.0f) || (count_y > 0.0f)) {
533		if (count_x > 0.0f) {
534			DarwinSendPointerEvents(darwinPointer, ButtonPress, sign_x, pointer_x, pointer_y, pressure, tilt_x, tilt_y);
535			DarwinSendPointerEvents(darwinPointer, ButtonRelease, sign_x, pointer_x, pointer_y, pressure, tilt_x, tilt_y);
536			count_x = count_x - 1.0f;
537		}
538		if (count_y > 0.0f) {
539			DarwinSendPointerEvents(darwinPointer, ButtonPress, sign_y, pointer_x, pointer_y, pressure, tilt_x, tilt_y);
540			DarwinSendPointerEvents(darwinPointer, ButtonRelease, sign_y, pointer_x, pointer_y, pressure, tilt_x, tilt_y);
541			count_y = count_y - 1.0f;
542		}
543	}
544}
545
546/* Send the appropriate KeyPress/KeyRelease events to GetKeyboardEvents to
547   reflect changing modifier flags (alt, control, meta, etc) */
548void DarwinUpdateModKeys(int flags) {
549	DarwinUpdateModifiers(KeyRelease, darwin_all_modifier_flags & ~flags & darwin_x11_modifier_mask);
550	DarwinUpdateModifiers(KeyPress, ~darwin_all_modifier_flags & flags & darwin_x11_modifier_mask);
551	darwin_all_modifier_flags = flags;
552}
553
554/*
555 * DarwinSendDDXEvent
556 *  Send the X server thread a message by placing it on the event queue.
557 */
558void DarwinSendDDXEvent(int type, int argc, ...) {
559    XQuartzEvent e;
560    int i;
561    va_list args;
562
563    memset(&e, 0, sizeof(e));
564    e.header = ET_Internal;
565    e.type = ET_XQuartz;
566    e.length = sizeof(e);
567    e.time = GetTimeInMillis();
568    e.subtype = type;
569
570    if (argc > 0 && argc < XQUARTZ_EVENT_MAXARGS) {
571        va_start (args, argc);
572        for (i = 0; i < argc; i++)
573            e.data[i] = (uint32_t) va_arg (args, uint32_t);
574        va_end (args);
575    }
576
577    darwinEvents_lock(); {
578        mieqEnqueue(NULL, (InternalEvent*)&e);
579        DarwinPokeEQ();
580    } darwinEvents_unlock();
581}
582