winemac: Add support for "query" events which wait for synchronous responses.

This commit is contained in:
Ken Thomases 2013-03-10 22:58:26 -05:00 committed by Alexandre Julliard
parent bf824ed38c
commit 69e631e04e
10 changed files with 314 additions and 20 deletions

View File

@ -32,6 +32,10 @@
@interface WineApplication : NSApplication <NSApplicationDelegate> @interface WineApplication : NSApplication <NSApplicationDelegate>
{ {
CFRunLoopSourceRef requestSource;
NSMutableArray* requests;
dispatch_queue_t requestsManipQueue;
NSMutableArray* eventQueues; NSMutableArray* eventQueues;
NSLock* eventQueuesLock; NSLock* eventQueuesLock;
@ -83,6 +87,8 @@ - (double) ticksForEventTime:(NSTimeInterval)eventTime;
- (void) windowGotFocus:(WineWindow*)window; - (void) windowGotFocus:(WineWindow*)window;
- (BOOL) waitUntilQueryDone:(int*)done timeout:(NSDate*)timeout;
- (void) keyboardSelectionDidChange; - (void) keyboardSelectionDidChange;
- (void) flipRect:(NSRect*)rect; - (void) flipRect:(NSRect*)rect;
@ -93,7 +99,6 @@ - (void) wineWindow:(WineWindow*)window
@end @end
void OnMainThread(dispatch_block_t block);
void OnMainThreadAsync(dispatch_block_t block); void OnMainThreadAsync(dispatch_block_t block);
void LogError(const char* func, NSString* format, ...); void LogError(const char* func, NSString* format, ...);

View File

@ -26,6 +26,9 @@
#import "cocoa_window.h" #import "cocoa_window.h"
static NSString* const WineAppWaitQueryResponseMode = @"WineAppWaitQueryResponseMode";
int macdrv_err_on; int macdrv_err_on;
@ -56,6 +59,8 @@ @interface WineApplication ()
@property (copy, nonatomic) NSArray* cursorFrames; @property (copy, nonatomic) NSArray* cursorFrames;
@property (retain, nonatomic) NSTimer* cursorTimer; @property (retain, nonatomic) NSTimer* cursorTimer;
static void PerformRequest(void *info);
@end @end
@ -70,6 +75,20 @@ - (id) init
self = [super init]; self = [super init];
if (self != nil) 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]; eventQueues = [[NSMutableArray alloc] init];
eventQueuesLock = [[NSLock alloc] init]; eventQueuesLock = [[NSLock alloc] init];
@ -80,8 +99,8 @@ - (id) init
warpRecords = [[NSMutableArray alloc] init]; warpRecords = [[NSMutableArray alloc] init];
if (!eventQueues || !eventQueuesLock || !keyWindows || !orderedWineWindows || if (!requests || !requestsManipQueue || !eventQueues || !eventQueuesLock ||
!originalDisplayModes || !warpRecords) !keyWindows || !orderedWineWindows || !originalDisplayModes || !warpRecords)
{ {
[self release]; [self release];
return nil; return nil;
@ -100,6 +119,13 @@ - (void) dealloc
[keyWindows release]; [keyWindows release];
[eventQueues release]; [eventQueues release];
[eventQueuesLock release]; [eventQueuesLock release];
if (requestsManipQueue) dispatch_release(requestsManipQueue);
[requests release];
if (requestSource)
{
CFRunLoopSourceInvalidate(requestSource);
CFRelease(requestSource);
}
[super dealloc]; [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 - (BOOL) registerEventQueue:(WineEventQueue*)queue
{ {
[eventQueuesLock lock]; [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) 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 * LogError
*/ */

View File

@ -20,6 +20,7 @@
#include "macdrv_cocoa.h" #include "macdrv_cocoa.h"
#import "cocoa_app.h" #import "cocoa_app.h"
#import "cocoa_event.h"
static int owned_change_count = -1; static int owned_change_count = -1;

View File

@ -28,9 +28,16 @@ @interface WineEventQueue : NSObject
NSLock* eventsLock; NSLock* eventsLock;
int fds[2]; /* Pipe signaling when there are events queued. */ 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) postEvent:(const macdrv_event*)inEvent;
- (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window; - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window;
- (BOOL) query:(macdrv_query*)query timeout:(NSTimeInterval)timeout;
@end @end
void OnMainThread(dispatch_block_t block);

View File

@ -29,6 +29,9 @@
#import "cocoa_window.h" #import "cocoa_window.h"
static NSString* const WineEventQueueThreadDictionaryKey = @"WineEventQueueThreadDictionaryKey";
@interface MacDrvEvent : NSObject @interface MacDrvEvent : NSObject
{ {
@public @public
@ -58,11 +61,24 @@ @implementation WineEventQueue
- (id) init - (id) init
{ {
[self doesNotRecognizeSelector:_cmd];
[self release];
return nil;
}
- (id) initWithEventHandler:(macdrv_event_handler)handler
{
NSParameterAssert(handler != nil);
self = [super init]; self = [super init];
if (self != nil) 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]; events = [[NSMutableArray alloc] init];
eventsLock = [[NSLock alloc] init]; eventsLock = [[NSLock alloc] init];
@ -81,6 +97,24 @@ - (id) init
[self release]; [self release];
return nil; 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; return self;
} }
@ -90,6 +124,7 @@ - (void) dealloc
[events release]; [events release];
[eventsLock release]; [eventsLock release];
if (kq != -1) close(kq);
if (fds[0] != -1) close(fds[0]); if (fds[0] != -1) close(fds[0]);
if (fds[1] != -1) close(fds[1]); if (fds[1] != -1) close(fds[1]);
@ -217,6 +252,69 @@ - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)
[eventsLock unlock]; [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 * 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 * 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. * 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]; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
WineEventQueue* queue = [[WineEventQueue alloc] init]; WineEventQueue* queue = [threadDict objectForKey:WineEventQueueThreadDictionaryKey];
if (queue && ![NSApp registerEventQueue:queue]) if (!queue)
{ {
[queue release]; queue = [[[WineEventQueue alloc] initWithEventHandler:handler] autorelease];
queue = nil; if (queue)
{
if ([NSApp registerEventQueue:queue])
[threadDict setObject:queue forKey:WineEventQueueThreadDictionaryKey];
else
queue = nil;
}
} }
[pool release]; [pool release];
@ -249,9 +354,10 @@ void macdrv_destroy_event_queue(macdrv_event_queue queue)
{ {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
WineEventQueue* q = (WineEventQueue*)queue; WineEventQueue* q = (WineEventQueue*)queue;
NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
[NSApp unregisterEventQueue:q]; [NSApp unregisterEventQueue:q];
[q release]; [threadDict removeObjectForKey:WineEventQueueThreadDictionaryKey];
[pool release]; [pool release];
} }
@ -308,6 +414,9 @@ void macdrv_cleanup_event(macdrv_event *event)
case KEYBOARD_CHANGED: case KEYBOARD_CHANGED:
CFRelease(event->keyboard_changed.uchr); CFRelease(event->keyboard_changed.uchr);
break; break;
case QUERY_EVENT:
macdrv_release_query(event->query_event.query);
break;
case WINDOW_GOT_FOCUS: case WINDOW_GOT_FOCUS:
[(NSMutableSet*)event->window_got_focus.tried_windows release]; [(NSMutableSet*)event->window_got_focus.tried_windows release];
break; break;
@ -318,4 +427,50 @@ void macdrv_cleanup_event(macdrv_event *event)
[pool release]; [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 @end

View File

@ -21,7 +21,7 @@
#import "cocoa_opengl.h" #import "cocoa_opengl.h"
#include "macdrv_cocoa.h" #include "macdrv_cocoa.h"
#include "cocoa_app.h" #include "cocoa_event.h"
@interface WineOpenGLContext () @interface WineOpenGLContext ()

View File

@ -41,6 +41,7 @@ static const char *dbgstr_event(int type)
"MOUSE_MOVED", "MOUSE_MOVED",
"MOUSE_MOVED_ABSOLUTE", "MOUSE_MOVED_ABSOLUTE",
"MOUSE_SCROLL", "MOUSE_SCROLL",
"QUERY_EVENT",
"WINDOW_CLOSE_REQUESTED", "WINDOW_CLOSE_REQUESTED",
"WINDOW_DID_MINIMIZE", "WINDOW_DID_MINIMIZE",
"WINDOW_DID_UNMINIMIZE", "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); event_mask |= event_mask_for_type(WINDOW_LOST_FOCUS);
} }
if (mask & QS_SENDMESSAGE)
{
event_mask |= event_mask_for_type(QUERY_EVENT);
}
return event_mask; 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 * macdrv_handle_event
*/ */
@ -138,6 +167,9 @@ void macdrv_handle_event(macdrv_event *event)
case MOUSE_SCROLL: case MOUSE_SCROLL:
macdrv_mouse_scroll(hwnd, event); macdrv_mouse_scroll(hwnd, event);
break; break;
case QUERY_EVENT:
macdrv_query_event(hwnd, event);
break;
case WINDOW_CLOSE_REQUESTED: case WINDOW_CLOSE_REQUESTED:
macdrv_window_close_requested(hwnd); macdrv_window_close_requested(hwnd);
break; break;

View File

@ -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_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 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_close_requested(HWND hwnd) DECLSPEC_HIDDEN;
extern void macdrv_window_frame_changed(HWND hwnd, CGRect frame) 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; extern void macdrv_window_got_focus(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN;

View File

@ -104,6 +104,7 @@
typedef struct macdrv_opaque_view* macdrv_view; typedef struct macdrv_opaque_view* macdrv_view;
typedef struct macdrv_opaque_opengl_context* macdrv_opengl_context; typedef struct macdrv_opaque_opengl_context* macdrv_opengl_context;
struct macdrv_event; struct macdrv_event;
struct macdrv_query;
struct macdrv_display { struct macdrv_display {
CGDirectDisplayID displayID; CGDirectDisplayID displayID;
@ -145,6 +146,7 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display,
MOUSE_MOVED, MOUSE_MOVED,
MOUSE_MOVED_ABSOLUTE, MOUSE_MOVED_ABSOLUTE,
MOUSE_SCROLL, MOUSE_SCROLL,
QUERY_EVENT,
WINDOW_CLOSE_REQUESTED, WINDOW_CLOSE_REQUESTED,
WINDOW_DID_MINIMIZE, WINDOW_DID_MINIMIZE,
WINDOW_DID_UNMINIMIZE, WINDOW_DID_UNMINIMIZE,
@ -192,6 +194,9 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display,
int y; int y;
unsigned long time_ms; unsigned long time_ms;
} mouse_scroll; } mouse_scroll;
struct {
struct macdrv_query *query;
} query_event;
struct { struct {
CGRect frame; CGRect frame;
} window_frame_changed; } window_frame_changed;
@ -202,12 +207,26 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display,
}; };
} macdrv_event; } 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) static inline macdrv_event_mask event_mask_for_type(int type)
{ {
return ((macdrv_event_mask)1 << 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 void macdrv_destroy_event_queue(macdrv_event_queue queue) DECLSPEC_HIDDEN;
extern int macdrv_get_event_queue_fd(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; macdrv_event_mask mask, macdrv_event *event) DECLSPEC_HIDDEN;
extern void macdrv_cleanup_event(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 */ /* window */
struct macdrv_window_features { struct macdrv_window_features {

View File

@ -157,7 +157,7 @@ struct macdrv_thread_data *macdrv_init_thread_data(void)
ExitProcess(1); 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"); ERR("macdrv: Can't create event queue.\n");
ExitProcess(1); ExitProcess(1);