dmime: Include dmobject.h in dmime_private.h.
[wine.git] / dlls / winemac.drv / cocoa_event.m
blob701de6eb8f7863f323ab82d3ea177bb70fbb9cfe
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 @implementation NSEvent (WineExtensions)
45     static BOOL wine_commandKeyDown(NSUInteger flags)
46     {
47         return ((flags & (NSEventModifierFlagShift |
48                           NSEventModifierFlagControl |
49                           NSEventModifierFlagOption |
50                           NSEventModifierFlagCommand)) == NSEventModifierFlagCommand);
51     }
53     + (BOOL) wine_commandKeyDown
54     {
55         return wine_commandKeyDown([self modifierFlags]);
56     }
58     - (BOOL) wine_commandKeyDown
59     {
60         return wine_commandKeyDown([self modifierFlags]);
61     }
63 @end
66 @interface MacDrvEvent : NSObject
68 @public
69     macdrv_event* event;
72     - (id) initWithEvent:(macdrv_event*)event;
74 @end
76 @implementation MacDrvEvent
78     - (id) initWithEvent:(macdrv_event*)inEvent
79     {
80         self = [super init];
81         if (self)
82         {
83             event = macdrv_retain_event(inEvent);
84         }
85         return self;
86     }
88     - (void) dealloc
89     {
90         if (event) macdrv_release_event(event);
91         [super dealloc];
92     }
94 @end
97 @implementation WineEventQueue
99     - (id) init
100     {
101         [self doesNotRecognizeSelector:_cmd];
102         [self release];
103         return nil;
104     }
106     - (id) initWithEventHandler:(macdrv_event_handler)handler
107     {
108         NSParameterAssert(handler != nil);
110         self = [super init];
111         if (self != nil)
112         {
113             struct kevent kev;
114             int rc;
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)
123             {
124                 [self release];
125                 return nil;
126             }
128             if (pipe(fds) ||
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)
133             {
134                 [self release];
135                 return nil;
136             }
138             kq = kqueue();
139             if (kq < 0)
140             {
141                 [self release];
142                 return nil;
143             }
145             EV_SET(&kev, fds[0], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
146             do
147             {
148                 rc = kevent(kq, &kev, 1, NULL, 0, NULL);
149             } while (rc == -1 && errno == EINTR);
150             if (rc == -1)
151             {
152                 [self release];
153                 return nil;
154             }
155         }
156         return self;
157     }
159     - (void) dealloc
160     {
161         NSNumber* hotKeyMacID;
163         for (hotKeyMacID in hotKeysByMacID)
164         {
165             NSDictionary<NSString *, id> *hotKeyDict = hotKeysByMacID[hotKeyMacID];
166             EventHotKeyRef hotKeyRef = [hotKeyDict[WineHotKeyCarbonRefKey] pointerValue];
167             UnregisterEventHotKey(hotKeyRef);
168         }
169         [hotKeysByMacID release];
170         [hotKeysByWinID release];
171         [events 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]);
178         [super dealloc];
179     }
181     - (void) signalEventAvailable
182     {
183         char junk = 1;
184         int rc;
186         do
187         {
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));
193     }
195     - (void) postEventObject:(MacDrvEvent*)event
196     {
197         NSIndexSet* indexes;
198         MacDrvEvent* lastEvent;
200         [eventsLock lock];
202         indexes = [events indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
203             return ((MacDrvEvent*)obj)->event->deliver <= 0;
204         }];
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)
216         {
217             if (event->event->type == MOUSE_MOVED_RELATIVE)
218             {
219                 lastEvent->event->mouse_moved.x += event->event->mouse_moved.x;
220                 lastEvent->event->mouse_moved.y += event->event->mouse_moved.y;
221             }
222             else
223             {
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;
227             }
229             lastEvent->event->mouse_moved.time_ms = event->event->mouse_moved.time_ms;
230         }
231         else
232             [events addObject:event];
234         [eventsLock unlock];
236         [self signalEventAvailable];
237     }
239     - (void) postEvent:(macdrv_event*)inEvent
240     {
241         MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
242         [self postEventObject:event];
243         [event release];
244     }
246     - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
247     {
248         char buf[512];
249         int rc;
250         NSUInteger index;
251         MacDrvEvent* ret = nil;
253         /* Clear the pipe which signals there are pending events. */
254         do
255         {
256             rc = read(fds[0], buf, sizeof(buf));
257         } while (rc > 0 || (rc < 0 && errno == EINTR));
258         if (rc == 0 || (rc < 0 && errno != EAGAIN))
259         {
260             if (rc == 0)
261                 ERR(@"%@: event queue signaling pipe unexpectedly closed\n", self);
262             else
263                 ERR(@"%@: got error reading from event queue signaling pipe: %s\n", self, strerror(errno));
264             return nil;
265         }
267         [eventsLock lock];
269         index = 0;
270         while (index < [events count])
271         {
272             MacDrvEvent* event = events[index];
273             if (event_mask_for_type(event->event->type) & mask)
274             {
275                 [[event retain] autorelease];
276                 [events removeObjectAtIndex:index];
278                 if (event->event->deliver == INT_MAX ||
279                     OSAtomicDecrement32Barrier(&event->event->deliver) >= 0)
280                 {
281                     ret = event;
282                     break;
283                 }
284             }
285             else
286                 index++;
287         }
289         [eventsLock unlock];
290         return ret;
291     }
293     - (void) discardEventsPassingTest:(BOOL (^)(macdrv_event* event))block
294     {
295     @autoreleasepool
296     {
297         NSIndexSet* indexes;
299         [eventsLock lock];
301         indexes = [events indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
302             MacDrvEvent* event = obj;
303             return block(event->event);
304         }];
306         [events removeObjectsAtIndexes:indexes];
308         [eventsLock unlock];
309     }
310     }
312     - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
313     {
314         [self discardEventsPassingTest:^BOOL (macdrv_event* event){
315             return ((event_mask_for_type(event->type) & mask) &&
316                     (!window || event->window == (macdrv_window)window));
317         }];
318     }
320     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout flags:(NSUInteger)flags
321     {
322         int type;
323         macdrv_event* event;
324         NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
325         BOOL timedout;
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);
330         query->done = FALSE;
332         [self postEvent:event];
333         macdrv_release_event(event);
334         timedout = ![[WineApplicationController sharedController] waitUntilQueryDone:&query->done
335                                                                              timeout:timeoutDate
336                                                                        processEvents:(flags & WineQueryProcessEvents) != 0];
337         return !timedout && query->status;
338     }
340     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
341     {
342         return [self query:query timeout:timeout flags:0];
343     }
345     - (void) resetMouseEventPositions:(CGPoint)pos
346     {
347         MacDrvEvent* event;
349         pos = cgpoint_win_from_mac(pos);
351         [eventsLock lock];
353         for (event in events)
354         {
355             if (event->event->type == MOUSE_BUTTON)
356             {
357                 event->event->mouse_button.x = pos.x;
358                 event->event->mouse_button.y = pos.y;
359             }
360             else if (event->event->type == MOUSE_SCROLL)
361             {
362                 event->event->mouse_scroll.x = pos.x;
363                 event->event->mouse_scroll.y = pos.y;
364             }
365         }
367         [eventsLock unlock];
368     }
370     - (BOOL) postHotKeyEvent:(UInt32)hotKeyNumber time:(double)time
371     {
372         NSDictionary<NSString *, id> *hotKeyDict = hotKeysByMacID[@(hotKeyNumber)];
373         if (hotKeyDict)
374         {
375             macdrv_event* event;
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);
386         }
388         return hotKeyDict != nil;
389     }
391     static OSStatus HotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
392     {
393         WineEventQueue* self = userData;
394         OSStatus status;
395         EventHotKeyID hotKeyID;
397         status = GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL,
398                                    sizeof(hotKeyID), NULL, &hotKeyID);
399         if (status == noErr)
400         {
401             if (hotKeyID.signature != WineHotKeySignature ||
402                 ![self postHotKeyEvent:hotKeyID.id time:GetEventTime(theEvent)])
403                 status = eventNotHandledErr;
404         }
406         return status;
407     }
409     - (void) unregisterHotKey:(unsigned int)vkey modFlags:(unsigned int)modFlags
410     {
411         NSArray<NSNumber *> *winIDPair = @[@(vkey), @(modFlags)];
412         NSDictionary<NSString *, id> *hotKeyDict = hotKeysByWinID[winIDPair];
413         if (hotKeyDict)
414         {
415             EventHotKeyRef hotKeyRef = [hotKeyDict[WineHotKeyCarbonRefKey] pointerValue];
416             NSNumber* macID = hotKeyDict[WineHotKeyMacIDKey];
418             UnregisterEventHotKey(hotKeyRef);
419             [hotKeysByMacID removeObjectForKey:macID];
420             [hotKeysByWinID removeObjectForKey:winIDPair];
421         }
422     }
424     - (int) registerHotKey:(UInt32)keyCode modifiers:(UInt32)modifiers vkey:(unsigned int)vkey modFlags:(unsigned int)modFlags
425     {
426         static EventHandlerRef handler;
427         static UInt32 hotKeyNumber;
428         OSStatus status;
429         NSArray<NSNumber *> *winIDPair;
430         EventHotKeyID hotKeyID;
431         EventHotKeyRef hotKeyRef;
432         NSDictionary<NSString *, id> *hotKeyDict;
434         if (!handler)
435         {
436             EventTypeSpec eventType = { kEventClassKeyboard, kEventHotKeyPressed };
437             status = InstallApplicationEventHandler(HotKeyHandler, 1, &eventType, self, &handler);
438             if (status != noErr)
439             {
440                 ERR(@"InstallApplicationEventHandler() failed: %d\n", status);
441                 handler = NULL;
442                 return MACDRV_HOTKEY_FAILURE;
443             }
444         }
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;
462         if (status != noErr)
463         {
464             ERR(@"RegisterEventHotKey() failed: %d\n", status);
465             return MACDRV_HOTKEY_FAILURE;
466         }
468         hotKeyDict =
469         @{
470                 WineHotKeyMacIDKey : @(hotKeyID.id),
471                  WineHotKeyVkeyKey : @(vkey),
472              WineHotKeyModFlagsKey : @(modFlags),
473               WineHotKeyKeyCodeKey : @(keyCode),
474             WineHotKeyCarbonRefKey : [NSValue valueWithPointer:hotKeyRef]
475         };
476         hotKeysByMacID[@(hotKeyID.id)] = hotKeyDict;
477         hotKeysByWinID[winIDPair] = hotKeyDict;
479         return MACDRV_HOTKEY_SUCCESS;
480     }
483 /***********************************************************************
484  *              OnMainThread
486  * Run a block on the main thread synchronously.
487  */
488 void OnMainThread(dispatch_block_t block)
490 @autoreleasepool
492     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
493     WineEventQueue* queue = threadDict[WineEventQueueThreadDictionaryKey];
494     dispatch_semaphore_t semaphore = NULL;
495     __block BOOL finished;
497     if (!queue)
498     {
499         semaphore = dispatch_semaphore_create(0);
500         dispatch_retain(semaphore);
501     }
503     finished = FALSE;
504     OnMainThreadAsync(^{
505         block();
506         finished = TRUE;
507         if (queue)
508             [queue signalEventAvailable];
509         else
510         {
511             dispatch_semaphore_signal(semaphore);
512             dispatch_release(semaphore);
513         }
514     });
516     if (queue)
517     {
518         while (!finished)
519         {
520             @autoreleasepool
521             {
522                 MacDrvEvent* macDrvEvent;
523                 struct kevent kev;
525                 while (!finished &&
526                        (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
527                 {
528                     queue->event_handler(macDrvEvent->event);
529                 }
531                 if (!finished)
532                     kevent(queue->kq, NULL, 0, &kev, 1, NULL);
533             }
534         }
535     }
536     else
537     {
538         dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
539         dispatch_release(semaphore);
540     }
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.
550  */
551 macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler)
553 @autoreleasepool
555     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
557     WineEventQueue* queue = threadDict[WineEventQueueThreadDictionaryKey];
558     if (!queue)
559     {
560         queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
561         if (queue)
562         {
563             if ([[WineApplicationController sharedController] registerEventQueue:queue])
564                 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
565             else
566                 queue = nil;
567         }
568     }
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.
579  */
580 void macdrv_destroy_event_queue(macdrv_event_queue queue)
582 @autoreleasepool
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.
597  */
598 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
600     WineEventQueue* q = (WineEventQueue*)queue;
601     return q->fds[0];
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.
613  */
614 int macdrv_copy_event_from_queue(macdrv_event_queue queue,
615         macdrv_event_mask mask, macdrv_event **event)
617 @autoreleasepool
619     WineEventQueue* q = (WineEventQueue*)queue;
621     MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
622     if (macDrvEvent)
623         *event = macdrv_retain_event(macDrvEvent->event);
625     return (macDrvEvent != nil);
629 /***********************************************************************
630  *              macdrv_create_event
631  */
632 macdrv_event* macdrv_create_event(int type, WineWindow* window)
634     macdrv_event *event;
636     event = calloc(1, sizeof(*event));
637     event->refs = 1;
638     event->deliver = INT_MAX;
639     event->type = type;
640     event->window = (macdrv_window)[window retain];
641     return event;
644 /***********************************************************************
645  *              macdrv_retain_event
646  */
647 macdrv_event* macdrv_retain_event(macdrv_event *event)
649     OSAtomicIncrement32Barrier(&event->refs);
650     return event;
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
659  */
660 void macdrv_release_event(macdrv_event *event)
662 @autoreleasepool
664     if (OSAtomicDecrement32Barrier(&event->refs) <= 0)
665     {
666         switch (event->type)
667         {
668             case IM_SET_TEXT:
669                 if (event->im_set_text.text)
670                     CFRelease(event->im_set_text.text);
671                 break;
672             case KEYBOARD_CHANGED:
673                 CFRelease(event->keyboard_changed.uchr);
674                 CFRelease(event->keyboard_changed.input_source);
675                 break;
676             case QUERY_EVENT:
677             case QUERY_EVENT_NO_PREEMPT_WAIT:
678                 macdrv_release_query(event->query_event.query);
679                 break;
680             case WINDOW_GOT_FOCUS:
681                 [(NSMutableSet*)event->window_got_focus.tried_windows release];
682                 break;
683         }
685         [(WineWindow*)event->window release];
686         free(event);
687     }
691 /***********************************************************************
692  *              macdrv_create_query
693  */
694 macdrv_query* macdrv_create_query(void)
696     macdrv_query *query;
698     query = calloc(1, sizeof(*query));
699     query->refs = 1;
700     return query;
703 /***********************************************************************
704  *              macdrv_retain_query
705  */
706 macdrv_query* macdrv_retain_query(macdrv_query *query)
708     OSAtomicIncrement32Barrier(&query->refs);
709     return query;
712 /***********************************************************************
713  *              macdrv_release_query
714  */
715 void macdrv_release_query(macdrv_query *query)
717     if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
718     {
719         switch (query->type)
720         {
721             case QUERY_DRAG_OPERATION:
722                 if (query->drag_operation.pasteboard)
723                     CFRelease(query->drag_operation.pasteboard);
724                 break;
725             case QUERY_DRAG_DROP:
726                 if (query->drag_drop.pasteboard)
727                     CFRelease(query->drag_drop.pasteboard);
728                 break;
729             case QUERY_PASTEBOARD_DATA:
730                 if (query->pasteboard_data.type)
731                     CFRelease(query->pasteboard_data.type);
732                 break;
733         }
734         [(WineWindow*)query->window release];
735         free(query);
736     }
739 /***********************************************************************
740  *              macdrv_set_query_done
741  */
742 void macdrv_set_query_done(macdrv_query *query)
744     macdrv_retain_query(query);
746     OnMainThreadAsync(^{
747         NSEvent* event;
749         query->done = TRUE;
750         macdrv_release_query(query);
752         event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
753                                    location:NSZeroPoint
754                               modifierFlags:0
755                                   timestamp:[[NSProcessInfo processInfo] systemUptime]
756                                windowNumber:0
757                                     context:nil
758                                     subtype:WineApplicationEventWakeQuery
759                                       data1:0
760                                       data2:0];
761         [NSApp postEvent:event atStart:TRUE];
762     });
765 @end
768 /***********************************************************************
769  *              macdrv_register_hot_key
770  */
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;
775     __block int ret;
777     OnMainThread(^{
778         ret = [queue registerHotKey:keycode modifiers:modifiers vkey:vkey modFlags:mod_flags];
779     });
781     return ret;
785 /***********************************************************************
786  *              macdrv_unregister_hot_key
787  */
788 void macdrv_unregister_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags)
790     WineEventQueue* queue = (WineEventQueue*)q;
792     OnMainThreadAsync(^{
793         [queue unregisterHotKey:vkey modFlags:mod_flags];
794     });