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>
{
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, ...);

View File

@ -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
*/

View File

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

View File

@ -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);

View File

@ -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

View File

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

View File

@ -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;

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_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;

View File

@ -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 {

View File

@ -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);