2 * MACDRV Cocoa event queue code
4 * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <sys/types.h>
22 #include <sys/event.h>
24 #include <libkern/OSAtomic.h>
25 #import <Carbon/Carbon.h>
27 #include "macdrv_cocoa.h"
28 #import "cocoa_event.h"
30 #import "cocoa_window.h"
33 static NSString* const WineEventQueueThreadDictionaryKey = @"WineEventQueueThreadDictionaryKey";
35 static NSString* const WineHotKeyMacIDKey = @"macID";
36 static NSString* const WineHotKeyVkeyKey = @"vkey";
37 static NSString* const WineHotKeyModFlagsKey = @"modFlags";
38 static NSString* const WineHotKeyKeyCodeKey = @"keyCode";
39 static NSString* const WineHotKeyCarbonRefKey = @"hotKeyRef";
40 static const OSType WineHotKeySignature = 'Wine';
43 @implementation NSEvent (WineExtensions)
45 static BOOL wine_commandKeyDown(NSUInteger flags)
47 return ((flags & (NSEventModifierFlagShift |
48 NSEventModifierFlagControl |
49 NSEventModifierFlagOption |
50 NSEventModifierFlagCommand)) == NSEventModifierFlagCommand);
53 + (BOOL) wine_commandKeyDown
55 return wine_commandKeyDown([self modifierFlags]);
58 - (BOOL) wine_commandKeyDown
60 return wine_commandKeyDown([self modifierFlags]);
66 @interface MacDrvEvent : NSObject
72 - (id) initWithEvent:(macdrv_event*)event;
76 @implementation MacDrvEvent
78 - (id) initWithEvent:(macdrv_event*)inEvent
83 event = macdrv_retain_event(inEvent);
90 if (event) macdrv_release_event(event);
97 @implementation WineEventQueue
101 [self doesNotRecognizeSelector:_cmd];
106 - (id) initWithEventHandler:(macdrv_event_handler)handler
108 NSParameterAssert(handler != nil);
116 fds[0] = fds[1] = kq = -1;
118 event_handler = handler;
119 events = [[NSMutableArray alloc] init];
120 eventsLock = [[NSLock alloc] init];
122 if (!events || !eventsLock)
129 fcntl(fds[0], F_SETFD, 1) == -1 ||
130 fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
131 fcntl(fds[1], F_SETFD, 1) == -1 ||
132 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1)
145 EV_SET(&kev, fds[0], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
148 rc = kevent(kq, &kev, 1, NULL, 0, NULL);
149 } while (rc == -1 && errno == EINTR);
161 NSNumber* hotKeyMacID;
163 for (hotKeyMacID in hotKeysByMacID)
165 NSDictionary<NSString *, id> *hotKeyDict = hotKeysByMacID[hotKeyMacID];
166 EventHotKeyRef hotKeyRef = [hotKeyDict[WineHotKeyCarbonRefKey] pointerValue];
167 UnregisterEventHotKey(hotKeyRef);
169 [hotKeysByMacID release];
170 [hotKeysByWinID release];
172 [eventsLock release];
174 if (kq != -1) close(kq);
175 if (fds[0] != -1) close(fds[0]);
176 if (fds[1] != -1) close(fds[1]);
181 - (void) signalEventAvailable
188 rc = write(fds[1], &junk, 1);
189 } while (rc < 0 && errno == EINTR);
191 if (rc < 0 && errno != EAGAIN)
192 ERR(@"%@: got error writing to event queue signaling pipe: %s\n", self, strerror(errno));
195 - (void) postEventObject:(MacDrvEvent*)event
198 MacDrvEvent* lastEvent;
202 indexes = [events indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
203 return ((MacDrvEvent*)obj)->event->deliver <= 0;
205 [events removeObjectsAtIndexes:indexes];
207 if ((event->event->type == MOUSE_MOVED_RELATIVE ||
208 event->event->type == MOUSE_MOVED_ABSOLUTE) &&
209 event->event->deliver == INT_MAX &&
210 (lastEvent = [events lastObject]) &&
211 (lastEvent->event->type == MOUSE_MOVED_RELATIVE ||
212 lastEvent->event->type == MOUSE_MOVED_ABSOLUTE) &&
213 lastEvent->event->deliver == INT_MAX &&
214 lastEvent->event->window == event->event->window &&
215 lastEvent->event->mouse_moved.drag == event->event->mouse_moved.drag)
217 if (event->event->type == MOUSE_MOVED_RELATIVE)
219 lastEvent->event->mouse_moved.x += event->event->mouse_moved.x;
220 lastEvent->event->mouse_moved.y += event->event->mouse_moved.y;
224 lastEvent->event->type = MOUSE_MOVED_ABSOLUTE;
225 lastEvent->event->mouse_moved.x = event->event->mouse_moved.x;
226 lastEvent->event->mouse_moved.y = event->event->mouse_moved.y;
229 lastEvent->event->mouse_moved.time_ms = event->event->mouse_moved.time_ms;
232 [events addObject:event];
236 [self signalEventAvailable];
239 - (void) postEvent:(macdrv_event*)inEvent
241 MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
242 [self postEventObject:event];
246 - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
251 MacDrvEvent* ret = nil;
253 /* Clear the pipe which signals there are pending events. */
256 rc = read(fds[0], buf, sizeof(buf));
257 } while (rc > 0 || (rc < 0 && errno == EINTR));
258 if (rc == 0 || (rc < 0 && errno != EAGAIN))
261 ERR(@"%@: event queue signaling pipe unexpectedly closed\n", self);
263 ERR(@"%@: got error reading from event queue signaling pipe: %s\n", self, strerror(errno));
270 while (index < [events count])
272 MacDrvEvent* event = events[index];
273 if (event_mask_for_type(event->event->type) & mask)
275 [[event retain] autorelease];
276 [events removeObjectAtIndex:index];
278 if (event->event->deliver == INT_MAX ||
279 OSAtomicDecrement32Barrier(&event->event->deliver) >= 0)
293 - (void) discardEventsPassingTest:(BOOL (^)(macdrv_event* event))block
301 indexes = [events indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
302 MacDrvEvent* event = obj;
303 return block(event->event);
306 [events removeObjectsAtIndexes:indexes];
312 - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
314 [self discardEventsPassingTest:^BOOL (macdrv_event* event){
315 return ((event_mask_for_type(event->type) & mask) &&
316 (!window || event->window == (macdrv_window)window));
320 - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout flags:(NSUInteger)flags
324 NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
327 type = (flags & WineQueryNoPreemptWait) ? QUERY_EVENT_NO_PREEMPT_WAIT : QUERY_EVENT;
328 event = macdrv_create_event(type, (WineWindow*)query->window);
329 event->query_event.query = macdrv_retain_query(query);
332 [self postEvent:event];
333 macdrv_release_event(event);
334 timedout = ![[WineApplicationController sharedController] waitUntilQueryDone:&query->done
336 processEvents:(flags & WineQueryProcessEvents) != 0];
337 return !timedout && query->status;
340 - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
342 return [self query:query timeout:timeout flags:0];
345 - (void) resetMouseEventPositions:(CGPoint)pos
349 pos = cgpoint_win_from_mac(pos);
353 for (event in events)
355 if (event->event->type == MOUSE_BUTTON)
357 event->event->mouse_button.x = pos.x;
358 event->event->mouse_button.y = pos.y;
360 else if (event->event->type == MOUSE_SCROLL)
362 event->event->mouse_scroll.x = pos.x;
363 event->event->mouse_scroll.y = pos.y;
370 - (BOOL) postHotKeyEvent:(UInt32)hotKeyNumber time:(double)time
372 NSDictionary<NSString *, id> *hotKeyDict = hotKeysByMacID[@(hotKeyNumber)];
377 event = macdrv_create_event(HOTKEY_PRESS, nil);
378 event->hotkey_press.vkey = [hotKeyDict[WineHotKeyVkeyKey] unsignedIntValue];
379 event->hotkey_press.mod_flags = [hotKeyDict[WineHotKeyModFlagsKey] unsignedIntValue];
380 event->hotkey_press.keycode = [hotKeyDict[WineHotKeyKeyCodeKey] unsignedIntValue];
381 event->hotkey_press.time_ms = [[WineApplicationController sharedController] ticksForEventTime:time];
383 [self postEvent:event];
385 macdrv_release_event(event);
388 return hotKeyDict != nil;
391 static OSStatus HotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
393 WineEventQueue* self = userData;
395 EventHotKeyID hotKeyID;
397 status = GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL,
398 sizeof(hotKeyID), NULL, &hotKeyID);
401 if (hotKeyID.signature != WineHotKeySignature ||
402 ![self postHotKeyEvent:hotKeyID.id time:GetEventTime(theEvent)])
403 status = eventNotHandledErr;
409 - (void) unregisterHotKey:(unsigned int)vkey modFlags:(unsigned int)modFlags
411 NSArray<NSNumber *> *winIDPair = @[@(vkey), @(modFlags)];
412 NSDictionary<NSString *, id> *hotKeyDict = hotKeysByWinID[winIDPair];
415 EventHotKeyRef hotKeyRef = [hotKeyDict[WineHotKeyCarbonRefKey] pointerValue];
416 NSNumber* macID = hotKeyDict[WineHotKeyMacIDKey];
418 UnregisterEventHotKey(hotKeyRef);
419 [hotKeysByMacID removeObjectForKey:macID];
420 [hotKeysByWinID removeObjectForKey:winIDPair];
424 - (int) registerHotKey:(UInt32)keyCode modifiers:(UInt32)modifiers vkey:(unsigned int)vkey modFlags:(unsigned int)modFlags
426 static EventHandlerRef handler;
427 static UInt32 hotKeyNumber;
429 NSArray<NSNumber *> *winIDPair;
430 EventHotKeyID hotKeyID;
431 EventHotKeyRef hotKeyRef;
432 NSDictionary<NSString *, id> *hotKeyDict;
436 EventTypeSpec eventType = { kEventClassKeyboard, kEventHotKeyPressed };
437 status = InstallApplicationEventHandler(HotKeyHandler, 1, &eventType, self, &handler);
440 ERR(@"InstallApplicationEventHandler() failed: %d\n", status);
442 return MACDRV_HOTKEY_FAILURE;
446 if (!hotKeysByMacID && !(hotKeysByMacID = [[NSMutableDictionary alloc] init]))
447 return MACDRV_HOTKEY_FAILURE;
448 if (!hotKeysByWinID && !(hotKeysByWinID = [[NSMutableDictionary alloc] init]))
449 return MACDRV_HOTKEY_FAILURE;
451 winIDPair = @[@(vkey), @(modFlags)];
452 if (hotKeysByWinID[winIDPair])
453 return MACDRV_HOTKEY_ALREADY_REGISTERED;
455 hotKeyID.signature = WineHotKeySignature;
456 hotKeyID.id = hotKeyNumber++;
458 status = RegisterEventHotKey(keyCode, modifiers, hotKeyID, GetApplicationEventTarget(),
459 kEventHotKeyExclusive, &hotKeyRef);
460 if (status == eventHotKeyExistsErr)
461 return MACDRV_HOTKEY_ALREADY_REGISTERED;
464 ERR(@"RegisterEventHotKey() failed: %d\n", status);
465 return MACDRV_HOTKEY_FAILURE;
470 WineHotKeyMacIDKey : @(hotKeyID.id),
471 WineHotKeyVkeyKey : @(vkey),
472 WineHotKeyModFlagsKey : @(modFlags),
473 WineHotKeyKeyCodeKey : @(keyCode),
474 WineHotKeyCarbonRefKey : [NSValue valueWithPointer:hotKeyRef]
476 hotKeysByMacID[@(hotKeyID.id)] = hotKeyDict;
477 hotKeysByWinID[winIDPair] = hotKeyDict;
479 return MACDRV_HOTKEY_SUCCESS;
483 /***********************************************************************
486 * Run a block on the main thread synchronously.
488 void OnMainThread(dispatch_block_t block)
492 NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
493 WineEventQueue* queue = threadDict[WineEventQueueThreadDictionaryKey];
494 dispatch_semaphore_t semaphore = NULL;
495 __block BOOL finished;
499 semaphore = dispatch_semaphore_create(0);
500 dispatch_retain(semaphore);
508 [queue signalEventAvailable];
511 dispatch_semaphore_signal(semaphore);
512 dispatch_release(semaphore);
522 MacDrvEvent* macDrvEvent;
526 (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
528 queue->event_handler(macDrvEvent->event);
532 kevent(queue->kq, NULL, 0, &kev, 1, NULL);
538 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
539 dispatch_release(semaphore);
545 /***********************************************************************
546 * macdrv_create_event_queue
548 * Register this thread with the application on the main thread, and set
549 * up an event queue on which it can deliver events to this thread.
551 macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler)
555 NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
557 WineEventQueue* queue = threadDict[WineEventQueueThreadDictionaryKey];
560 queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
563 if ([[WineApplicationController sharedController] registerEventQueue:queue])
564 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
570 return (macdrv_event_queue)queue;
574 /***********************************************************************
575 * macdrv_destroy_event_queue
577 * Tell the application that this thread is exiting and destroy the
578 * associated event queue.
580 void macdrv_destroy_event_queue(macdrv_event_queue queue)
584 WineEventQueue* q = (WineEventQueue*)queue;
585 NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
587 [[WineApplicationController sharedController] unregisterEventQueue:q];
588 [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey];
592 /***********************************************************************
593 * macdrv_get_event_queue_fd
595 * Get the file descriptor whose readability signals that there are
596 * events on the event queue.
598 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
600 WineEventQueue* q = (WineEventQueue*)queue;
604 /***********************************************************************
605 * macdrv_copy_event_from_queue
607 * Pull an event matching the event mask from the event queue and store
608 * it in the event record pointed to by the event parameter. If a
609 * matching event was found, return non-zero; otherwise, return 0.
611 * The caller is responsible for calling macdrv_release_event on any
612 * event returned by this function.
614 int macdrv_copy_event_from_queue(macdrv_event_queue queue,
615 macdrv_event_mask mask, macdrv_event **event)
619 WineEventQueue* q = (WineEventQueue*)queue;
621 MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
623 *event = macdrv_retain_event(macDrvEvent->event);
625 return (macDrvEvent != nil);
629 /***********************************************************************
630 * macdrv_create_event
632 macdrv_event* macdrv_create_event(int type, WineWindow* window)
636 event = calloc(1, sizeof(*event));
638 event->deliver = INT_MAX;
640 event->window = (macdrv_window)[window retain];
644 /***********************************************************************
645 * macdrv_retain_event
647 macdrv_event* macdrv_retain_event(macdrv_event *event)
649 OSAtomicIncrement32Barrier(&event->refs);
653 /***********************************************************************
654 * macdrv_release_event
656 * Decrements the reference count of an event. If the count falls to
657 * zero, cleans up any resources, such as allocated memory or retained
658 * objects, held by the event and deallocates it
660 void macdrv_release_event(macdrv_event *event)
664 if (OSAtomicDecrement32Barrier(&event->refs) <= 0)
669 if (event->im_set_text.text)
670 CFRelease(event->im_set_text.text);
672 case KEYBOARD_CHANGED:
673 CFRelease(event->keyboard_changed.uchr);
674 CFRelease(event->keyboard_changed.input_source);
677 case QUERY_EVENT_NO_PREEMPT_WAIT:
678 macdrv_release_query(event->query_event.query);
680 case WINDOW_GOT_FOCUS:
681 [(NSMutableSet*)event->window_got_focus.tried_windows release];
685 [(WineWindow*)event->window release];
691 /***********************************************************************
692 * macdrv_create_query
694 macdrv_query* macdrv_create_query(void)
698 query = calloc(1, sizeof(*query));
703 /***********************************************************************
704 * macdrv_retain_query
706 macdrv_query* macdrv_retain_query(macdrv_query *query)
708 OSAtomicIncrement32Barrier(&query->refs);
712 /***********************************************************************
713 * macdrv_release_query
715 void macdrv_release_query(macdrv_query *query)
717 if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
721 case QUERY_DRAG_OPERATION:
722 if (query->drag_operation.pasteboard)
723 CFRelease(query->drag_operation.pasteboard);
725 case QUERY_DRAG_DROP:
726 if (query->drag_drop.pasteboard)
727 CFRelease(query->drag_drop.pasteboard);
729 case QUERY_PASTEBOARD_DATA:
730 if (query->pasteboard_data.type)
731 CFRelease(query->pasteboard_data.type);
734 [(WineWindow*)query->window release];
739 /***********************************************************************
740 * macdrv_set_query_done
742 void macdrv_set_query_done(macdrv_query *query)
744 macdrv_retain_query(query);
750 macdrv_release_query(query);
752 event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
755 timestamp:[[NSProcessInfo processInfo] systemUptime]
758 subtype:WineApplicationEventWakeQuery
761 [NSApp postEvent:event atStart:TRUE];
768 /***********************************************************************
769 * macdrv_register_hot_key
771 int macdrv_register_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags,
772 unsigned int keycode, unsigned int modifiers)
774 WineEventQueue* queue = (WineEventQueue*)q;
778 ret = [queue registerHotKey:keycode modifiers:modifiers vkey:vkey modFlags:mod_flags];
785 /***********************************************************************
786 * macdrv_unregister_hot_key
788 void macdrv_unregister_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags)
790 WineEventQueue* queue = (WineEventQueue*)q;
793 [queue unregisterHotKey:vkey modFlags:mod_flags];