New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8211999: Window positioning bugs due to overlapping GraphicsDevice bounds (Windows/HiDPI) #375
Conversation
👋 Welcome back serb! A progress list of the required criteria for merging this PR into |
@mrserb The following labels will be automatically applied to this pull request: When this pull request is ready to be reviewed, an RFR email will be sent to the corresponding mailing lists. If you would like to change these labels, use the |
@prsadhuk can you review this? |
@prrace Can you review this? |
src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java
Outdated
Show resolved
Hide resolved
src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java
Outdated
Show resolved
Hide resolved
src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java
Outdated
Show resolved
Hide resolved
src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp
Outdated
Show resolved
Hide resolved
test/jdk/java/awt/Component/SetComponentsBounds/SetComponentsBounds.java
Outdated
Show resolved
Hide resolved
test/jdk/java/awt/List/ListMultipleSelectTest/ListMultipleSelectTest.java
Outdated
Show resolved
Hide resolved
test/jdk/java/awt/Window/WindowSizeDifferentScreens/WindowSizeDifferentScreens.java
Outdated
Show resolved
Hide resolved
Co-authored-by: Aleksei Ivanov <70774172+aivanov-jdk@users.noreply.github.com>
You found this bug https://bugs.openjdk.java.net/browse/JDK-8249164 the case when the window is split across two screens with different DPI are not properly supported before and after the fix, in both cases, there are values which break it. But I need to confirm that, please provide exact display resolution/java pipeline/and scales. |
@mrserb This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 3 new commits pushed to the
Please see this link for an up-to-date comparison between the source branch of this pull request and the ➡️ To integrate this PR with the above commit message to the |
* | ||
* @param config the graphics configuration which bounds are requested | ||
* @return the bounds of the area covered by this | ||
* {@code GraphicsConfiguration} in device space(pixels). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* {@code GraphicsConfiguration} in device space(pixels). | |
* {@code GraphicsConfiguration} in device space (pixels). |
If changed here, all other similar places should be updated too to keep doc comments consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll fix it and also merge the master, then update the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, also consider adding a space between the word and the opening parenthesis in these coordinates (x, y) and size (w, h) and, probably, a space after comma.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spaces are added.
/integrate |
@mrserb Since your change was applied there have been 4 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit be63525. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
…unds (Windows/HiDPI) Reviewed-by: kizune, aivanov
Hello.
Please review the fix for jdk.
Old review request:
https://mail.openjdk.java.net/pipermail/awt-dev/2020-July/015991.html
(Note: the fix use API available since Windows 8.1: WM_DPICHANGED, but it should be fine for
Windows 7, because it does not support different DPI for different monitors)
========================================================
Short description:
In the multi-screen configurations when each screen have different DPI
we calculate the bounds of each monitor by these formulas:
This formula makes the next configuration completely broken:
- The main screen on the left and 100% DPI
- The second screen on the right and 200% DPI
When we translate the bounds of the config from the device space to the user's space,
the bounds of both screen overlap in the user's space, because we use bounds of
the main screen as-is, and the X/Y of the second screen are divided by the scaleX/Y.
Since the screens are overlapped we cannot be sure how to translate the user's space
coordinates to device space in the overlapped zone.
As a result => we use the wrong screen
=> got wrong coordinates in the device space
=> show top-level windows/popups/tooltips/menus/etc on the wrong screen
The proposed solution for this bug is to change the formulas to these:
In other words, we should not transform the X and Y coordinates of the screen(the top/left corner). This will
complicate the way of how we transform coordinates on the screen: user's <--> device spaces:
Before the fix:
After the fix(only the part appeared on this screen should be scaled)
Note that these new formulas are applicable only for the coordinates on the screen such as X and Y.
If we will need to calculate the "size" such as W and H then the old formula should be used.
The main changes for the problem above are:
http://cr.openjdk.java.net/~serb/8211999/webrev.04/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp.sdiff.html
http://cr.openjdk.java.net/~serb/8211999/webrev.04/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp.sdiff.html
http://cr.openjdk.java.net/~serb/8211999/webrev.04/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java.sdiff.html
========================================================
Long description:
Unfortunately, the changes above are not enough to fix the problem when different monitors
have different DPI, even if the bounds are NOT overlapped.
Currently, when we try to set the bounds of the window, we manually convert it in "java" to the
expected device position based on the current GraphicsConfiguration of the peer, and then
additionally, tweak it in native using the device scale stored in native code. Unfortunately
this two scale might not be in sync:(after we use the GraphicsConfiguration scale in peer,
the config might be changed and the tweak in native will use a different screen).
As a fix I have moved all transformation from the peer to the native code, it will be executed
on the toolkit thread:
See the change in WWindowPeer.setBounds() and awt_Window.cpp AwtWindow::Reshape
http://cr.openjdk.java.net/~serb/8211999/webrev.04/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java.sdiff.html
http://cr.openjdk.java.net/~serb/8211999/webrev.04/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp.sdiff.html
I think at some point we should delete all transformation in java, and apply it in the native code only.
We had a special code that tracked the dragging of the window by the user from one screen to another,
and change the size of the window only when the user stops the drag operation. I've proposed to use standard Windows
machinery for that via WM_DPICHANGED: https://docs.microsoft.com/en-us/windows/win32/hidpi/wm-dpichanged
As a result, Windows will provide the "best" windows bounds on the new screen. Note that the code has a
workaround for https://bugs.openjdk.java.net/browse/JDK-8249164
I've also fix a variation of the bug previously fixed on macOS https://hg.openjdk.java.net/jdk/jdk/rev/b5cdba232fca
If we try to change the position of the window, and Windows ignores this request then we need to call all "callbacks" manually, otherwise, the shared code will use the bounds ignored by the Windows.
See the end of void AwtWindow::Reshape():
http://cr.openjdk.java.net/~serb/8211999/webrev.04/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp.sdiff.html
Currently the HW componets such as Canvas scales everything based on such code:
But it does not work well if the smaller part of the top-level window is located on one screen1(DPI=100) but
the biggest part is located on the screen2(DPI=200). If the Canvas is located on the "smaller" part of the
window, then the canvas will use DPI=100 for scale, but the whole window will use DPI=200
As a fix, all HW components will try to use the scale factor of the top-level window, see AwtComponent::GetScreenImOn:
http://cr.openjdk.java.net/~serb/8211999/webrev.04/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp.sdiff.html
Our current implementation does not take care of the case when the HW component bounds are updated when the top-level
the window is moved to another screen. For example, if the window does not use LayoutManager and the user sets some specific bounds for the Canvas. It is expected that the size of the Canvas will be updated when the window will be moved to another screen, but only HW top-level window and Swing components will update its size.
As a fix I suggest to resync the bounds of the HW components if the GraphicsCOnfiguration is changed, see WComponentPeer.syncBounds():
http://cr.openjdk.java.net/~serb/8211999/webrev.04/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java.sdiff.html
Note that the current fix is for Windows only, but the bug exists on Linux as well, so I have to create a kind of "ifdef" in the
Robot class, on windows we will use the new logic, on other platforms the old logic will be used.
The win32GraphicsDevice incorrectly sets the size of the full-screen window. It uses the display mode width/height(which are stored in pixels), but the bounds of the graphics config(which are stored in user's space) should be used.
========================================================
Some other bugs which are not fixed.
Probably there are some other bugs and possibility to cleanups, but I would like to do that in separate issues.
========================================================
The tests
- I have updated a couple of the tests to check multiple screens if possible, it helps to found some bugs
in my initial implementation
- Four new tests were added: SetComponentsBounds, SlowMotion, WindowSizeDifferentScreens, FullscreenWindowProps
- One test is moved from the closed repo(I made a small cleanup of it): ListMultipleSelectTest
========================================================
I have run jck, regression tests in different configurations, when the main screen is on the left, up, down,
right, and have different DPI such as 100, 125, 150, and 200. No new issues were found so far.
The mach5 is also green.
PS: hope I did not forget some important information, will send it later if any.
Progress
Testing
Issue
Reviewers
Download
$ git fetch https://git.openjdk.java.net/jdk pull/375/head:pull/375
$ git checkout pull/375