gdiplus: Add option to save metafile tests to files.
[wine/multimedia.git] / dlls / winemac.drv / cocoa_event.m
blob85cac5f4996b8bef091f6ce34d8d04e5972abb6b
1 /*
2  * MACDRV Cocoa event queue code
3  *
4  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
5  *
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.
10  *
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.
15  *
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
19  */
21 #include <sys/types.h>
22 #include <sys/event.h>
23 #include <sys/time.h>
24 #include <libkern/OSAtomic.h>
25 #import <Carbon/Carbon.h>
27 #include "macdrv_cocoa.h"
28 #import "cocoa_event.h"
29 #import "cocoa_app.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
45 @public
46     macdrv_event* event;
49     - (id) initWithEvent:(macdrv_event*)event;
51 @end
53 @implementation MacDrvEvent
55     - (id) initWithEvent:(macdrv_event*)inEvent
56     {
57         self = [super init];
58         if (self)
59         {
60             event = macdrv_retain_event(inEvent);
61         }
62         return self;
63     }
65     - (void) dealloc
66     {
67         if (event) macdrv_release_event(event);
68         [super dealloc];
69     }
71 @end
74 @implementation WineEventQueue
76     - (id) init
77     {
78         [self doesNotRecognizeSelector:_cmd];
79         [self release];
80         return nil;
81     }
83     - (id) initWithEventHandler:(macdrv_event_handler)handler
84     {
85         NSParameterAssert(handler != nil);
87         self = [super init];
88         if (self != nil)
89         {
90             struct kevent kev;
91             int rc;
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)
100             {
101                 [self release];
102                 return nil;
103             }
105             if (pipe(fds) ||
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)
110             {
111                 [self release];
112                 return nil;
113             }
115             kq = kqueue();
116             if (kq < 0)
117             {
118                 [self release];
119                 return nil;
120             }
122             EV_SET(&kev, fds[0], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
123             do
124             {
125                 rc = kevent(kq, &kev, 1, NULL, 0, NULL);
126             } while (rc == -1 && errno == EINTR);
127             if (rc == -1)
128             {
129                 [self release];
130                 return nil;
131             }
132         }
133         return self;
134     }
136     - (void) dealloc
137     {
138         NSNumber* hotKeyMacID;
140         for (hotKeyMacID in hotKeysByMacID)
141         {
142             NSDictionary* hotKeyDict = [hotKeysByMacID objectForKey:hotKeyMacID];
143             EventHotKeyRef hotKeyRef = [[hotKeyDict objectForKey:WineHotKeyCarbonRefKey] pointerValue];
144             UnregisterEventHotKey(hotKeyRef);
145         }
146         [hotKeysByMacID release];
147         [hotKeysByWinID release];
148         [events 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]);
155         [super dealloc];
156     }
158     - (void) signalEventAvailable
159     {
160         char junk = 1;
161         int rc;
163         do
164         {
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));
170     }
172     - (void) postEventObject:(MacDrvEvent*)event
173     {
174         NSIndexSet* indexes;
175         MacDrvEvent* lastEvent;
177         [eventsLock lock];
179         indexes = [events indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
180             return ((MacDrvEvent*)obj)->event->deliver <= 0;
181         }];
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)
193         {
194             if (event->event->type == MOUSE_MOVED)
195             {
196                 lastEvent->event->mouse_moved.x += event->event->mouse_moved.x;
197                 lastEvent->event->mouse_moved.y += event->event->mouse_moved.y;
198             }
199             else
200             {
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;
204             }
206             lastEvent->event->mouse_moved.time_ms = event->event->mouse_moved.time_ms;
207         }
208         else
209             [events addObject:event];
211         [eventsLock unlock];
213         [self signalEventAvailable];
214     }
216     - (void) postEvent:(macdrv_event*)inEvent
217     {
218         MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
219         [self postEventObject:event];
220         [event release];
221     }
223     - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
224     {
225         char buf[512];
226         int rc;
227         NSUInteger index;
228         MacDrvEvent* ret = nil;
230         /* Clear the pipe which signals there are pending events. */
231         do
232         {
233             rc = read(fds[0], buf, sizeof(buf));
234         } while (rc > 0 || (rc < 0 && errno == EINTR));
235         if (rc == 0 || (rc < 0 && errno != EAGAIN))
236         {
237             if (rc == 0)
238                 ERR(@"%@: event queue signaling pipe unexpectedly closed\n", self);
239             else
240                 ERR(@"%@: got error reading from event queue signaling pipe: %s\n", self, strerror(errno));
241             return nil;
242         }
244         [eventsLock lock];
246         index = 0;
247         while (index < [events count])
248         {
249             MacDrvEvent* event = [events objectAtIndex:index];
250             if (event_mask_for_type(event->event->type) & mask)
251             {
252                 [[event retain] autorelease];
253                 [events removeObjectAtIndex:index];
255                 if (event->event->deliver == INT_MAX ||
256                     OSAtomicDecrement32Barrier(&event->event->deliver) >= 0)
257                 {
258                     ret = event;
259                     break;
260                 }
261             }
262             else
263                 index++;
264         }
266         [eventsLock unlock];
267         return ret;
268     }
270     - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
271     {
272         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
273         NSIndexSet* indexes;
275         [eventsLock lock];
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));
281         }];
283         [events removeObjectsAtIndexes:indexes];
285         [eventsLock unlock];
287         [pool release];
288     }
290     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout processEvents:(BOOL)processEvents
291     {
292         macdrv_event* event;
293         NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
294         BOOL timedout;
296         event = macdrv_create_event(QUERY_EVENT, (WineWindow*)query->window);
297         event->query_event.query = macdrv_retain_query(query);
298         query->done = FALSE;
300         [self postEvent:event];
301         macdrv_release_event(event);
302         timedout = ![[WineApplicationController sharedController] waitUntilQueryDone:&query->done
303                                                                              timeout:timeoutDate
304                                                                        processEvents:processEvents];
305         return !timedout && query->status;
306     }
308     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
309     {
310         return [self query:query timeout:timeout processEvents:FALSE];
311     }
313     - (void) resetMouseEventPositions:(CGPoint)pos
314     {
315         MacDrvEvent* event;
317         [eventsLock lock];
319         for (event in events)
320         {
321             if (event->event->type == MOUSE_BUTTON)
322             {
323                 event->event->mouse_button.x = pos.x;
324                 event->event->mouse_button.y = pos.y;
325             }
326             else if (event->event->type == MOUSE_SCROLL)
327             {
328                 event->event->mouse_scroll.x = pos.x;
329                 event->event->mouse_scroll.y = pos.y;
330             }
331         }
333         [eventsLock unlock];
334     }
336     - (BOOL) postHotKeyEvent:(UInt32)hotKeyNumber time:(double)time
337     {
338         NSDictionary* hotKeyDict = [hotKeysByMacID objectForKey:[NSNumber numberWithUnsignedInt:hotKeyNumber]];
339         if (hotKeyDict)
340         {
341             macdrv_event* event;
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);
352         }
354         return hotKeyDict != nil;
355     }
357     static OSStatus HotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
358     {
359         WineEventQueue* self = userData;
360         OSStatus status;
361         EventHotKeyID hotKeyID;
363         status = GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL,
364                                    sizeof(hotKeyID), NULL, &hotKeyID);
365         if (status == noErr)
366         {
367             if (hotKeyID.signature != WineHotKeySignature ||
368                 ![self postHotKeyEvent:hotKeyID.id time:GetEventTime(theEvent)])
369                 status = eventNotHandledErr;
370         }
372         return status;
373     }
375     - (void) unregisterHotKey:(unsigned int)vkey modFlags:(unsigned int)modFlags
376     {
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];
381         if (hotKeyDict)
382         {
383             EventHotKeyRef hotKeyRef = [[hotKeyDict objectForKey:WineHotKeyCarbonRefKey] pointerValue];
384             NSNumber* macID = [hotKeyDict objectForKey:WineHotKeyMacIDKey];
386             UnregisterEventHotKey(hotKeyRef);
387             [hotKeysByMacID removeObjectForKey:macID];
388             [hotKeysByWinID removeObjectForKey:winIDPair];
389         }
390     }
392     - (int) registerHotKey:(UInt32)keyCode modifiers:(UInt32)modifiers vkey:(unsigned int)vkey modFlags:(unsigned int)modFlags
393     {
394         static EventHandlerRef handler;
395         static UInt32 hotKeyNumber;
396         OSStatus status;
397         NSNumber* vkeyNumber;
398         NSNumber* modFlagsNumber;
399         NSArray* winIDPair;
400         EventHotKeyID hotKeyID;
401         EventHotKeyRef hotKeyRef;
402         NSNumber* macIDNumber;
403         NSDictionary* hotKeyDict;
405         if (!handler)
406         {
407             EventTypeSpec eventType = { kEventClassKeyboard, kEventHotKeyPressed };
408             status = InstallApplicationEventHandler(HotKeyHandler, 1, &eventType, self, &handler);
409             if (status != noErr)
410             {
411                 ERR(@"InstallApplicationEventHandler() failed: %d\n", status);
412                 handler = NULL;
413                 return MACDRV_HOTKEY_FAILURE;
414             }
415         }
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;
435         if (status != noErr)
436         {
437             ERR(@"RegisterEventHotKey() failed: %d\n", status);
438             return MACDRV_HOTKEY_FAILURE;
439         }
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,
448                       nil];
449         [hotKeysByMacID setObject:hotKeyDict forKey:macIDNumber];
450         [hotKeysByWinID setObject:hotKeyDict forKey:winIDPair];
452         return MACDRV_HOTKEY_SUCCESS;
453     }
456 /***********************************************************************
457  *              OnMainThread
459  * Run a block on the main thread synchronously.
460  */
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;
469     if (!queue)
470     {
471         semaphore = dispatch_semaphore_create(0);
472         dispatch_retain(semaphore);
473     }
475     finished = FALSE;
476     OnMainThreadAsync(^{
477         block();
478         finished = TRUE;
479         if (queue)
480             [queue signalEventAvailable];
481         else
482         {
483             dispatch_semaphore_signal(semaphore);
484             dispatch_release(semaphore);
485         }
486     });
488     if (queue)
489     {
490         while (!finished)
491         {
492             MacDrvEvent* macDrvEvent;
493             struct kevent kev;
495             while (!finished &&
496                    (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
497             {
498                 queue->event_handler(macDrvEvent->event);
499             }
501             if (!finished)
502             {
503                 [pool release];
504                 pool = [[NSAutoreleasePool alloc] init];
506                 kevent(queue->kq, NULL, 0, &kev, 1, NULL);
507             }
508         }
509     }
510     else
511     {
512         dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
513         dispatch_release(semaphore);
514     }
516     [pool release];
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.
525  */
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];
532     if (!queue)
533     {
534         queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
535         if (queue)
536         {
537             if ([[WineApplicationController sharedController] registerEventQueue:queue])
538                 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
539             else
540                 queue = nil;
541         }
542     }
544     [pool release];
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.
553  */
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];
563     [pool release];
566 /***********************************************************************
567  *              macdrv_get_event_queue_fd
569  * Get the file descriptor whose readability signals that there are
570  * events on the event queue.
571  */
572 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
574     WineEventQueue* q = (WineEventQueue*)queue;
575     return q->fds[0];
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.
587  */
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];
595     if (macDrvEvent)
596         *event = macdrv_retain_event(macDrvEvent->event);
598     [pool release];
599     return (macDrvEvent != nil);
602 /***********************************************************************
603  *              macdrv_create_event
604  */
605 macdrv_event* macdrv_create_event(int type, WineWindow* window)
607     macdrv_event *event;
609     event = calloc(1, sizeof(*event));
610     event->refs = 1;
611     event->deliver = INT_MAX;
612     event->type = type;
613     event->window = (macdrv_window)[window retain];
614     return event;
617 /***********************************************************************
618  *              macdrv_retain_event
619  */
620 macdrv_event* macdrv_retain_event(macdrv_event *event)
622     OSAtomicIncrement32Barrier(&event->refs);
623     return event;
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
632  */
633 void macdrv_release_event(macdrv_event *event)
635     if (OSAtomicDecrement32Barrier(&event->refs) <= 0)
636     {
637         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
639         switch (event->type)
640         {
641             case IM_SET_TEXT:
642                 if (event->im_set_text.text)
643                     CFRelease(event->im_set_text.text);
644                 break;
645             case KEYBOARD_CHANGED:
646                 CFRelease(event->keyboard_changed.uchr);
647                 CFRelease(event->keyboard_changed.input_source);
648                 break;
649             case QUERY_EVENT:
650                 macdrv_release_query(event->query_event.query);
651                 break;
652             case WINDOW_GOT_FOCUS:
653                 [(NSMutableSet*)event->window_got_focus.tried_windows release];
654                 break;
655         }
657         [(WineWindow*)event->window release];
658         free(event);
660         [pool release];
661     }
664 /***********************************************************************
665  *              macdrv_create_query
666  */
667 macdrv_query* macdrv_create_query(void)
669     macdrv_query *query;
671     query = calloc(1, sizeof(*query));
672     query->refs = 1;
673     return query;
676 /***********************************************************************
677  *              macdrv_retain_query
678  */
679 macdrv_query* macdrv_retain_query(macdrv_query *query)
681     OSAtomicIncrement32Barrier(&query->refs);
682     return query;
685 /***********************************************************************
686  *              macdrv_release_query
687  */
688 void macdrv_release_query(macdrv_query *query)
690     if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
691     {
692         switch (query->type)
693         {
694             case QUERY_DRAG_OPERATION:
695                 if (query->drag_operation.pasteboard)
696                     CFRelease(query->drag_operation.pasteboard);
697                 break;
698             case QUERY_DRAG_DROP:
699                 if (query->drag_drop.pasteboard)
700                     CFRelease(query->drag_drop.pasteboard);
701                 break;
702             case QUERY_PASTEBOARD_DATA:
703                 if (query->pasteboard_data.type)
704                     CFRelease(query->pasteboard_data.type);
705                 break;
706         }
707         [(WineWindow*)query->window release];
708         free(query);
709     }
712 /***********************************************************************
713  *              macdrv_set_query_done
714  */
715 void macdrv_set_query_done(macdrv_query *query)
717     macdrv_retain_query(query);
719     OnMainThreadAsync(^{
720         NSEvent* event;
722         query->done = TRUE;
723         macdrv_release_query(query);
725         event = [NSEvent otherEventWithType:NSApplicationDefined
726                                    location:NSZeroPoint
727                               modifierFlags:0
728                                   timestamp:[[NSProcessInfo processInfo] systemUptime]
729                                windowNumber:0
730                                     context:nil
731                                     subtype:WineApplicationEventWakeQuery
732                                       data1:0
733                                       data2:0];
734         [NSApp postEvent:event atStart:TRUE];
735     });
738 @end
741 /***********************************************************************
742  *              macdrv_register_hot_key
743  */
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;
748     __block int ret;
750     OnMainThread(^{
751         ret = [queue registerHotKey:keycode modifiers:modifiers vkey:vkey modFlags:mod_flags];
752     });
754     return ret;
758 /***********************************************************************
759  *              macdrv_unregister_hot_key
760  */
761 void macdrv_unregister_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags)
763     WineEventQueue* queue = (WineEventQueue*)q;
765     OnMainThreadAsync(^{
766         [queue unregisterHotKey:vkey modFlags:mod_flags];
767     });