winemac: When removing the status item for a systray icon, discard any associated...
[wine/multimedia.git] / dlls / winemac.drv / cocoa_event.m
blob757ad6b3bf31190398b9bd8e3dc89d3e40dad4f5
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) discardEventsPassingTest:(BOOL (^)(macdrv_event* event))block
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 block(event->event);
280         }];
282         [events removeObjectsAtIndexes:indexes];
284         [eventsLock unlock];
286         [pool release];
287     }
289     - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
290     {
291         [self discardEventsPassingTest:^BOOL (macdrv_event* event){
292             return ((event_mask_for_type(event->type) & mask) &&
293                     (!window || event->window == (macdrv_window)window));
294         }];
295     }
297     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout processEvents:(BOOL)processEvents
298     {
299         macdrv_event* event;
300         NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
301         BOOL timedout;
303         event = macdrv_create_event(QUERY_EVENT, (WineWindow*)query->window);
304         event->query_event.query = macdrv_retain_query(query);
305         query->done = FALSE;
307         [self postEvent:event];
308         macdrv_release_event(event);
309         timedout = ![[WineApplicationController sharedController] waitUntilQueryDone:&query->done
310                                                                              timeout:timeoutDate
311                                                                        processEvents:processEvents];
312         return !timedout && query->status;
313     }
315     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
316     {
317         return [self query:query timeout:timeout processEvents:FALSE];
318     }
320     - (void) resetMouseEventPositions:(CGPoint)pos
321     {
322         MacDrvEvent* event;
324         [eventsLock lock];
326         for (event in events)
327         {
328             if (event->event->type == MOUSE_BUTTON)
329             {
330                 event->event->mouse_button.x = pos.x;
331                 event->event->mouse_button.y = pos.y;
332             }
333             else if (event->event->type == MOUSE_SCROLL)
334             {
335                 event->event->mouse_scroll.x = pos.x;
336                 event->event->mouse_scroll.y = pos.y;
337             }
338         }
340         [eventsLock unlock];
341     }
343     - (BOOL) postHotKeyEvent:(UInt32)hotKeyNumber time:(double)time
344     {
345         NSDictionary* hotKeyDict = [hotKeysByMacID objectForKey:[NSNumber numberWithUnsignedInt:hotKeyNumber]];
346         if (hotKeyDict)
347         {
348             macdrv_event* event;
350             event = macdrv_create_event(HOTKEY_PRESS, nil);
351             event->hotkey_press.vkey        = [[hotKeyDict objectForKey:WineHotKeyVkeyKey] unsignedIntValue];
352             event->hotkey_press.mod_flags   = [[hotKeyDict objectForKey:WineHotKeyModFlagsKey] unsignedIntValue];
353             event->hotkey_press.keycode     = [[hotKeyDict objectForKey:WineHotKeyKeyCodeKey] unsignedIntValue];
354             event->hotkey_press.time_ms     = [[WineApplicationController sharedController] ticksForEventTime:time];
356             [self postEvent:event];
358             macdrv_release_event(event);
359         }
361         return hotKeyDict != nil;
362     }
364     static OSStatus HotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
365     {
366         WineEventQueue* self = userData;
367         OSStatus status;
368         EventHotKeyID hotKeyID;
370         status = GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL,
371                                    sizeof(hotKeyID), NULL, &hotKeyID);
372         if (status == noErr)
373         {
374             if (hotKeyID.signature != WineHotKeySignature ||
375                 ![self postHotKeyEvent:hotKeyID.id time:GetEventTime(theEvent)])
376                 status = eventNotHandledErr;
377         }
379         return status;
380     }
382     - (void) unregisterHotKey:(unsigned int)vkey modFlags:(unsigned int)modFlags
383     {
384         NSNumber* vkeyNumber = [NSNumber numberWithUnsignedInt:vkey];
385         NSNumber* modFlagsNumber = [NSNumber numberWithUnsignedInt:modFlags];
386         NSArray* winIDPair = [NSArray arrayWithObjects:vkeyNumber, modFlagsNumber, nil];
387         NSDictionary* hotKeyDict = [hotKeysByWinID objectForKey:winIDPair];
388         if (hotKeyDict)
389         {
390             EventHotKeyRef hotKeyRef = [[hotKeyDict objectForKey:WineHotKeyCarbonRefKey] pointerValue];
391             NSNumber* macID = [hotKeyDict objectForKey:WineHotKeyMacIDKey];
393             UnregisterEventHotKey(hotKeyRef);
394             [hotKeysByMacID removeObjectForKey:macID];
395             [hotKeysByWinID removeObjectForKey:winIDPair];
396         }
397     }
399     - (int) registerHotKey:(UInt32)keyCode modifiers:(UInt32)modifiers vkey:(unsigned int)vkey modFlags:(unsigned int)modFlags
400     {
401         static EventHandlerRef handler;
402         static UInt32 hotKeyNumber;
403         OSStatus status;
404         NSNumber* vkeyNumber;
405         NSNumber* modFlagsNumber;
406         NSArray* winIDPair;
407         EventHotKeyID hotKeyID;
408         EventHotKeyRef hotKeyRef;
409         NSNumber* macIDNumber;
410         NSDictionary* hotKeyDict;
412         if (!handler)
413         {
414             EventTypeSpec eventType = { kEventClassKeyboard, kEventHotKeyPressed };
415             status = InstallApplicationEventHandler(HotKeyHandler, 1, &eventType, self, &handler);
416             if (status != noErr)
417             {
418                 ERR(@"InstallApplicationEventHandler() failed: %d\n", status);
419                 handler = NULL;
420                 return MACDRV_HOTKEY_FAILURE;
421             }
422         }
424         if (!hotKeysByMacID && !(hotKeysByMacID = [[NSMutableDictionary alloc] init]))
425             return MACDRV_HOTKEY_FAILURE;
426         if (!hotKeysByWinID && !(hotKeysByWinID = [[NSMutableDictionary alloc] init]))
427             return MACDRV_HOTKEY_FAILURE;
429         vkeyNumber = [NSNumber numberWithUnsignedInt:vkey];
430         modFlagsNumber = [NSNumber numberWithUnsignedInt:modFlags];
431         winIDPair = [NSArray arrayWithObjects:vkeyNumber, modFlagsNumber, nil];
432         if ([hotKeysByWinID objectForKey:winIDPair])
433             return MACDRV_HOTKEY_ALREADY_REGISTERED;
435         hotKeyID.signature  = WineHotKeySignature;
436         hotKeyID.id         = hotKeyNumber++;
438         status = RegisterEventHotKey(keyCode, modifiers, hotKeyID, GetApplicationEventTarget(),
439                                      kEventHotKeyExclusive, &hotKeyRef);
440         if (status == eventHotKeyExistsErr)
441             return MACDRV_HOTKEY_ALREADY_REGISTERED;
442         if (status != noErr)
443         {
444             ERR(@"RegisterEventHotKey() failed: %d\n", status);
445             return MACDRV_HOTKEY_FAILURE;
446         }
448         macIDNumber = [NSNumber numberWithUnsignedInt:hotKeyID.id];
449         hotKeyDict = [NSDictionary dictionaryWithObjectsAndKeys:
450                       macIDNumber, WineHotKeyMacIDKey,
451                       vkeyNumber, WineHotKeyVkeyKey,
452                       modFlagsNumber, WineHotKeyModFlagsKey,
453                       [NSNumber numberWithUnsignedInt:keyCode], WineHotKeyKeyCodeKey,
454                       [NSValue valueWithPointer:hotKeyRef], WineHotKeyCarbonRefKey,
455                       nil];
456         [hotKeysByMacID setObject:hotKeyDict forKey:macIDNumber];
457         [hotKeysByWinID setObject:hotKeyDict forKey:winIDPair];
459         return MACDRV_HOTKEY_SUCCESS;
460     }
463 /***********************************************************************
464  *              OnMainThread
466  * Run a block on the main thread synchronously.
467  */
468 void OnMainThread(dispatch_block_t block)
470     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
471     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
472     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
473     dispatch_semaphore_t semaphore;
474     __block BOOL finished;
476     if (!queue)
477     {
478         semaphore = dispatch_semaphore_create(0);
479         dispatch_retain(semaphore);
480     }
482     finished = FALSE;
483     OnMainThreadAsync(^{
484         block();
485         finished = TRUE;
486         if (queue)
487             [queue signalEventAvailable];
488         else
489         {
490             dispatch_semaphore_signal(semaphore);
491             dispatch_release(semaphore);
492         }
493     });
495     if (queue)
496     {
497         while (!finished)
498         {
499             MacDrvEvent* macDrvEvent;
500             struct kevent kev;
502             while (!finished &&
503                    (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
504             {
505                 queue->event_handler(macDrvEvent->event);
506             }
508             if (!finished)
509             {
510                 [pool release];
511                 pool = [[NSAutoreleasePool alloc] init];
513                 kevent(queue->kq, NULL, 0, &kev, 1, NULL);
514             }
515         }
516     }
517     else
518     {
519         dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
520         dispatch_release(semaphore);
521     }
523     [pool release];
527 /***********************************************************************
528  *              macdrv_create_event_queue
530  * Register this thread with the application on the main thread, and set
531  * up an event queue on which it can deliver events to this thread.
532  */
533 macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler)
535     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
536     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
538     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
539     if (!queue)
540     {
541         queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
542         if (queue)
543         {
544             if ([[WineApplicationController sharedController] registerEventQueue:queue])
545                 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
546             else
547                 queue = nil;
548         }
549     }
551     [pool release];
552     return (macdrv_event_queue)queue;
555 /***********************************************************************
556  *              macdrv_destroy_event_queue
558  * Tell the application that this thread is exiting and destroy the
559  * associated event queue.
560  */
561 void macdrv_destroy_event_queue(macdrv_event_queue queue)
563     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
564     WineEventQueue* q = (WineEventQueue*)queue;
565     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
567     [[WineApplicationController sharedController] unregisterEventQueue:q];
568     [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey];
570     [pool release];
573 /***********************************************************************
574  *              macdrv_get_event_queue_fd
576  * Get the file descriptor whose readability signals that there are
577  * events on the event queue.
578  */
579 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
581     WineEventQueue* q = (WineEventQueue*)queue;
582     return q->fds[0];
585 /***********************************************************************
586  *              macdrv_copy_event_from_queue
588  * Pull an event matching the event mask from the event queue and store
589  * it in the event record pointed to by the event parameter.  If a
590  * matching event was found, return non-zero; otherwise, return 0.
592  * The caller is responsible for calling macdrv_release_event on any
593  * event returned by this function.
594  */
595 int macdrv_copy_event_from_queue(macdrv_event_queue queue,
596         macdrv_event_mask mask, macdrv_event **event)
598     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
599     WineEventQueue* q = (WineEventQueue*)queue;
601     MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
602     if (macDrvEvent)
603         *event = macdrv_retain_event(macDrvEvent->event);
605     [pool release];
606     return (macDrvEvent != nil);
609 /***********************************************************************
610  *              macdrv_create_event
611  */
612 macdrv_event* macdrv_create_event(int type, WineWindow* window)
614     macdrv_event *event;
616     event = calloc(1, sizeof(*event));
617     event->refs = 1;
618     event->deliver = INT_MAX;
619     event->type = type;
620     event->window = (macdrv_window)[window retain];
621     return event;
624 /***********************************************************************
625  *              macdrv_retain_event
626  */
627 macdrv_event* macdrv_retain_event(macdrv_event *event)
629     OSAtomicIncrement32Barrier(&event->refs);
630     return event;
633 /***********************************************************************
634  *              macdrv_release_event
636  * Decrements the reference count of an event.  If the count falls to
637  * zero, cleans up any resources, such as allocated memory or retained
638  * objects, held by the event and deallocates it
639  */
640 void macdrv_release_event(macdrv_event *event)
642     if (OSAtomicDecrement32Barrier(&event->refs) <= 0)
643     {
644         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
646         switch (event->type)
647         {
648             case IM_SET_TEXT:
649                 if (event->im_set_text.text)
650                     CFRelease(event->im_set_text.text);
651                 break;
652             case KEYBOARD_CHANGED:
653                 CFRelease(event->keyboard_changed.uchr);
654                 CFRelease(event->keyboard_changed.input_source);
655                 break;
656             case QUERY_EVENT:
657                 macdrv_release_query(event->query_event.query);
658                 break;
659             case WINDOW_GOT_FOCUS:
660                 [(NSMutableSet*)event->window_got_focus.tried_windows release];
661                 break;
662         }
664         [(WineWindow*)event->window release];
665         free(event);
667         [pool release];
668     }
671 /***********************************************************************
672  *              macdrv_create_query
673  */
674 macdrv_query* macdrv_create_query(void)
676     macdrv_query *query;
678     query = calloc(1, sizeof(*query));
679     query->refs = 1;
680     return query;
683 /***********************************************************************
684  *              macdrv_retain_query
685  */
686 macdrv_query* macdrv_retain_query(macdrv_query *query)
688     OSAtomicIncrement32Barrier(&query->refs);
689     return query;
692 /***********************************************************************
693  *              macdrv_release_query
694  */
695 void macdrv_release_query(macdrv_query *query)
697     if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
698     {
699         switch (query->type)
700         {
701             case QUERY_DRAG_OPERATION:
702                 if (query->drag_operation.pasteboard)
703                     CFRelease(query->drag_operation.pasteboard);
704                 break;
705             case QUERY_DRAG_DROP:
706                 if (query->drag_drop.pasteboard)
707                     CFRelease(query->drag_drop.pasteboard);
708                 break;
709             case QUERY_PASTEBOARD_DATA:
710                 if (query->pasteboard_data.type)
711                     CFRelease(query->pasteboard_data.type);
712                 break;
713         }
714         [(WineWindow*)query->window release];
715         free(query);
716     }
719 /***********************************************************************
720  *              macdrv_set_query_done
721  */
722 void macdrv_set_query_done(macdrv_query *query)
724     macdrv_retain_query(query);
726     OnMainThreadAsync(^{
727         NSEvent* event;
729         query->done = TRUE;
730         macdrv_release_query(query);
732         event = [NSEvent otherEventWithType:NSApplicationDefined
733                                    location:NSZeroPoint
734                               modifierFlags:0
735                                   timestamp:[[NSProcessInfo processInfo] systemUptime]
736                                windowNumber:0
737                                     context:nil
738                                     subtype:WineApplicationEventWakeQuery
739                                       data1:0
740                                       data2:0];
741         [NSApp postEvent:event atStart:TRUE];
742     });
745 @end
748 /***********************************************************************
749  *              macdrv_register_hot_key
750  */
751 int macdrv_register_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags,
752                             unsigned int keycode, unsigned int modifiers)
754     WineEventQueue* queue = (WineEventQueue*)q;
755     __block int ret;
757     OnMainThread(^{
758         ret = [queue registerHotKey:keycode modifiers:modifiers vkey:vkey modFlags:mod_flags];
759     });
761     return ret;
765 /***********************************************************************
766  *              macdrv_unregister_hot_key
767  */
768 void macdrv_unregister_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags)
770     WineEventQueue* queue = (WineEventQueue*)q;
772     OnMainThreadAsync(^{
773         [queue unregisterHotKey:vkey modFlags:mod_flags];
774     });