8025815: Child FileDialog of modal dialog does not get focus on Gnome
authorssadetsky
Tue, 28 Jul 2015 20:55:45 +0300
changeset 32119 1778956d7bee
parent 32118 5b0d9a83143b
child 32120 06c83c5f2912
8025815: Child FileDialog of modal dialog does not get focus on Gnome Reviewed-by: azvegint, serb
jdk/src/java.desktop/unix/classes/sun/awt/X11/GtkFileDialogPeer.java
jdk/src/java.desktop/unix/classes/sun/awt/X11/XFramePeer.java
jdk/src/java.desktop/unix/classes/sun/awt/X11/XNETProtocol.java
jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c
jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h
jdk/src/java.desktop/unix/native/libawt_xawt/awt/sun_awt_X11_GtkFileDialogPeer.c
jdk/test/java/awt/FileDialog/ModalFocus/FileDialogModalFocusTest.java
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/GtkFileDialogPeer.java	Tue Jul 28 20:39:43 2015 +0300
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/GtkFileDialogPeer.java	Tue Jul 28 20:55:45 2015 +0300
@@ -42,6 +42,8 @@
 
     // A pointer to the native GTK FileChooser widget
     private volatile long widget = 0L;
+    private long standaloneWindow;
+    private volatile boolean quit;
 
     GtkFileDialogPeer(FileDialog fd) {
         super(fd);
@@ -111,9 +113,11 @@
     public void setVisible(boolean b) {
         XToolkit.awtLock();
         try {
+            quit = !b;
             if (b) {
                 Runnable task = () -> {
                     showNativeDialog();
+                    standaloneWindow = 0;
                     fd.setVisible(false);
                 };
                 new ManagedLocalsThread(task).start();
@@ -128,7 +132,14 @@
 
     @Override
     public void dispose() {
-        quit();
+        XToolkit.awtLock();
+        try {
+            quit = true;
+            quit();
+        }
+        finally {
+            XToolkit.awtUnlock();
+        }
         super.dispose();
     }
 
@@ -144,6 +155,17 @@
         // have delegated to FileDialog#setFile
     }
 
+    protected void requestXFocus(long time, boolean timeProvided) {
+        if(standaloneWindow == 0) {
+            super.requestXFocus(time, timeProvided);
+            return;
+        }
+        XNETProtocol net_protocol = XWM.getWM().getNETProtocol();
+        if (net_protocol != null) {
+            net_protocol.setActiveWindow(standaloneWindow);
+        }
+    }
+
     @Override
     public void setFilenameFilter(FilenameFilter filter) {
         // We do not implement this method because we
@@ -170,7 +192,21 @@
                 dirname = file.getParent();
             }
         }
-        run(fd.getTitle(), fd.getMode(), dirname, filename,
-            fd.getFilenameFilter(), fd.isMultipleMode(), fd.getX(), fd.getY());
+        if (!quit) {
+            run(fd.getTitle(), fd.getMode(), dirname, filename,
+                    fd.getFilenameFilter(), fd.isMultipleMode(), fd.getX(), fd.getY());
+        }
+    }
+
+    /**
+     * Called by native code when GTK dialog is created.
+     */
+    boolean setWindow(long xid) {
+        if (!quit && widget != 0) {
+            standaloneWindow = xid;
+            requestXFocus();
+            return true;
+        }
+        return false;
     }
 }
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XFramePeer.java	Tue Jul 28 20:39:43 2015 +0300
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XFramePeer.java	Tue Jul 28 20:55:45 2015 +0300
@@ -289,7 +289,7 @@
 
             XNETProtocol net_protocol = XWM.getWM().getNETProtocol();
             if (net_protocol != null) {
-                net_protocol.setActiveWindow(this);
+                net_protocol.setActiveWindow(getWindow());
             }
             xSetVisible(true);
         }
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XNETProtocol.java	Tue Jul 28 20:39:43 2015 +0300
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XNETProtocol.java	Tue Jul 28 20:55:45 2015 +0300
@@ -326,7 +326,7 @@
         return res;
     }
 
-    public void setActiveWindow(XWindow window) {
+    public void setActiveWindow(long window) {
         if (!active() || !checkProtocol(XA_NET_SUPPORTED, XA_NET_ACTIVE_WINDOW)) {
             return;
         }
@@ -336,7 +336,7 @@
         msg.set_type(XConstants.ClientMessage);
         msg.set_message_type(XA_NET_ACTIVE_WINDOW.getAtom());
         msg.set_display(XToolkit.getDisplay());
-        msg.set_window(window.getWindow());
+        msg.set_window(window);
         msg.set_format(32);
         msg.set_data(0, 1);
         msg.set_data(1, XToolkit.getCurrentServerTime());
--- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c	Tue Jul 28 20:39:43 2015 +0300
+++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c	Tue Jul 28 20:55:45 2015 +0300
@@ -576,6 +576,7 @@
     fp_gtk_file_chooser_get_filenames = dl_symbol(
             "gtk_file_chooser_get_filenames");
     fp_gtk_g_slist_length = dl_symbol("g_slist_length");
+    fp_gdk_x11_drawable_get_xid = dl_symbol("gdk_x11_drawable_get_xid");
 }
 
 gboolean gtk2_load(JNIEnv *env)
--- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h	Tue Jul 28 20:39:43 2015 +0300
+++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h	Tue Jul 28 20:55:45 2015 +0300
@@ -27,6 +27,7 @@
 
 #include <stdlib.h>
 #include <jni.h>
+#include <X11/X.h>
 
 #define _G_TYPE_CIC(ip, gt, ct)       ((ct*) ip)
 #define G_TYPE_CHECK_INSTANCE_CAST(instance, g_type, c_type)    (_G_TYPE_CIC ((instance), (g_type), c_type))
@@ -820,6 +821,7 @@
 void (*fp_gtk_main)(void);
 guint (*fp_gtk_main_level)(void);
 gchar* (*fp_g_path_get_dirname) (const gchar *file_name);
+XID (*fp_gdk_x11_drawable_get_xid) (GdkWindow *drawable);
 
 /**
  * This function is available for GLIB > 2.20, so it MUST be
--- a/jdk/src/java.desktop/unix/native/libawt_xawt/awt/sun_awt_X11_GtkFileDialogPeer.c	Tue Jul 28 20:39:43 2015 +0300
+++ b/jdk/src/java.desktop/unix/native/libawt_xawt/awt/sun_awt_X11_GtkFileDialogPeer.c	Tue Jul 28 20:55:45 2015 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2015, 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
@@ -27,6 +27,7 @@
 #include <stdio.h>
 #include <jni_util.h>
 #include <string.h>
+#include <X11/X.h>
 #include "gtk2_interface.h"
 #include "sun_awt_X11_GtkFileDialogPeer.h"
 #include "java_awt_FileDialog.h"
@@ -38,6 +39,7 @@
 static jmethodID filenameFilterCallbackMethodID = NULL;
 static jmethodID setFileInternalMethodID = NULL;
 static jfieldID  widgetFieldID = NULL;
+static jmethodID  setWindowMethodID = NULL;
 
 JNIEXPORT void JNICALL Java_sun_awt_X11_GtkFileDialogPeer_initIDs
 (JNIEnv *env, jclass cx)
@@ -54,6 +56,10 @@
 
     widgetFieldID = (*env)->GetFieldID(env, cx, "widget", "J");
     DASSERT(widgetFieldID != NULL);
+    CHECK_NULL(widgetFieldID);
+
+    setWindowMethodID = (*env)->GetMethodID(env, cx, "setWindow", "(J)Z");
+    DASSERT(setWindowMethodID != NULL);
 }
 
 static gboolean filenameFilterCallback(const GtkFileFilterInfo * filter_info, gpointer obj)
@@ -401,7 +407,11 @@
 
     fp_gtk_widget_show(dialog);
 
-    fp_gtk_main();
+    XID xid = fp_gdk_x11_drawable_get_xid(dialog->window);
+    if( (*env)->CallBooleanMethod(env, jpeer, setWindowMethodID, xid) ) {
+        fp_gtk_main();
+    }
+
     fp_gdk_threads_leave();
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/FileDialog/ModalFocus/FileDialogModalFocusTest.java	Tue Jul 28 20:55:45 2015 +0300
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+   @bug 8025815
+   @summary Child FileDialog of modal dialog does not get focus on Gnome
+   @author Semyon Sadetsky
+  */
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class FileDialogModalFocusTest {
+    public static void main(String[] args) throws Exception {
+        Frame frame = new Frame();
+        FileDialog fileDialog = new FileDialog((Frame) null);
+        test(frame, fileDialog);
+        frame = new Frame();
+        fileDialog = new FileDialog(frame);
+        test(frame, fileDialog);
+        System.out.println("ok");
+    }
+
+    private static void test(final Frame frame, final FileDialog fileDialog)
+            throws InterruptedException, InvocationTargetException,
+            AWTException {
+        Button button = new Button();
+        button.setBackground(Color.RED);
+        button.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                fileDialog.setVisible(true);
+            }
+        });
+        frame.add(button);
+        frame.setSize(200, 200);
+        EventQueue.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                frame.setVisible(true);
+            }
+        });
+        Robot robot = new Robot();
+        robot.setAutoDelay(200);
+        robot.waitForIdle();
+        Point point = button.getLocationOnScreen();
+        point.translate(100, 100);
+        robot.mouseMove(point.x, point.y);
+        robot.mousePress(InputEvent.BUTTON1_MASK);
+        robot.mouseRelease(InputEvent.BUTTON1_MASK);
+        int delay = 0;
+        while (frame.isFocused() && delay < 2000) {
+            robot.delay(50);
+            delay += 50;
+        }
+        ReentrantLock lock = new ReentrantLock();
+        Condition condition = lock.newCondition();
+        button.addComponentListener(new ComponentAdapter() {
+            @Override
+            public void componentResized(ComponentEvent e) {
+                lock.lock();
+                condition.signal();
+                lock.unlock();
+            }
+        });
+        lock.lock();
+        EventQueue.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
+            }
+        });
+        condition.await(5, TimeUnit.SECONDS);
+        lock.unlock();
+        robot.delay(200);
+        robot.waitForIdle();
+        EventQueue.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                button.requestFocus();
+                Point p = new Point(button.getWidth() - 10, button.getHeight() - 10);
+                SwingUtilities.convertPointToScreen(p, button);
+                robot.mouseMove(p.x, p.y);
+                robot.mousePress(InputEvent.BUTTON1_MASK);
+                robot.mouseRelease(InputEvent.BUTTON1_MASK);
+            }
+        });
+        robot.waitForIdle();
+        Point p = new Point(100, 100);
+        SwingUtilities.convertPointToScreen(p, button);
+        BufferedImage image = robot.createScreenCapture(
+                new Rectangle(p,
+                        new Dimension(button.getWidth() - 200,
+                                button.getHeight() - 200)));
+        boolean found = false;
+        for (int x = 0; x < image.getWidth(); x+=50) {
+            for (int y = 0; y < image.getHeight(); y+=50) {
+                if( (image.getRGB(x, y) & 0x00FFFF) != 0 ) {
+                    found = true;
+                    break;
+                };
+            }
+        }
+        frame.dispose();
+        robot.waitForIdle();
+        fileDialog.dispose();
+        if(!found) {
+            throw new RuntimeException("file chooser is underneath");
+        }
+    }
+}
\ No newline at end of file