mshtml: Use lazy allocation for connection points.
[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:(const macdrv_event*)event;
42
43 @end
44
45 @implementation MacDrvEvent
46
47     - (id) initWithEvent:(const macdrv_event*)inEvent
48     {
49         self = [super init];
50         if (self)
51         {
52             event = *inEvent;
53         }
54         return self;
55     }
56
57 @end
58
59
60 @implementation WineEventQueue
61
62     - (id) init
63     {
64         [self doesNotRecognizeSelector:_cmd];
65         [self release];
66         return nil;
67     }
68
69     - (id) initWithEventHandler:(macdrv_event_handler)handler
70     {
71         NSParameterAssert(handler != nil);
72
73         self = [super init];
74         if (self != nil)
75         {
76             struct kevent kev;
77             int rc;
78
79             fds[0] = fds[1] = kq = -1;
80
81             event_handler = handler;
82             events = [[NSMutableArray alloc] init];
83             eventsLock = [[NSLock alloc] init];
84
85             if (!events || !eventsLock)
86             {
87                 [self release];
88                 return nil;
89             }
90
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             }
100
101             kq = kqueue();
102             if (kq < 0)
103             {
104                 [self release];
105                 return nil;
106             }
107
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     }
121
122     - (void) dealloc
123     {
124         [events release];
125         [eventsLock release];
126
127         if (kq != -1) close(kq);
128         if (fds[0] != -1) close(fds[0]);
129         if (fds[1] != -1) close(fds[1]);
130
131         [super dealloc];
132     }
133
134     - (void) signalEventAvailable
135     {
136         char junk = 1;
137         int rc;
138
139         do
140         {
141             rc = write(fds[1], &junk, 1);
142         } while (rc < 0 && errno == EINTR);
143
144         if (rc < 0 && errno != EAGAIN)
145             ERR(@"%@: got error writing to event queue signaling pipe: %s\n", self, strerror(errno));
146     }
147
148     - (void) postEventObject:(MacDrvEvent*)event
149     {
150         MacDrvEvent* lastEvent;
151
152         [eventsLock lock];
153
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             }
172
173             lastEvent->event.mouse_moved.time_ms = event->event.mouse_moved.time_ms;
174
175             macdrv_cleanup_event(&event->event);
176         }
177         else
178             [events addObject:event];
179
180         [eventsLock unlock];
181
182         [self signalEventAvailable];
183     }
184
185     - (void) postEvent:(const macdrv_event*)inEvent
186     {
187         MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
188         [self postEventObject:event];
189         [event release];
190     }
191
192     - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
193     {
194         char buf[512];
195         int rc;
196         NSUInteger index;
197         MacDrvEvent* event;
198
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         }
212
213         [eventsLock lock];
214
215         index = 0;
216         for (event in events)
217         {
218             if (event_mask_for_type(event->event.type) & mask)
219                 break;
220
221             index++;
222         }
223
224         if (event)
225         {
226             [event retain];
227             [events removeObjectAtIndex:index];
228         }
229
230         [eventsLock unlock];
231         return [event autorelease];
232     }
233
234     - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
235     {
236         NSMutableIndexSet* indexes = [[[NSMutableIndexSet alloc] init] autorelease];
237
238         [eventsLock lock];
239
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         }];
249
250         [events removeObjectsAtIndexes:indexes];
251
252         [eventsLock unlock];
253     }
254
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;
260
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;
265
266         [self postEvent:&event];
267         timedout = ![NSApp waitUntilQueryDone:&query->done timeout:timeoutDate processEvents:processEvents];
268         return !timedout && query->status;
269     }
270
271     - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
272     {
273         return [self query:query timeout:timeout processEvents:FALSE];
274     }
275
276
277 /***********************************************************************
278  *              OnMainThread
279  *
280  * Run a block on the main thread synchronously.
281  */
282 void OnMainThread(dispatch_block_t block)
283 {
284     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
285     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
286     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
287     __block BOOL finished;
288
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     }
296
297     finished = FALSE;
298     OnMainThreadAsync(^{
299         block();
300         finished = TRUE;
301         [queue signalEventAvailable];
302     });
303
304     while (!finished)
305     {
306         MacDrvEvent* macDrvEvent;
307         struct kevent kev;
308
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         }
315
316         if (!finished)
317             kevent(queue->kq, NULL, 0, &kev, 1, NULL);
318     }
319
320     [pool release];
321 }
322
323
324 /***********************************************************************
325  *              macdrv_create_event_queue
326  *
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)
331 {
332     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
333     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
334
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     }
347
348     [pool release];
349     return (macdrv_event_queue)queue;
350 }
351
352 /***********************************************************************
353  *              macdrv_destroy_event_queue
354  *
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)
359 {
360     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
361     WineEventQueue* q = (WineEventQueue*)queue;
362     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
363
364     [NSApp unregisterEventQueue:q];
365     [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey];
366
367     [pool release];
368 }
369
370 /***********************************************************************
371  *              macdrv_get_event_queue_fd
372  *
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)
377 {
378     WineEventQueue* q = (WineEventQueue*)queue;
379     return q->fds[0];
380 }
381
382 /***********************************************************************
383  *              macdrv_get_event_from_queue
384  *
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.
388  *
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)
394 {
395     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
396     WineEventQueue* q = (WineEventQueue*)queue;
397
398     MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
399     if (macDrvEvent)
400         *event = macDrvEvent->event;
401
402     [pool release];
403     return (macDrvEvent != nil);
404 }
405
406 /***********************************************************************
407  *              macdrv_cleanup_event
408  *
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)
414 {
415     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
416
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     }
429
430     [(WineWindow*)event->window release];
431
432     [pool release];
433 }
434
435 /***********************************************************************
436  *              macdrv_create_query
437  */
438 macdrv_query* macdrv_create_query(void)
439 {
440     macdrv_query *query;
441
442     query = calloc(1, sizeof(*query));
443     query->refs = 1;
444     return query;
445 }
446
447 /***********************************************************************
448  *              macdrv_retain_query
449  */
450 macdrv_query* macdrv_retain_query(macdrv_query *query)
451 {
452     OSAtomicIncrement32Barrier(&query->refs);
453     return query;
454 }
455
456 /***********************************************************************
457  *              macdrv_release_query
458  */
459 void macdrv_release_query(macdrv_query *query)
460 {
461     if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
462     {
463         switch (query->type)
464         {
465             case QUERY_DRAG_OPERATION:
466                 if (query->drag_operation.pasteboard)
467                     CFRelease(query->drag_operation.pasteboard);
468                 break;
469             case QUERY_DRAG_DROP:
470                 if (query->drag_drop.pasteboard)
471                     CFRelease(query->drag_drop.pasteboard);
472                 break;
473             case QUERY_PASTEBOARD_DATA:
474                 if (query->pasteboard_data.type)
475                     CFRelease(query->pasteboard_data.type);
476                 break;
477         }
478         [(WineWindow*)query->window release];
479         free(query);
480     }
481 }
482
483 /***********************************************************************
484  *              macdrv_set_query_done
485  */
486 void macdrv_set_query_done(macdrv_query *query)
487 {
488     macdrv_retain_query(query);
489
490     OnMainThreadAsync(^{
491         NSEvent* event;
492
493         query->done = TRUE;
494         macdrv_release_query(query);
495
496         event = [NSEvent otherEventWithType:NSApplicationDefined
497                                    location:NSZeroPoint
498                               modifierFlags:0
499                                   timestamp:[[NSProcessInfo processInfo] systemUptime]
500                                windowNumber:0
501                                     context:nil
502                                     subtype:WineApplicationEventWakeQuery
503                                       data1:0
504                                       data2:0];
505         [NSApp postEvent:event atStart:TRUE];
506     });
507 }
508
509 @end