< 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,171 ****
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;
};
--- 151,160 ----
*** 177,190 ****
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;
--- 166,175 ----
*** 1125,1164 ****
window->InitOwner(awtParent);
} else {
// specify WS_EX_TOOLWINDOW to remove parentless windows from taskbar
exStyle |= WS_EX_TOOLWINDOW;
}
window->CreateHWnd(env, L"",
style, exStyle,
! 0, 0, 0, 0,
(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);
}
} catch (...) {
env->DeleteLocalRef(target);
throw;
}
--- 1110,1142 ----
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,
! x, y, width, height,
(awtParent != NULL) ? awtParent->GetHWnd() : NULL,
NULL,
::GetSysColor(COLOR_WINDOWTEXT),
::GetSysColor(COLOR_WINDOW),
self);
/*
* 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();
! window->RecalcNonClient();
}
} catch (...) {
env->DeleteLocalRef(target);
throw;
}
*** 1212,1221 ****
--- 1190,1240 ----
}
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,1770 ****
--- 1780,1798 ----
}
}
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,1809 ****
// 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();
! }
/* 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);
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));
SendComponentEvent(java_awt_event_ComponentEvent_COMPONENT_MOVED);
env->DeleteLocalRef(target);
return AwtComponent::WmMove(x, y);
}
--- 1804,1828 ----
// it's target's position since minimized Win32 windows
// move to -32000, -32000 for whatever reason
// NOTE: See also AwtWindow::Reshape
return mrDoDefault;
}
! // 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 target = GetTarget(env);
RECT rect;
::GetWindowRect(GetHWnd(), &rect);
! (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,1860 ****
}
MsgRouting AwtWindow::WmEnterSizeMove()
{
m_winSizeMove = TRUE;
return mrDoDefault;
}
MsgRouting AwtWindow::WmExitSizeMove()
{
m_winSizeMove = FALSE;
! CheckWindowDPIChange();
return mrDoDefault;
}
/*
* Override AwtComponent's size handling to first update the
--- 1863,1888 ----
}
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(); // workaround
return mrDoDefault;
}
/*
* Override AwtComponent's size handling to first update the
*** 1867,1892 ****
if (type == SIZE_MINIMIZED) {
UpdateSecurityWarningVisibility();
return mrDoDefault;
}
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));
if (!AwtWindow::IsResizing()) {
WindowResized();
}
--- 1895,1915 ----
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);
! (env)->SetIntField(target, AwtComponent::widthID, ScaleDownX(w));
! (env)->SetIntField(target, AwtComponent::heightID, ScaleDownY(h));
if (!AwtWindow::IsResizing()) {
WindowResized();
}
*** 1964,1973 ****
--- 1987,2001 ----
{
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,2124 ****
DASSERT(scrnNum > -1);
return scrnNum;
}
! /* Check to see if we've been moved onto another screen.
* If so, update internal data, surfaces, etc.
*/
!
! void AwtWindow::CheckIfOnNewScreen() {
int curScrn = GetScreenImOn();
if (curScrn != m_screenNum) { // we've been moved
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jclass peerCls = env->GetObjectClass(m_peerObject);
DASSERT(peerCls);
CHECK_NULL(peerCls);
--- 2135,2166 ----
DASSERT(scrnNum > -1);
return scrnNum;
}
! /*
! * Check to see if we've been moved onto another screen.
* If so, update internal data, surfaces, etc.
*/
! 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,2204 ****
env->DeleteLocalRef(peerCls);
}
}
void AwtWindow::CheckWindowDPIChange() {
!
! if (prevScaleRec.screen != -1 ) {
! float prevScaleX = prevScaleRec.scaleX;
! float prevScaleY = prevScaleRec.scaleY;
!
! if (prevScaleX >= 1 && prevScaleY >= 1) {
Devices::InstanceAccess devices;
! 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;
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) {
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);
}
BOOL AwtWindow::IsFocusableWindow() {
/*
* For Window/Frame/Dialog to accept focus it should:
--- 2178,2218 ----
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 && prevScaleRec.screen != m_screenNum) {
Devices::InstanceAccess devices;
! AwtWin32GraphicsDevice *device = devices->GetDevice(m_screenNum);
if (device) {
! if (prevScaleRec.scaleX != device->GetScaleX()
! || prevScaleRec.scaleY != device->GetScaleY()) {
RECT rect;
::GetWindowRect(GetHWnd(), &rect);
! 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);
}
}
! prevScaleRec.screen = -1;
! prevScaleRec.scaleX = -1.0f;
! prevScaleRec.scaleY = -1.0f;
! }
}
BOOL AwtWindow::IsFocusableWindow() {
/*
* For Window/Frame/Dialog to accept focus it should:
*** 2569,2587 ****
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;
--- 2583,2597 ----
*** 3244,3286 ****
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" {
/*
--- 3254,3263 ----
*** 3334,3348 ****
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;
}
--- 3311,3320 ----
*** 3974,4010 ****
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
--- 3946,3955 ----
< prev index next >