4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.awt.Color; 25 import java.awt.FlowLayout; 26 import java.awt.Font; 27 import java.awt.Graphics; 28 import java.awt.Graphics2D; 29 import java.awt.GraphicsConfiguration; 30 import java.awt.GraphicsDevice; 31 import java.awt.GraphicsEnvironment; 32 import java.awt.GridBagConstraints; 33 import java.awt.GridBagLayout; 34 import java.awt.HeadlessException; 35 import java.awt.Image; 36 import java.awt.Insets; 37 import java.awt.Rectangle; 38 import java.awt.event.ActionEvent; 39 import java.awt.event.ActionListener; 40 import java.awt.event.WindowAdapter; 41 import java.awt.event.WindowEvent; 42 import java.awt.geom.AffineTransform; 43 import java.awt.image.AbstractMultiResolutionImage; 44 import java.awt.image.BufferedImage; 45 import java.awt.image.ImageObserver; 46 import java.util.Arrays; 47 import java.util.Collections; 48 import java.util.List; 49 import java.util.concurrent.CountDownLatch; 50 import java.util.concurrent.TimeUnit; 51 import javax.swing.JButton; 52 import javax.swing.JFrame; 53 import javax.swing.JPanel; 54 import javax.swing.JTextArea; 55 import javax.swing.SwingUtilities; 56 57 /* @test 58 * @bug 8175293 59 * @summary HiDPI (Windows): Swing components have incorrect sizes after 60 * changing display resolution 61 * @run main/manual/othervm WindowResizingOnSetLocationTest 62 */ 63 public class WindowResizingOnSetLocationTest { 64 65 private static volatile boolean testResult = false; 66 private static volatile CountDownLatch countDownLatch; 67 private static TestFrame frame; 68 private static JFrame mainFrame; 69 70 private static final String INSTRUCTIONS = "INSTRUCTIONS:\n" 71 + "Verify that a window is properly resized after setting the location" 72 + " to a display with different DPI.\n" 73 + "\n" 74 + "The test is applicable for a multi-monitor system where displays" 75 + " are configured to have different DPI\n" 76 + "\n" 77 + "1. Press Show Frame button\n" 78 + "The frame appear.\n" 79 + "2. Check that the string \"scales [ScaleX, ScaleY]\" is painted on the window" 80 + " where ScaleX and ScaleY are the scales for current display.\n" 81 + "The scales are calculated as DPI / 96 and are 1 for the DPI value 96" 82 + " and 2 for the DPI value 192.\n" 83 + "3. Press 'Move to another display' button.\n" 84 + "4. Check that the frame appears on the another display.\n" 85 + "5. Check that the string \"scales [ScaleX, ScaleY]\" is updated" 86 + " to show the right display scales.\n" 87 + "6. Check that the window is properly resized.\n" 88 + "7. Check that the window is properly repainted and does not contain drawing artifacts\n" 89 + "Try different display positions (left, right, top, bottom).\n" 90 + "If all tests are passed, press PASS, else press FAIL.\n"; 91 92 public static void main(String args[]) throws Exception { 93 94 countDownLatch = new CountDownLatch(1); 95 SwingUtilities.invokeLater(WindowResizingOnSetLocationTest::createUI); 96 countDownLatch.await(15, TimeUnit.MINUTES); 97 if (!testResult) { 98 throw new RuntimeException("Test fails!"); 99 } 100 } 101 102 private static void createUI() { 103 104 mainFrame = new JFrame("DPI change test"); 105 GridBagLayout layout = new GridBagLayout(); 106 JPanel mainControlPanel = new JPanel(layout); 107 JPanel resultButtonPanel = new JPanel(layout); 108 109 GridBagConstraints gbc = new GridBagConstraints(); 110 111 JPanel testPanel = new JPanel(new FlowLayout()); 112 JButton frameButton = new JButton("Show Frame"); 113 frameButton.addActionListener((e) -> { 114 int x = 20; 115 int y = 10; 116 int w = 400; 117 int h = 300; 118 119 frame = new TestFrame(w, h); 120 frame.setLocation(x, y); 121 frame.setVisible(true); 122 123 }); 124 testPanel.add(frameButton); 125 126 gbc.gridx = 0; 127 gbc.gridy = 0; 128 gbc.fill = GridBagConstraints.HORIZONTAL; 129 mainControlPanel.add(testPanel, gbc); 130 131 JTextArea instructionTextArea = new JTextArea(); 132 instructionTextArea.setText(INSTRUCTIONS); 133 instructionTextArea.setEditable(false); 134 instructionTextArea.setBackground(Color.white); 135 136 gbc.gridx = 0; 137 gbc.gridy = 1; 138 gbc.fill = GridBagConstraints.HORIZONTAL; 139 mainControlPanel.add(instructionTextArea, gbc); 140 141 JButton passButton = new JButton("Pass"); 142 passButton.setActionCommand("Pass"); 143 passButton.addActionListener((ActionEvent e) -> { 144 testResult = true; 176 177 @Override 178 public void windowClosing(WindowEvent e) { 179 disposeFrames(); 180 countDownLatch.countDown(); 181 } 182 }); 183 mainFrame.setVisible(true); 184 } 185 186 private static void disposeFrames() { 187 if (frame != null && frame.isVisible()) { 188 frame.dispose(); 189 } 190 191 if (mainFrame != null && mainFrame.isVisible()) { 192 mainFrame.dispose(); 193 } 194 } 195 196 static class TestFrame extends JFrame { 197 198 private final TestMultiResolutionImage mrImage; 199 200 public TestFrame(int width, int height) throws HeadlessException { 201 super("Test Frame"); 202 setSize(width, height); 203 mrImage = new TestMultiResolutionImage(width, height); 204 205 JPanel panel = new JPanel(new FlowLayout()) { 206 @Override 207 public void paint(Graphics g) { 208 super.paint(g); 209 AffineTransform tx = ((Graphics2D) g).getTransform(); 210 mrImage.scaleX = tx.getScaleX(); 211 mrImage.scaleY = tx.getScaleY(); 212 Insets insets = getInsets(); 213 g.drawImage(mrImage, insets.left, insets.bottom, null); 214 } 215 }; 216 217 JButton button = new JButton("Move to another display"); 218 button.addActionListener((e) -> { 219 GraphicsConfiguration config = getGraphicsConfiguration(); 220 GraphicsDevice device = config.getDevice(); 221 222 GraphicsDevice[] devices = GraphicsEnvironment 223 .getLocalGraphicsEnvironment() 224 .getScreenDevices(); 225 226 boolean found = false; 227 for (GraphicsDevice dev : devices) { 228 if (!dev.equals(device)) { 229 found = true; 230 Rectangle bounds = dev.getDefaultConfiguration().getBounds(); 231 232 AffineTransform tx = config.getDefaultTransform(); 233 int x = (int) Math.round(bounds.x / tx.getScaleX()) + 15; 234 int y = (int) Math.round(bounds.y / tx.getScaleY()) + 15; 235 frame.setLocation(x, y); 236 break; 237 } 238 } 239 240 if (!found) { 241 System.out.println("Another display not found!"); 242 } 243 }); 244 245 panel.add(button); 246 add(panel); 247 } 248 } 249 250 static class TestMultiResolutionImage extends AbstractMultiResolutionImage { 251 252 final int width; 253 final int height; 254 double scaleX; 255 double scaleY; 256 257 public TestMultiResolutionImage(int width, int height) { 258 this.width = width; 259 this.height = height; 260 } 261 262 @Override | 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.awt.BasicStroke; 25 import java.awt.BorderLayout; 26 import java.awt.Color; 27 import java.awt.Dimension; 28 import java.awt.FlowLayout; 29 import java.awt.Font; 30 import java.awt.Graphics; 31 import java.awt.Graphics2D; 32 import java.awt.GraphicsConfiguration; 33 import java.awt.GraphicsDevice; 34 import java.awt.GraphicsEnvironment; 35 import java.awt.GridBagConstraints; 36 import java.awt.GridBagLayout; 37 import java.awt.HeadlessException; 38 import java.awt.Image; 39 import java.awt.Insets; 40 import java.awt.Rectangle; 41 import java.awt.event.ActionEvent; 42 import java.awt.event.ActionListener; 43 import java.awt.event.WindowAdapter; 44 import java.awt.event.WindowEvent; 45 import java.awt.geom.AffineTransform; 46 import java.awt.image.AbstractMultiResolutionImage; 47 import java.awt.image.BufferedImage; 48 import java.awt.image.ImageObserver; 49 import java.util.Arrays; 50 import java.util.Collections; 51 import java.util.List; 52 import java.util.concurrent.CountDownLatch; 53 import java.util.concurrent.TimeUnit; 54 import javax.swing.JButton; 55 import javax.swing.JComponent; 56 import javax.swing.JFrame; 57 import javax.swing.JPanel; 58 import javax.swing.JTextArea; 59 import javax.swing.SwingUtilities; 60 61 /* @test 62 * @bug 8175293 8176097 63 * @summary Window set location to a display with different DPI does not properly work 64 * @run main/manual/othervm WindowResizingOnSetLocationTest 65 */ 66 public class WindowResizingOnSetLocationTest { 67 68 private static volatile boolean testResult = false; 69 private static volatile CountDownLatch countDownLatch; 70 private static TestFrame frame; 71 private static JFrame mainFrame; 72 private static Rectangle[] screenBounds; 73 private static double[][] scales; 74 private static int screen1 = -1; 75 private static int screen2 = -1; 76 77 private static final String INSTRUCTIONS = "INSTRUCTIONS:\n" 78 + "Verify that a window is properly resized after setting the location" 79 + " to a display with different DPI.\n" 80 + "\n" 81 + "The test is applicable for a multi-monitor system where displays" 82 + " are configured to have different DPI\n" 83 + "\n" 84 + "1. Press Show Frame button\n" 85 + " (note that the button is disabled in case there are no two monitors" 86 + " with different DPI)\n" 87 + "The frame should appear in the center of the display (either" 88 + " on the first or on the second).\n" 89 + "2. Check that the string \"scales [ScaleX, ScaleY]\" is painted on the window" 90 + " where ScaleX and ScaleY are the scales for current display.\n" 91 + "The scales are calculated as DPI / 96 and are 1 for the DPI value 96" 92 + " and 2 for the DPI value 192.\n" 93 + "3. Press 'Move to another display' button.\n" 94 + "4. Check that the frame appears on the another display.\n" 95 + "5. Check that the string \"scales [ScaleX, ScaleY]\" is updated" 96 + " to show the right display scales.\n" 97 + "6. Check that the window is properly resized.\n" 98 + "7. Check that the window is properly repainted and does not contain drawing artifacts\n" 99 + "If all tests are passed, press PASS, else press FAIL.\n"; 100 101 public static void main(String args[]) throws Exception { 102 103 countDownLatch = new CountDownLatch(1); 104 SwingUtilities.invokeLater(WindowResizingOnSetLocationTest::createUI); 105 countDownLatch.await(15, TimeUnit.MINUTES); 106 if (!testResult) { 107 throw new RuntimeException("Test fails!"); 108 } 109 } 110 111 private static void createUI() { 112 113 initScreenBounds(); 114 115 mainFrame = new JFrame("DPI change test"); 116 GridBagLayout layout = new GridBagLayout(); 117 JPanel mainControlPanel = new JPanel(layout); 118 JPanel resultButtonPanel = new JPanel(layout); 119 120 GridBagConstraints gbc = new GridBagConstraints(); 121 122 JPanel testPanel = new JPanel(new BorderLayout()); 123 JButton frameButton = new JButton("Show Frame"); 124 frameButton.addActionListener((e) -> { 125 GraphicsConfiguration gc = GraphicsEnvironment 126 .getLocalGraphicsEnvironment() 127 .getScreenDevices()[screen1] 128 .getDefaultConfiguration(); 129 130 Rectangle rect = getCenterRect(screenBounds[screen2]); 131 frame = new TestFrame(gc, rect); 132 frame.setVisible(true); 133 134 }); 135 frameButton.setEnabled(screen1 != -1 && screen2 != -1); 136 testPanel.add(getDisplaysComponent(), BorderLayout.CENTER); 137 testPanel.add(frameButton, BorderLayout.SOUTH); 138 139 gbc.gridx = 0; 140 gbc.gridy = 0; 141 gbc.fill = GridBagConstraints.HORIZONTAL; 142 mainControlPanel.add(testPanel, gbc); 143 144 JTextArea instructionTextArea = new JTextArea(); 145 instructionTextArea.setText(INSTRUCTIONS); 146 instructionTextArea.setEditable(false); 147 instructionTextArea.setBackground(Color.white); 148 149 gbc.gridx = 0; 150 gbc.gridy = 1; 151 gbc.fill = GridBagConstraints.HORIZONTAL; 152 mainControlPanel.add(instructionTextArea, gbc); 153 154 JButton passButton = new JButton("Pass"); 155 passButton.setActionCommand("Pass"); 156 passButton.addActionListener((ActionEvent e) -> { 157 testResult = true; 189 190 @Override 191 public void windowClosing(WindowEvent e) { 192 disposeFrames(); 193 countDownLatch.countDown(); 194 } 195 }); 196 mainFrame.setVisible(true); 197 } 198 199 private static void disposeFrames() { 200 if (frame != null && frame.isVisible()) { 201 frame.dispose(); 202 } 203 204 if (mainFrame != null && mainFrame.isVisible()) { 205 mainFrame.dispose(); 206 } 207 } 208 209 static void initScreenBounds() { 210 211 GraphicsDevice[] devices = GraphicsEnvironment 212 .getLocalGraphicsEnvironment() 213 .getScreenDevices(); 214 215 screenBounds = new Rectangle[devices.length]; 216 scales = new double[devices.length][2]; 217 for (int i = 0; i < devices.length; i++) { 218 GraphicsConfiguration gc = devices[i].getDefaultConfiguration(); 219 screenBounds[i] = gc.getBounds(); 220 AffineTransform tx = gc.getDefaultTransform(); 221 scales[i][0] = tx.getScaleX(); 222 scales[i][1] = tx.getScaleY(); 223 } 224 225 for (int i = 0; i < devices.length; i++) { 226 for (int j = i + 1; j < devices.length; j++) { 227 if (scales[i][0] != scales[j][0] || scales[i][1] != scales[j][1]) { 228 screen1 = i; 229 screen2 = j; 230 } 231 } 232 } 233 } 234 235 private static Rectangle getCenterRect(Rectangle rect) { 236 int w = rect.width / 2; 237 int h = rect.height / 2; 238 int x = rect.x + w / 2; 239 int y = rect.y + h / 2; 240 241 return new Rectangle(x, y, w, h); 242 } 243 244 static JComponent getDisplaysComponent() { 245 246 Rectangle rect = screenBounds[0]; 247 for (int i = 0; i < screenBounds.length; i++) { 248 rect = rect.union(screenBounds[i]); 249 } 250 251 final BufferedImage img = new BufferedImage(rect.width, rect.height, BufferedImage.TYPE_INT_RGB); 252 Graphics2D g = img.createGraphics(); 253 g.setColor(Color.WHITE); 254 g.fillRect(0, 0, rect.width, rect.height); 255 g.translate(-rect.x, -rect.y); 256 257 g.setStroke(new BasicStroke(8f)); 258 for (int i = 0; i < screenBounds.length; i++) { 259 Rectangle r = screenBounds[i]; 260 g.setColor(Color.BLACK); 261 g.drawRect(r.x, r.y, r.width, r.height); 262 263 g.setColor(Color.ORANGE); 264 Rectangle cr = getCenterRect(r); 265 g.fillRect(cr.x, cr.y, cr.width, cr.height); 266 267 double scaleX = scales[i][0]; 268 double scaleY = scales[i][1]; 269 float fontSize = rect.height / 7; 270 g.setFont(g.getFont().deriveFont(fontSize)); 271 g.setColor(Color.BLUE); 272 g.drawString(String.format("Scale: [%2.1f, %2.1f]", scaleX, scaleY), 273 r.x + r.width / 8, r.y + r.height / 2); 274 275 } 276 277 g.dispose(); 278 279 JPanel panel = new JPanel() { 280 281 @Override 282 public void paint(Graphics g) { 283 super.paint(g); 284 g.drawImage(img, 0, 0, getWidth(), getHeight(), this); 285 286 } 287 }; 288 289 panel.setPreferredSize(new Dimension(400, 200)); 290 291 return panel; 292 } 293 294 static class TestFrame extends JFrame { 295 296 private final TestMultiResolutionImage mrImage; 297 298 public TestFrame(GraphicsConfiguration gc, Rectangle rect) throws HeadlessException { 299 super(gc); 300 setBounds(rect); 301 mrImage = new TestMultiResolutionImage(rect.width, rect.height); 302 303 JPanel panel = new JPanel(new FlowLayout()) { 304 @Override 305 public void paint(Graphics g) { 306 super.paint(g); 307 AffineTransform tx = ((Graphics2D) g).getTransform(); 308 mrImage.scaleX = tx.getScaleX(); 309 mrImage.scaleY = tx.getScaleY(); 310 Insets insets = getInsets(); 311 g.drawImage(mrImage, insets.left, insets.bottom, null); 312 } 313 }; 314 315 JButton button = new JButton("Move to another display"); 316 button.addActionListener((e) -> { 317 GraphicsConfiguration config = getGraphicsConfiguration(); 318 319 GraphicsDevice[] devices = GraphicsEnvironment 320 .getLocalGraphicsEnvironment() 321 .getScreenDevices(); 322 323 324 int index = devices[screen1].getDefaultConfiguration().equals(config) 325 ? screen2 : screen1; 326 327 Rectangle r = getCenterRect(screenBounds[index]); 328 frame.setBounds(r); 329 }); 330 331 panel.add(button); 332 add(panel); 333 } 334 } 335 336 static class TestMultiResolutionImage extends AbstractMultiResolutionImage { 337 338 final int width; 339 final int height; 340 double scaleX; 341 double scaleY; 342 343 public TestMultiResolutionImage(int width, int height) { 344 this.width = width; 345 this.height = height; 346 } 347 348 @Override |