From 69e631e04e75e7596f58d2d16ba674b343886877 Mon Sep 17 00:00:00 2001 From: Ken Thomases Date: Sun, 10 Mar 2013 22:58:26 -0500 Subject: [PATCH] winemac: Add support for "query" events which wait for synchronous responses. --- dlls/winemac.drv/cocoa_app.h | 7 +- dlls/winemac.drv/cocoa_app.m | 86 +++++++++++++-- dlls/winemac.drv/cocoa_clipboard.m | 1 + dlls/winemac.drv/cocoa_event.h | 7 ++ dlls/winemac.drv/cocoa_event.m | 169 +++++++++++++++++++++++++++-- dlls/winemac.drv/cocoa_opengl.m | 2 +- dlls/winemac.drv/event.c | 32 ++++++ dlls/winemac.drv/macdrv.h | 2 + dlls/winemac.drv/macdrv_cocoa.h | 26 ++++- dlls/winemac.drv/macdrv_main.c | 2 +- 10 files changed, 314 insertions(+), 20 deletions(-) diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h index 4e81e305dfc..67f75e1c098 100644 --- a/dlls/winemac.drv/cocoa_app.h +++ b/dlls/winemac.drv/cocoa_app.h @@ -32,6 +32,10 @@ @interface WineApplication : NSApplication { + CFRunLoopSourceRef requestSource; + NSMutableArray* requests; + dispatch_queue_t requestsManipQueue; + NSMutableArray* eventQueues; NSLock* eventQueuesLock; @@ -83,6 +87,8 @@ - (double) ticksForEventTime:(NSTimeInterval)eventTime; - (void) windowGotFocus:(WineWindow*)window; + - (BOOL) waitUntilQueryDone:(int*)done timeout:(NSDate*)timeout; + - (void) keyboardSelectionDidChange; - (void) flipRect:(NSRect*)rect; @@ -93,7 +99,6 @@ - (void) wineWindow:(WineWindow*)window @end -void OnMainThread(dispatch_block_t block); void OnMainThreadAsync(dispatch_block_t block); void LogError(const char* func, NSString* format, ...); diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index 0d6913e6e75..5dc30089887 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -26,6 +26,9 @@ #import "cocoa_window.h" +static NSString* const WineAppWaitQueryResponseMode = @"WineAppWaitQueryResponseMode"; + + int macdrv_err_on; @@ -56,6 +59,8 @@ @interface WineApplication () @property (copy, nonatomic) NSArray* cursorFrames; @property (retain, nonatomic) NSTimer* cursorTimer; + static void PerformRequest(void *info); + @end @@ -70,6 +75,20 @@ - (id) init self = [super init]; if (self != nil) { + CFRunLoopSourceContext context = { 0 }; + context.perform = PerformRequest; + requestSource = CFRunLoopSourceCreate(NULL, 0, &context); + if (!requestSource) + { + [self release]; + return nil; + } + CFRunLoopAddSource(CFRunLoopGetMain(), requestSource, kCFRunLoopCommonModes); + CFRunLoopAddSource(CFRunLoopGetMain(), requestSource, (CFStringRef)WineAppWaitQueryResponseMode); + + requests = [[NSMutableArray alloc] init]; + requestsManipQueue = dispatch_queue_create("org.winehq.WineAppRequestManipQueue", NULL); + eventQueues = [[NSMutableArray alloc] init]; eventQueuesLock = [[NSLock alloc] init]; @@ -80,8 +99,8 @@ - (id) init warpRecords = [[NSMutableArray alloc] init]; - if (!eventQueues || !eventQueuesLock || !keyWindows || !orderedWineWindows || - !originalDisplayModes || !warpRecords) + if (!requests || !requestsManipQueue || !eventQueues || !eventQueuesLock || + !keyWindows || !orderedWineWindows || !originalDisplayModes || !warpRecords) { [self release]; return nil; @@ -100,6 +119,13 @@ - (void) dealloc [keyWindows release]; [eventQueues release]; [eventQueuesLock release]; + if (requestsManipQueue) dispatch_release(requestsManipQueue); + [requests release]; + if (requestSource) + { + CFRunLoopSourceInvalidate(requestSource); + CFRelease(requestSource); + } [super dealloc]; } @@ -146,6 +172,18 @@ - (void) transformProcessToForeground } } + - (BOOL) waitUntilQueryDone:(int*)done timeout:(NSDate*)timeout + { + PerformRequest(NULL); + + do + { + [[NSRunLoop currentRunLoop] runMode:WineAppWaitQueryResponseMode beforeDate:timeout]; + } while (!*done && [timeout timeIntervalSinceNow] >= 0); + + return *done; + } + - (BOOL) registerEventQueue:(WineEventQueue*)queue { [eventQueuesLock lock]; @@ -1102,16 +1140,36 @@ - (void)applicationWillResignActive:(NSNotification *)notification }]; } -@end - /*********************************************************************** - * OnMainThread + * PerformRequest * - * Run a block on the main thread synchronously. + * Run-loop-source perform callback. Pull request blocks from the + * array of queued requests and invoke them. */ -void OnMainThread(dispatch_block_t block) +static void PerformRequest(void *info) { - dispatch_sync(dispatch_get_main_queue(), block); + WineApplication* app = (WineApplication*)NSApp; + + for (;;) + { + __block dispatch_block_t block; + + dispatch_sync(app->requestsManipQueue, ^{ + if ([app->requests count]) + { + block = (dispatch_block_t)[[app->requests objectAtIndex:0] retain]; + [app->requests removeObjectAtIndex:0]; + } + else + block = nil; + }); + + if (!block) + break; + + block(); + [block release]; + } } /*********************************************************************** @@ -1121,9 +1179,19 @@ void OnMainThread(dispatch_block_t block) */ void OnMainThreadAsync(dispatch_block_t block) { - dispatch_async(dispatch_get_main_queue(), block); + WineApplication* app = (WineApplication*)NSApp; + + block = [block copy]; + dispatch_sync(app->requestsManipQueue, ^{ + [app->requests addObject:block]; + }); + [block release]; + CFRunLoopSourceSignal(app->requestSource); + CFRunLoopWakeUp(CFRunLoopGetMain()); } +@end + /*********************************************************************** * LogError */ diff --git a/dlls/winemac.drv/cocoa_clipboard.m b/dlls/winemac.drv/cocoa_clipboard.m index e908eab286b..74fa75b6afa 100644 --- a/dlls/winemac.drv/cocoa_clipboard.m +++ b/dlls/winemac.drv/cocoa_clipboard.m @@ -20,6 +20,7 @@ #include "macdrv_cocoa.h" #import "cocoa_app.h" +#import "cocoa_event.h" static int owned_change_count = -1; diff --git a/dlls/winemac.drv/cocoa_event.h b/dlls/winemac.drv/cocoa_event.h index fb2b9ff0e18..56964da326f 100644 --- a/dlls/winemac.drv/cocoa_event.h +++ b/dlls/winemac.drv/cocoa_event.h @@ -28,9 +28,16 @@ @interface WineEventQueue : NSObject NSLock* eventsLock; int fds[2]; /* Pipe signaling when there are events queued. */ + int kq; /* kqueue for waiting in OnMainThread(). */ + + macdrv_event_handler event_handler; } - (void) postEvent:(const macdrv_event*)inEvent; - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window; + - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout; + @end + +void OnMainThread(dispatch_block_t block); diff --git a/dlls/winemac.drv/cocoa_event.m b/dlls/winemac.drv/cocoa_event.m index 377faa825d5..15d2d29d097 100644 --- a/dlls/winemac.drv/cocoa_event.m +++ b/dlls/winemac.drv/cocoa_event.m @@ -29,6 +29,9 @@ #import "cocoa_window.h" +static NSString* const WineEventQueueThreadDictionaryKey = @"WineEventQueueThreadDictionaryKey"; + + @interface MacDrvEvent : NSObject { @public @@ -58,11 +61,24 @@ @implementation WineEventQueue - (id) init { + [self doesNotRecognizeSelector:_cmd]; + [self release]; + return nil; + } + + - (id) initWithEventHandler:(macdrv_event_handler)handler + { + NSParameterAssert(handler != nil); + self = [super init]; if (self != nil) { - fds[0] = fds[1] = -1; + struct kevent kev; + int rc; + fds[0] = fds[1] = kq = -1; + + event_handler = handler; events = [[NSMutableArray alloc] init]; eventsLock = [[NSLock alloc] init]; @@ -81,6 +97,24 @@ - (id) init [self release]; return nil; } + + kq = kqueue(); + if (kq < 0) + { + [self release]; + return nil; + } + + EV_SET(&kev, fds[0], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); + do + { + rc = kevent(kq, &kev, 1, NULL, 0, NULL); + } while (rc == -1 && errno == EINTR); + if (rc == -1) + { + [self release]; + return nil; + } } return self; } @@ -90,6 +124,7 @@ - (void) dealloc [events release]; [eventsLock release]; + if (kq != -1) close(kq); if (fds[0] != -1) close(fds[0]); if (fds[1] != -1) close(fds[1]); @@ -217,6 +252,69 @@ - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*) [eventsLock unlock]; } + - (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout + { + macdrv_event event; + NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout]; + BOOL timedout; + + event.type = QUERY_EVENT; + event.window = (macdrv_window)[(WineWindow*)query->window retain]; + event.query_event.query = macdrv_retain_query(query); + query->done = FALSE; + + [self postEvent:&event]; + timedout = ![NSApp waitUntilQueryDone:&query->done timeout:timeoutDate]; + return !timedout && query->status; + } + + +/*********************************************************************** + * OnMainThread + * + * Run a block on the main thread synchronously. + */ +void OnMainThread(dispatch_block_t block) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary]; + WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey]; + __block BOOL finished; + + if (!queue) + { + /* Fall back to synchronous dispatch without handling query events. */ + dispatch_sync(dispatch_get_main_queue(), block); + [pool release]; + return; + } + + finished = FALSE; + OnMainThreadAsync(^{ + block(); + finished = TRUE; + [queue signalEventAvailable]; + }); + + while (!finished) + { + MacDrvEvent* macDrvEvent; + struct kevent kev; + + while (!finished && + (macDrvEvent = [queue getEventMatchingMask:event_mask_for_type(QUERY_EVENT)])) + { + queue->event_handler(&macDrvEvent->event); + macdrv_cleanup_event(&macDrvEvent->event); + } + + if (!finished) + kevent(queue->kq, NULL, 0, &kev, 1, NULL); + } + + [pool release]; +} + /*********************************************************************** * macdrv_create_event_queue @@ -224,15 +322,22 @@ - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*) * Register this thread with the application on the main thread, and set * up an event queue on which it can deliver events to this thread. */ -macdrv_event_queue macdrv_create_event_queue(void) +macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary]; - WineEventQueue* queue = [[WineEventQueue alloc] init]; - if (queue && ![NSApp registerEventQueue:queue]) + WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey]; + if (!queue) { - [queue release]; - queue = nil; + queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease]; + if (queue) + { + if ([NSApp registerEventQueue:queue]) + [threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey]; + else + queue = nil; + } } [pool release]; @@ -249,9 +354,10 @@ void macdrv_destroy_event_queue(macdrv_event_queue queue) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; WineEventQueue* q = (WineEventQueue*)queue; + NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary]; [NSApp unregisterEventQueue:q]; - [q release]; + [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey]; [pool release]; } @@ -308,6 +414,9 @@ void macdrv_cleanup_event(macdrv_event *event) case KEYBOARD_CHANGED: CFRelease(event->keyboard_changed.uchr); break; + case QUERY_EVENT: + macdrv_release_query(event->query_event.query); + break; case WINDOW_GOT_FOCUS: [(NSMutableSet*)event->window_got_focus.tried_windows release]; break; @@ -318,4 +427,50 @@ void macdrv_cleanup_event(macdrv_event *event) [pool release]; } +/*********************************************************************** + * macdrv_create_query + */ +macdrv_query* macdrv_create_query(void) +{ + macdrv_query *query; + + query = calloc(1, sizeof(*query)); + query->refs = 1; + return query; +} + +/*********************************************************************** + * macdrv_retain_query + */ +macdrv_query* macdrv_retain_query(macdrv_query *query) +{ + OSAtomicIncrement32Barrier(&query->refs); + return query; +} + +/*********************************************************************** + * macdrv_release_query + */ +void macdrv_release_query(macdrv_query *query) +{ + if (OSAtomicDecrement32Barrier(&query->refs) <= 0) + { + [(WineWindow*)query->window release]; + free(query); + } +} + +/*********************************************************************** + * macdrv_set_query_done + */ +void macdrv_set_query_done(macdrv_query *query) +{ + macdrv_retain_query(query); + + OnMainThreadAsync(^{ + query->done = TRUE; + macdrv_release_query(query); + }); +} + @end diff --git a/dlls/winemac.drv/cocoa_opengl.m b/dlls/winemac.drv/cocoa_opengl.m index f67ea89b242..1368c7318f8 100644 --- a/dlls/winemac.drv/cocoa_opengl.m +++ b/dlls/winemac.drv/cocoa_opengl.m @@ -21,7 +21,7 @@ #import "cocoa_opengl.h" #include "macdrv_cocoa.h" -#include "cocoa_app.h" +#include "cocoa_event.h" @interface WineOpenGLContext () diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index b9131f7be78..915dddc05fb 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -41,6 +41,7 @@ static const char *dbgstr_event(int type) "MOUSE_MOVED", "MOUSE_MOVED_ABSOLUTE", "MOUSE_SCROLL", + "QUERY_EVENT", "WINDOW_CLOSE_REQUESTED", "WINDOW_DID_MINIMIZE", "WINDOW_DID_UNMINIMIZE", @@ -94,10 +95,38 @@ static macdrv_event_mask get_event_mask(DWORD mask) event_mask |= event_mask_for_type(WINDOW_LOST_FOCUS); } + if (mask & QS_SENDMESSAGE) + { + event_mask |= event_mask_for_type(QUERY_EVENT); + } + return event_mask; } +/*********************************************************************** + * macdrv_query_event + * + * Handler for QUERY_EVENT queries. + */ +static void macdrv_query_event(HWND hwnd, macdrv_event *event) +{ + BOOL success = FALSE; + macdrv_query *query = event->query_event.query; + + switch (query->type) + { + default: + FIXME("unrecognized query type %d\n", query->type); + break; + } + + TRACE("success %d\n", success); + query->status = success; + macdrv_set_query_done(query); +} + + /*********************************************************************** * macdrv_handle_event */ @@ -138,6 +167,9 @@ void macdrv_handle_event(macdrv_event *event) case MOUSE_SCROLL: macdrv_mouse_scroll(hwnd, event); break; + case QUERY_EVENT: + macdrv_query_event(hwnd, event); + break; case WINDOW_CLOSE_REQUESTED: macdrv_window_close_requested(hwnd); break; diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 95e06f019c0..f9b70456a0b 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -138,6 +138,8 @@ static inline RECT rect_from_cgrect(CGRect cgrect) extern void set_window_surface(macdrv_window window, struct window_surface *window_surface) DECLSPEC_HIDDEN; extern void set_surface_use_alpha(struct window_surface *window_surface, BOOL use_alpha) DECLSPEC_HIDDEN; +extern void macdrv_handle_event(macdrv_event *event) DECLSPEC_HIDDEN; + extern void macdrv_window_close_requested(HWND hwnd) DECLSPEC_HIDDEN; extern void macdrv_window_frame_changed(HWND hwnd, CGRect frame) DECLSPEC_HIDDEN; extern void macdrv_window_got_focus(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index e7cd9f82081..9bb960ae224 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -104,6 +104,7 @@ typedef struct macdrv_opaque_view* macdrv_view; typedef struct macdrv_opaque_opengl_context* macdrv_opengl_context; struct macdrv_event; +struct macdrv_query; struct macdrv_display { CGDirectDisplayID displayID; @@ -145,6 +146,7 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display, MOUSE_MOVED, MOUSE_MOVED_ABSOLUTE, MOUSE_SCROLL, + QUERY_EVENT, WINDOW_CLOSE_REQUESTED, WINDOW_DID_MINIMIZE, WINDOW_DID_UNMINIMIZE, @@ -192,6 +194,9 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display, int y; unsigned long time_ms; } mouse_scroll; + struct { + struct macdrv_query *query; + } query_event; struct { CGRect frame; } window_frame_changed; @@ -202,12 +207,26 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display, }; } macdrv_event; +enum { + NUM_QUERY_TYPES +}; + +typedef struct macdrv_query { + int refs; + int type; + macdrv_window window; + int status; + int done; +} macdrv_query; + static inline macdrv_event_mask event_mask_for_type(int type) { return ((macdrv_event_mask)1 << type); } -extern macdrv_event_queue macdrv_create_event_queue(void) DECLSPEC_HIDDEN; +typedef void (*macdrv_event_handler)(macdrv_event *event); + +extern macdrv_event_queue macdrv_create_event_queue(macdrv_event_handler handler) DECLSPEC_HIDDEN; extern void macdrv_destroy_event_queue(macdrv_event_queue queue) DECLSPEC_HIDDEN; extern int macdrv_get_event_queue_fd(macdrv_event_queue queue) DECLSPEC_HIDDEN; @@ -215,6 +234,11 @@ extern int macdrv_get_event_from_queue(macdrv_event_queue queue, macdrv_event_mask mask, macdrv_event *event) DECLSPEC_HIDDEN; extern void macdrv_cleanup_event(macdrv_event *event) DECLSPEC_HIDDEN; +extern macdrv_query* macdrv_create_query(void) DECLSPEC_HIDDEN; +extern macdrv_query* macdrv_retain_query(macdrv_query *query) DECLSPEC_HIDDEN; +extern void macdrv_release_query(macdrv_query *query) DECLSPEC_HIDDEN; +extern void macdrv_set_query_done(macdrv_query *query) DECLSPEC_HIDDEN; + /* window */ struct macdrv_window_features { diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c index 3161659e5c5..10c54fa7f6c 100644 --- a/dlls/winemac.drv/macdrv_main.c +++ b/dlls/winemac.drv/macdrv_main.c @@ -157,7 +157,7 @@ struct macdrv_thread_data *macdrv_init_thread_data(void) ExitProcess(1); } - if (!(data->queue = macdrv_create_event_queue())) + if (!(data->queue = macdrv_create_event_queue(macdrv_handle_event))) { ERR("macdrv: Can't create event queue.\n"); ExitProcess(1);