Hi,

I committed this patch to implement Component.getMousePosition() and Container.getMousePosition(boolean).

I also reverted Roman's 2007-09-11 changes to gnu_java_awt_peer_gtk_GtkWindowPeer.c. As I mentioned here:

http://developer.classpath.org/pipermail/classpath-patches/2007-September/005610.html

the change breaks window inset calculations. To accomplish Roman's original goal, the reverted sections should be ifdef'd based on the presence or absence of X.

I've attached a test case that demonstrates the new getMousePosition methods. It also demonstrates the window inset breakage (compare screenshots of IcedTea's and GNU Classpath's button placement relative to the window frame).

Tom
Index: gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java,v
retrieving revision 1.17
diff -u -r1.17 GdkGraphicsEnvironment.java
--- gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java	25 Apr 2007 14:53:03 -0000	1.17
+++ gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java	27 Nov 2007 21:51:55 -0000
@@ -155,6 +155,7 @@
    * Used by GtkMouseInfoPeer.
    */ 
   native int[] getMouseCoordinates();
+  native boolean isWindowUnderMouse(GtkWindowPeer windowPeer);
   
   public WritableRaster createRaster(ColorModel cm, SampleModel sm)
   {
Index: gnu/java/awt/peer/gtk/GtkComponentPeer.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java,v
retrieving revision 1.129
diff -u -r1.129 GtkComponentPeer.java
--- gnu/java/awt/peer/gtk/GtkComponentPeer.java	23 Aug 2007 20:09:53 -0000	1.129
+++ gnu/java/awt/peer/gtk/GtkComponentPeer.java	27 Nov 2007 21:51:55 -0000
@@ -253,20 +253,10 @@
   public Point getLocationOnScreen () 
   { 
     int point[] = new int[2];
-    if( this instanceof WindowPeer )
-      {
-        if (Thread.currentThread() == GtkMainThread.mainThread)
-          gtkWindowGetLocationOnScreenUnlocked (point);
-        else
-          gtkWindowGetLocationOnScreen (point);
-      }
+    if (Thread.currentThread() == GtkMainThread.mainThread)
+        gtkWidgetGetLocationOnScreenUnlocked (point);
     else
-      {
-        if (Thread.currentThread() == GtkMainThread.mainThread)
-          gtkWidgetGetLocationOnScreenUnlocked (point);
-        else
-          gtkWidgetGetLocationOnScreen (point);
-      }
+        gtkWidgetGetLocationOnScreen (point);
     return new Point (point[0], point[1]);
   }
 
Index: gnu/java/awt/peer/gtk/GtkMouseInfoPeer.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkMouseInfoPeer.java,v
retrieving revision 1.3
diff -u -r1.3 GtkMouseInfoPeer.java
--- gnu/java/awt/peer/gtk/GtkMouseInfoPeer.java	12 Feb 2007 21:39:20 -0000	1.3
+++ gnu/java/awt/peer/gtk/GtkMouseInfoPeer.java	27 Nov 2007 21:51:55 -0000
@@ -60,17 +60,7 @@
   
   public boolean isWindowUnderMouse(Window w)
   {
-    int[] coords = gde.getMouseCoordinates();
-    GraphicsDevice[] gds = gde.getScreenDevices();
-
-    // Check if the screen  of the Window and the cursor match
-    if( gds[ coords[0] ] != w.getGraphicsConfiguration().getDevice() )
-      return false;
-
-    // Return the bounds-check.
-    Point p = w.getLocationOnScreen();
-    return (coords[1] >= p.x && coords[1] < p.x + w.getWidth() &&
-	    coords[2] >= p.y && coords[2] < p.y + w.getHeight() );
-    }
+    return gde.isWindowUnderMouse((GtkWindowPeer) w.getPeer());
+  }
 }
 
Index: gnu/java/awt/peer/gtk/GtkWindowPeer.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java,v
retrieving revision 1.61
diff -u -r1.61 GtkWindowPeer.java
--- gnu/java/awt/peer/gtk/GtkWindowPeer.java	22 Jun 2007 14:27:57 -0000	1.61
+++ gnu/java/awt/peer/gtk/GtkWindowPeer.java	27 Nov 2007 21:51:55 -0000
@@ -45,6 +45,7 @@
 import java.awt.Frame;
 import java.awt.Graphics;
 import java.awt.KeyboardFocusManager;
+import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.Window;
 import java.awt.event.ComponentEvent;
@@ -392,6 +393,16 @@
                           clickCount, popupTrigger);
   }
 
+  public Point getLocationOnScreen()
+  {
+    int point[] = new int[2];
+    if (Thread.currentThread() == GtkMainThread.mainThread)
+      gtkWindowGetLocationOnScreenUnlocked(point);
+    else
+      gtkWindowGetLocationOnScreen(point);
+    return new Point(point[0], point[1]);
+  }
+
   // We override this to keep it in sync with our internal
   // representation.
   public Rectangle getBounds()
Index: include/gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.h
===================================================================
RCS file: /sources/classpath/classpath/include/gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.h,v
retrieving revision 1.6
diff -u -r1.6 gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.h
--- include/gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.h	25 Apr 2007 14:53:03 -0000	1.6
+++ include/gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.h	27 Nov 2007 21:51:55 -0000
@@ -1,10 +1,10 @@
 /* DO NOT EDIT THIS FILE - it is machine generated */
 
+#include <jni.h>
+
 #ifndef __gnu_java_awt_peer_gtk_GdkGraphicsEnvironment__
 #define __gnu_java_awt_peer_gtk_GdkGraphicsEnvironment__
 
-#include <jni.h>
-
 #ifdef __cplusplus
 extern "C"
 {
@@ -17,6 +17,7 @@
 JNIEXPORT jint JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphicsEnvironment_nativeGetNumFontFamilies (JNIEnv *env, jobject);
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphicsEnvironment_nativeGetFontFamilies (JNIEnv *env, jobject, jobjectArray);
 JNIEXPORT jintArray JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphicsEnvironment_getMouseCoordinates (JNIEnv *env, jobject);
+JNIEXPORT jboolean JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphicsEnvironment_isWindowUnderMouse (JNIEnv *env, jobject, jobject);
 
 #ifdef __cplusplus
 }
Index: java/awt/Component.java
===================================================================
RCS file: /sources/classpath/classpath/java/awt/Component.java,v
retrieving revision 1.156
diff -u -r1.156 Component.java
--- java/awt/Component.java	1 Mar 2007 01:01:00 -0000	1.156
+++ java/awt/Component.java	27 Nov 2007 21:51:55 -0000
@@ -5834,6 +5834,62 @@
   }
 
   /**
+   * Returns the mouse pointer position relative to this Component's
+   * top-left corner.
+   *
+   * @return relative mouse pointer position
+   *
+   * @throws HeadlessException if in a headless environment
+   */
+  public Point getMousePosition() throws HeadlessException
+  {
+    return getMousePositionHelper(true);
+  }
+
+  Point getMousePositionHelper(boolean allowChildren) throws HeadlessException
+  {
+    if (GraphicsEnvironment.isHeadless())
+      throw new HeadlessException("can't get mouse position"
+                                  + " in headless environment");
+    if (!isShowing())
+      return null;
+
+    Component parent = this;
+    int windowRelativeXOffset = 0;
+    int windowRelativeYOffset = 0;
+    while (parent != null && !(parent instanceof Window))
+      {
+        windowRelativeXOffset += parent.getX();
+        windowRelativeYOffset += parent.getY();
+        parent = parent.getParent();
+      }
+    if (parent == null)
+      return null;
+
+    Window window = (Window) parent;
+    if (!Toolkit.getDefaultToolkit()
+        .getMouseInfoPeer().isWindowUnderMouse(window))
+      return null;
+
+    PointerInfo info = MouseInfo.getPointerInfo();
+    Point mouseLocation = info.getLocation();
+    Point windowLocation = window.getLocationOnScreen();
+
+    int x = mouseLocation.x - windowLocation.x;
+    int y = mouseLocation.y - windowLocation.y;
+
+    if (!mouseOverComponent(window.getComponentAt(x, y), allowChildren))
+      return null;
+
+    return new Point(x - windowRelativeXOffset, y - windowRelativeYOffset);
+  }
+
+  boolean mouseOverComponent(Component component, boolean allowChildren)
+  {
+    return component == this;
+  }
+
+  /**
    * This method is used to implement transferFocus(). CHILD is the child
    * making the request. This is overridden by Container; when called for an
    * ordinary component there is no child and so we always return null.
Index: java/awt/Container.java
===================================================================
RCS file: /sources/classpath/classpath/java/awt/Container.java,v
retrieving revision 1.111
diff -u -r1.111 Container.java
--- java/awt/Container.java	10 Dec 2006 20:25:43 -0000	1.111
+++ java/awt/Container.java	27 Nov 2007 21:51:57 -0000
@@ -1098,6 +1098,33 @@
   }
 
   /**
+   * Returns the mouse pointer position relative to this Container's
+   * top-left corner.  If allowChildren is false, the mouse pointer
+   * must be directly over this container.  If allowChildren is true,
+   * the mouse pointer may be over this container or any of its
+   * descendents.
+   *
+   * @param allowChildren true to allow descendents, false if pointer
+   * must be directly over Container.
+   *
+   * @return relative mouse pointer position
+   *
+   * @throws HeadlessException if in a headless environment
+   */
+  public Point getMousePosition(boolean allowChildren) throws HeadlessException
+  {
+    return super.getMousePositionHelper(allowChildren);
+  }
+
+  boolean mouseOverComponent(Component component, boolean allowChildren)
+  {
+    if (allowChildren)
+      return isAncestorOf(component);
+    else
+      return component == this;
+  }
+
+  /**
    * Returns the component located at the specified point.  This is done
    * by checking whether or not a child component claims to contain this
    * point.  The first child component that does is returned.  If no
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.c
===================================================================
RCS file: /sources/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.c,v
retrieving revision 1.9
diff -u -r1.9 gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.c	21 Jun 2007 14:07:01 -0000	1.9
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.c	27 Nov 2007 21:51:57 -0000
@@ -263,3 +263,37 @@
 
   return retArray;
 }
+
+JNIEXPORT jboolean JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphicsEnvironment_isWindowUnderMouse
+(JNIEnv *env, jobject obj, jobject windowPeer)
+{
+  GdkDisplay *display = NULL;
+  gint x = 0;
+  gint y = 0;
+  GtkWidget *windowToTest = NULL;
+  GdkWindow *windowAtPointer = NULL;
+  jboolean retVal = JNI_FALSE;
+
+  display = (GdkDisplay *) gtkpeer_get_display (env, obj);
+  g_assert (display != NULL);
+
+  windowToTest = (GtkWidget *) gtkpeer_get_widget (env, windowPeer);
+
+  gdk_threads_enter ();
+
+  windowAtPointer = gdk_display_get_window_at_pointer (display, &x, &y);
+
+  while (windowAtPointer
+         && windowAtPointer != windowToTest->window)
+    windowAtPointer = gdk_window_get_parent (windowAtPointer);
+
+  gdk_threads_leave ();
+
+  if (windowAtPointer)
+    retVal = JNI_TRUE;
+  else
+    retVal = JNI_FALSE;
+
+  return retVal;
+}
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkWindowPeer.c
===================================================================
RCS file: /sources/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkWindowPeer.c,v
retrieving revision 1.73
diff -u -r1.73 gnu_java_awt_peer_gtk_GtkWindowPeer.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkWindowPeer.c	11 Sep 2007 09:48:50 -0000	1.73
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkWindowPeer.c	27 Nov 2007 21:51:57 -0000
@@ -40,6 +40,8 @@
 #include "gtkpeer.h"
 #include "gnu_java_awt_peer_gtk_GtkWindowPeer.h"
 #include <gdk/gdkprivate.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
 #include <gdk/gdkkeysyms.h>
 
 #define AWT_WINDOW_CLOSING 201
@@ -1037,6 +1039,12 @@
                                       int *top, int *left,
                                       int *bottom, int *right);
 
+static void request_frame_extents (GtkWidget *window);
+
+static Bool property_notify_predicate (Display *display,
+                                       XEvent  *xevent,
+                                       XPointer arg);
+
 static gboolean window_delete_cb (GtkWidget *widget, GdkEvent *event,
 			      jobject peer);
 static void window_destroy_cb (GtkWidget *widget, GdkEvent *event,
@@ -1142,6 +1150,12 @@
   unsigned long **extents;
 };
 
+union atom_list_union
+{
+  guchar **gu_extents;
+  Atom **atom_list;
+};
+
 JNIEXPORT void JNICALL
 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_create
   (JNIEnv *env, jobject obj, jint type, jboolean decorated, jobject parent)
@@ -1503,22 +1517,118 @@
 window_get_frame_extents (GtkWidget *window,
                           int *top, int *left, int *bottom, int *right)
 {
-  GdkRectangle extents;
-  GdkWindow* gdkWindow;
+  unsigned long *extents = NULL;
+  union extents_union gu_ex;
+
+  /* Guess frame extents in case _NET_FRAME_EXTENTS is not
+     supported. */
+  if (!gtk_window_get_decorated (GTK_WINDOW (window)))
+    {
+      *top = 0;
+      *left = 0;
+      *bottom = 0;
+      *right = 0;
+
+      return;
+    }
+
+  *top = 23;
+  *left = 6;
+  *bottom = 6;
+  *right = 6;
+
+  /* Request that the window manager set window's
+     _NET_FRAME_EXTENTS property. */
+  request_frame_extents (window);
+
+  /* Attempt to retrieve window's frame extents. */
+  gu_ex.extents = &extents;
+  if (gdk_property_get (window->window,
+                        gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE),
+                        gdk_atom_intern ("CARDINAL", FALSE),
+                        0,
+                        sizeof (unsigned long) * 4,
+                        FALSE,
+                        NULL,
+                        NULL,
+                        NULL,
+                        gu_ex.gu_extents))
+    {
+      *left = extents [0];
+      *right = extents [1];
+      *top = extents [2];
+      *bottom = extents [3];
+    }
+}
+
+static Atom extents_atom = 0;
+
+/* Requests that the window manager set window's
+   _NET_FRAME_EXTENTS property. */
+static void
+request_frame_extents (GtkWidget *window)
+{
+  const char *request_str = "_NET_REQUEST_FRAME_EXTENTS";
+  GdkAtom request_extents = gdk_atom_intern (request_str, FALSE);
+
+  /* Check if the current window manager supports
+     _NET_REQUEST_FRAME_EXTENTS. */
+  if (gdk_net_wm_supports (request_extents))
+    {
+      GdkDisplay *display = gtk_widget_get_display (window);
+      Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
 
-  gint x, y, w, h;
+      GdkWindow *root_window = gdk_get_default_root_window ();
+      Window xroot_window = GDK_WINDOW_XID (root_window);
 
-  gdkWindow = window->window;
+      Atom extents_request_atom =
+	gdk_x11_get_xatom_by_name_for_display (display, request_str);
 
-  gdk_window_get_frame_extents(gdkWindow, &extents);
-  gdk_drawable_get_size(gdkWindow, &w, &h);
-  gdk_window_get_origin(gdkWindow, &x, &y);
+      XEvent xevent;
+      XEvent notify_xevent;
+
+      unsigned long window_id = GDK_WINDOW_XID (GDK_DRAWABLE(window->window));
+
+      if (!extents_atom)
+	{
+	  const char *extents_str = "_NET_FRAME_EXTENTS";
+	  extents_atom =
+	    gdk_x11_get_xatom_by_name_for_display (display, extents_str);
+	}
+
+      xevent.xclient.type = ClientMessage;
+      xevent.xclient.message_type = extents_request_atom;
+      xevent.xclient.display = xdisplay;
+      xevent.xclient.window = window_id;
+      xevent.xclient.format = 32;
+      xevent.xclient.data.l[0] = 0;
+      xevent.xclient.data.l[1] = 0;
+      xevent.xclient.data.l[2] = 0;
+      xevent.xclient.data.l[3] = 0;
+      xevent.xclient.data.l[4] = 0;
+
+      XSendEvent (xdisplay, xroot_window, False,
+		  (SubstructureRedirectMask | SubstructureNotifyMask),
+                  &xevent);
 
-  *left = extents.x - x;
-  *top = extents.y - y;
-  *right = extents.width - w - *left;
-  *bottom = extents.height - h - *top;
+      XIfEvent(xdisplay, &notify_xevent,
+	       property_notify_predicate, (XPointer) &window_id);
+    }
+}
 
+static Bool
+property_notify_predicate (Display *xdisplay __attribute__((unused)),
+                           XEvent  *event,
+                           XPointer window_id)
+{
+  unsigned long *window = (unsigned long *) window_id;
+
+  if (event->xany.type == PropertyNotify
+      && event->xany.window == *window
+      && event->xproperty.atom == extents_atom)
+    return True;
+  else
+    return False;
 }
 
 static gboolean
import java.awt.Button;
import java.awt.Frame;

public class GetMousePositionTest
{
  public static void main(String[] args)
  {
    Frame f = new Frame("GetMousePositionTest");
    Button b = new Button("Button");
    f.setLayout(null);
    f.add(b);
    b.setSize(60, 60);
    b.setLocation(40, 80);
    f.setSize(300, 300);
    f.show();

    while (true)
      {
        try
          {
            System.out.println("[Frame,  children   ] "
                               + f.getMousePosition(true));
            System.out.println("[Frame,  no children] "
                               + f.getMousePosition(false));
            System.out.println("[Frame,  component  ] "
                               + f.getMousePosition());
            System.out.println("[Button, component  ] "
                               + b.getMousePosition());
            System.out.println("");
            Thread.sleep(1000);
          }
        catch (Exception e)
          {
            e.printStackTrace();
          }
      }
  }
}

Reply via email to