2 * MACDRV Cocoa event queue code
4 * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
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.
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.
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
21 #include <sys/types.h>
22 #include <sys/event.h>
24 #include <libkern/OSAtomic.h>
26 #include "macdrv_cocoa.h"
27 #import "cocoa_event.h"
29 #import "cocoa_window.h"
32 static NSString* const WineEventQueueThreadDictionaryKey = @"WineEventQueueThreadDictionaryKey";
35 @interface MacDrvEvent : NSObject
41 - (id) initWithEvent:(const macdrv_event*)event;
45 @implementation MacDrvEvent
47 - (id) initWithEvent:(const macdrv_event*)inEvent
60 @implementation WineEventQueue
64 [self doesNotRecognizeSelector:_cmd];
69 - (id) initWithEventHandler:(macdrv_event_handler)handler
71 NSParameterAssert(handler != nil);
79 fds[0] = fds[1] = kq = -1;
81 event_handler = handler;
82 events = [[NSMutableArray alloc] init];
83 eventsLock = [[NSLock alloc] init];
85 if (!events || !eventsLock)
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)
108 EV_SET(&kev, fds[0], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
111 rc = kevent(kq, &kev, 1, NULL, 0, NULL);
112 } while (rc == -1 && errno == EINTR);
125 [eventsLock release];
127 if (kq != -1) close(kq);
128 if (fds[0] != -1) close(fds[0]);
129 if (fds[1] != -1) close(fds[1]);
134 - (void) signalEventAvailable
141 rc = write(fds[1], &junk, 1);
142 } while (rc < 0 && errno == EINTR);
144 if (rc < 0 && errno != EAGAIN)
145 ERR(@"%@: got error writing to event queue signaling pipe: %s\n", self, strerror(errno));
148 - (void) postEventObject:(MacDrvEvent*)event
150 MacDrvEvent* lastEvent;
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)
161 if (event->event.type == MOUSE_MOVED)
163 lastEvent->event.mouse_moved.x += event->event.mouse_moved.x;
164 lastEvent->event.mouse_moved.y += event->event.mouse_moved.y;
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;
173 lastEvent->event.mouse_moved.time_ms = event->event.mouse_moved.time_ms;
175 macdrv_cleanup_event(&event->event);
178 [events addObject:event];
182 [self signalEventAvailable];
185 - (void) postEvent:(const macdrv_event*)inEvent
187 MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
188 [self postEventObject:event];
192 - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
199 /* Clear the pipe which signals there are pending events. */
202 rc = read(fds[0], buf, sizeof(buf));
203 } while (rc > 0 || (rc < 0 && errno == EINTR));
204 if (rc == 0 || (rc < 0 && errno != EAGAIN))
207 ERR(@"%@: event queue signaling pipe unexpectedly closed\n", self);
209 ERR(@"%@: got error reading from event queue signaling pipe: %s\n", self, strerror(errno));
216 for (event in events)
218 if (event_mask_for_type(event->event.type) & mask)
227 [events removeObjectAtIndex:index];
231 return [event autorelease];
234 - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
236 NSMutableIndexSet* indexes = [[[NSMutableIndexSet alloc] init] autorelease];
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))
245 macdrv_cleanup_event(&event->event);
246 [indexes addIndex:idx];
250 [events removeObjectsAtIndexes:indexes];
255 - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout processEvents:(BOOL)processEvents
258 NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
261 event.type = QUERY_EVENT;
262 event.window = (macdrv_window)[(WineWindow*)query->window retain];
263 event.query_event.query = macdrv_retain_query(query);
266 [self postEvent:&event];
267 timedout = ![NSApp waitUntilQueryDone:&query->done timeout:timeoutDate processEvents:processEvents];
268 return !timedout && query->status;
271 - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
273 return [self query:query timeout:timeout processEvents:FALSE];
277 /***********************************************************************
280 * Run a block on the main thread synchronously.
282 void OnMainThread(dispatch_block_t block)
284 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
285 NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
286 WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
287 __block BOOL finished;
291 /* Fall back to synchronous dispatch without handling query events. */
292 dispatch_sync(dispatch_get_main_queue(), block);
301 [queue signalEventAvailable];
306 MacDrvEvent* macDrvEvent;
310 (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
312 queue->event_handler(&macDrvEvent->event);
313 macdrv_cleanup_event(&macDrvEvent->event);
317 kevent(queue->kq, NULL, 0, &kev, 1, NULL);
324 /***********************************************************************
325 * macdrv_create_event_queue
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.
330 macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler)
332 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
333 NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
335 WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
338 queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
341 if ([NSApp registerEventQueue:queue])
342 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
349 return (macdrv_event_queue)queue;
352 /***********************************************************************
353 * macdrv_destroy_event_queue
355 * Tell the application that this thread is exiting and destroy the
356 * associated event queue.
358 void macdrv_destroy_event_queue(macdrv_event_queue queue)
360 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
361 WineEventQueue* q = (WineEventQueue*)queue;
362 NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
364 [NSApp unregisterEventQueue:q];
365 [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey];
370 /***********************************************************************
371 * macdrv_get_event_queue_fd
373 * Get the file descriptor whose readability signals that there are
374 * events on the event queue.
376 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
378 WineEventQueue* q = (WineEventQueue*)queue;
382 /***********************************************************************
383 * macdrv_get_event_from_queue
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.
389 * The caller is responsible for calling macdrv_cleanup_event on any
390 * event returned by this function.
392 int macdrv_get_event_from_queue(macdrv_event_queue queue,
393 macdrv_event_mask mask, macdrv_event *event)
395 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
396 WineEventQueue* q = (WineEventQueue*)queue;
398 MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
400 *event = macDrvEvent->event;
403 return (macDrvEvent != nil);
406 /***********************************************************************
407 * macdrv_cleanup_event
409 * Performs cleanup of an event. For event types which carry resources
410 * such as allocated memory or retained objects, frees/releases those
413 void macdrv_cleanup_event(macdrv_event *event)
415 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
419 case KEYBOARD_CHANGED:
420 CFRelease(event->keyboard_changed.uchr);
423 macdrv_release_query(event->query_event.query);
425 case WINDOW_GOT_FOCUS:
426 [(NSMutableSet*)event->window_got_focus.tried_windows release];
430 [(WineWindow*)event->window release];
435 /***********************************************************************
436 * macdrv_create_query
438 macdrv_query* macdrv_create_query(void)
442 query = calloc(1, sizeof(*query));
447 /***********************************************************************
448 * macdrv_retain_query
450 macdrv_query* macdrv_retain_query(macdrv_query *query)
452 OSAtomicIncrement32Barrier(&query->refs);
456 /***********************************************************************
457 * macdrv_release_query
459 void macdrv_release_query(macdrv_query *query)
461 if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
463 if (query->type == QUERY_PASTEBOARD_DATA && query->pasteboard_data.type)
464 CFRelease(query->pasteboard_data.type);
465 [(WineWindow*)query->window release];
470 /***********************************************************************
471 * macdrv_set_query_done
473 void macdrv_set_query_done(macdrv_query *query)
475 macdrv_retain_query(query);
481 macdrv_release_query(query);
483 event = [NSEvent otherEventWithType:NSApplicationDefined
486 timestamp:[[NSProcessInfo processInfo] systemUptime]
489 subtype:WineApplicationEventWakeQuery
492 [NSApp postEvent:event atStart:TRUE];