Release 1.5.29.
[wine] / dlls / winemac.drv / cocoa_event.m
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  */
20
21 #include <sys/types.h>
22 #include <sys/event.h>
23 #include <sys/time.h>
24 #include <libkern/OSAtomic.h>
25
26 #include "macdrv_cocoa.h"
27 #import "cocoa_event.h"
28 #import "cocoa_app.h"
29 #import "cocoa_window.h"
30
31
32 static NSString* const WineEventQueueThreadDictionaryKey = @"WineEventQueueThreadDictionaryKey";
33
34
35 @interface MacDrvEvent : NSObject
36 {
37 @public
38     macdrv_event* event;
39 }
40
41     - (id) initWithEvent:(macdrv_event*)event;
42
43 @end
44
45 @implementation MacDrvEvent
46
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     }
56
57     - (void) dealloc
58     {
59         if (event) macdrv_release_event(event);
60         [super dealloc];
61     }
62
63 @end
64
65
66 @implementation WineEventQueue
67
68     - (id) init
69     {
70         [self doesNotRecognizeSelector:_cmd];
71         [self release];
72         return nil;
73     }
74
75     - (id) initWithEventHandler:(macdrv_event_handler)handler
76     {
77         NSParameterAssert(handler != nil);
78
79         self = [super init];
80         if (self != nil)
81         {
82             struct kevent kev;
83             int rc;
84
85             fds[0] = fds[1] = kq = -1;
86
87             event_handler = handler;
88             events = [[NSMutableArray alloc] init];
89             eventsLock = [[NSLock alloc] init];
90
91             if (!events || !eventsLock)
92             {
93                 [self release];
94                 return nil;
95             }
96
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             }
106
107             kq = kqueue();
108             if (kq < 0)
109             {
110                 [self release];
111                 return nil;
112             }
113
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     }
127
128     - (void) dealloc
129     {
130         [events release];
131         [eventsLock release];
132
133         if (kq != -1) close(kq);
134         if (fds[0] != -1) close(fds[0]);
135         if (fds[1] != -1) close(fds[1]);
136
137         [super dealloc];
138     }
139
140     - (void) signalEventAvailable
141     {
142         char junk = 1;
143         int rc;
144
145         do
146         {
147             rc = write(fds[1], &junk, 1);
148         } while (rc < 0 && errno == EINTR);
149
150         if (rc < 0 && errno != EAGAIN)
151             ERR(@"%@: got error writing to event queue signaling pipe: %s\n", self, strerror(errno));
152     }
153
154     - (void) postEventObject:(MacDrvEvent*)event
155     {
156         MacDrvEvent* lastEvent;
157
158         [eventsLock lock];
159
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             }
178
179             lastEvent->event->mouse_moved.time_ms = event->event->mouse_moved.time_ms;
180         }
181         else
182             [events addObject:event];
183
184         [eventsLock unlock];
185
186         [self signalEventAvailable];
187     }
188
189     - (void) postEvent:(macdrv_event*)inEvent
190     {
191         MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
192         [self postEventObject:event];
193         [event release];
194     }
195
196     - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
197     {
198         char buf[512];
199         int rc;
200         NSUInteger index;
201         MacDrvEvent* ret = nil;
202
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         }
216
217         [eventsLock lock];
218
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];
227
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         }
238
239         [eventsLock unlock];
240         return ret;
241     }
242
243     - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
244     {
245         NSIndexSet* indexes;
246
247         [eventsLock lock];
248
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         }];
254
255         [events removeObjectsAtIndexes:indexes];
256
257         [eventsLock unlock];
258     }
259
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;
265
266         event = macdrv_create_event(QUERY_EVENT, (WineWindow*)query->window);
267         event->query_event.query = macdrv_retain_query(query);
268         query->done = FALSE;
269
270         [self postEvent:event];
271         macdrv_release_event(event);
272         timedout = ![[WineApplicationController sharedController] waitUntilQueryDone:&query->done
273                                                                              timeout:timeoutDate
274                                                                        processEvents:processEvents];
275         return !timedout && query->status;
276     }
277
278     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
279     {
280         return [self query:query timeout:timeout processEvents:FALSE];
281     }
282
283     - (void) resetMouseEventPositions:(CGPoint)pos
284     {
285         MacDrvEvent* event;
286
287         [eventsLock lock];
288
289         for (event in events)
290         {
291             if (event->event->type == MOUSE_BUTTON)
292             {
293                 event->event->mouse_button.x = pos.x;
294                 event->event->mouse_button.y = pos.y;
295             }
296             else if (event->event->type == MOUSE_SCROLL)
297             {
298                 event->event->mouse_scroll.x = pos.x;
299                 event->event->mouse_scroll.y = pos.y;
300             }
301         }
302
303         [eventsLock unlock];
304     }
305
306
307 /***********************************************************************
308  *              OnMainThread
309  *
310  * Run a block on the main thread synchronously.
311  */
312 void OnMainThread(dispatch_block_t block)
313 {
314     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
315     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
316     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
317     __block BOOL finished;
318
319     if (!queue)
320     {
321         /* Fall back to synchronous dispatch without handling query events. */
322         dispatch_sync(dispatch_get_main_queue(), block);
323         [pool release];
324         return;
325     }
326
327     finished = FALSE;
328     OnMainThreadAsync(^{
329         block();
330         finished = TRUE;
331         [queue signalEventAvailable];
332     });
333
334     while (!finished)
335     {
336         MacDrvEvent* macDrvEvent;
337         struct kevent kev;
338
339         while (!finished &&
340                (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
341         {
342             queue->event_handler(macDrvEvent->event);
343         }
344
345         if (!finished)
346         {
347             [pool release];
348             pool = [[NSAutoreleasePool alloc] init];
349
350             kevent(queue->kq, NULL, 0, &kev, 1, NULL);
351         }
352     }
353
354     [pool release];
355 }
356
357
358 /***********************************************************************
359  *              macdrv_create_event_queue
360  *
361  * Register this thread with the application on the main thread, and set
362  * up an event queue on which it can deliver events to this thread.
363  */
364 macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler)
365 {
366     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
367     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
368
369     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
370     if (!queue)
371     {
372         queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
373         if (queue)
374         {
375             if ([[WineApplicationController sharedController] registerEventQueue:queue])
376                 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
377             else
378                 queue = nil;
379         }
380     }
381
382     [pool release];
383     return (macdrv_event_queue)queue;
384 }
385
386 /***********************************************************************
387  *              macdrv_destroy_event_queue
388  *
389  * Tell the application that this thread is exiting and destroy the
390  * associated event queue.
391  */
392 void macdrv_destroy_event_queue(macdrv_event_queue queue)
393 {
394     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
395     WineEventQueue* q = (WineEventQueue*)queue;
396     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
397
398     [[WineApplicationController sharedController] unregisterEventQueue:q];
399     [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey];
400
401     [pool release];
402 }
403
404 /***********************************************************************
405  *              macdrv_get_event_queue_fd
406  *
407  * Get the file descriptor whose readability signals that there are
408  * events on the event queue.
409  */
410 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
411 {
412     WineEventQueue* q = (WineEventQueue*)queue;
413     return q->fds[0];
414 }
415
416 /***********************************************************************
417  *              macdrv_copy_event_from_queue
418  *
419  * Pull an event matching the event mask from the event queue and store
420  * it in the event record pointed to by the event parameter.  If a
421  * matching event was found, return non-zero; otherwise, return 0.
422  *
423  * The caller is responsible for calling macdrv_release_event on any
424  * event returned by this function.
425  */
426 int macdrv_copy_event_from_queue(macdrv_event_queue queue,
427         macdrv_event_mask mask, macdrv_event **event)
428 {
429     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
430     WineEventQueue* q = (WineEventQueue*)queue;
431
432     MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
433     if (macDrvEvent)
434         *event = macdrv_retain_event(macDrvEvent->event);
435
436     [pool release];
437     return (macDrvEvent != nil);
438 }
439
440 /***********************************************************************
441  *              macdrv_create_event
442  */
443 macdrv_event* macdrv_create_event(int type, WineWindow* window)
444 {
445     macdrv_event *event;
446
447     event = calloc(1, sizeof(*event));
448     event->refs = 1;
449     event->deliver = INT_MAX;
450     event->type = type;
451     event->window = (macdrv_window)[window retain];
452     return event;
453 }
454
455 /***********************************************************************
456  *              macdrv_retain_event
457  */
458 macdrv_event* macdrv_retain_event(macdrv_event *event)
459 {
460     OSAtomicIncrement32Barrier(&event->refs);
461     return event;
462 }
463
464 /***********************************************************************
465  *              macdrv_release_event
466  *
467  * Decrements the reference count of an event.  If the count falls to
468  * zero, cleans up any resources, such as allocated memory or retained
469  * objects, held by the event and deallocates it
470  */
471 void macdrv_release_event(macdrv_event *event)
472 {
473     if (OSAtomicDecrement32Barrier(&event->refs) <= 0)
474     {
475         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
476
477         switch (event->type)
478         {
479             case IM_SET_TEXT:
480                 if (event->im_set_text.text)
481                     CFRelease(event->im_set_text.text);
482                 break;
483             case KEYBOARD_CHANGED:
484                 CFRelease(event->keyboard_changed.uchr);
485                 break;
486             case QUERY_EVENT:
487                 macdrv_release_query(event->query_event.query);
488                 break;
489             case WINDOW_GOT_FOCUS:
490                 [(NSMutableSet*)event->window_got_focus.tried_windows release];
491                 break;
492         }
493
494         [(WineWindow*)event->window release];
495         free(event);
496
497         [pool release];
498     }
499 }
500
501 /***********************************************************************
502  *              macdrv_create_query
503  */
504 macdrv_query* macdrv_create_query(void)
505 {
506     macdrv_query *query;
507
508     query = calloc(1, sizeof(*query));
509     query->refs = 1;
510     return query;
511 }
512
513 /***********************************************************************
514  *              macdrv_retain_query
515  */
516 macdrv_query* macdrv_retain_query(macdrv_query *query)
517 {
518     OSAtomicIncrement32Barrier(&query->refs);
519     return query;
520 }
521
522 /***********************************************************************
523  *              macdrv_release_query
524  */
525 void macdrv_release_query(macdrv_query *query)
526 {
527     if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
528     {
529         switch (query->type)
530         {
531             case QUERY_DRAG_OPERATION:
532                 if (query->drag_operation.pasteboard)
533                     CFRelease(query->drag_operation.pasteboard);
534                 break;
535             case QUERY_DRAG_DROP:
536                 if (query->drag_drop.pasteboard)
537                     CFRelease(query->drag_drop.pasteboard);
538                 break;
539             case QUERY_PASTEBOARD_DATA:
540                 if (query->pasteboard_data.type)
541                     CFRelease(query->pasteboard_data.type);
542                 break;
543         }
544         [(WineWindow*)query->window release];
545         free(query);
546     }
547 }
548
549 /***********************************************************************
550  *              macdrv_set_query_done
551  */
552 void macdrv_set_query_done(macdrv_query *query)
553 {
554     macdrv_retain_query(query);
555
556     OnMainThreadAsync(^{
557         NSEvent* event;
558
559         query->done = TRUE;
560         macdrv_release_query(query);
561
562         event = [NSEvent otherEventWithType:NSApplicationDefined
563                                    location:NSZeroPoint
564                               modifierFlags:0
565                                   timestamp:[[NSProcessInfo processInfo] systemUptime]
566                                windowNumber:0
567                                     context:nil
568                                     subtype:WineApplicationEventWakeQuery
569                                       data1:0
570                                       data2:0];
571         [NSApp postEvent:event atStart:TRUE];
572     });
573 }
574
575 @end