8170937: Swing apps are slow if displaying from a remote source to many local displays
authorserb
Tue, 18 Sep 2018 18:32:03 -0700
changeset 51928 d96a607e9594
parent 51927 c88fd713b51c
child 51929 0e514f1549b4
8170937: Swing apps are slow if displaying from a remote source to many local displays Reviewed-by: prr, aivanov
src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java
test/jdk/java/awt/Mixing/AWT_Mixing/FrameBorderCounter.java
test/jdk/java/awt/Toolkit/ScreenInsetsTest/ScreenInsetsTest.java
--- a/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java	Tue Sep 18 18:12:40 2018 +0530
+++ b/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java	Tue Sep 18 18:32:03 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -215,6 +215,12 @@
     static long awt_defaultFg; // Pixel
     private static XMouseInfoPeer xPeer;
 
+    /**
+     * Should we check "_NET_WM_STRUT/_NET_WM_STRUT_PARTIAL" during insets
+     * calculation.
+     */
+    private static Boolean checkSTRUT;
+
     static {
         initSecurityWarning();
         if (GraphicsEnvironment.isHeadless()) {
@@ -826,13 +832,26 @@
     }
 
     /*
-     * If we're running in non-Xinerama environment and the current
-     * window manager supports _NET protocol then the screen insets
-     * are calculated using _NET_WM_WORKAREA property of the root
-     * window.
-     * Otherwise, i. e. if Xinerama is on or _NET_WM_WORKAREA is
-     * not set, we try to calculate the insets ourselves using
-     * getScreenInsetsManually method.
+     * If the current window manager supports _NET protocol then the screen
+     * insets are calculated using _NET_WORKAREA property of the root window.
+     * <p>
+     * Note that _NET_WORKAREA is a rectangular area and it does not work
+     * well in the Xinerama mode.
+     * <p>
+     * We will trust the part of this rectangular area only if it starts at the
+     * requested graphics configuration. Below is an example when the
+     * _NET_WORKAREA intersects with the requested graphics configuration but
+     * produces wrong result.
+     *
+     *         //<-x1,y1///////
+     *         //            // ////////////////
+     *         //  SCREEN1   // // SCREEN2    //
+     *         // ********** // //     x2,y2->//
+     *         //////////////// //            //
+     *                          ////////////////
+     *
+     * When two screens overlap and the first contains a dock(*****), then
+     * _NET_WORKAREA may start at point x1,y1 and end at point x2,y2.
      */
     @Override
     public Insets getScreenInsets(GraphicsConfiguration gc)
@@ -846,30 +865,33 @@
         XToolkit.awtLock();
         try
         {
-            X11GraphicsConfig x11gc = (X11GraphicsConfig)gc;
-            X11GraphicsDevice x11gd = x11gc.getDevice();
-            long root = XlibUtil.getRootWindow(x11gd.getScreen());
+            X11GraphicsEnvironment x11ge = (X11GraphicsEnvironment)
+                    GraphicsEnvironment.getLocalGraphicsEnvironment();
+            X11GraphicsConfig x11gc = (X11GraphicsConfig) gc;
+            long root = XlibUtil.getRootWindow(x11gc.getDevice().getScreen());
             int scale = x11gc.getScale();
-            Rectangle rootBounds = XlibUtil.getWindowGeometry(root, scale);
-
-            X11GraphicsEnvironment x11ge = (X11GraphicsEnvironment)
-                GraphicsEnvironment.getLocalGraphicsEnvironment();
-            if (!x11ge.runningXinerama())
-            {
-                Insets screenInsets = getInsets(root, rootBounds, scale);
-                if (screenInsets != null) return screenInsets;
+            if (x11ge.runningXinerama() && checkSTRUT()) {
+                // implementation based on _NET_WM_STRUT/_NET_WM_STRUT_PARTIAL
+                Rectangle rootBounds = XlibUtil.getWindowGeometry(root, scale);
+                Insets insets = getScreenInsetsManually(root, rootBounds,
+                                                        gc.getBounds(), scale);
+                if ((insets.left | insets.top | insets.bottom | insets.right) != 0
+                        || rootBounds == null) {
+                    return insets;
+                }
             }
-
-            Insets insets = getScreenInsetsManually(root, rootBounds,
-                    gc.getBounds(), scale);
-            if ((insets.left | insets.top | insets.bottom | insets.right) == 0
-                    && rootBounds != null ) {
-                root = XlibWrapper.RootWindow(XToolkit.getDisplay(),
-                        x11gd.getScreen());
-                Insets screenInsets = getInsets(root, rootBounds, scale);
-                if (screenInsets != null) return screenInsets;
+            Rectangle workArea = XToolkit.getWorkArea(root, scale);
+            Rectangle screen = gc.getBounds();
+            if (workArea != null && screen.contains(workArea.getLocation())) {
+                workArea = workArea.intersection(screen);
+                int top = workArea.y - screen.y;
+                int left = workArea.x - screen.x;
+                int bottom = screen.height - workArea.height - top;
+                int right = screen.width - workArea.width - left;
+                return new Insets(top, left, bottom, right);
             }
-            return insets;
+            // Note that it is better to return zeros than inadequate values
+            return new Insets(0, 0, 0, 0);
         }
         finally
         {
@@ -877,14 +899,16 @@
         }
     }
 
-    private Insets getInsets(long root, Rectangle rootBounds, int scale) {
-        Rectangle workArea = XToolkit.getWorkArea(root, scale);
-        if (workArea == null) {
-            return null;
+    /**
+     * Returns the value of "sun.awt.X11.checkSTRUT" property. Default value is
+     * {@code false}.
+     */
+    private static boolean checkSTRUT() {
+        if (checkSTRUT == null) {
+            checkSTRUT = AccessController.doPrivileged(
+                    new GetBooleanAction("sun.awt.X11.checkSTRUT"));
         }
-        return new Insets(workArea.y, workArea.x,
-                rootBounds.height - workArea.height - workArea.y,
-                rootBounds.width - workArea.width - workArea.x);
+        return checkSTRUT;
     }
 
     /*
@@ -893,6 +917,14 @@
      * hints' values to screen insets.
      *
      * This method should be called under XToolkit.awtLock()
+     *
+     * This method is unused by default because of two reasons:
+     *  - Iteration over windows may be extremely slow, and execution of
+     *    getScreenInsets() can be x100 slower than in one monitor config.
+     *  - _NET_WM_STRUT/_NET_WM_STRUT_PARTIAL are hints for the applications.
+     *    WM should take into account these hints when "_NET_WORKAREA" is
+     *    calculated, but the system panels do not necessarily contain these
+     *    hints(Gnome 3 for example).
      */
     private Insets getScreenInsetsManually(long root, Rectangle rootBounds,
                                            Rectangle screenBounds, int scale)
--- a/test/jdk/java/awt/Mixing/AWT_Mixing/FrameBorderCounter.java	Tue Sep 18 18:12:40 2018 +0530
+++ b/test/jdk/java/awt/Mixing/AWT_Mixing/FrameBorderCounter.java	Tue Sep 18 18:32:03 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -20,15 +20,14 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
+
 import java.awt.Dimension;
 import java.awt.EventQueue;
 import java.awt.Frame;
 import java.awt.Point;
 import java.awt.Robot;
+import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.WindowEvent;
-import java.awt.event.WindowAdapter;
 
 public class FrameBorderCounter {
 
@@ -59,6 +58,7 @@
                 background.setVisible(true);
             }
         });
+        robot.waitForIdle();
         EventQueue.invokeAndWait(new Runnable() {
             public void run() {
                 frame = new Frame("Frame");
--- a/test/jdk/java/awt/Toolkit/ScreenInsetsTest/ScreenInsetsTest.java	Tue Sep 18 18:12:40 2018 +0530
+++ b/test/jdk/java/awt/Toolkit/ScreenInsetsTest/ScreenInsetsTest.java	Tue Sep 18 18:32:03 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,8 +32,13 @@
   @run main ScreenInsetsTest
 */
 
-import java.awt.*;
-import java.awt.event.*;
+import java.awt.Frame;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
 
 import test.java.awt.regtesthelpers.Util;
 
@@ -41,21 +46,33 @@
 {
     public static void main(String[] args)
     {
-        if (!Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH))
-        {
-            // this state is used in the test - sorry
-            return;
-        }
-
         boolean passed = true;
 
         GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
         GraphicsDevice[] gds = ge.getScreenDevices();
-        for (GraphicsDevice gd : gds)
-        {
+        for (GraphicsDevice gd : gds) {
+
             GraphicsConfiguration gc = gd.getDefaultConfiguration();
             Rectangle gcBounds = gc.getBounds();
             Insets gcInsets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
+            int left = gcInsets.left;
+            int right = gcInsets.right;
+            int bottom = gcInsets.bottom;
+            int top = gcInsets.top;
+            if (left < 0 || right < 0 || bottom < 0 || top < 0) {
+                throw new RuntimeException("Negative value: " + gcInsets);
+            }
+            int maxW = gcBounds.width / 3;
+            int maxH = gcBounds.height / 3;
+            if (left > maxW || right > maxW || bottom > maxH || top > maxH) {
+                throw new RuntimeException("Big value: " + gcInsets);
+            }
+
+            if (!Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH))
+            {
+                // this state is used in the test - sorry
+                continue;
+            }
 
             Frame f = new Frame("Test", gc);
             f.setUndecorated(true);