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 @interface MacDrvEvent : NSObject
49 - (id) initWithEvent:(macdrv_event*)event;
53 @implementation MacDrvEvent
55 - (id) initWithEvent:(macdrv_event*)inEvent
60 event = macdrv_retain_event(inEvent);
67 if (event) macdrv_release_event(event);
74 @implementation WineEventQueue
78 [self doesNotRecognizeSelector:_cmd];
83 - (id) initWithEventHandler:(macdrv_event_handler)handler
85 NSParameterAssert(handler != nil);
93 fds[0] = fds[1] = kq = -1;
95 event_handler = handler;
96 events = [[NSMutableArray alloc] init];
97 eventsLock = [[NSLock alloc] init];
99 if (!events || !eventsLock)
106 fcntl(fds[0], F_SETFD, 1) == -1 ||
107 fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
108 fcntl(fds[1], F_SETFD, 1) == -1 ||
109 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1)
122 EV_SET(&kev, fds[0], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
125 rc = kevent(kq, &kev, 1, NULL, 0, NULL);
126 } while (rc == -1 && errno == EINTR);
138 NSNumber* hotKeyMacID;
140 for (hotKeyMacID in hotKeysByMacID)
142 NSDictionary* hotKeyDict = [hotKeysByMacID objectForKey:hotKeyMacID];
143 EventHotKeyRef hotKeyRef = [[hotKeyDict objectForKey:WineHotKeyCarbonRefKey] pointerValue];
144 UnregisterEventHotKey(hotKeyRef);
146 [hotKeysByMacID release];
147 [hotKeysByWinID release];
149 [eventsLock release];
151 if (kq != -1) close(kq);
152 if (fds[0] != -1) close(fds[0]);
153 if (fds[1] != -1) close(fds[1]);
158 - (void) signalEventAvailable
165 rc = write(fds[1], &junk, 1);
166 } while (rc < 0 && errno == EINTR);
168 if (rc < 0 && errno != EAGAIN)
169 ERR(@"%@: got error writing to event queue signaling pipe: %s\n", self, strerror(errno));
172 - (void) postEventObject:(MacDrvEvent*)event
175 MacDrvEvent* lastEvent;
179 indexes = [events indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
180 return ((MacDrvEvent*)obj)->event->deliver <= 0;
182 [events removeObjectsAtIndexes:indexes];
184 if ((event->event->type == MOUSE_MOVED ||
185 event->event->type == MOUSE_MOVED_ABSOLUTE) &&
186 event->event->deliver == INT_MAX &&
187 (lastEvent = [events lastObject]) &&
188 (lastEvent->event->type == MOUSE_MOVED ||
189 lastEvent->event->type == MOUSE_MOVED_ABSOLUTE) &&
190 lastEvent->event->deliver == INT_MAX &&
191 lastEvent->event->window == event->event->window &&
192 lastEvent->event->mouse_moved.drag == event->event->mouse_moved.drag)
194 if (event->event->type == MOUSE_MOVED)
196 lastEvent->event->mouse_moved.x += event->event->mouse_moved.x;
197 lastEvent->event->mouse_moved.y += event->event->mouse_moved.y;
201 lastEvent->event->type = MOUSE_MOVED_ABSOLUTE;
202 lastEvent->event->mouse_moved.x = event->event->mouse_moved.x;
203 lastEvent->event->mouse_moved.y = event->event->mouse_moved.y;
206 lastEvent->event->mouse_moved.time_ms = event->event->mouse_moved.time_ms;
209 [events addObject:event];
213 [self signalEventAvailable];
216 - (void) postEvent:(macdrv_event*)inEvent
218 MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
219 [self postEventObject:event];
223 - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
228 MacDrvEvent* ret = nil;
230 /* Clear the pipe which signals there are pending events. */
233 rc = read(fds[0], buf, sizeof(buf));
234 } while (rc > 0 || (rc < 0 && errno == EINTR));
235 if (rc == 0 || (rc < 0 && errno != EAGAIN))
238 ERR(@"%@: event queue signaling pipe unexpectedly closed\n", self);
240 ERR(@"%@: got error reading from event queue signaling pipe: %s\n", self, strerror(errno));
247 while (index < [events count])
249 MacDrvEvent* event = [events objectAtIndex:index];
250 if (event_mask_for_type(event->event->type) & mask)
252 [[event retain] autorelease];
253 [events removeObjectAtIndex:index];
255 if (event->event->deliver == INT_MAX ||
256 OSAtomicDecrement32Barrier(&event->event->deliver) >= 0)
270 - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
272 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
277 indexes = [events indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
278 MacDrvEvent* event = obj;
279 return ((event_mask_for_type(event->event->type) & mask) &&
280 (!window || event->event->window == (macdrv_window)window));
283 [events removeObjectsAtIndexes:indexes];
290 - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout processEvents:(BOOL)processEvents
293 NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
296 event = macdrv_create_event(QUERY_EVENT, (WineWindow*)query->window);
297 event->query_event.query = macdrv_retain_query(query);
300 [self postEvent:event];
301 macdrv_release_event(event);
302 timedout = ![[WineApplicationController sharedController] waitUntilQueryDone:&query->done
304 processEvents:processEvents];
305 return !timedout && query->status;
308 - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
310 return [self query:query timeout:timeout processEvents:FALSE];
313 - (void) resetMouseEventPositions:(CGPoint)pos
319 for (event in events)
321 if (event->event->type == MOUSE_BUTTON)
323 event->event->mouse_button.x = pos.x;
324 event->event->mouse_button.y = pos.y;
326 else if (event->event->type == MOUSE_SCROLL)
328 event->event->mouse_scroll.x = pos.x;
329 event->event->mouse_scroll.y = pos.y;
336 - (BOOL) postHotKeyEvent:(UInt32)hotKeyNumber time:(double)time
338 NSDictionary* hotKeyDict = [hotKeysByMacID objectForKey:[NSNumber numberWithUnsignedInt:hotKeyNumber]];
343 event = macdrv_create_event(HOTKEY_PRESS, nil);
344 event->hotkey_press.vkey = [[hotKeyDict objectForKey:WineHotKeyVkeyKey] unsignedIntValue];
345 event->hotkey_press.mod_flags = [[hotKeyDict objectForKey:WineHotKeyModFlagsKey] unsignedIntValue];
346 event->hotkey_press.keycode = [[hotKeyDict objectForKey:WineHotKeyKeyCodeKey] unsignedIntValue];
347 event->hotkey_press.time_ms = [[WineApplicationController sharedController] ticksForEventTime:time];
349 [self postEvent:event];
351 macdrv_release_event(event);
354 return hotKeyDict != nil;
357 static OSStatus HotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
359 WineEventQueue* self = userData;
361 EventHotKeyID hotKeyID;
363 status = GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL,
364 sizeof(hotKeyID), NULL, &hotKeyID);
367 if (hotKeyID.signature != WineHotKeySignature ||
368 ![self postHotKeyEvent:hotKeyID.id time:GetEventTime(theEvent)])
369 status = eventNotHandledErr;
375 - (void) unregisterHotKey:(unsigned int)vkey modFlags:(unsigned int)modFlags
377 NSNumber* vkeyNumber = [NSNumber numberWithUnsignedInt:vkey];
378 NSNumber* modFlagsNumber = [NSNumber numberWithUnsignedInt:modFlags];
379 NSArray* winIDPair = [NSArray arrayWithObjects:vkeyNumber, modFlagsNumber, nil];
380 NSDictionary* hotKeyDict = [hotKeysByWinID objectForKey:winIDPair];
383 EventHotKeyRef hotKeyRef = [[hotKeyDict objectForKey:WineHotKeyCarbonRefKey] pointerValue];
384 NSNumber* macID = [hotKeyDict objectForKey:WineHotKeyMacIDKey];
386 UnregisterEventHotKey(hotKeyRef);
387 [hotKeysByMacID removeObjectForKey:macID];
388 [hotKeysByWinID removeObjectForKey:winIDPair];
392 - (int) registerHotKey:(UInt32)keyCode modifiers:(UInt32)modifiers vkey:(unsigned int)vkey modFlags:(unsigned int)modFlags
394 static EventHandlerRef handler;
395 static UInt32 hotKeyNumber;
397 NSNumber* vkeyNumber;
398 NSNumber* modFlagsNumber;
400 EventHotKeyID hotKeyID;
401 EventHotKeyRef hotKeyRef;
402 NSNumber* macIDNumber;
403 NSDictionary* hotKeyDict;
407 EventTypeSpec eventType = { kEventClassKeyboard, kEventHotKeyPressed };
408 status = InstallApplicationEventHandler(HotKeyHandler, 1, &eventType, self, &handler);
411 ERR(@"InstallApplicationEventHandler() failed: %d\n", status);
413 return MACDRV_HOTKEY_FAILURE;
417 if (!hotKeysByMacID && !(hotKeysByMacID = [[NSMutableDictionary alloc] init]))
418 return MACDRV_HOTKEY_FAILURE;
419 if (!hotKeysByWinID && !(hotKeysByWinID = [[NSMutableDictionary alloc] init]))
420 return MACDRV_HOTKEY_FAILURE;
422 vkeyNumber = [NSNumber numberWithUnsignedInt:vkey];
423 modFlagsNumber = [NSNumber numberWithUnsignedInt:modFlags];
424 winIDPair = [NSArray arrayWithObjects:vkeyNumber, modFlagsNumber, nil];
425 if ([hotKeysByWinID objectForKey:winIDPair])
426 return MACDRV_HOTKEY_ALREADY_REGISTERED;
428 hotKeyID.signature = WineHotKeySignature;
429 hotKeyID.id = hotKeyNumber++;
431 status = RegisterEventHotKey(keyCode, modifiers, hotKeyID, GetApplicationEventTarget(),
432 kEventHotKeyExclusive, &hotKeyRef);
433 if (status == eventHotKeyExistsErr)
434 return MACDRV_HOTKEY_ALREADY_REGISTERED;
437 ERR(@"RegisterEventHotKey() failed: %d\n", status);
438 return MACDRV_HOTKEY_FAILURE;
441 macIDNumber = [NSNumber numberWithUnsignedInt:hotKeyID.id];
442 hotKeyDict = [NSDictionary dictionaryWithObjectsAndKeys:
443 macIDNumber, WineHotKeyMacIDKey,
444 vkeyNumber, WineHotKeyVkeyKey,
445 modFlagsNumber, WineHotKeyModFlagsKey,
446 [NSNumber numberWithUnsignedInt:keyCode], WineHotKeyKeyCodeKey,
447 [NSValue valueWithPointer:hotKeyRef], WineHotKeyCarbonRefKey,
449 [hotKeysByMacID setObject:hotKeyDict forKey:macIDNumber];
450 [hotKeysByWinID setObject:hotKeyDict forKey:winIDPair];
452 return MACDRV_HOTKEY_SUCCESS;
456 /***********************************************************************
459 * Run a block on the main thread synchronously.
461 void OnMainThread(dispatch_block_t block)
463 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
464 NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
465 WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
466 dispatch_semaphore_t semaphore;
467 __block BOOL finished;
471 semaphore = dispatch_semaphore_create(0);
472 dispatch_retain(semaphore);
480 [queue signalEventAvailable];
483 dispatch_semaphore_signal(semaphore);
484 dispatch_release(semaphore);
492 MacDrvEvent* macDrvEvent;
496 (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
498 queue->event_handler(macDrvEvent->event);
504 pool = [[NSAutoreleasePool alloc] init];
506 kevent(queue->kq, NULL, 0, &kev, 1, NULL);
512 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
513 dispatch_release(semaphore);
520 /***********************************************************************
521 * macdrv_create_event_queue
523 * Register this thread with the application on the main thread, and set
524 * up an event queue on which it can deliver events to this thread.
526 macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler)
528 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
529 NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
531 WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
534 queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
537 if ([[WineApplicationController sharedController] registerEventQueue:queue])
538 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
545 return (macdrv_event_queue)queue;
548 /***********************************************************************
549 * macdrv_destroy_event_queue
551 * Tell the application that this thread is exiting and destroy the
552 * associated event queue.
554 void macdrv_destroy_event_queue(macdrv_event_queue queue)
556 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
557 WineEventQueue* q = (WineEventQueue*)queue;
558 NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
560 [[WineApplicationController sharedController] unregisterEventQueue:q];
561 [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey];
566 /***********************************************************************
567 * macdrv_get_event_queue_fd
569 * Get the file descriptor whose readability signals that there are
570 * events on the event queue.
572 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
574 WineEventQueue* q = (WineEventQueue*)queue;
578 /***********************************************************************
579 * macdrv_copy_event_from_queue
581 * Pull an event matching the event mask from the event queue and store
582 * it in the event record pointed to by the event parameter. If a
583 * matching event was found, return non-zero; otherwise, return 0.
585 * The caller is responsible for calling macdrv_release_event on any
586 * event returned by this function.
588 int macdrv_copy_event_from_queue(macdrv_event_queue queue,
589 macdrv_event_mask mask, macdrv_event **event)
591 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
592 WineEventQueue* q = (WineEventQueue*)queue;
594 MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
596 *event = macdrv_retain_event(macDrvEvent->event);
599 return (macDrvEvent != nil);
602 /***********************************************************************
603 * macdrv_create_event
605 macdrv_event* macdrv_create_event(int type, WineWindow* window)
609 event = calloc(1, sizeof(*event));
611 event->deliver = INT_MAX;
613 event->window = (macdrv_window)[window retain];
617 /***********************************************************************
618 * macdrv_retain_event
620 macdrv_event* macdrv_retain_event(macdrv_event *event)
622 OSAtomicIncrement32Barrier(&event->refs);
626 /***********************************************************************
627 * macdrv_release_event
629 * Decrements the reference count of an event. If the count falls to
630 * zero, cleans up any resources, such as allocated memory or retained
631 * objects, held by the event and deallocates it
633 void macdrv_release_event(macdrv_event *event)
635 if (OSAtomicDecrement32Barrier(&event->refs) <= 0)
637 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
642 if (event->im_set_text.text)
643 CFRelease(event->im_set_text.text);
645 case KEYBOARD_CHANGED:
646 CFRelease(event->keyboard_changed.uchr);
647 CFRelease(event->keyboard_changed.input_source);
650 macdrv_release_query(event->query_event.query);
652 case WINDOW_GOT_FOCUS:
653 [(NSMutableSet*)event->window_got_focus.tried_windows release];
657 [(WineWindow*)event->window release];
664 /***********************************************************************
665 * macdrv_create_query
667 macdrv_query* macdrv_create_query(void)
671 query = calloc(1, sizeof(*query));
676 /***********************************************************************
677 * macdrv_retain_query
679 macdrv_query* macdrv_retain_query(macdrv_query *query)
681 OSAtomicIncrement32Barrier(&query->refs);
685 /***********************************************************************
686 * macdrv_release_query
688 void macdrv_release_query(macdrv_query *query)
690 if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
694 case QUERY_DRAG_OPERATION:
695 if (query->drag_operation.pasteboard)
696 CFRelease(query->drag_operation.pasteboard);
698 case QUERY_DRAG_DROP:
699 if (query->drag_drop.pasteboard)
700 CFRelease(query->drag_drop.pasteboard);
702 case QUERY_PASTEBOARD_DATA:
703 if (query->pasteboard_data.type)
704 CFRelease(query->pasteboard_data.type);
707 [(WineWindow*)query->window release];
712 /***********************************************************************
713 * macdrv_set_query_done
715 void macdrv_set_query_done(macdrv_query *query)
717 macdrv_retain_query(query);
723 macdrv_release_query(query);
725 event = [NSEvent otherEventWithType:NSApplicationDefined
728 timestamp:[[NSProcessInfo processInfo] systemUptime]
731 subtype:WineApplicationEventWakeQuery
734 [NSApp postEvent:event atStart:TRUE];
741 /***********************************************************************
742 * macdrv_register_hot_key
744 int macdrv_register_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags,
745 unsigned int keycode, unsigned int modifiers)
747 WineEventQueue* queue = (WineEventQueue*)q;
751 ret = [queue registerHotKey:keycode modifiers:modifiers vkey:vkey modFlags:mod_flags];
758 /***********************************************************************
759 * macdrv_unregister_hot_key
761 void macdrv_unregister_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags)
763 WineEventQueue* queue = (WineEventQueue*)q;
766 [queue unregisterHotKey:vkey modFlags:mod_flags];