jscript: Store concatenated strings as a rope string to avoid useless copying.
[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
284 /***********************************************************************
285  *              OnMainThread
286  *
287  * Run a block on the main thread synchronously.
288  */
289 void OnMainThread(dispatch_block_t block)
290 {
291     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
292     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
293     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
294     __block BOOL finished;
295
296     if (!queue)
297     {
298         /* Fall back to synchronous dispatch without handling query events. */
299         dispatch_sync(dispatch_get_main_queue(), block);
300         [pool release];
301         return;
302     }
303
304     finished = FALSE;
305     OnMainThreadAsync(^{
306         block();
307         finished = TRUE;
308         [queue signalEventAvailable];
309     });
310
311     while (!finished)
312     {
313         MacDrvEvent* macDrvEvent;
314         struct kevent kev;
315
316         while (!finished &&
317                (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
318         {
319             queue->event_handler(macDrvEvent->event);
320         }
321
322         if (!finished)
323         {
324             [pool release];
325             pool = [[NSAutoreleasePool alloc] init];
326
327             kevent(queue->kq, NULL, 0, &kev, 1, NULL);
328         }
329     }
330
331     [pool release];
332 }
333
334
335 /***********************************************************************
336  *              macdrv_create_event_queue
337  *
338  * Register this thread with the application on the main thread, and set
339  * up an event queue on which it can deliver events to this thread.
340  */
341 macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler)
342 {
343     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
344     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
345
346     WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
347     if (!queue)
348     {
349         queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
350         if (queue)
351         {
352             if ([[WineApplicationController sharedController] registerEventQueue:queue])
353                 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
354             else
355                 queue = nil;
356         }
357     }
358
359     [pool release];
360     return (macdrv_event_queue)queue;
361 }
362
363 /***********************************************************************
364  *              macdrv_destroy_event_queue
365  *
366  * Tell the application that this thread is exiting and destroy the
367  * associated event queue.
368  */
369 void macdrv_destroy_event_queue(macdrv_event_queue queue)
370 {
371     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
372     WineEventQueue* q = (WineEventQueue*)queue;
373     NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
374
375     [[WineApplicationController sharedController] unregisterEventQueue:q];
376     [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey];
377
378     [pool release];
379 }
380
381 /***********************************************************************
382  *              macdrv_get_event_queue_fd
383  *
384  * Get the file descriptor whose readability signals that there are
385  * events on the event queue.
386  */
387 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
388 {
389     WineEventQueue* q = (WineEventQueue*)queue;
390     return q->fds[0];
391 }
392
393 /***********************************************************************
394  *              macdrv_copy_event_from_queue
395  *
396  * Pull an event matching the event mask from the event queue and store
397  * it in the event record pointed to by the event parameter.  If a
398  * matching event was found, return non-zero; otherwise, return 0.
399  *
400  * The caller is responsible for calling macdrv_release_event on any
401  * event returned by this function.
402  */
403 int macdrv_copy_event_from_queue(macdrv_event_queue queue,
404         macdrv_event_mask mask, macdrv_event **event)
405 {
406     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
407     WineEventQueue* q = (WineEventQueue*)queue;
408
409     MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
410     if (macDrvEvent)
411         *event = macdrv_retain_event(macDrvEvent->event);
412
413     [pool release];
414     return (macDrvEvent != nil);
415 }
416
417 /***********************************************************************
418  *              macdrv_create_event
419  */
420 macdrv_event* macdrv_create_event(int type, WineWindow* window)
421 {
422     macdrv_event *event;
423
424     event = calloc(1, sizeof(*event));
425     event->refs = 1;
426     event->deliver = INT_MAX;
427     event->type = type;
428     event->window = (macdrv_window)[window retain];
429     return event;
430 }
431
432 /***********************************************************************
433  *              macdrv_retain_event
434  */
435 macdrv_event* macdrv_retain_event(macdrv_event *event)
436 {
437     OSAtomicIncrement32Barrier(&event->refs);
438     return event;
439 }
440
441 /***********************************************************************
442  *              macdrv_release_event
443  *
444  * Decrements the reference count of an event.  If the count falls to
445  * zero, cleans up any resources, such as allocated memory or retained
446  * objects, held by the event and deallocates it
447  */
448 void macdrv_release_event(macdrv_event *event)
449 {
450     if (OSAtomicDecrement32Barrier(&event->refs) <= 0)
451     {
452         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
453
454         switch (event->type)
455         {
456             case IM_SET_TEXT:
457                 if (event->im_set_text.text)
458                     CFRelease(event->im_set_text.text);
459                 break;
460             case KEYBOARD_CHANGED:
461                 CFRelease(event->keyboard_changed.uchr);
462                 break;
463             case QUERY_EVENT:
464                 macdrv_release_query(event->query_event.query);
465                 break;
466             case WINDOW_GOT_FOCUS:
467                 [(NSMutableSet*)event->window_got_focus.tried_windows release];
468                 break;
469         }
470
471         [(WineWindow*)event->window release];
472         free(event);
473
474         [pool release];
475     }
476 }
477
478 /***********************************************************************
479  *              macdrv_create_query
480  */
481 macdrv_query* macdrv_create_query(void)
482 {
483     macdrv_query *query;
484
485     query = calloc(1, sizeof(*query));
486     query->refs = 1;
487     return query;
488 }
489
490 /***********************************************************************
491  *              macdrv_retain_query
492  */
493 macdrv_query* macdrv_retain_query(macdrv_query *query)
494 {
495     OSAtomicIncrement32Barrier(&query->refs);
496     return query;
497 }
498
499 /***********************************************************************
500  *              macdrv_release_query
501  */
502 void macdrv_release_query(macdrv_query *query)
503 {
504     if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
505     {
506         switch (query->type)
507         {
508             case QUERY_DRAG_OPERATION:
509                 if (query->drag_operation.pasteboard)
510                     CFRelease(query->drag_operation.pasteboard);
511                 break;
512             case QUERY_DRAG_DROP:
513                 if (query->drag_drop.pasteboard)
514                     CFRelease(query->drag_drop.pasteboard);
515                 break;
516             case QUERY_PASTEBOARD_DATA:
517                 if (query->pasteboard_data.type)
518                     CFRelease(query->pasteboard_data.type);
519                 break;
520         }
521         [(WineWindow*)query->window release];
522         free(query);
523     }
524 }
525
526 /***********************************************************************
527  *              macdrv_set_query_done
528  */
529 void macdrv_set_query_done(macdrv_query *query)
530 {
531     macdrv_retain_query(query);
532
533     OnMainThreadAsync(^{
534         NSEvent* event;
535
536         query->done = TRUE;
537         macdrv_release_query(query);
538
539         event = [NSEvent otherEventWithType:NSApplicationDefined
540                                    location:NSZeroPoint
541                               modifierFlags:0
542                                   timestamp:[[NSProcessInfo processInfo] systemUptime]
543                                windowNumber:0
544                                     context:nil
545                                     subtype:WineApplicationEventWakeQuery
546                                       data1:0
547                                       data2:0];
548         [NSApp postEvent:event atStart:TRUE];
549     });
550 }
551
552 @end