JDK-8243925 : Toolkit#getScreenInsets() returns wrong value on HiDPI screens (Windows)
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 11,14,15
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows
  • CPU: generic
  • Submitted: 2020-04-27
  • Updated: 2024-09-28
  • Resolved: 2020-05-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 13 JDK 15
11.0.9-oracleFixed 13.0.7Fixed 15 b25Fixed
Related Reports
Relates :  
Description
Run the test case below on the Windows on the HiDPI screen when the TaskBar is enabled. Note that the toggled window is shifted too much from the Taskbar. The root cause of the bug is that we do not scale the screen insets from the pixels to the units.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Rectangle;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class UndecoratedJFrame extends JFrame {

    public static void main( String[] args ) {
        UndecoratedJFrame frame = new UndecoratedJFrame();
        frame.setVisible( true );
    }

    @Override
    public void setExtendedState( int state ) {
        if( state == MAXIMIZED_BOTH ) {
            // calculate setMaximizedBounds
            GraphicsConfiguration graphicsConfiguration = getGraphicsConfiguration();
            Rectangle screenBounds = graphicsConfiguration.getBounds();
            Insets in = getToolkit().getScreenInsets( graphicsConfiguration );

            Rectangle maxBounds = new Rectangle( screenBounds.x+in.left, screenBounds.y+in.top, screenBounds.width - in.left - in.right, screenBounds.height - in.top - in.bottom );
            setMaximizedBounds( maxBounds );
            super.setExtendedState( MAXIMIZED_BOTH );

            // since Java 9 we need calculate the scale factor because setMaximizedBounds reduce the resulting bounds with the display scale factor
            Rectangle bounds = getBounds();
            if( bounds.width != maxBounds.width || bounds.height != maxBounds.height ) {
                double factorX = (double)maxBounds.width / bounds.width;
                double factorY = (double)maxBounds.height / bounds.height;
                maxBounds = new Rectangle( in.left, in.top, (int)Math.round( screenBounds.width * factorX ) - in.left - in.right, (int)Math.round( screenBounds.height * factorY ) - in.top - in.bottom );
                setMaximizedBounds( maxBounds );
                super.setExtendedState( ICONIFIED ); // state must change that the new MaximizedBounds is used from the GUI
                SwingUtilities.invokeLater( () -> {
                    UndecoratedJFrame.super.setExtendedState( MAXIMIZED_BOTH );
                } );
            }
        } else {
            super.setExtendedState( state );
        }
    }

    /**
     * Create a test frame
     */
    UndecoratedJFrame() {
        setUndecorated( true );
        setSize(500,500);
        setLocationRelativeTo(null);
        setTitle( "setMaximizedBounds Test" );
        setDefaultCloseOperation( DISPOSE_ON_CLOSE );

        // find a position in the other monitor
        for( GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() ) {
            GraphicsConfiguration gc = gd.getDefaultConfiguration();
            Rectangle gcBounds = gc.getBounds();
            if( gcBounds.x != 0 || gcBounds.y != 0 ) {
                setBounds( gcBounds.x + 100, gcBounds.y + 100, 250, 150 );
                break;
            }
        }

        // we use 2 buttons instead a full L&F to make it simple
        // Toggle button
        JButton button = new JButton( "Toggle" );
        button.addActionListener( ( e ) -> {
            if( getExtendedState() == MAXIMIZED_BOTH ) {
                setExtendedState( NORMAL );
            } else {
                setExtendedState( MAXIMIZED_BOTH );
            }
        } );
        getContentPane().add( button, BorderLayout.WEST );
        // Close Button
        button = new JButton( "Close" );
        button.addActionListener( ( e ) -> {
            dispose();
        } );
        getContentPane().add( button, BorderLayout.EAST );
        getContentPane().add( new JComponent() {
            @Override
            public void paint( Graphics g ) {
                super.paint( g );
                // draw a red cross to see the size of the window
                g.setColor( Color.RED );
                g.drawLine( 0, 0, getWidth(), getHeight() );
                g.drawLine( 0, getHeight(), getWidth(), 0 );
            }
        }, BorderLayout.CENTER );
    }
}
Comments
Fix request (13u) I'd like to port it to 13u as well. Patch applies without modifications. Tests running...
08-02-2021

Fix request (11u) -- will label after testing completed. I would like to downport this for parity with 11.0.9-oracle. Applies clean.
03-06-2020

URL: https://hg.openjdk.java.net/jdk/jdk/rev/0f83f6255168 User: psadhukhan Date: 2020-05-21 04:28:28 +0000
21-05-2020

URL: https://hg.openjdk.java.net/jdk/client/rev/0f83f6255168 User: serb Date: 2020-05-18 18:39:24 +0000
18-05-2020