< prev index next >

src/java.desktop/windows/native/libawt/windows/awt_Window.cpp

Print this page
rev 60071 : 8211999: Window positioning bugs due to overlapping GraphicsDevice bounds (Windows/HiDPI)
Reviewed-by: XXX

@@ -151,21 +151,10 @@
 struct SetFullScreenExclusiveModeStateStruct {
     jobject window;
     jboolean isFSEMState;
 };
 
-// struct for _WindowDPIChange() method
-struct ScaleStruct {
-    jobject window;
-    jint prevScreen;
-    jfloat prevScaleX;
-    jfloat prevScaleY;
-    jint screen;
-    jfloat scaleX;
-    jfloat scaleY;
-};
-
 struct OverrideHandle {
     jobject frame;
     HWND handle;
 };
 

@@ -177,14 +166,10 @@
 jfieldID AwtWindow::locationByPlatformID;
 jfieldID AwtWindow::autoRequestFocusID;
 jfieldID AwtWindow::securityWarningWidthID;
 jfieldID AwtWindow::securityWarningHeightID;
 
-jfieldID AwtWindow::sysXID;
-jfieldID AwtWindow::sysYID;
-jfieldID AwtWindow::sysWID;
-jfieldID AwtWindow::sysHID;
 jfieldID AwtWindow::windowTypeID;
 
 jmethodID AwtWindow::getWarningStringMID;
 jmethodID AwtWindow::calculateSecurityWarningPositionMID;
 jmethodID AwtWindow::windowTypeNameMID;

@@ -1125,40 +1110,33 @@
                 window->InitOwner(awtParent);
             } else {
                 // specify WS_EX_TOOLWINDOW to remove parentless windows from taskbar
                 exStyle |= WS_EX_TOOLWINDOW;
             }
+            jint x = env->GetIntField(target, AwtComponent::xID);

+            jint y = env->GetIntField(target, AwtComponent::yID);

+            jint width = env->GetIntField(target, AwtComponent::widthID);

+            jint height = env->GetIntField(target, AwtComponent::heightID);

+

             window->CreateHWnd(env, L"",
                                style, exStyle,
-                               0, 0, 0, 0,
+                               x, y, width, height,

                                (awtParent != NULL) ? awtParent->GetHWnd() : NULL,
                                NULL,
                                ::GetSysColor(COLOR_WINDOWTEXT),
                                ::GetSysColor(COLOR_WINDOW),
                                self);
-
-            jint x = env->GetIntField(target, AwtComponent::xID);
-            jint y = env->GetIntField(target, AwtComponent::yID);
-            jint width = env->GetIntField(target, AwtComponent::widthID);
-            jint height = env->GetIntField(target, AwtComponent::heightID);
-
             /*
              * Initialize icon as inherited from parent if it exists
              */
             if (parent != NULL) {
                 window->m_hIcon = awtParent->GetHIcon();
                 window->m_hIconSm = awtParent->GetHIconSm();
                 window->m_iconInherited = TRUE;
             }
             window->DoUpdateIcon();
-
-
-            /*
-             * Reshape here instead of during create, so that a WM_NCCALCSIZE
-             * is sent.
-             */
-            window->Reshape(x, y, width, height);
+            window->RecalcNonClient();

         }
     } catch (...) {
         env->DeleteLocalRef(target);
         throw;
     }

@@ -1212,10 +1190,51 @@
     }
     VERIFY(::DestroyWindow(boggy));
     VERIFY(::SetWindowPos(GetHWnd(), NULL, defLoc.left, defLoc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER));
 }
 
+/**

+ * Override AwtComponent::Reshape() to handle absolute screen coordinates used

+ * by the top-level windows.

+ */

+void AwtWindow::Reshape(int x, int y, int w, int h) {

+    if (IsEmbeddedFrame()) {

+        // Not the "real" top level window

+        return AwtComponent::Reshape(x, y, w, h);

+    }

+    // Yes, use x,y in user's space to find the nearest monitor in device space.

+    POINT pt = {x + w / 2, y + h / 2};

+    Devices::InstanceAccess devices;

+    HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);

+    int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(monitor);

+    AwtWin32GraphicsDevice *device = devices->GetDevice(screen);

+    // Try set the correct size and jump to the correct location, even if it is

+    // on the different monitor. Note that for the "size" we use the current

+    // monitor, so the WM_DPICHANGED will adjust it for the "target" monitor.

+    ReshapeNoScale(device->ScaleUpAbsX(x), device->ScaleUpAbsY(y),

+                   ScaleUpX(w), ScaleUpY(h));

+    // The window manager may tweak the size for different reasons, so try

+    // to make sure our window has the correct size in the user's space.

+    // NOOP if the size was changed already or changing is in progress.

+    RECT rc;

+    ::GetWindowRect(GetHWnd(), &rc);

+    ReshapeNoScale(rc.left, rc.top, ScaleUpX(w), ScaleUpY(h));

+    // the window manager may ignore our "SetWindowPos" request, in this,

+    // case the WmMove/WmSize will not come and we need to manually resync

+    // the "java.awt.Window" locations, because "java.awt.Window" already

+    // uses location ignored by the window manager.

+    ::GetWindowRect(GetHWnd(), &rc);

+    if (x != ScaleDownAbsX(rc.left) || y != ScaleDownAbsY(rc.top)) {

+        WmMove(rc.left, rc.top);

+    }

+    int userW = ScaleDownX(rc.right - rc.left);

+    int userH = ScaleDownY(rc.bottom - rc.top);

+    if (w != userW || h != userH) {

+        WmSize(SIZENORMAL, rc.right - rc.left, rc.bottom - rc.top);

+    }

+}

+

 void AwtWindow::Show()
 {
     m_visible = true;
     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
     BOOL  done = false;

@@ -1761,10 +1780,19 @@
         }
     }
     return AwtCanvas::WmShowWindow(show, status);
 }
 
+void AwtWindow::WmDPIChanged(const LPARAM &lParam) {

+    // need to update the scales now, otherwise the ReshapeNoScale() will

+    // calculate the bounds wrongly

+    AwtWin32GraphicsDevice::ResetAllDesktopScales();

+    RECT *r = (RECT *) lParam;

+    ReshapeNoScale(r->left, r->top, r->right - r->left, r->bottom - r->top);

+    CheckIfOnNewScreen(true);

+}

+

 /*
  * Override AwtComponent's move handling to first update the
  * java AWT target's position fields directly, since Windows
  * and below can be resized from outside of java (by user)
  */

@@ -1776,34 +1804,25 @@
     // it's target's position since minimized Win32 windows
     // move to -32000, -32000 for whatever reason
     // NOTE: See also AwtWindow::Reshape
         return mrDoDefault;
     }
-
-    if (m_screenNum == -1) {
-    // Set initial value
-        m_screenNum = GetScreenImOn();
-    }
-    else {
-        CheckIfOnNewScreen();
-    }
+    // Check for the new screen and update the java peer

+    CheckIfOnNewScreen(false); // postpone if different DPI

 
     /* Update the java AWT target component's fields directly */
     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
     if (env->EnsureLocalCapacity(1) < 0) {
         return mrConsume;
     }
-    jobject peer = GetPeer(env);
-    jobject target = env->GetObjectField(peer, AwtObject::targetID);
+    jobject target = GetTarget(env);

 
     RECT rect;
     ::GetWindowRect(GetHWnd(), &rect);
 
-    (env)->SetIntField(target, AwtComponent::xID, ScaleDownX(rect.left));
-    (env)->SetIntField(target, AwtComponent::yID, ScaleDownY(rect.top));
-    (env)->SetIntField(peer, AwtWindow::sysXID, ScaleDownX(rect.left));
-    (env)->SetIntField(peer, AwtWindow::sysYID, ScaleDownY(rect.top));
+    (env)->SetIntField(target, AwtComponent::xID, ScaleDownAbsX(rect.left));

+    (env)->SetIntField(target, AwtComponent::yID, ScaleDownAbsY(rect.top));

     SendComponentEvent(java_awt_event_ComponentEvent_COMPONENT_MOVED);
 
     env->DeleteLocalRef(target);
     return AwtComponent::WmMove(x, y);
 }

@@ -1844,17 +1863,26 @@
 }
 
 MsgRouting AwtWindow::WmEnterSizeMove()
 {
     m_winSizeMove = TRUE;
+    // Below is a workaround, see CheckWindowDPIChange

+    Devices::InstanceAccess devices;

+    AwtWin32GraphicsDevice* device = devices->GetDevice(m_screenNum);

+    if (device) {

+        prevScaleRec.screen = m_screenNum;

+        prevScaleRec.scaleX = device->GetScaleX();

+        prevScaleRec.scaleY = device->GetScaleY();

+    }

+    // Above is a workaround

     return mrDoDefault;
 }
 
 MsgRouting AwtWindow::WmExitSizeMove()
 {
     m_winSizeMove = FALSE;
-    CheckWindowDPIChange();
+    CheckWindowDPIChange(); // workaround

     return mrDoDefault;
 }
 
 /*
  * Override AwtComponent's size handling to first update the

@@ -1867,26 +1895,21 @@
 
     if (type == SIZE_MINIMIZED) {
         UpdateSecurityWarningVisibility();
         return mrDoDefault;
     }
+    // Check for the new screen and update the java peer

+    CheckIfOnNewScreen(false); // postpone if different DPI

 
     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
     if (env->EnsureLocalCapacity(1) < 0)
         return mrDoDefault;
     jobject target = GetTarget(env);
     // fix 4167248 : ensure the insets are up-to-date before using
     BOOL insetsChanged = UpdateInsets(NULL);
-    int newWidth = w + m_insets.left + m_insets.right;
-    int newHeight = h + m_insets.top + m_insets.bottom;
-
-    (env)->SetIntField(target, AwtComponent::widthID, ScaleDownX(newWidth));
-    (env)->SetIntField(target, AwtComponent::heightID, ScaleDownY(newHeight));
-
-    jobject peer = GetPeer(env);
-    (env)->SetIntField(peer, AwtWindow::sysWID, ScaleDownX(newWidth));
-    (env)->SetIntField(peer, AwtWindow::sysHID, ScaleDownY(newHeight));
+    (env)->SetIntField(target, AwtComponent::widthID, ScaleDownX(w));

+    (env)->SetIntField(target, AwtComponent::heightID, ScaleDownY(h));

 
     if (!AwtWindow::IsResizing()) {
         WindowResized();
     }
 

@@ -1964,10 +1987,15 @@
 {
     MsgRouting mr = mrDoDefault;
     LRESULT retValue = 0L;
 
     switch(message) {
+        case WM_DPICHANGED: {

+            WmDPIChanged(lParam);

+            mr = mrConsume;

+            break;

+        }

         case WM_GETICON:
             mr = WmGetIcon(wParam, retValue);
             break;
         case WM_SYSCOMMAND:
             //Fixed 6355340: Contents of frame are not layed out properly on maximize

@@ -2107,18 +2135,32 @@
     DASSERT(scrnNum > -1);
 
     return scrnNum;
 }
 
-/* Check to see if we've been moved onto another screen.
+/*

+ * Check to see if we've been moved onto another screen.

  * If so, update internal data, surfaces, etc.
  */
-
-void AwtWindow::CheckIfOnNewScreen() {
+void AwtWindow::CheckIfOnNewScreen(BOOL force) {

     int curScrn = GetScreenImOn();
 
     if (curScrn != m_screenNum) {  // we've been moved
+        // if moved from one monitor to another with different DPI, we should

+        // update the m_screenNum only if the size was updated as well in the

+        // WM_DPICHANGED.

+        Devices::InstanceAccess devices;

+        AwtWin32GraphicsDevice* oldDevice = devices->GetDevice(m_screenNum);

+        AwtWin32GraphicsDevice* newDevice = devices->GetDevice(curScrn);

+        if (!force && m_winSizeMove && oldDevice && newDevice) {

+            if (oldDevice->GetScaleX() != newDevice->GetScaleX()

+                    || oldDevice->GetScaleY() != newDevice->GetScaleY()) {

+                // scales are different, wait for WM_DPICHANGED

+                return;

+            }

+        }

+

         JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 
         jclass peerCls = env->GetObjectClass(m_peerObject);
         DASSERT(peerCls);
         CHECK_NULL(peerCls);

@@ -2136,69 +2178,41 @@
 
         env->DeleteLocalRef(peerCls);
     }
 }
 
+// The shared code is not ready to the top-level window which crosses a few

+// monitors with different DPI. Popup windows will start to use wrong screen,

+// will be placed in the wrong place and will be use wrong size, see 8249164

+// So we will "JUMP TO" the new screen.

 void AwtWindow::CheckWindowDPIChange() {
-
-    if (prevScaleRec.screen != -1 ) {
-        float prevScaleX = prevScaleRec.scaleX;
-        float prevScaleY = prevScaleRec.scaleY;
-
-        if (prevScaleX >= 1 && prevScaleY >= 1) {
+    if (prevScaleRec.screen != -1 && prevScaleRec.screen != m_screenNum) {

             Devices::InstanceAccess devices;
-            AwtWin32GraphicsDevice* device = devices->GetDevice(m_screenNum);
+        AwtWin32GraphicsDevice *device = devices->GetDevice(m_screenNum);

             if (device) {
-                float scaleX = device->GetScaleX();
-                float scaleY = device->GetScaleY();
-                if (prevScaleX != scaleX || prevScaleY != scaleY) {
-                    WindowDPIChange(prevScaleRec.screen, prevScaleX, prevScaleY,
-                                    m_screenNum, scaleX, scaleY);
-                }
-            }
-        }
-        prevScaleRec.screen = -1;
-    }
-}
-
-void AwtWindow::WindowDPIChange(int prevScreen,
-                                float prevScaleX, float prevScaleY,
-                                int screen, float scaleX,
-                                float scaleY)
-{
-    int x;
-    int y;
-    int w;
-    int h;
+            if (prevScaleRec.scaleX != device->GetScaleX()

+                    || prevScaleRec.scaleY != device->GetScaleY()) {

     RECT rect;
-
-    if (prevScaleX == scaleX && prevScaleY == scaleY) {
-        return;
-    }
-
     ::GetWindowRect(GetHWnd(), &rect);
-    x = rect.left;
-    y = rect.top;
-    w = (rect.right - rect.left) * scaleX / prevScaleX;
-    h = (rect.bottom - rect.top) * scaleY / prevScaleY;
-
-    if (prevScreen != screen) {
-        Devices::InstanceAccess devices;
-        AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
-        if (device) {
+                int x = rect.left;

+                int y = rect.top;

+                int w = rect.right - rect.left;

+                int h = rect.bottom - rect.top;

             RECT bounds;
             if (MonitorBounds(device->GetMonitor(), &bounds)) {
                 x = x < bounds.left ? bounds.left : x;
                 y = y < bounds.top ? bounds.top : y;
-
                 x = (x + w > bounds.right) ? bounds.right - w : x;
                 y = (y + h > bounds.bottom) ? bounds.bottom - h : y;
             }
+                ReshapeNoScale(x, y, w, h);

         }
     }
-
-    ReshapeNoScale(x, y, w, h);
+        prevScaleRec.screen = -1;

+        prevScaleRec.scaleX = -1.0f;

+        prevScaleRec.scaleY = -1.0f;

+    }

 }
 
 BOOL AwtWindow::IsFocusableWindow() {
     /*
      * For Window/Frame/Dialog to accept focus it should:

@@ -2569,19 +2583,15 @@
                 int minHeight = p->ScaleDownY(::GetSystemMetrics(SM_CYMIN));
                 if (w < minWidth)
                 {
                     env->SetIntField(target, AwtComponent::widthID,
                         w = minWidth);
-                    env->SetIntField(peer, AwtWindow::sysWID,
-                        w);
                 }
                 if (h < minHeight)
                 {
                     env->SetIntField(target, AwtComponent::heightID,
                         h = minHeight);
-                    env->SetIntField(peer, AwtWindow::sysHID,
-                        h);
                 }
             }
             env->DeleteLocalRef(target);
 
             RECT *r = new RECT;

@@ -3244,43 +3254,10 @@
     ss->h = rc.bottom - rc.top;
 
     env->DeleteGlobalRef(self);
 }
 
-void AwtWindow::_WindowDPIChange(void* param)
-{
-    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
-
-    ScaleStruct *ss = (ScaleStruct *)param;
-    jobject self = ss->window;
-    jint prevScreen = ss->prevScreen;
-    jfloat prevScaleX = ss->prevScaleX;
-    jfloat prevScaleY = ss->prevScaleY;
-    jint screen = ss->screen;
-    jfloat scaleX = ss->scaleX;
-    jfloat scaleY = ss->scaleY;
-
-    PDATA pData;
-    JNI_CHECK_PEER_GOTO(self, ret);
-    AwtWindow *window = (AwtWindow *)pData;
-
-    if (window->m_winSizeMove) {
-        if (window->prevScaleRec.screen == -1) {
-            window->prevScaleRec.screen = prevScreen;
-            window->prevScaleRec.scaleX = prevScaleX;
-            window->prevScaleRec.scaleY = prevScaleY;
-        }
-    }
-    else {
-        window->WindowDPIChange(prevScreen, prevScaleX, prevScaleY,
-                                screen, scaleX, scaleY);
-    }
-
-ret:
-    env->DeleteGlobalRef(self);
-    delete ss;
-}
 
 extern "C" int getSystemMetricValue(int msgType);
 extern "C" {
 
 /*

@@ -3334,15 +3311,10 @@
 JNIEXPORT void JNICALL
 Java_sun_awt_windows_WWindowPeer_initIDs(JNIEnv *env, jclass cls)
 {
     TRY;
 
-    CHECK_NULL(AwtWindow::sysXID = env->GetFieldID(cls, "sysX", "I"));
-    CHECK_NULL(AwtWindow::sysYID = env->GetFieldID(cls, "sysY", "I"));
-    CHECK_NULL(AwtWindow::sysWID = env->GetFieldID(cls, "sysW", "I"));
-    CHECK_NULL(AwtWindow::sysHID = env->GetFieldID(cls, "sysH", "I"));
-
     AwtWindow::windowTypeID = env->GetFieldID(cls, "windowType",
             "Ljava/awt/Window$Type;");
 
     CATCH_BAD_ALLOC;
 }

@@ -3974,37 +3946,10 @@
 
     CATCH_BAD_ALLOC;
 }
 
 /*
-* Class:     sun_awt_windows_WWindowPeer
-* Method:    windowDPIChange
-* Signature: (IFFIFF)V
-*/
-JNIEXPORT void JNICALL
-Java_sun_awt_windows_WWindowPeer_windowDPIChange(JNIEnv *env, jobject self,
-    jint prevScreen, jfloat prevScaleX, jfloat prevScaleY,
-    jint screen, jfloat scaleX, jfloat scaleY)
-{
-    TRY;
-
-    ScaleStruct *ss = new ScaleStruct;
-    ss->window = env->NewGlobalRef(self);
-    ss->prevScreen = prevScreen;
-    ss->prevScaleX = prevScaleX;
-    ss->prevScaleY = prevScaleY;
-    ss->screen = screen;
-    ss->scaleX = scaleX;
-    ss->scaleY = scaleY;
-
-    AwtToolkit::GetInstance().InvokeFunction(AwtWindow::_WindowDPIChange, ss);
-    // global refs and ss are deleted in _WindowDPIChange
-
-    CATCH_BAD_ALLOC;
-}
-
-/*
  * Class:     sun_awt_windows_WLightweightFramePeer
  * Method:    overrideNativeHandle
  * Signature: (J)V
  */
 JNIEXPORT void JNICALL Java_sun_awt_windows_WLightweightFramePeer_overrideNativeHandle
< prev index next >