Project: SWT
Bug 177368 Display.getCursorControl returning null for complex Xlib widget on Eclipse/Gtk
Reported: 2007-03-14 12:40:06 | Status: resolved fixed

Build ID: M20070212-1330 Steps To Reproduce: 1. We have a set of existing widgets created using Xlib which have child windows. 2. We create an SWT control representing the top-level widget and use GtkSocket/plug to parent our widget in the SWT control. 3. If we create a viewpart with this control and try dragging another view over this view, then the cursor will change to the cursor that appears when you drag a view over the desktop. Stepping through the code reveals that when Display.getCursorControl() is called from DragUtil.java, the result is null, so Eclipse thinks we've moved outside the bounds of the Eclipse window. More information: I did some investigation on the problem and noticed that getCursorControl is calling gdk_window_at_pointer to get the GdkWindow that the mouse pointer is currently over. Unfortunately, the child Xlib windows are "blocking" the parent GdkWindow from being seen, so gdk_window_at_pointer is returning 0. I experimented with using gdk_window_foreign_new on all the child windows, so that every window in the embedded widget is represented by a GdkWindow. However, this causes a problem when getCursorControl attempts to lookup a GtkWidget handle for the GdkWindow returned by gdk_window_at_pointer. It wouldn't be practical for us to create a GtkWidget for every child window. What I propose is: There is logic already for where the cursor is over a child GtkWidget but that widget isn't directly represented by an SWT control. In that case, getCursorControl will walk up the tree until it finds the top level GtkWidget that is represented by a Java control. It's this Java/SWT control that gets returned. We should extend the tree walking logic, so that we can work up the tree from an Xlib window until we find a parent GdkWindow that has an associated GtkWidget and then continue walking until we find the associated SWT control. That way, embedding foreign controls should work as well as it does in Windows. Here is some possible code that would do the above: org.eclipse.swt.Control getCursorControl() { int handle = OS.getCursorControlHandle(); //walk up the window tree to find the first one who's created by SWT. //We have to walk up here for that there are cases the application could create //child GtkWidget under an SWT control from JNI code, such as GtkSocket. for( ; handle != 0; handle = OS.GetParent( handle ) ) { org.eclipse.swt.Widget widget = Display.findWidget( handle ); if( widget != null )return (Control)widget; } return null; } C code for OS.getCursorControlHandle(): Window GetParentWindow( Display *display, Window xid ) { Window root = 0, parent = 0, *children = 0; unsigned int nchildren = 0; if( !XQueryTree( display, xid, &root, &parent, &children, &nchildren ) )return 0; if( children )XFree( children ); return parent; } GtkWidget *GtkWidgetFromWindow( Window xid ) { GdkWindow *gdk_window = gdk_window_lookup( xid ); if( !gdk_window )return NULL; GtkWidget *widget; gdk_window_get_user_data( gdk_window, (void **)&widget ); return widget; } extern "C" jint Java_com_ibm_yourpackage_yourclass_getCursorControlHandle() { unsigned int mask; GtkWidget *widget = NULL; int root_x, root_y, x, y; Window root = None, parent, child, grand_child; //1. to avoid crash from an X error gdk_error_trap_push(); //2. use XQueryPointer and XTranslateCoordinates to find the smallest cursor window root = XDefaultRootWindow(gdk_display); if( !XQueryPointer( gdk_display, root, &root, &child, &root_x, &root_y, &x, &y, &mask ) ) goto lExit; for( (parent = root, x = root_x, y = root_y); child != None ; parent = child, child = grand_child ) { if( !XTranslateCoordinates( gdk_display, parent, child, x, y, &x, &y, &grand_child ) ) goto lExit; } child = parent; //3, walk up the ancestors of the cursor window, to find the GtkWidget. for( ; child && child != root; child = GetParentWindow( gdk_display, child ) ) { widget = GtkWidgetFromWindow( child ); if( widget )break; } lExit: gdk_error_trap_pop(); return (jint)widget; }

Files changed in fix:
bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java