2 * Darwin event queue and event handling
4 * Copyright 2007-2008 Apple Inc.
5 * Copyright 2004 Kaleb S. KEITHLEY. All Rights Reserved.
6 * Copyright (c) 2002-2004 Torrey T. Lyons. All Rights Reserved.
8 * This file is based on mieq.c by Keith Packard,
9 * which contains the following copyright:
10 * Copyright 1990, 1998 The Open Group
13 * Copyright (c) 2002-2012 Apple Inc. All rights reserved.
15 * Permission is hereby granted, free of charge, to any person
16 * obtaining a copy of this software and associated documentation files
17 * (the "Software"), to deal in the Software without restriction,
18 * including without limitation the rights to use, copy, modify, merge,
19 * publish, distribute, sublicense, and/or sell copies of the Software,
20 * and to permit persons to whom the Software is furnished to do so,
21 * subject to the following conditions:
23 * The above copyright notice and this permission notice shall be
24 * included in all copies or substantial portions of the Software.
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
30 * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
31 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33 * DEALINGS IN THE SOFTWARE.
35 * Except as contained in this notice, the name(s) of the above
36 * copyright holders shall not be used in advertising or otherwise to
37 * promote the sale, use or other dealings in this Software without
38 * prior written authorization.
41 #include "sanitizedCarbon.h"
43 #ifdef HAVE_DIX_CONFIG_H
44 #include <dix-config.h>
49 #include <X11/Xproto.h>
51 #include "windowstr.h"
52 #include "pixmapstr.h"
54 #include "inpututils.h"
57 #include "scrnintstr.h"
58 #include "mipointer.h"
60 #include "exglobals.h"
64 #include "quartzKeyboard.h"
65 #include "quartzRandR.h"
66 #include "darwinEvents.h"
68 #include <sys/types.h>
75 #include <IOKit/hidsystem/IOLLEvent.h>
77 #include <X11/extensions/applewmconst.h>
78 #include "applewmExt.h"
80 /* FIXME: Abstract this better */
82 QuartzModeEventHandler(int screenNum
, XQuartzEvent
*e
, DeviceIntPtr dev
);
84 int darwin_all_modifier_flags
= 0; // last known modifier state
85 int darwin_all_modifier_mask
= 0;
86 int darwin_x11_modifier_mask
= 0;
88 #define FD_ADD_MAX 128
89 static int fd_add
[FD_ADD_MAX
];
91 static pthread_mutex_t fd_add_lock
= PTHREAD_MUTEX_INITIALIZER
;
92 static pthread_cond_t fd_add_ready_cond
= PTHREAD_COND_INITIALIZER
;
93 static pthread_t fd_add_tid
= NULL
;
95 static InternalEvent
* darwinEvents
= NULL
;
97 static pthread_mutex_t mieq_lock
= PTHREAD_MUTEX_INITIALIZER
;
98 static pthread_cond_t mieq_ready_cond
= PTHREAD_COND_INITIALIZER
;
100 /*** Pthread Magics ***/
102 create_thread(void *(*func
)(void *), void *arg
)
107 pthread_attr_init(&attr
);
108 pthread_attr_setscope(&attr
, PTHREAD_SCOPE_SYSTEM
);
109 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
110 pthread_create(&tid
, &attr
, func
, arg
);
111 pthread_attr_destroy(&attr
);
117 darwinEvents_lock(void);
119 darwinEvents_lock(void)
122 if ((err
= pthread_mutex_lock(&mieq_lock
))) {
123 ErrorF("%s:%s:%d: Failed to lock mieq_lock: %d\n",
124 __FILE__
, __FUNCTION__
, __LINE__
, err
);
127 if (darwinEvents
== NULL
) {
128 pthread_cond_wait(&mieq_ready_cond
, &mieq_lock
);
133 darwinEvents_unlock(void);
135 darwinEvents_unlock(void)
138 if ((err
= pthread_mutex_unlock(&mieq_lock
))) {
139 ErrorF("%s:%s:%d: Failed to unlock mieq_lock: %d\n",
140 __FILE__
, __FUNCTION__
, __LINE__
, err
);
146 * DarwinPressModifierKey
147 * Press or release the given modifier key (one of NX_MODIFIERKEY_* constants)
150 DarwinPressModifierKey(int pressed
, int key
)
152 int keycode
= DarwinModifierNXKeyToNXKeycode(key
, 0);
155 ErrorF("DarwinPressModifierKey bad keycode: key=%d\n", key
);
159 DarwinSendKeyboardEvents(pressed
, keycode
);
163 * DarwinUpdateModifiers
164 * Send events to update the modifier state.
167 static int darwin_x11_modifier_mask_list
[] = {
168 #ifdef NX_DEVICELCMDKEYMASK
169 NX_DEVICELCTLKEYMASK
, NX_DEVICERCTLKEYMASK
,
170 NX_DEVICELSHIFTKEYMASK
, NX_DEVICERSHIFTKEYMASK
,
171 NX_DEVICELCMDKEYMASK
, NX_DEVICERCMDKEYMASK
,
172 NX_DEVICELALTKEYMASK
, NX_DEVICERALTKEYMASK
,
174 NX_CONTROLMASK
, NX_SHIFTMASK
, NX_COMMANDMASK
,
181 static int darwin_all_modifier_mask_additions
[] = { NX_SECONDARYFNMASK
, };
184 DarwinUpdateModifiers(int pressed
, // KeyPress or KeyRelease
185 int flags
) // modifier flags that have changed
190 /* Capslock is special. This mask is the state of capslock (on/off),
191 * not the state of the button. Hopefully we can find a better solution.
193 if (NX_ALPHASHIFTMASK
& flags
) {
194 DarwinPressModifierKey(KeyPress
, NX_MODIFIERKEY_ALPHALOCK
);
195 DarwinPressModifierKey(KeyRelease
, NX_MODIFIERKEY_ALPHALOCK
);
198 for (f
= darwin_x11_modifier_mask_list
; *f
; f
++)
199 if (*f
& flags
&& *f
!= NX_ALPHASHIFTMASK
) {
200 key
= DarwinModifierNXMaskToNXKey(*f
);
202 ErrorF("DarwinUpdateModifiers: Unsupported NXMask: 0x%x\n",
205 DarwinPressModifierKey(pressed
, key
);
209 /* Generic handler for Xquartz-specifc events. When possible, these should
210 be moved into their own individual functions and set as handlers using
214 DarwinEventHandler(int screenNum
, InternalEvent
*ie
, DeviceIntPtr dev
)
216 XQuartzEvent
*e
= &(ie
->xquartz_event
);
218 switch (e
->subtype
) {
219 case kXquartzControllerNotify
:
220 DEBUG_LOG("kXquartzControllerNotify\n");
221 AppleWMSendEvent(AppleWMControllerNotify
,
222 AppleWMControllerNotifyMask
,
227 case kXquartzPasteboardNotify
:
228 DEBUG_LOG("kXquartzPasteboardNotify\n");
229 AppleWMSendEvent(AppleWMPasteboardNotify
,
230 AppleWMPasteboardNotifyMask
,
235 case kXquartzActivate
:
236 DEBUG_LOG("kXquartzActivate\n");
238 AppleWMSendEvent(AppleWMActivationNotify
,
239 AppleWMActivationNotifyMask
,
243 case kXquartzDeactivate
:
244 DEBUG_LOG("kXquartzDeactivate\n");
245 AppleWMSendEvent(AppleWMActivationNotify
,
246 AppleWMActivationNotifyMask
,
247 AppleWMIsInactive
, 0);
251 case kXquartzReloadPreferences
:
252 DEBUG_LOG("kXquartzReloadPreferences\n");
253 AppleWMSendEvent(AppleWMActivationNotify
,
254 AppleWMActivationNotifyMask
,
255 AppleWMReloadPreferences
, 0);
258 case kXquartzToggleFullscreen
:
259 DEBUG_LOG("kXquartzToggleFullscreen\n");
260 if (XQuartzIsRootless
)
262 "Ignoring kXquartzToggleFullscreen because of rootless mode.");
264 QuartzRandRToggleFullscreen();
267 case kXquartzSetRootless
:
268 DEBUG_LOG("kXquartzSetRootless\n");
270 QuartzRandRSetFakeRootless();
273 QuartzRandRSetFakeFullscreen(FALSE
);
277 case kXquartzSetRootClip
:
278 QuartzSetRootClip((Bool
)e
->data
[0]);
285 case kXquartzSpaceChanged
:
286 DEBUG_LOG("kXquartzSpaceChanged\n");
287 QuartzSpaceChanged(e
->data
[0]);
290 case kXquartzListenOnOpenFD
:
291 ErrorF("Calling ListenOnOpenFD() for new fd: %d\n", (int)e
->data
[0]);
292 ListenOnOpenFD((int)e
->data
[0], 1);
295 case kXquartzReloadKeymap
:
296 DarwinKeyboardReloadHandler();
299 case kXquartzDisplayChanged
:
300 DEBUG_LOG("kXquartzDisplayChanged\n");
301 QuartzUpdateScreens();
303 /* Update our RandR info */
304 QuartzRandRUpdateFakeModes(TRUE
);
308 if (!QuartzModeEventHandler(screenNum
, e
, dev
))
309 ErrorF("Unknown application defined event type %d.\n", e
->subtype
);
314 DarwinListenOnOpenFD(int fd
)
316 ErrorF("DarwinListenOnOpenFD: %d\n", fd
);
318 pthread_mutex_lock(&fd_add_lock
);
319 if (fd_add_count
< FD_ADD_MAX
)
320 fd_add
[fd_add_count
++] = fd
;
322 ErrorF("FD Addition buffer at max. Dropping fd addition request.\n");
324 pthread_cond_broadcast(&fd_add_ready_cond
);
325 pthread_mutex_unlock(&fd_add_lock
);
329 DarwinProcessFDAdditionQueue_thread(void *args
)
331 /* TODO: Possibly adjust this to no longer be a race... maybe trigger this
332 * once a client connects and claims to be the WM.
335 * There's already an internal callback chain for setting selection [in 1.5]
336 * ownership. See the CallSelectionCallback at the bottom of
337 * ProcSetSelectionOwner, and xfixes/select.c for an example of how to hook
341 struct timespec sleep_for
;
342 struct timespec sleep_remaining
;
344 sleep_for
.tv_sec
= 3;
345 sleep_for
.tv_nsec
= 0;
348 "X11.app: DarwinProcessFDAdditionQueue_thread: Sleeping to allow xinitrc to catchup.\n");
349 while (nanosleep(&sleep_for
, &sleep_remaining
) != 0) {
350 sleep_for
= sleep_remaining
;
353 pthread_mutex_lock(&fd_add_lock
);
355 while (fd_add_count
) {
356 DarwinSendDDXEvent(kXquartzListenOnOpenFD
, 1,
357 fd_add
[--fd_add_count
]);
359 pthread_cond_wait(&fd_add_ready_cond
, &fd_add_lock
);
370 for (p
= darwin_x11_modifier_mask_list
, darwin_all_modifier_mask
= 0; *p
;
372 darwin_x11_modifier_mask
|= *p
;
375 for (p
= darwin_all_modifier_mask_additions
,
376 darwin_all_modifier_mask
= darwin_x11_modifier_mask
;
378 darwin_all_modifier_mask
|= *p
;
382 mieqSetHandler(ET_XQuartz
, DarwinEventHandler
);
384 /* Note that this *could* cause a potential async issue, since we're checking
385 * darwinEvents without holding the lock, but darwinEvents is only ever set
386 * here, so I don't bother.
389 darwinEvents
= InitEventList(GetMaximumEventsNum());
393 FatalError("Couldn't allocate event buffer\n");
396 pthread_cond_broadcast(&mieq_ready_cond
);
397 darwinEvents_unlock();
401 fd_add_tid
= create_thread(DarwinProcessFDAdditionQueue_thread
, NULL
);
414 * Read and process events from the event queue until it is empty.
417 ProcessInputEvents(void)
420 int x
= sizeof(nullbyte
);
422 mieqProcessInputEvents();
424 // Empty the signaling pipe
425 while (x
== sizeof(nullbyte
)) {
426 x
= read(darwinEventReadFD
, &nullbyte
, sizeof(nullbyte
));
430 /* Sends a null byte down darwinEventWriteFD, which will cause the
431 Dispatch() event loop to check out event queue */
436 // <daniels> oh, i ... er ... christ.
437 write(darwinEventWriteFD
, &nullbyte
, sizeof(nullbyte
));
441 DarwinInputReleaseButtonsAndKeys(DeviceIntPtr pDev
)
447 for (i
= 0; i
< pDev
->button
->numButtons
; i
++) {
448 if (BitIsOn(pDev
->button
->down
, i
)) {
449 QueuePointerEvents(pDev
, ButtonRelease
, i
,
457 for (i
= 0; i
< NUM_KEYCODES
; i
++) {
458 if (BitIsOn(pDev
->key
->down
, i
+ MIN_KEYCODE
)) {
459 QueueKeyboardEvents(pDev
, KeyRelease
, i
+ MIN_KEYCODE
,
465 } darwinEvents_unlock();
469 DarwinSendTabletEvents(DeviceIntPtr pDev
, int ev_type
, int ev_button
,
470 double pointer_x
, double pointer_y
,
471 double pressure
, double tilt_x
,
475 ValuatorMask valuators
;
478 DEBUG_LOG("%s called before darwinEvents was initialized\n",
483 screen
= miPointerGetScreen(pDev
);
485 DEBUG_LOG("%s called before screen was initialized\n",
490 /* Fix offset between darwin and X screens */
491 pointer_x
-= darwinMainScreenX
+ screen
->x
;
492 pointer_y
-= darwinMainScreenY
+ screen
->y
;
494 /* Adjust our pointer location to the [0,1] range */
495 pointer_x
= pointer_x
/ (double)screenInfo
.width
;
496 pointer_y
= pointer_y
/ (double)screenInfo
.height
;
498 valuator_mask_zero(&valuators
);
499 valuator_mask_set_double(&valuators
, 0, XQUARTZ_VALUATOR_LIMIT
* pointer_x
);
500 valuator_mask_set_double(&valuators
, 1, XQUARTZ_VALUATOR_LIMIT
* pointer_y
);
501 valuator_mask_set_double(&valuators
, 2, XQUARTZ_VALUATOR_LIMIT
* pressure
);
502 valuator_mask_set_double(&valuators
, 3, XQUARTZ_VALUATOR_LIMIT
* tilt_x
);
503 valuator_mask_set_double(&valuators
, 4, XQUARTZ_VALUATOR_LIMIT
* tilt_y
);
507 if (ev_type
== ProximityIn
|| ev_type
== ProximityOut
) {
508 QueueProximityEvents(pDev
, ev_type
, &valuators
);
510 QueuePointerEvents(pDev
, ev_type
, ev_button
, POINTER_ABSOLUTE
,
514 } darwinEvents_unlock();
518 DarwinSendPointerEvents(DeviceIntPtr pDev
, int ev_type
, int ev_button
,
519 double pointer_x
, double pointer_y
,
520 double pointer_dx
, double pointer_dy
)
522 static int darwinFakeMouseButtonDown
= 0;
524 ValuatorMask valuators
;
527 DEBUG_LOG("%s called before darwinEvents was initialized\n",
532 screen
= miPointerGetScreen(pDev
);
534 DEBUG_LOG("%s called before screen was initialized\n",
539 /* Handle fake click */
540 if (ev_type
== ButtonPress
&& darwinFakeButtons
&& ev_button
== 1) {
541 if (darwinFakeMouseButtonDown
!= 0) {
542 /* We're currently "down" with another button, so release it first */
543 DarwinSendPointerEvents(pDev
, ButtonRelease
,
544 darwinFakeMouseButtonDown
,
545 pointer_x
, pointer_y
, 0.0, 0.0);
546 darwinFakeMouseButtonDown
= 0;
548 if (darwin_all_modifier_flags
& darwinFakeMouse2Mask
) {
550 darwinFakeMouseButtonDown
= 2;
552 darwin_all_modifier_flags
& ~darwinFakeMouse2Mask
);
554 else if (darwin_all_modifier_flags
& darwinFakeMouse3Mask
) {
556 darwinFakeMouseButtonDown
= 3;
558 darwin_all_modifier_flags
& ~darwinFakeMouse3Mask
);
562 if (ev_type
== ButtonRelease
&& ev_button
== 1) {
563 if (darwinFakeMouseButtonDown
) {
564 ev_button
= darwinFakeMouseButtonDown
;
567 if (darwinFakeMouseButtonDown
== 2) {
569 darwin_all_modifier_flags
& ~darwinFakeMouse2Mask
);
571 else if (darwinFakeMouseButtonDown
== 3) {
573 darwin_all_modifier_flags
& ~darwinFakeMouse3Mask
);
576 darwinFakeMouseButtonDown
= 0;
579 /* Fix offset between darwin and X screens */
580 pointer_x
-= darwinMainScreenX
+ screen
->x
;
581 pointer_y
-= darwinMainScreenY
+ screen
->y
;
583 valuator_mask_zero(&valuators
);
584 valuator_mask_set_double(&valuators
, 0, pointer_x
);
585 valuator_mask_set_double(&valuators
, 1, pointer_y
);
587 if (ev_type
== MotionNotify
) {
588 if (pointer_dx
!= 0.0)
589 valuator_mask_set_double(&valuators
, 2, pointer_dx
);
590 if (pointer_dy
!= 0.0)
591 valuator_mask_set_double(&valuators
, 3, pointer_dy
);
596 QueuePointerEvents(pDev
, ev_type
, ev_button
, POINTER_ABSOLUTE
,
599 } darwinEvents_unlock();
603 DarwinSendKeyboardEvents(int ev_type
, int keycode
)
608 "DarwinSendKeyboardEvents called before darwinEvents was initialized\n");
614 QueueKeyboardEvents(darwinKeyboard
, ev_type
, keycode
+ MIN_KEYCODE
,
617 } darwinEvents_unlock();
620 /* Send the appropriate number of button clicks to emulate scroll wheel */
622 DarwinSendScrollEvents(double scroll_x
, double scroll_y
) {
624 ValuatorMask valuators
;
628 "DarwinSendScrollEvents called before darwinEvents was initialized\n");
632 screen
= miPointerGetScreen(darwinPointer
);
635 "DarwinSendScrollEvents called before screen was initialized\n");
639 valuator_mask_zero(&valuators
);
640 valuator_mask_set_double(&valuators
, 4, scroll_y
);
641 valuator_mask_set_double(&valuators
, 5, scroll_x
);
645 QueuePointerEvents(darwinPointer
, MotionNotify
, 0,
646 POINTER_RELATIVE
, &valuators
);
648 } darwinEvents_unlock();
651 /* Send the appropriate KeyPress/KeyRelease events to GetKeyboardEvents to
652 reflect changing modifier flags (alt, control, meta, etc) */
654 DarwinUpdateModKeys(int flags
)
656 DarwinUpdateModifiers(
657 KeyRelease
, darwin_all_modifier_flags
& ~flags
&
658 darwin_x11_modifier_mask
);
659 DarwinUpdateModifiers(
660 KeyPress
, ~darwin_all_modifier_flags
& flags
&
661 darwin_x11_modifier_mask
);
662 darwin_all_modifier_flags
= flags
;
667 * Send the X server thread a message by placing it on the event queue.
670 DarwinSendDDXEvent(int type
, int argc
, ...)
676 memset(&e
, 0, sizeof(e
));
677 e
.header
= ET_Internal
;
679 e
.length
= sizeof(e
);
680 e
.time
= GetTimeInMillis();
683 if (argc
> 0 && argc
< XQUARTZ_EVENT_MAXARGS
) {
684 va_start(args
, argc
);
685 for (i
= 0; i
< argc
; i
++)
686 e
.data
[i
] = (uint32_t)va_arg(args
, uint32_t);
692 mieqEnqueue(NULL
, (InternalEvent
*)&e
);
694 } darwinEvents_unlock();