8150954: Taking screenshots on x11 composite desktop produce wrong result
authorneugens
Mon, 11 Jul 2016 16:52:50 +0200
changeset 39852 7da63510ba70
parent 39851 394674e47655
child 39853 a317160ac73b
8150954: Taking screenshots on x11 composite desktop produce wrong result Summary: The AWT Robot X11 code that takes screenshots uses the default root window, which may not contain the final composited desktop. Reviewed-by: alexsch, ssadetsky
jdk/make/mapfiles/libawt_xawt/mapfile-vers
jdk/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java
jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c
--- a/jdk/make/mapfiles/libawt_xawt/mapfile-vers	Mon Jul 11 20:36:13 2016 +0300
+++ b/jdk/make/mapfiles/libawt_xawt/mapfile-vers	Mon Jul 11 16:52:50 2016 +0200
@@ -164,6 +164,7 @@
         Java_sun_awt_X11_XRobotPeer_mouseReleaseImpl;
         Java_sun_awt_X11_XRobotPeer_mouseWheelImpl;
         Java_sun_awt_X11_XRobotPeer_setup;
+        Java_sun_awt_X11_XRobotPeer_loadNativeLibraries;
         Java_sun_awt_X11_XToolkit_getNumberOfButtonsImpl;
         Java_java_awt_Component_initIDs;
         Java_java_awt_Container_initIDs;
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java	Mon Jul 11 20:36:13 2016 +0300
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java	Mon Jul 11 16:52:50 2016 +0200
@@ -35,8 +35,16 @@
 
 class XRobotPeer implements RobotPeer {
 
-    private static volatile boolean isGtkSupported;
+    static final boolean tryGtk;
+    static {
+        loadNativeLibraries();
+        tryGtk = Boolean.getBoolean("awt.robot.gtk");
+    }
+
+    private static boolean isGtkSupported =  false;
+    private static volatile boolean useGtk;
     private X11GraphicsConfig   xgc = null;
+
     /*
      * native implementation uses some static shared data (pipes, processes)
      * so use a class lock to synchronize native method calls
@@ -49,13 +57,14 @@
         setup(tk.getNumberOfButtons(),
                 AWTAccessor.getInputEventAccessor().getButtonDownMasks());
 
-        Toolkit toolkit = Toolkit.getDefaultToolkit();
-        if (!isGtkSupported) {
-            if (toolkit instanceof UNIXToolkit
-                    && ((UNIXToolkit) toolkit).loadGTK()) {
+        boolean isGtkSupported = false;
+        if (tryGtk) {
+            if (tk instanceof UNIXToolkit && ((UNIXToolkit) tk).loadGTK()) {
                 isGtkSupported = true;
             }
         }
+
+        useGtk = (tryGtk && isGtkSupported);
     }
 
     @Override
@@ -104,7 +113,7 @@
     public int getRGBPixel(int x, int y) {
         int pixelArray[] = new int[1];
         getRGBPixelsImpl(xgc, x, y, 1, 1, xgc.getScale(), pixelArray,
-                         isGtkSupported);
+                         useGtk);
         return pixelArray[0];
     }
 
@@ -112,11 +121,12 @@
     public int [] getRGBPixels(Rectangle bounds) {
         int pixelArray[] = new int[bounds.width*bounds.height];
         getRGBPixelsImpl(xgc, bounds.x, bounds.y, bounds.width, bounds.height,
-                         xgc.getScale(), pixelArray, isGtkSupported);
+                         xgc.getScale(), pixelArray, useGtk);
         return pixelArray;
     }
 
     private static synchronized native void setup(int numberOfButtons, int[] buttonDownMasks);
+    private static native void loadNativeLibraries();
 
     private static synchronized native void mouseMoveImpl(X11GraphicsConfig xgc, int x, int y);
     private static synchronized native void mousePressImpl(int buttons);
--- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c	Mon Jul 11 20:36:13 2016 +0300
+++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c	Mon Jul 11 16:52:50 2016 +0200
@@ -27,6 +27,9 @@
     #error This file should not be included in headless library
 #endif
 
+#include "jvm_md.h"
+#include <dlfcn.h>
+
 #include "awt_p.h"
 #include "awt_GraphicsEnv.h"
 #define XK_MISCELLANY
@@ -50,11 +53,46 @@
 #include <sys/socket.h>
 #endif
 
+static Bool   (*compositeQueryExtension)   (Display*, int*, int*);
+static Status (*compositeQueryVersion)     (Display*, int*, int*);
+static Window (*compositeGetOverlayWindow) (Display *, Window);
+
 extern struct X11GraphicsConfigIDs x11GraphicsConfigIDs;
 
 static jint * masks;
 static jint num_buttons;
 
+static void *xCompositeHandle;
+
+static const char* XCOMPOSITE = JNI_LIB_NAME("Xcomposite");
+static const char* XCOMPOSITE_VERSIONED = VERSIONED_JNI_LIB_NAME("Xcomposite", "1");
+
+static Bool checkXCompositeFunctions(void) {
+    return (compositeQueryExtension   != NULL   &&
+            compositeQueryVersion     != NULL   &&
+            compositeGetOverlayWindow != NULL);
+}
+
+static void initXCompositeFunctions(void) {
+
+    if (xCompositeHandle == NULL) {
+        xCompositeHandle = dlopen(XCOMPOSITE, RTLD_LAZY | RTLD_GLOBAL);
+        if (xCompositeHandle == NULL) {
+            xCompositeHandle = dlopen(XCOMPOSITE_VERSIONED, RTLD_LAZY | RTLD_GLOBAL);
+        }
+    }
+    //*(void **)(&asyncGetCallTraceFunction)
+    if (xCompositeHandle != NULL) {
+        *(void **)(&compositeQueryExtension) = dlsym(xCompositeHandle, "XCompositeQueryExtension");
+        *(void **)(&compositeQueryVersion) = dlsym(xCompositeHandle, "XCompositeQueryVersion");
+        *(void **)(&compositeGetOverlayWindow) = dlsym(xCompositeHandle, "XCompositeGetOverlayWindow");
+    }
+
+    if (xCompositeHandle && !checkXCompositeFunctions()) {
+        dlclose(xCompositeHandle);
+    }
+}
+
 static int32_t isXTestAvailable() {
     int32_t major_opcode, first_event, first_error;
     int32_t  event_basep, error_basep, majorp, minorp;
@@ -89,6 +127,35 @@
     return isXTestAvailable;
 }
 
+static Bool hasXCompositeOverlayExtension(Display *display) {
+
+    int xoverlay = False;
+    int eventBase, errorBase;
+    if (checkXCompositeFunctions() &&
+        compositeQueryExtension(display, &eventBase, &errorBase))
+    {
+        int major = 0;
+        int minor = 0;
+
+        compositeQueryVersion(display, &major, &minor);
+        if (major > 0 || minor >= 3) {
+            xoverlay = True;
+        }
+    }
+
+    return xoverlay;
+}
+
+static jboolean isXCompositeDisplay(Display *display, int screenNumber) {
+
+    char NET_WM_CM_Sn[25];
+    snprintf(NET_WM_CM_Sn, sizeof(NET_WM_CM_Sn), "_NET_WM_CM_S%d\0", screenNumber);
+
+    Atom managerSelection = XInternAtom(display, NET_WM_CM_Sn, 0);
+    Window owner = XGetSelectionOwner(display, managerSelection);
+
+    return owner != 0;
+}
 
 static XImage *getWindowImage(Display * display, Window window,
                               int32_t x, int32_t y,
@@ -211,7 +278,7 @@
                              jint jheight,
                              jint scale,
                              jintArray pixelArray,
-                             jboolean isGtkSupported) {
+                             jboolean useGtk) {
     XImage *image;
     jint *ary;               /* Array of jints for sending pixel values back
                               * to parent process.
@@ -238,6 +305,14 @@
 
     rootWindow = XRootWindow(awt_display, adata->awt_visInfo.screen);
 
+    if (!useGtk) {
+        if (hasXCompositeOverlayExtension(awt_display) &&
+            isXCompositeDisplay(awt_display, adata->awt_visInfo.screen))
+        {
+            rootWindow = compositeGetOverlayWindow(awt_display, rootWindow);
+        }
+    }
+
     if (!XGetWindowAttributes(awt_display, rootWindow, &attr)
             || sx + swidth <= attr.x
             || attr.x + attr.width <= sx
@@ -262,7 +337,7 @@
 
     int index;
 
-    if (isGtkSupported) {
+    if (useGtk) {
         gtk->gdk_threads_enter();
         gtk_failed = gtk->get_drawable_data(env, pixelArray, x, y, width,
                                             height, jwidth, dx, dy, scale);
@@ -454,3 +529,8 @@
 
     AWT_UNLOCK();
 }
+
+JNIEXPORT void JNICALL
+Java_sun_awt_X11_XRobotPeer_loadNativeLibraries (JNIEnv *env, jclass cls) {
+    initXCompositeFunctions();
+}