winemac: Add a delivery limit to events.
[wine/multimedia.git] / dlls / winemac.drv / cocoa_event.m
blob366aa4653551bf40c9d8510b9d4f3c644fc54edd
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:(macdrv_event*)event;
43 @end
45 @implementation MacDrvEvent
47     - (id) initWithEvent:(macdrv_event*)inEvent
48     {
49         self = [super init];
50         if (self)
51         {
52             event = macdrv_retain_event(inEvent);
53         }
54         return self;
55     }
57     - (void) dealloc
58     {
59         if (event) macdrv_release_event(event);
60         [super dealloc];
61     }
63 @end
66 @implementation WineEventQueue
68     - (id) init
69     {
70         [self doesNotRecognizeSelector:_cmd];
71         [self release];
72         return nil;
73     }
75     - (id) initWithEventHandler:(macdrv_event_handler)handler
76     {
77         NSParameterAssert(handler != nil);
79         self = [super init];
80         if (self != nil)
81         {
82             struct kevent kev;
83             int rc;
85             fds[0] = fds[1] = kq = -1;
87             event_handler = handler;
88             events = [[NSMutableArray alloc] init];
89             eventsLock = [[NSLock alloc] init];
91             if (!events || !eventsLock)
92             {
93                 [self release];
94                 return nil;
95             }
97             if (pipe(fds) ||
98                 fcntl(fds[0], F_SETFD, 1) == -1 ||
99                 fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
100                 fcntl(fds[1], F_SETFD, 1) == -1 ||
101                 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1)
102             {
103                 [self release];
104                 return nil;
105             }
107             kq = kqueue();
108             if (kq < 0)
109             {
110                 [self release];
111                 return nil;
112             }
114             EV_SET(&kev, fds[0], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
115             do
116             {
117                 rc = kevent(kq, &kev, 1, NULL, 0, NULL);
118             } while (rc == -1 && errno == EINTR);
119             if (rc == -1)
120             {
121                 [self release];
122                 return nil;
123             }
124         }
125         return self;
126     }
128     - (void) dealloc
129     {
130         [events release];
131         [eventsLock release];
133         if (kq != -1) close(kq);
134         if (fds[0] != -1) close(fds[0]);
135         if (fds[1] != -1) close(fds[1]);
137         [super dealloc];
138     }
140     - (void) signalEventAvailable
141     {
142         char junk = 1;
143         int rc;
145         do
146         {
147             rc = write(fds[1], &junk, 1);
148         } while (rc < 0 && errno == EINTR);
150         if (rc < 0 && errno != EAGAIN)
151             ERR(@"%@: got error writing to event queue signaling pipe: %s\n", self, strerror(errno));
152     }
154     - (void) postEventObject:(MacDrvEvent*)event
155     {
156         MacDrvEvent* lastEvent;
158         [eventsLock lock];
160         if ((event->event->type == MOUSE_MOVED ||
161              event->event->type == MOUSE_MOVED_ABSOLUTE) &&
162             (lastEvent = [events lastObject]) &&
163             (lastEvent->event->type == MOUSE_MOVED ||
164              lastEvent->event->type == MOUSE_MOVED_ABSOLUTE) &&
165             lastEvent->event->window == event->event->window)
166         {
167             if (event->event->type == MOUSE_MOVED)
168             {
169                 lastEvent->event->mouse_moved.x += event->event->mouse_moved.x;
170                 lastEvent->event->mouse_moved.y += event->event->mouse_moved.y;
171             }
172             else
173             {
174                 lastEvent->event->type = MOUSE_MOVED_ABSOLUTE;
175                 lastEvent->event->mouse_moved.x = event->event->mouse_moved.x;
176                 lastEvent->event->mouse_moved.y = event->event->mouse_moved.y;
177             }
179             lastEvent->event->mouse_moved.time_ms = event->event->mouse_moved.time_ms;
180         }
181         else
182             [events addObject:event];
184         [eventsLock unlock];
186         [self signalEventAvailable];
187     }
189     - (void) postEvent:(macdrv_event*)inEvent
190     {
191         MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
192         [self postEventObject:event];
193         [event release];
194     }
196     - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
197     {
198         char buf[512];
199         int rc;
200         NSUInteger index;
201         MacDrvEvent* ret = nil;
203         /* Clear the pipe which signals there are pending events. */
204         do
205         {
206             rc = read(fds[0], buf, sizeof(buf));
207         } while (rc > 0 || (rc < 0 && errno == EINTR));
208         if (rc == 0 || (rc < 0 && errno != EAGAIN))
209         {
210             if (rc == 0)
211                 ERR(@"%@: event queue signaling pipe unexpectedly closed\n", self);
212             else
213                 ERR(@"%@: got error reading from event queue signaling pipe: %s\n", self, strerror(errno));
214             return nil;
215         }
217         [eventsLock lock];
219         index = 0;
220         while (index < [events count])
221         {
222             MacDrvEvent* event = [events objectAtIndex:index];
223             if (event_mask_for_type(event->event->type) & mask)
224             {
225                 [[event retain] autorelease];
226                 [events removeObjectAtIndex:index];
228                 if (event->event->deliver == INT_MAX ||
229                     OSAtomicDecrement32Barrier(&event->event->deliver) >= 0)
230                 {
231                     ret = event;
232                     break;
233                 }
234             }
235             else
236                 index++;
237         }
239         [eventsLock unlock];
240         return ret;
241     }
243     - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
244     {
245         NSIndexSet* indexes;
247         [eventsLock lock];
249         indexes = [events indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
250             MacDrvEvent* event = obj;
251             return ((event_mask_for_type(event->event->type) & mask) &&
252                     (!window || event->event->window == (macdrv_window)window));
253         }];
255         [events removeObjectsAtIndexes:indexes];
257         [eventsLock unlock];
258     }
260     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout processEvents:(BOOL)processEvents
261     {
262         macdrv_event* event;
263         NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
264         BOOL timedout;
266         event = macdrv_create_event(QUERY_EVENT, (WineWindow*)query->window);
267         event->query_event.query = macdrv_retain_query(query);
268         query->done = FALSE;
270         [self postEvent:event];
271         macdrv_release_event(event);
272         timedout = ![NSApp waitUntilQueryDone:&query->done timeout:timeoutDate processEvents:processEvents];
273         return !timedout && query->status;
274     }
276     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
277     {
278         return [self query:query timeout:timeout processEvents:FALSE];
279     }
282 /***********************************************************************
283  *              OnMainThread
285  * Run a block on the main thread synchronously.
286  */
287 void OnMainThread(dispatch_block_t block)
289     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
290     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
291     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
292     __block BOOL finished;
294     if (!queue)
295     {
296         /* Fall back to synchronous dispatch without handling query events. */
297         dispatch_sync(dispatch_get_main_queue(), block);
298         [pool release];
299         return;
300     }
302     finished = FALSE;
303     OnMainThreadAsync(^{
304         block();
305         finished = TRUE;
306         [queue signalEventAvailable];
307     });
309     while (!finished)
310     {
311         MacDrvEvent* macDrvEvent;
312         struct kevent kev;
314         while (!finished &&
315                (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
316         {
317             queue->event_handler(macDrvEvent->event);
318         }
320         if (!finished)
321         {
322             [pool release];
323             pool = [[NSAutoreleasePool alloc] init];
325             kevent(queue->kq, NULL, 0, &kev, 1, NULL);
326         }
327     }
329     [pool release];
333 /***********************************************************************
334  *              macdrv_create_event_queue
336  * Register this thread with the application on the main thread, and set
337  * up an event queue on which it can deliver events to this thread.
338  */
339 macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler)
341     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
342     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
344     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
345     if (!queue)
346     {
347         queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
348         if (queue)
349         {
350             if ([NSApp registerEventQueue:queue])
351                 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
352             else
353                 queue = nil;
354         }
355     }
357     [pool release];
358     return (macdrv_event_queue)queue;
361 /***********************************************************************
362  *              macdrv_destroy_event_queue
364  * Tell the application that this thread is exiting and destroy the
365  * associated event queue.
366  */
367 void macdrv_destroy_event_queue(macdrv_event_queue queue)
369     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
370     WineEventQueue* q = (WineEventQueue*)queue;
371     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
373     [NSApp unregisterEventQueue:q];
374     [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey];
376     [pool release];
379 /***********************************************************************
380  *              macdrv_get_event_queue_fd
382  * Get the file descriptor whose readability signals that there are
383  * events on the event queue.
384  */
385 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
387     WineEventQueue* q = (WineEventQueue*)queue;
388     return q->fds[0];
391 /***********************************************************************
392  *              macdrv_copy_event_from_queue
394  * Pull an event matching the event mask from the event queue and store
395  * it in the event record pointed to by the event parameter.  If a
396  * matching event was found, return non-zero; otherwise, return 0.
398  * The caller is responsible for calling macdrv_release_event on any
399  * event returned by this function.
400  */
401 int macdrv_copy_event_from_queue(macdrv_event_queue queue,
402         macdrv_event_mask mask, macdrv_event **event)
404     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
405     WineEventQueue* q = (WineEventQueue*)queue;
407     MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
408     if (macDrvEvent)
409         *event = macdrv_retain_event(macDrvEvent->event);
411     [pool release];
412     return (macDrvEvent != nil);
415 /***********************************************************************
416  *              macdrv_create_event
417  */
418 macdrv_event* macdrv_create_event(int type, WineWindow* window)
420     macdrv_event *event;
422     event = calloc(1, sizeof(*event));
423     event->refs = 1;
424     event->deliver = INT_MAX;
425     event->type = type;
426     event->window = (macdrv_window)[window retain];
427     return event;
430 /***********************************************************************
431  *              macdrv_retain_event
432  */
433 macdrv_event* macdrv_retain_event(macdrv_event *event)
435     OSAtomicIncrement32Barrier(&event->refs);
436     return event;
439 /***********************************************************************
440  *              macdrv_release_event
442  * Decrements the reference count of an event.  If the count falls to
443  * zero, cleans up any resources, such as allocated memory or retained
444  * objects, held by the event and deallocates it
445  */
446 void macdrv_release_event(macdrv_event *event)
448     if (OSAtomicDecrement32Barrier(&event->refs) <= 0)
449     {
450         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
452         switch (event->type)
453         {
454             case KEYBOARD_CHANGED:
455                 CFRelease(event->keyboard_changed.uchr);
456                 break;
457             case QUERY_EVENT:
458                 macdrv_release_query(event->query_event.query);
459                 break;
460             case WINDOW_GOT_FOCUS:
461                 [(NSMutableSet*)event->window_got_focus.tried_windows release];
462                 break;
463         }
465         [(WineWindow*)event->window release];
466         free(event);
468         [pool release];
469     }
472 /***********************************************************************
473  *              macdrv_create_query
474  */
475 macdrv_query* macdrv_create_query(void)
477     macdrv_query *query;
479     query = calloc(1, sizeof(*query));
480     query->refs = 1;
481     return query;
484 /***********************************************************************
485  *              macdrv_retain_query
486  */
487 macdrv_query* macdrv_retain_query(macdrv_query *query)
489     OSAtomicIncrement32Barrier(&query->refs);
490     return query;
493 /***********************************************************************
494  *              macdrv_release_query
495  */
496 void macdrv_release_query(macdrv_query *query)
498     if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
499     {
500         switch (query->type)
501         {
502             case QUERY_DRAG_OPERATION:
503                 if (query->drag_operation.pasteboard)
504                     CFRelease(query->drag_operation.pasteboard);
505                 break;
506             case QUERY_DRAG_DROP:
507                 if (query->drag_drop.pasteboard)
508                     CFRelease(query->drag_drop.pasteboard);
509                 break;
510             case QUERY_PASTEBOARD_DATA:
511                 if (query->pasteboard_data.type)
512                     CFRelease(query->pasteboard_data.type);
513                 break;
514         }
515         [(WineWindow*)query->window release];
516         free(query);
517     }
520 /***********************************************************************
521  *              macdrv_set_query_done
522  */
523 void macdrv_set_query_done(macdrv_query *query)
525     macdrv_retain_query(query);
527     OnMainThreadAsync(^{
528         NSEvent* event;
530         query->done = TRUE;
531         macdrv_release_query(query);
533         event = [NSEvent otherEventWithType:NSApplicationDefined
534                                    location:NSZeroPoint
535                               modifierFlags:0
536                                   timestamp:[[NSProcessInfo processInfo] systemUptime]
537                                windowNumber:0
538                                     context:nil
539                                     subtype:WineApplicationEventWakeQuery
540                                       data1:0
541                                       data2:0];
542         [NSApp postEvent:event atStart:TRUE];
543     });
546 @end