Ken Thomases : winemac: Queue an event to reassert the WinAPI window position before Cocoa adjusts its position for a display change.

Alexandre Julliard julliard at wine.codeweavers.com
Tue Oct 6 09:43:59 CDT 2015


Module: wine
Branch: master
Commit: 9372af77a546609b703070d60ef773d5ae7c2ddd
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=9372af77a546609b703070d60ef773d5ae7c2ddd

Author: Ken Thomases <ken at codeweavers.com>
Date:   Mon Oct  5 15:44:28 2015 -0500

winemac: Queue an event to reassert the WinAPI window position before Cocoa adjusts its position for a display change.

When the display mode changes such that the screen height changes, we'd like
our windows to keep their position relative to the top-left of the primary
screen.  That's how WinAPI's coordinate system works and we want the WinAPI
position of the window to not change just because the display mode changed.

Unfortunately that's not achievable in Cocoa.  Cocoa keeps the window
stationary relative to the screen it's on, not necessarily the primary screen,
and it's sometimes relative to the bottom-left and sometimes the top-left of
that screen.

So, what we do instead is queue an event to get the back end to reassert the
WinAPI position of the window.  This is queued before Cocoa can adjust the
Cocoa position of the window which would queue a WINDOW_FRAME_CHANGED to the
back end and mess up the WinAPI position.  The back end's reassertion of the
WinAPI position won't be processed by the Cocoa thread until after Cocoa has
adjusted the position and will thus override it.  It will also discard any
wrong WINDOW_FRAME_CHANGED that may have been queued.

Signed-off-by: Ken Thomases <ken at codeweavers.com>

---

 dlls/winemac.drv/cocoa_window.m | 37 +++++++++++++++++++++++++++++++++++++
 dlls/winemac.drv/event.c        |  5 +++++
 dlls/winemac.drv/macdrv.h       |  1 +
 dlls/winemac.drv/macdrv_cocoa.h |  1 +
 dlls/winemac.drv/window.c       | 24 ++++++++++++++++++------
 5 files changed, 62 insertions(+), 6 deletions(-)

diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m
index 4ac03a7..35488b0 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
 }
 
 
+ at interface NSWindow (WineAccessPrivateMethods)
+    - (id) _displayChanged;
+ at end
+
+
 @interface WineContentView : NSView <NSTextInputClient>
 {
     NSMutableArray* glContexts;
@@ -1593,6 +1598,38 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
         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 58b507c..785bc0a 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 a31d7e3..44b79ac 100644
--- a/dlls/winemac.drv/macdrv.h
+++ b/dlls/winemac.drv/macdrv.h
@@ -175,6 +175,7 @@ extern void macdrv_window_resize_ended(HWND hwnd) DECLSPEC_HIDDEN;
 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 d5e3f46..2385097 100644
--- a/dlls/winemac.drv/macdrv_cocoa.h
+++ b/dlls/winemac.drv/macdrv_cocoa.h
@@ -195,6 +195,7 @@ enum {
     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 9d996ec..a48ac17 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;




More information about the wine-cvs mailing list