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:(macdrv_event*)event;
45 @implementation MacDrvEvent
47 - (id) initWithEvent:(macdrv_event*)inEvent
52 event = macdrv_retain_event(inEvent);
59 if (event) macdrv_release_event(event);
66 @implementation WineEventQueue
70 [self doesNotRecognizeSelector:_cmd];
75 - (id) initWithEventHandler:(macdrv_event_handler)handler
77 NSParameterAssert(handler != nil);
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)
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)
114 EV_SET(&kev, fds[0], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
117 rc = kevent(kq, &kev, 1, NULL, 0, NULL);
118 } while (rc == -1 && errno == EINTR);
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]);
140 - (void) signalEventAvailable
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));
154 - (void) postEventObject:(MacDrvEvent*)event
156 MacDrvEvent* lastEvent;
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)
168 if (event->event->type == MOUSE_MOVED)
170 lastEvent->event->mouse_moved.x += event->event->mouse_moved.x;
171 lastEvent->event->mouse_moved.y += event->event->mouse_moved.y;
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;
180 lastEvent->event->mouse_moved.time_ms = event->event->mouse_moved.time_ms;
183 [events addObject:event];
187 [self signalEventAvailable];
190 - (void) postEvent:(macdrv_event*)inEvent
192 MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
193 [self postEventObject:event];
197 - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
202 MacDrvEvent* ret = nil;
204 /* Clear the pipe which signals there are pending events. */
207 rc = read(fds[0], buf, sizeof(buf));
208 } while (rc > 0 || (rc < 0 && errno == EINTR));
209 if (rc == 0 || (rc < 0 && errno != EAGAIN))
212 ERR(@"%@: event queue signaling pipe unexpectedly closed\n", self);
214 ERR(@"%@: got error reading from event queue signaling pipe: %s\n", self, strerror(errno));
221 while (index < [events count])
223 MacDrvEvent* event = [events objectAtIndex:index];
224 if (event_mask_for_type(event->event->type) & mask)
226 [[event retain] autorelease];
227 [events removeObjectAtIndex:index];
229 if (event->event->deliver == INT_MAX ||
230 OSAtomicDecrement32Barrier(&event->event->deliver) >= 0)
244 - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
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));
256 [events removeObjectsAtIndexes:indexes];
261 - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout processEvents:(BOOL)processEvents
264 NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
267 event = macdrv_create_event(QUERY_EVENT, (WineWindow*)query->window);
268 event->query_event.query = macdrv_retain_query(query);
271 [self postEvent:event];
272 macdrv_release_event(event);
273 timedout = ![[WineApplicationController sharedController] waitUntilQueryDone:&query->done
275 processEvents:processEvents];
276 return !timedout && query->status;
279 - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout
281 return [self query:query timeout:timeout processEvents:FALSE];
284 - (void) resetMouseEventPositions:(CGPoint)pos
290 for (event in events)
292 if (event->event->type == MOUSE_BUTTON)
294 event->event->mouse_button.x = pos.x;
295 event->event->mouse_button.y = pos.y;
297 else if (event->event->type == MOUSE_SCROLL)
299 event->event->mouse_scroll.x = pos.x;
300 event->event->mouse_scroll.y = pos.y;
308 /***********************************************************************
311 * Run a block on the main thread synchronously.
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;
322 /* Fall back to synchronous dispatch without handling query events. */
323 dispatch_sync(dispatch_get_main_queue(), block);
332 [queue signalEventAvailable];
337 MacDrvEvent* macDrvEvent;
341 (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)]))
343 queue->event_handler(macDrvEvent->event);
349 pool = [[NSAutoreleasePool alloc] init];
351 kevent(queue->kq, NULL, 0, &kev, 1, NULL);
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.
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];
373 queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
376 if ([[WineApplicationController sharedController] registerEventQueue:queue])
377 [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
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.
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];
405 /***********************************************************************
406 * macdrv_get_event_queue_fd
408 * Get the file descriptor whose readability signals that there are
409 * events on the event queue.
411 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
413 WineEventQueue* q = (WineEventQueue*)queue;
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.
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];
435 *event = macdrv_retain_event(macDrvEvent->event);
438 return (macDrvEvent != nil);
441 /***********************************************************************
442 * macdrv_create_event
444 macdrv_event* macdrv_create_event(int type, WineWindow* window)
448 event = calloc(1, sizeof(*event));
450 event->deliver = INT_MAX;
452 event->window = (macdrv_window)[window retain];
456 /***********************************************************************
457 * macdrv_retain_event
459 macdrv_event* macdrv_retain_event(macdrv_event *event)
461 OSAtomicIncrement32Barrier(&event->refs);
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
472 void macdrv_release_event(macdrv_event *event)
474 if (OSAtomicDecrement32Barrier(&event->refs) <= 0)
476 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
481 if (event->im_set_text.text)
482 CFRelease(event->im_set_text.text);
484 case KEYBOARD_CHANGED:
485 CFRelease(event->keyboard_changed.uchr);
488 macdrv_release_query(event->query_event.query);
490 case WINDOW_GOT_FOCUS:
491 [(NSMutableSet*)event->window_got_focus.tried_windows release];
495 [(WineWindow*)event->window release];
502 /***********************************************************************
503 * macdrv_create_query
505 macdrv_query* macdrv_create_query(void)
509 query = calloc(1, sizeof(*query));
514 /***********************************************************************
515 * macdrv_retain_query
517 macdrv_query* macdrv_retain_query(macdrv_query *query)
519 OSAtomicIncrement32Barrier(&query->refs);
523 /***********************************************************************
524 * macdrv_release_query
526 void macdrv_release_query(macdrv_query *query)
528 if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
532 case QUERY_DRAG_OPERATION:
533 if (query->drag_operation.pasteboard)
534 CFRelease(query->drag_operation.pasteboard);
536 case QUERY_DRAG_DROP:
537 if (query->drag_drop.pasteboard)
538 CFRelease(query->drag_drop.pasteboard);
540 case QUERY_PASTEBOARD_DATA:
541 if (query->pasteboard_data.type)
542 CFRelease(query->pasteboard_data.type);
545 [(WineWindow*)query->window release];
550 /***********************************************************************
551 * macdrv_set_query_done
553 void macdrv_set_query_done(macdrv_query *query)
555 macdrv_retain_query(query);
561 macdrv_release_query(query);
563 event = [NSEvent otherEventWithType:NSApplicationDefined
566 timestamp:[[NSProcessInfo processInfo] systemUptime]
569 subtype:WineApplicationEventWakeQuery
572 [NSApp postEvent:event atStart:TRUE];