winemac: Implement a WINDOW_GOT_FOCUS event for when Cocoa tries to focus a window.

This commit is contained in:
Ken Thomases 2013-01-27 16:19:48 -06:00 committed by Alexandre Julliard
parent beccf0f8c0
commit 7863a230ec
9 changed files with 162 additions and 1 deletions

View File

@ -27,6 +27,7 @@
@class WineEventQueue;
@class WineWindow;
@interface WineApplication : NSApplication <NSApplicationDelegate>
@ -37,6 +38,8 @@ @interface WineApplication : NSApplication <NSApplicationDelegate>
NSTimeInterval eventTimeAdjustment;
NSMutableArray* keyWindows;
NSMutableSet* triedWindows;
unsigned long windowFocusSerial;
}
- (void) transformProcessToForeground;
@ -47,6 +50,8 @@ - (void) unregisterEventQueue:(WineEventQueue*)queue;
- (void) computeEventTimeAdjustmentFromTicks:(unsigned long long)tickcount uptime:(uint64_t)uptime_ns;
- (double) ticksForEventTime:(NSTimeInterval)eventTime;
- (void) windowGotFocus:(WineWindow*)window;
@end
void OnMainThread(dispatch_block_t block);

View File

@ -19,6 +19,8 @@
*/
#import "cocoa_app.h"
#import "cocoa_event.h"
#import "cocoa_window.h"
int macdrv_err_on;
@ -121,10 +123,65 @@ - (double) ticksForEventTime:(NSTimeInterval)eventTime
return (eventTime + eventTimeAdjustment) * 1000;
}
/* Invalidate old focus offers across all queues. */
- (void) invalidateGotFocusEvents
{
WineEventQueue* queue;
windowFocusSerial++;
[eventQueuesLock lock];
for (queue in eventQueues)
{
[queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS)
forWindow:nil];
}
[eventQueuesLock unlock];
}
- (void) windowGotFocus:(WineWindow*)window
{
macdrv_event event;
[NSApp invalidateGotFocusEvents];
event.type = WINDOW_GOT_FOCUS;
event.window = (macdrv_window)[window retain];
event.window_got_focus.serial = windowFocusSerial;
if (triedWindows)
event.window_got_focus.tried_windows = [triedWindows retain];
else
event.window_got_focus.tried_windows = [[NSMutableSet alloc] init];
[window.queue postEvent:&event];
}
- (void) windowRejectedFocusEvent:(const macdrv_event*)event
{
if (event->window_got_focus.serial == windowFocusSerial)
{
triedWindows = (NSMutableSet*)event->window_got_focus.tried_windows;
[triedWindows addObject:(WineWindow*)event->window];
for (NSWindow* window in [keyWindows arrayByAddingObjectsFromArray:[self orderedWindows]])
{
if (![triedWindows containsObject:window] && [window canBecomeKeyWindow])
{
[window makeKeyWindow];
break;
}
}
triedWindows = nil;
}
}
/*
* ---------- NSApplicationDelegate methods ----------
*/
- (void)applicationDidResignActive:(NSNotification *)notification
{
[self invalidateGotFocusEvents];
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
@ -189,3 +246,16 @@ void LogErrorv(const char* func, NSString* format, va_list args)
fprintf(stderr, "err:%s:%s", func, [message UTF8String]);
[message release];
}
/***********************************************************************
* macdrv_window_rejected_focus
*
* Pass focus to the next window that hasn't already rejected this same
* WINDOW_GOT_FOCUS event.
*/
void macdrv_window_rejected_focus(const macdrv_event *event)
{
OnMainThread(^{
[NSApp windowRejectedFocusEvent:event];
});
}

View File

@ -275,6 +275,13 @@ void macdrv_cleanup_event(macdrv_event *event)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
switch (event->type)
{
case WINDOW_GOT_FOCUS:
[(NSMutableSet*)event->window_got_focus.tried_windows release];
break;
}
[(WineWindow*)event->window release];
[pool release];

View File

@ -49,4 +49,6 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
BOOL causing_becomeKeyWindow;
}
@property (retain, readonly, nonatomic) WineEventQueue* queue;
@end

View File

@ -67,7 +67,7 @@ @interface WineWindow ()
@property (retain, nonatomic) NSWindow* latentParentWindow;
@property (nonatomic) void* hwnd;
@property (retain, nonatomic) WineEventQueue* queue;
@property (retain, readwrite, nonatomic) WineEventQueue* queue;
@property (nonatomic) void* surface;
@property (nonatomic) pthread_mutex_t* surface_mutex;
@ -505,6 +505,29 @@ - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
return [super validateMenuItem:menuItem];
}
/* We don't call this. It's the action method of the items in the Window menu. */
- (void) makeKeyAndOrderFront:(id)sender
{
if (![self isKeyWindow] && !self.disabled && !self.noActivate)
[NSApp windowGotFocus:self];
}
- (void) sendEvent:(NSEvent*)event
{
if ([event type] == NSLeftMouseDown)
{
/* Since our windows generally claim they can't be made key, clicks
in their title bars are swallowed by the theme frame stuff. So,
we hook directly into the event stream and assume that any click
in the window will activate it, if Wine and the Win32 program
accept. */
if (![self isKeyWindow] && !self.disabled && !self.noActivate)
[NSApp windowGotFocus:self];
}
[super sendEvent:event];
}
/*
* ---------- NSResponder method overrides ----------
@ -521,6 +544,13 @@ - (void) otherMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
/*
* ---------- NSWindowDelegate methods ----------
*/
- (void)windowDidBecomeKey:(NSNotification *)notification
{
if (causing_becomeKeyWindow) return;
[NSApp windowGotFocus:self];
}
- (void)windowDidMove:(NSNotification *)notification
{
[self windowDidResize:notification];

View File

@ -35,6 +35,7 @@ static const char *dbgstr_event(int type)
"MOUSE_BUTTON",
"WINDOW_CLOSE_REQUESTED",
"WINDOW_FRAME_CHANGED",
"WINDOW_GOT_FOCUS",
};
if (0 <= type && type < NUM_EVENT_TYPES) return event_names[type];
@ -58,6 +59,7 @@ static macdrv_event_mask get_event_mask(DWORD mask)
{
event_mask |= event_mask_for_type(WINDOW_CLOSE_REQUESTED);
event_mask |= event_mask_for_type(WINDOW_FRAME_CHANGED);
event_mask |= event_mask_for_type(WINDOW_GOT_FOCUS);
}
return event_mask;
@ -90,6 +92,9 @@ void macdrv_handle_event(macdrv_event *event)
case WINDOW_FRAME_CHANGED:
macdrv_window_frame_changed(hwnd, event->window_frame_changed.frame);
break;
case WINDOW_GOT_FOCUS:
macdrv_window_got_focus(hwnd, event);
break;
default:
TRACE(" ignoring\n");
break;

View File

@ -119,6 +119,7 @@ static inline RECT rect_from_cgrect(CGRect cgrect)
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;
extern void macdrv_mouse_button(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN;

View File

@ -101,6 +101,7 @@
typedef struct macdrv_opaque_window* macdrv_window;
typedef struct macdrv_opaque_event_queue* macdrv_event_queue;
struct macdrv_event;
struct macdrv_display {
CGDirectDisplayID displayID;
@ -113,6 +114,7 @@
extern int macdrv_err_on;
extern int macdrv_start_cocoa_app(unsigned long long tickcount) DECLSPEC_HIDDEN;
extern void macdrv_window_rejected_focus(const struct macdrv_event *event) DECLSPEC_HIDDEN;
/* display */
@ -125,6 +127,7 @@
MOUSE_BUTTON,
WINDOW_CLOSE_REQUESTED,
WINDOW_FRAME_CHANGED,
WINDOW_GOT_FOCUS,
NUM_EVENT_TYPES
};
@ -144,6 +147,10 @@
struct {
CGRect frame;
} window_frame_changed;
struct {
unsigned long serial;
void *tried_windows;
} window_got_focus;
};
} macdrv_event;

View File

@ -1419,3 +1419,37 @@ done:
if (!(flags & SWP_NOSIZE) || !(flags & SWP_NOMOVE))
SetWindowPos(hwnd, 0, rect.left, rect.top, width, height, flags);
}
/***********************************************************************
* macdrv_window_got_focus
*
* Handler for WINDOW_GOT_FOCUS events.
*/
void macdrv_window_got_focus(HWND hwnd, const macdrv_event *event)
{
if (!hwnd) return;
TRACE("win %p/%p serial %lu enabled %d visible %d style %08x focus %p active %p fg %p\n",
hwnd, event->window, event->window_got_focus.serial, IsWindowEnabled(hwnd),
IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE), GetFocus(),
GetActiveWindow(), GetForegroundWindow());
if (can_activate_window(hwnd))
{
/* simulate a mouse click on the caption to find out
* whether the window wants to be activated */
LRESULT ma = SendMessageW(hwnd, WM_MOUSEACTIVATE,
(WPARAM)GetAncestor(hwnd, GA_ROOT),
MAKELONG(HTCAPTION,WM_LBUTTONDOWN));
if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
{
TRACE("setting foreground window to %p\n", hwnd);
SetForegroundWindow(hwnd);
return;
}
}
TRACE("win %p/%p rejecting focus\n", hwnd, event->window);
macdrv_window_rejected_focus(event);
}