diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 4ac03a77a2d..35488b05e63 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -150,6 +150,11 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif } +@interface NSWindow (WineAccessPrivateMethods) + - (id) _displayChanged; +@end + + @interface WineContentView : NSView { NSMutableArray* glContexts; @@ -1593,6 +1598,38 @@ - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen return frameRect; } + // This private method of NSWindow is called as Cocoa reacts to the display + // configuration changing. Among other things, it adjusts the window's + // frame based on how the screen(s) changed size. That tells Wine that the + // window has been moved. We don't want that. Rather, we want to make + // sure that the WinAPI notion of the window position is maintained/ + // restored, possibly undoing or overriding Cocoa's adjustment. + // + // So, we queue a REASSERT_WINDOW_POSITION event to the back end before + // Cocoa has a chance to adjust the frame, thus preceding any resulting + // WINDOW_FRAME_CHANGED event that may get queued. The back end will + // reassert its notion of the position. That call won't get processed + // until after this method returns, so it will override whatever this + // method does to the window position. It will also discard any pending + // WINDOW_FRAME_CHANGED events. + // + // Unfortunately, the only way I've found to know when Cocoa is _about to_ + // adjust the window's position due to a display change is to hook into + // this private method. This private method has remained stable from 10.6 + // through 10.11. If it does change, the most likely thing is that it + // will be removed and no longer called and this fix will simply stop + // working. The only real danger would be if Apple changed the return type + // to a struct or floating-point type, which would change the calling + // convention. + - (id) _displayChanged + { + macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self); + [queue postEvent:event]; + macdrv_release_event(event); + + return [super _displayChanged]; + } + - (BOOL) isExcludedFromWindowsMenu { return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle); diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index 58b507c51d1..785bc0af6d7 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -45,6 +45,7 @@ static const char *dbgstr_event(int type) "MOUSE_MOVED_ABSOLUTE", "MOUSE_SCROLL", "QUERY_EVENT", + "REASSERT_WINDOW_POSITION", "RELEASE_CAPTURE", "STATUS_ITEM_MOUSE_BUTTON", "STATUS_ITEM_MOUSE_MOVE", @@ -115,6 +116,7 @@ static macdrv_event_mask get_event_mask(DWORD mask) if (mask & QS_SENDMESSAGE) { event_mask |= event_mask_for_type(QUERY_EVENT); + event_mask |= event_mask_for_type(REASSERT_WINDOW_POSITION); event_mask |= event_mask_for_type(RELEASE_CAPTURE); event_mask |= event_mask_for_type(WINDOW_BROUGHT_FORWARD); event_mask |= event_mask_for_type(WINDOW_CLOSE_REQUESTED); @@ -237,6 +239,9 @@ void macdrv_handle_event(const macdrv_event *event) case QUERY_EVENT: macdrv_query_event(hwnd, event); break; + case REASSERT_WINDOW_POSITION: + macdrv_reassert_window_position(hwnd); + break; case RELEASE_CAPTURE: macdrv_release_capture(hwnd, event); break; diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index a31d7e32562..44b79ac0992 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -175,6 +175,7 @@ static inline RECT rect_from_cgrect(CGRect cgrect) extern void macdrv_window_restore_requested(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN; extern void macdrv_window_drag_begin(HWND hwnd) DECLSPEC_HIDDEN; extern void macdrv_window_drag_end(HWND hwnd) DECLSPEC_HIDDEN; +extern void macdrv_reassert_window_position(HWND hwnd) DECLSPEC_HIDDEN; extern BOOL query_resize_size(HWND hwnd, macdrv_query *query) DECLSPEC_HIDDEN; extern BOOL query_resize_start(HWND hwnd) DECLSPEC_HIDDEN; extern BOOL query_min_max_info(HWND hwnd) DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index d5e3f4669a6..2385097483f 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -195,6 +195,7 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display, MOUSE_MOVED_ABSOLUTE, MOUSE_SCROLL, QUERY_EVENT, + REASSERT_WINDOW_POSITION, RELEASE_CAPTURE, STATUS_ITEM_MOUSE_BUTTON, STATUS_ITEM_MOUSE_MOVE, diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c index 9d996ecd64a..a48ac17fb85 100644 --- a/dlls/winemac.drv/window.c +++ b/dlls/winemac.drv/window.c @@ -1682,12 +1682,7 @@ LRESULT CDECL macdrv_WindowMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) macdrv_reset_device_metrics(); return 0; case WM_MACDRV_DISPLAYCHANGE: - if ((data = get_win_data(hwnd))) - { - if (data->cocoa_window && data->on_screen) - sync_window_position(data, SWP_NOZORDER | SWP_NOACTIVATE, NULL, NULL); - release_win_data(data); - } + macdrv_reassert_window_position(hwnd); SendMessageW(hwnd, WM_DISPLAYCHANGE, wp, lp); return 0; case WM_MACDRV_ACTIVATE_ON_FOLLOWING_FOCUS: @@ -2244,6 +2239,23 @@ void macdrv_window_drag_end(HWND hwnd) } +/*********************************************************************** + * macdrv_reassert_window_position + * + * Handler for REASSERT_WINDOW_POSITION events. + */ +void macdrv_reassert_window_position(HWND hwnd) +{ + struct macdrv_win_data *data = get_win_data(hwnd); + if (data) + { + if (data->cocoa_window && data->on_screen) + sync_window_position(data, SWP_NOZORDER | SWP_NOACTIVATE, NULL, NULL); + release_win_data(data); + } +} + + struct quit_info { HWND *wins; UINT capacity;