winemac: Allow for processing Cocoa events while waiting for query results.
[wine/multimedia.git] / dlls / winemac.drv / cocoa_event.m
blobc95fac6673525e387deba6cfbf517bbd838a77aa
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>
26 #include "macdrv_cocoa.h"
27 #import "cocoa_event.h"
28 #import "cocoa_app.h"
29 #import "cocoa_window.h"
32 static NSString* const WineEventQueueThreadDictionaryKey = @"WineEventQueueThreadDictionaryKey";
35 @interface MacDrvEvent : NSObject
37 @public
38     macdrv_event event;
41     - (id) initWithEvent:(const macdrv_event*)event;
43 @end
45 @implementation MacDrvEvent
47     - (id) initWithEvent:(const macdrv_event*)inEvent
48     {
49         self = [super init];
50         if (self)
51         {
52             event = *inEvent;
53         }
54         return self;
55     }
57 @end
60 @implementation WineEventQueue
62     - (id) init
63     {
64         [self doesNotRecognizeSelector:_cmd];
65         [self release];
66         return nil;
67     }
69     - (id) initWithEventHandler:(macdrv_event_handler)handler
70     {
71         NSParameterAssert(handler != nil);
73         self = [super init];
74         if (self != nil)
75         {
76             struct kevent kev;
77             int rc;
79             fds[0] = fds[1] = kq = -1;
81             event_handler = handler;
82             events = [[NSMutableArray alloc] init];
83             eventsLock = [[NSLock alloc] init];
85             if (!events || !eventsLock)
86             {
87                 [self release];
88                 return nil;
89             }
91             if (pipe(fds) ||
92                 fcntl(fds[0], F_SETFD, 1) == -1 ||
93                 fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
94                 fcntl(fds[1], F_SETFD, 1) == -1 ||
95                 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1)
96             {
97                 [self release];
98                 return nil;
99             }
101             kq = kqueue();
102             if (kq < 0)
103             {
104                 [self release];
105                 return nil;
106             }
108             EV_SET(&kev, fds[0], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
109             do
110             {
111                 rc = kevent(kq, &kev, 1, NULL, 0, NULL);
112             } while (rc == -1 && errno == EINTR);
113             if (rc == -1)
114             {
115                 [self release];
116                 return nil;
117             }
118         }
119         return self;
120     }
122     - (void) dealloc
123     {
124         [events release];
125         [eventsLock release];
127         if (kq != -1) close(kq);
128         if (fds[0] != -1) close(fds[0]);
129         if (fds[1] != -1) close(fds[1]);
131         [super dealloc];
132     }
134     - (void) signalEventAvailable
135     {
136         char junk = 1;
137         int rc;
139         do
140         {
141             rc = write(fds[1], &junk, 1);
142         } while (rc < 0 && errno == EINTR);
144         if (rc < 0 && errno != EAGAIN)
145             ERR(@"%@: got error writing to event queue signaling pipe: %s\n", self, strerror(errno));
146     }
148     - (void) postEventObject:(MacDrvEvent*)event
149     {
150         MacDrvEvent* lastEvent;
152         [eventsLock lock];
154         if ((event->event.type == MOUSE_MOVED ||
155              event->event.type == MOUSE_MOVED_ABSOLUTE) &&
156             (lastEvent = [events lastObject]) &&
157             (lastEvent->event.type == MOUSE_MOVED ||
158              lastEvent->event.type == MOUSE_MOVED_ABSOLUTE) &&
159             lastEvent->event.window == event->event.window)
160         {
161             if (event->event.type == MOUSE_MOVED)
162             {
163                 lastEvent->event.mouse_moved.x += event->event.mouse_moved.x;
164                 lastEvent->event.mouse_moved.y += event->event.mouse_moved.y;
165             }
166             else
167             {
168                 lastEvent->event.type = MOUSE_MOVED_ABSOLUTE;
169                 lastEvent->event.mouse_moved.x = event->event.mouse_moved.x;
170                 lastEvent->event.mouse_moved.y = event->event.mouse_moved.y;
171             }
173             lastEvent->event.mouse_moved.time_ms = event->event.mouse_moved.time_ms;
175             macdrv_cleanup_event(&event->event);
176         }
177         else
178             [events addObject:event];
180         [eventsLock unlock];
182         [self signalEventAvailable];
183     }
185     - (void) postEvent:(const macdrv_event*)inEvent
186     {
187         MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
188         [self postEventObject:event];
189         [event release];
190     }
192     - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
193     {
194         char buf[512];
195         int rc;
196         NSUInteger index;
197         MacDrvEvent* event;
199         /* Clear the pipe which signals there are pending events. */
200         do
201         {
202             rc = read(fds[0], buf, sizeof(buf));
203         } while (rc > 0 || (rc < 0 && errno == EINTR));
204         if (rc == 0 || (rc < 0 && errno != EAGAIN))
205         {
206             if (rc == 0)
207                 ERR(@"%@: event queue signaling pipe unexpectedly closed\n", self);
208             else
209                 ERR(@"%@: got error reading from event queue signaling pipe: %s\n", self, strerror(errno));
210             return nil;
211         }
213         [eventsLock lock];
215         index = 0;
216         for (event in events)
217         {
218             if (event_mask_for_type(event->event.type) & mask)
219                 break;
221             index++;
222         }
224         if (event)
225         {
226             [event retain];
227             [events removeObjectAtIndex:index];
228         }
230         [eventsLock unlock];
231         return [event autorelease];
232     }
234     - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
235     {
236         NSMutableIndexSet* indexes = [[[NSMutableIndexSet alloc] init] autorelease];
238         [eventsLock lock];
240         [events enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
241             MacDrvEvent* event = obj;
242             if ((event_mask_for_type(event->event.type) & mask) &&
243                 (!window || event->event.window == (macdrv_window)window))
244             {
245                 macdrv_cleanup_event(&event->event);
246                 [indexes addIndex:idx];
247             }
248         }];
250         [events removeObjectsAtIndexes:indexes];
252         [eventsLock unlock];
253     }
255     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout processEvents:(BOOL)processEvents
256     {
257         macdrv_event event;
258         NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
259         BOOL timedout;
261         event.type = QUERY_EVENT;
262         event.window = (macdrv_window)[(WineWindow*)query->window retain];
263         event.query_event.query = macdrv_retain_query(query);
264         query->done = FALSE;
266         [self postEvent:&event];
267         timedout = ![NSApp waitUntilQueryDone:&query->done timeout:timeoutDate processEvents:processEvents];
268         return !timedout && query->status;
269     }
271     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
272     {
273         return [self query:query timeout:timeout processEvents:FALSE];
274     }
277 /***********************************************************************
278  *              OnMainThread
280  * Run a block on the main thread synchronously.
281  */
282 void OnMainThread(dispatch_block_t block)
284     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
285     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
286     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
287     __block BOOL finished;
289     if (!queue)
290     {
291         /* Fall back to synchronous dispatch without handling query events. */
292         dispatch_sync(dispatch_get_main_queue(), block);
293         [pool release];
294         return;
295     }
297     finished = FALSE;
298     OnMainThreadAsync(^{
299         block();
300         finished = TRUE;
301         [queue signalEventAvailable];
302     });
304     while (!finished)
305     {
306         MacDrvEvent* macDrvEvent;
307         struct kevent kev;
309         while (!finished &&
310                (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
311         {
312             queue->event_handler(&macDrvEvent->event);
313             macdrv_cleanup_event(&macDrvEvent->event);
314         }
316         if (!finished)
317             kevent(queue->kq, NULL, 0, &kev, 1, NULL);
318     }
320     [pool release];
324 /***********************************************************************
325  *              macdrv_create_event_queue
327  * Register this thread with the application on the main thread, and set
328  * up an event queue on which it can deliver events to this thread.
329  */
330 macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler)
332     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
333     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
335     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
336     if (!queue)
337     {
338         queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
339         if (queue)
340         {
341             if ([NSApp registerEventQueue:queue])
342                 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
343             else
344                 queue = nil;
345         }
346     }
348     [pool release];
349     return (macdrv_event_queue)queue;
352 /***********************************************************************
353  *              macdrv_destroy_event_queue
355  * Tell the application that this thread is exiting and destroy the
356  * associated event queue.
357  */
358 void macdrv_destroy_event_queue(macdrv_event_queue queue)
360     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
361     WineEventQueue* q = (WineEventQueue*)queue;
362     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
364     [NSApp unregisterEventQueue:q];
365     [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey];
367     [pool release];
370 /***********************************************************************
371  *              macdrv_get_event_queue_fd
373  * Get the file descriptor whose readability signals that there are
374  * events on the event queue.
375  */
376 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
378     WineEventQueue* q = (WineEventQueue*)queue;
379     return q->fds[0];
382 /***********************************************************************
383  *              macdrv_get_event_from_queue
385  * Pull an event matching the event mask from the event queue and store
386  * it in the event record pointed to by the event parameter.  If a
387  * matching event was found, return non-zero; otherwise, return 0.
389  * The caller is responsible for calling macdrv_cleanup_event on any
390  * event returned by this function.
391  */
392 int macdrv_get_event_from_queue(macdrv_event_queue queue,
393         macdrv_event_mask mask, macdrv_event *event)
395     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
396     WineEventQueue* q = (WineEventQueue*)queue;
398     MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
399     if (macDrvEvent)
400         *event = macDrvEvent->event;
402     [pool release];
403     return (macDrvEvent != nil);
406 /***********************************************************************
407  *              macdrv_cleanup_event
409  * Performs cleanup of an event.  For event types which carry resources
410  * such as allocated memory or retained objects, frees/releases those
411  * resources.
412  */
413 void macdrv_cleanup_event(macdrv_event *event)
415     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
417     switch (event->type)
418     {
419         case KEYBOARD_CHANGED:
420             CFRelease(event->keyboard_changed.uchr);
421             break;
422         case QUERY_EVENT:
423             macdrv_release_query(event->query_event.query);
424             break;
425         case WINDOW_GOT_FOCUS:
426             [(NSMutableSet*)event->window_got_focus.tried_windows release];
427             break;
428     }
430     [(WineWindow*)event->window release];
432     [pool release];
435 /***********************************************************************
436  *              macdrv_create_query
437  */
438 macdrv_query* macdrv_create_query(void)
440     macdrv_query *query;
442     query = calloc(1, sizeof(*query));
443     query->refs = 1;
444     return query;
447 /***********************************************************************
448  *              macdrv_retain_query
449  */
450 macdrv_query* macdrv_retain_query(macdrv_query *query)
452     OSAtomicIncrement32Barrier(&query->refs);
453     return query;
456 /***********************************************************************
457  *              macdrv_release_query
458  */
459 void macdrv_release_query(macdrv_query *query)
461     if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
462     {
463         if (query->type == QUERY_PASTEBOARD_DATA && query->pasteboard_data.type)
464             CFRelease(query->pasteboard_data.type);
465         [(WineWindow*)query->window release];
466         free(query);
467     }
470 /***********************************************************************
471  *              macdrv_set_query_done
472  */
473 void macdrv_set_query_done(macdrv_query *query)
475     macdrv_retain_query(query);
477     OnMainThreadAsync(^{
478         NSEvent* event;
480         query->done = TRUE;
481         macdrv_release_query(query);
483         event = [NSEvent otherEventWithType:NSApplicationDefined
484                                    location:NSZeroPoint
485                               modifierFlags:0
486                                   timestamp:[[NSProcessInfo processInfo] systemUptime]
487                                windowNumber:0
488                                     context:nil
489                                     subtype:WineApplicationEventWakeQuery
490                                       data1:0
491                                       data2:0];
492         [NSApp postEvent:event atStart:TRUE];
493     });
496 @end