From 7863a230ec8412506d3256c20a1940d9bddb9776 Mon Sep 17 00:00:00 2001 From: Ken Thomases Date: Sun, 27 Jan 2013 16:19:48 -0600 Subject: [PATCH] winemac: Implement a WINDOW_GOT_FOCUS event for when Cocoa tries to focus a window. --- dlls/winemac.drv/cocoa_app.h | 5 +++ dlls/winemac.drv/cocoa_app.m | 70 +++++++++++++++++++++++++++++++++ dlls/winemac.drv/cocoa_event.m | 7 ++++ dlls/winemac.drv/cocoa_window.h | 2 + dlls/winemac.drv/cocoa_window.m | 32 ++++++++++++++- dlls/winemac.drv/event.c | 5 +++ dlls/winemac.drv/macdrv.h | 1 + dlls/winemac.drv/macdrv_cocoa.h | 7 ++++ dlls/winemac.drv/window.c | 34 ++++++++++++++++ 9 files changed, 162 insertions(+), 1 deletion(-) diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h index 40f9e9db86c..1b254ee694e 100644 --- a/dlls/winemac.drv/cocoa_app.h +++ b/dlls/winemac.drv/cocoa_app.h @@ -27,6 +27,7 @@ @class WineEventQueue; +@class WineWindow; @interface WineApplication : NSApplication @@ -37,6 +38,8 @@ @interface WineApplication : NSApplication 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); diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index c69f09ed21f..54a6d50bfe1 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -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]; + }); +} diff --git a/dlls/winemac.drv/cocoa_event.m b/dlls/winemac.drv/cocoa_event.m index e9dc2a3806b..d5e927d7b8e 100644 --- a/dlls/winemac.drv/cocoa_event.m +++ b/dlls/winemac.drv/cocoa_event.m @@ -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]; diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h index 7708fa64f77..62ae75a6083 100644 --- a/dlls/winemac.drv/cocoa_window.h +++ b/dlls/winemac.drv/cocoa_window.h @@ -49,4 +49,6 @@ @interface WineWindow : NSPanel BOOL causing_becomeKeyWindow; } +@property (retain, readonly, nonatomic) WineEventQueue* queue; + @end diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index c1f90d2806e..a6e6856df1c 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -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]; diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index 4d350820a0c..969c5e76a30 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -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; diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 378b4ab7fdf..0be4b593895 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -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; diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index 4b7de10cf65..35229ba1a17 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -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; diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c index 74894e9bcb9..c87897a227b 100644 --- a/dlls/winemac.drv/window.c +++ b/dlls/winemac.drv/window.c @@ -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); +}