Commit 69e631e0 authored by Ken Thomases's avatar Ken Thomases Committed by Alexandre Julliard

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

parent bf824ed3
......@@ -32,6 +32,10 @@
@interface WineApplication : NSApplication <NSApplicationDelegate>
{
CFRunLoopSourceRef requestSource;
NSMutableArray* requests;
dispatch_queue_t requestsManipQueue;
NSMutableArray* eventQueues;
NSLock* eventQueuesLock;
......@@ -83,6 +87,8 @@
- (void) windowGotFocus:(WineWindow*)window;
- (BOOL) waitUntilQueryDone:(int*)done timeout:(NSDate*)timeout;
- (void) keyboardSelectionDidChange;
- (void) flipRect:(NSRect*)rect;
......@@ -93,7 +99,6 @@
@end
void OnMainThread(dispatch_block_t block);
void OnMainThreadAsync(dispatch_block_t block);
void LogError(const char* func, NSString* format, ...);
......
......@@ -26,6 +26,9 @@
#import "cocoa_window.h"
static NSString* const WineAppWaitQueryResponseMode = @"WineAppWaitQueryResponseMode";
int macdrv_err_on;
......@@ -56,6 +59,8 @@ int macdrv_err_on;
@property (copy, nonatomic) NSArray* cursorFrames;
@property (retain, nonatomic) NSTimer* cursorTimer;
static void PerformRequest(void *info);
@end
......@@ -70,6 +75,20 @@ int macdrv_err_on;
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 @@ int macdrv_err_on;
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 @@ int macdrv_err_on;
[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 @@ int macdrv_err_on;
}
}
- (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 @@ int macdrv_err_on;
}];
}
@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
*/
......
......@@ -20,6 +20,7 @@
#include "macdrv_cocoa.h"
#import "cocoa_app.h"
#import "cocoa_event.h"
static int owned_change_count = -1;
......
......@@ -28,9 +28,16 @@
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);
......@@ -29,6 +29,9 @@
#import "cocoa_window.h"
static NSString* const WineEventQueueThreadDictionaryKey = @"WineEventQueueThreadDictionaryKey";
@interface MacDrvEvent : NSObject
{
@public
......@@ -58,11 +61,24 @@
- (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 @@
[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 @@
[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 @@
[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 @@
* 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
......@@ -21,7 +21,7 @@
#import "cocoa_opengl.h"
#include "macdrv_cocoa.h"
#include "cocoa_app.h"
#include "cocoa_event.h"
@interface WineOpenGLContext ()
......
......@@ -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,11 +95,39 @@ 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
*/
void macdrv_handle_event(macdrv_event *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;
......
......@@ -138,6 +138,8 @@ extern struct window_surface *create_surface(macdrv_window window, const RECT *r
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;
......
......@@ -104,6 +104,7 @@ typedef struct macdrv_opaque_event_queue* macdrv_event_queue;
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 @@ enum {
MOUSE_MOVED,
MOUSE_MOVED_ABSOLUTE,
MOUSE_SCROLL,
QUERY_EVENT,
WINDOW_CLOSE_REQUESTED,
WINDOW_DID_MINIMIZE,
WINDOW_DID_UNMINIMIZE,
......@@ -193,6 +195,9 @@ typedef struct macdrv_event {
unsigned long time_ms;
} mouse_scroll;
struct {
struct macdrv_query *query;
} query_event;
struct {
CGRect frame;
} window_frame_changed;
struct {
......@@ -202,12 +207,26 @@ typedef struct 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)
{
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 {
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment