winemac: Don't accidentally unminimize a window when trying to change z-order.
[wine/multimedia.git] / dlls / winemac.drv / cocoa_event.m
blob8d997d0a2ac4da4ddde7ee0864eedacdabb08583
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             lastEvent->event->mouse_moved.drag == event->event->mouse_moved.drag)
167         {
168             if (event->event->type == MOUSE_MOVED)
169             {
170                 lastEvent->event->mouse_moved.x += event->event->mouse_moved.x;
171                 lastEvent->event->mouse_moved.y += event->event->mouse_moved.y;
172             }
173             else
174             {
175                 lastEvent->event->type = MOUSE_MOVED_ABSOLUTE;
176                 lastEvent->event->mouse_moved.x = event->event->mouse_moved.x;
177                 lastEvent->event->mouse_moved.y = event->event->mouse_moved.y;
178             }
180             lastEvent->event->mouse_moved.time_ms = event->event->mouse_moved.time_ms;
181         }
182         else
183             [events addObject:event];
185         [eventsLock unlock];
187         [self signalEventAvailable];
188     }
190     - (void) postEvent:(macdrv_event*)inEvent
191     {
192         MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
193         [self postEventObject:event];
194         [event release];
195     }
197     - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
198     {
199         char buf[512];
200         int rc;
201         NSUInteger index;
202         MacDrvEvent* ret = nil;
204         /* Clear the pipe which signals there are pending events. */
205         do
206         {
207             rc = read(fds[0], buf, sizeof(buf));
208         } while (rc > 0 || (rc < 0 && errno == EINTR));
209         if (rc == 0 || (rc < 0 && errno != EAGAIN))
210         {
211             if (rc == 0)
212                 ERR(@"%@: event queue signaling pipe unexpectedly closed\n", self);
213             else
214                 ERR(@"%@: got error reading from event queue signaling pipe: %s\n", self, strerror(errno));
215             return nil;
216         }
218         [eventsLock lock];
220         index = 0;
221         while (index < [events count])
222         {
223             MacDrvEvent* event = [events objectAtIndex:index];
224             if (event_mask_for_type(event->event->type) & mask)
225             {
226                 [[event retain] autorelease];
227                 [events removeObjectAtIndex:index];
229                 if (event->event->deliver == INT_MAX ||
230                     OSAtomicDecrement32Barrier(&event->event->deliver) >= 0)
231                 {
232                     ret = event;
233                     break;
234                 }
235             }
236             else
237                 index++;
238         }
240         [eventsLock unlock];
241         return ret;
242     }
244     - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
245     {
246         NSIndexSet* indexes;
248         [eventsLock lock];
250         indexes = [events indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
251             MacDrvEvent* event = obj;
252             return ((event_mask_for_type(event->event->type) & mask) &&
253                     (!window || event->event->window == (macdrv_window)window));
254         }];
256         [events removeObjectsAtIndexes:indexes];
258         [eventsLock unlock];
259     }
261     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout processEvents:(BOOL)processEvents
262     {
263         macdrv_event* event;
264         NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
265         BOOL timedout;
267         event = macdrv_create_event(QUERY_EVENT, (WineWindow*)query->window);
268         event->query_event.query = macdrv_retain_query(query);
269         query->done = FALSE;
271         [self postEvent:event];
272         macdrv_release_event(event);
273         timedout = ![[WineApplicationController sharedController] waitUntilQueryDone:&query->done
274                                                                              timeout:timeoutDate
275                                                                        processEvents:processEvents];
276         return !timedout && query->status;
277     }
279     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
280     {
281         return [self query:query timeout:timeout processEvents:FALSE];
282     }
284     - (void) resetMouseEventPositions:(CGPoint)pos
285     {
286         MacDrvEvent* event;
288         [eventsLock lock];
290         for (event in events)
291         {
292             if (event->event->type == MOUSE_BUTTON)
293             {
294                 event->event->mouse_button.x = pos.x;
295                 event->event->mouse_button.y = pos.y;
296             }
297             else if (event->event->type == MOUSE_SCROLL)
298             {
299                 event->event->mouse_scroll.x = pos.x;
300                 event->event->mouse_scroll.y = pos.y;
301             }
302         }
304         [eventsLock unlock];
305     }
308 /***********************************************************************
309  *              OnMainThread
311  * Run a block on the main thread synchronously.
312  */
313 void OnMainThread(dispatch_block_t block)
315     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
316     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
317     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
318     __block BOOL finished;
320     if (!queue)
321     {
322         /* Fall back to synchronous dispatch without handling query events. */
323         dispatch_sync(dispatch_get_main_queue(), block);
324         [pool release];
325         return;
326     }
328     finished = FALSE;
329     OnMainThreadAsync(^{
330         block();
331         finished = TRUE;
332         [queue signalEventAvailable];
333     });
335     while (!finished)
336     {
337         MacDrvEvent* macDrvEvent;
338         struct kevent kev;
340         while (!finished &&
341                (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
342         {
343             queue->event_handler(macDrvEvent->event);
344         }
346         if (!finished)
347         {
348             [pool release];
349             pool = [[NSAutoreleasePool alloc] init];
351             kevent(queue->kq, NULL, 0, &kev, 1, NULL);
352         }
353     }
355     [pool release];
359 /***********************************************************************
360  *              macdrv_create_event_queue
362  * Register this thread with the application on the main thread, and set
363  * up an event queue on which it can deliver events to this thread.
364  */
365 macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler)
367     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
368     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
370     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
371     if (!queue)
372     {
373         queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
374         if (queue)
375         {
376             if ([[WineApplicationController sharedController] registerEventQueue:queue])
377                 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
378             else
379                 queue = nil;
380         }
381     }
383     [pool release];
384     return (macdrv_event_queue)queue;
387 /***********************************************************************
388  *              macdrv_destroy_event_queue
390  * Tell the application that this thread is exiting and destroy the
391  * associated event queue.
392  */
393 void macdrv_destroy_event_queue(macdrv_event_queue queue)
395     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
396     WineEventQueue* q = (WineEventQueue*)queue;
397     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
399     [[WineApplicationController sharedController] unregisterEventQueue:q];
400     [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey];
402     [pool release];
405 /***********************************************************************
406  *              macdrv_get_event_queue_fd
408  * Get the file descriptor whose readability signals that there are
409  * events on the event queue.
410  */
411 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
413     WineEventQueue* q = (WineEventQueue*)queue;
414     return q->fds[0];
417 /***********************************************************************
418  *              macdrv_copy_event_from_queue
420  * Pull an event matching the event mask from the event queue and store
421  * it in the event record pointed to by the event parameter.  If a
422  * matching event was found, return non-zero; otherwise, return 0.
424  * The caller is responsible for calling macdrv_release_event on any
425  * event returned by this function.
426  */
427 int macdrv_copy_event_from_queue(macdrv_event_queue queue,
428         macdrv_event_mask mask, macdrv_event **event)
430     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
431     WineEventQueue* q = (WineEventQueue*)queue;
433     MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
434     if (macDrvEvent)
435         *event = macdrv_retain_event(macDrvEvent->event);
437     [pool release];
438     return (macDrvEvent != nil);
441 /***********************************************************************
442  *              macdrv_create_event
443  */
444 macdrv_event* macdrv_create_event(int type, WineWindow* window)
446     macdrv_event *event;
448     event = calloc(1, sizeof(*event));
449     event->refs = 1;
450     event->deliver = INT_MAX;
451     event->type = type;
452     event->window = (macdrv_window)[window retain];
453     return event;
456 /***********************************************************************
457  *              macdrv_retain_event
458  */
459 macdrv_event* macdrv_retain_event(macdrv_event *event)
461     OSAtomicIncrement32Barrier(&event->refs);
462     return event;
465 /***********************************************************************
466  *              macdrv_release_event
468  * Decrements the reference count of an event.  If the count falls to
469  * zero, cleans up any resources, such as allocated memory or retained
470  * objects, held by the event and deallocates it
471  */
472 void macdrv_release_event(macdrv_event *event)
474     if (OSAtomicDecrement32Barrier(&event->refs) <= 0)
475     {
476         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
478         switch (event->type)
479         {
480             case IM_SET_TEXT:
481                 if (event->im_set_text.text)
482                     CFRelease(event->im_set_text.text);
483                 break;
484             case KEYBOARD_CHANGED:
485                 CFRelease(event->keyboard_changed.uchr);
486                 break;
487             case QUERY_EVENT:
488                 macdrv_release_query(event->query_event.query);
489                 break;
490             case WINDOW_GOT_FOCUS:
491                 [(NSMutableSet*)event->window_got_focus.tried_windows release];
492                 break;
493         }
495         [(WineWindow*)event->window release];
496         free(event);
498         [pool release];
499     }
502 /***********************************************************************
503  *              macdrv_create_query
504  */
505 macdrv_query* macdrv_create_query(void)
507     macdrv_query *query;
509     query = calloc(1, sizeof(*query));
510     query->refs = 1;
511     return query;
514 /***********************************************************************
515  *              macdrv_retain_query
516  */
517 macdrv_query* macdrv_retain_query(macdrv_query *query)
519     OSAtomicIncrement32Barrier(&query->refs);
520     return query;
523 /***********************************************************************
524  *              macdrv_release_query
525  */
526 void macdrv_release_query(macdrv_query *query)
528     if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
529     {
530         switch (query->type)
531         {
532             case QUERY_DRAG_OPERATION:
533                 if (query->drag_operation.pasteboard)
534                     CFRelease(query->drag_operation.pasteboard);
535                 break;
536             case QUERY_DRAG_DROP:
537                 if (query->drag_drop.pasteboard)
538                     CFRelease(query->drag_drop.pasteboard);
539                 break;
540             case QUERY_PASTEBOARD_DATA:
541                 if (query->pasteboard_data.type)
542                     CFRelease(query->pasteboard_data.type);
543                 break;
544         }
545         [(WineWindow*)query->window release];
546         free(query);
547     }
550 /***********************************************************************
551  *              macdrv_set_query_done
552  */
553 void macdrv_set_query_done(macdrv_query *query)
555     macdrv_retain_query(query);
557     OnMainThreadAsync(^{
558         NSEvent* event;
560         query->done = TRUE;
561         macdrv_release_query(query);
563         event = [NSEvent otherEventWithType:NSApplicationDefined
564                                    location:NSZeroPoint
565                               modifierFlags:0
566                                   timestamp:[[NSProcessInfo processInfo] systemUptime]
567                                windowNumber:0
568                                     context:nil
569                                     subtype:WineApplicationEventWakeQuery
570                                       data1:0
571                                       data2:0];
572         [NSApp postEvent:event atStart:TRUE];
573     });
576 @end