NewtCanvasSWT Linux Exception with GTK_VERSION

classic Classic list List threaded Threaded
31 messages Options
12
Reply | Threaded
Open this post in threaded view
|

NewtCanvasSWT Linux Exception with GTK_VERSION

Marcel
Hello, I'm using JOGL 2.3.2 with an Eclipse RCP application. Recently I changed the SWT_AWT Canvas
to NewtCanvasSWT.

On Windows everything works fine, on MacOSX I have a problem with the parenting when I switch a perspective or change to fullscreen (coordinates are still from the parent composite!) but more important on Linux (tested on Ubuntu 18.10) the following error occurs (shortened to the important exception):

!ENTRY org.eclipse.e4.ui.workbench.swt 4 2 2019-01-29 12:09:52.725
!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.e4.ui.workbench.swt".
!STACK 0
java.lang.ExceptionInInitializerError
        at com.jogamp.newt.swt.NewtCanvasSWT.<init>(NewtCanvasSWT.java:127)
       
Caused by: com.jogamp.nativewindow.NativeWindowException: java.lang.NoSuchFieldException: GTK_VERSION
        at com.jogamp.nativewindow.swt.SWTAccessor.<clinit>(SWTAccessor.java:203)
        ... 154 more
Caused by: java.lang.NoSuchFieldException: GTK_VERSION
        at java.base/java.lang.Class.getField(Class.java:2000)
        at com.jogamp.nativewindow.swt.SWTAccessor.<clinit>(SWTAccessor.java:183)
        ... 154 more


Is there a workaround or new built available for the bug. I think this bug was already mentioned here:

http://forum.jogamp.org/com-jogamp-opengl-swt-GLCanvas-does-not-work-with-Eclipse-4-8-under-Linux-td4039229.html
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Wade Walker
Administrator
Would an unofficial build work for you? I could potentially take a look at this (since SWT/Eclipse is the area where I use JOGL as well), but I can only make unofficial builds that I post in my own GitHub (I did one to make JOGL work with Java 11, for example).
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

gouessej
Administrator
In reply to this post by Marcel
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Marcel
Thank you both for the answers.

@gouessej Is this fix somewhere already available or will be 2.3.3 released any time soon?

@wade walker I use OpenJDK 11, too and added several calls, e.g. like --add-opens java.desktop/sun.java2d.opengl=ALL-UNNAMED to avoid the warnings as vm arguments.
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Wade Walker
Administrator
Julien's fix looks like it should work; I'll try it out on Ubuntu. I need to rebuild my VM first though; my previous one is too old to be representative. I found a JUnit test that will sensitize this error, so I'll first confirm that it fails when I upgrade the SWT JARs in the build.
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

gouessej
Administrator
If you have a simple program to test that, I can run it under Mageia Linux.
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Marcel
This post was updated on .
Apropos, when I close the perspective which embedds the NewtCanvasSWT (I have several perspectives) I get an widget disposed exception in method getParentLocationOnScreen() from class NewtCanvasSWT on MacOSX.

But not when I close the view directly.

I simply copied the NewtCanvasSWT class in my workspace and changed the method to the following to avoid the exception on MacOSX (see NewtCanvasSWT.this.isDisposed() == false):

private Point getParentLocationOnScreen() {
                final org.eclipse.swt.graphics.Point[] parentLoc = new org.eclipse.swt.graphics.Point[] { null };
                SWTAccessor.invoke(true, new Runnable() {
                        public void run() {
                                if (NewtCanvasSWT.this.isDisposed() == false) {
                                        parentLoc[0] = getParent().toDisplay(0, 0);
                                }
                                else {
                                        parentLoc[0] = new org.eclipse.swt.graphics.Point(0,0);
                                }
                        }
                });
                return new Point(parentLoc[0].x, parentLoc[0].y);
        }

I also changed the Debug variable in SWTAccessor (which I also simply copied) to false because i by default it prints debug messages on the error stream, see:

https://github.com/sgothel/jogl/blob/master/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java#L56

https://github.com/sgothel/jogl/blob/master/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java#L225

@gouessej, Wade Walker: I could also test your fix on Ubuntu 18.10
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

gouessej
Administrator
Please can you mention your change somewhere in our bug tracker?
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Wade Walker
Administrator
Making progress! I got my VMWare virtual machine set up with Ubuntu 18.04, then installed Java/Eclipse/Ant/gcc/etc, built gluegen/JOGL from source, and verified that the unit test com.jogamp.opengl.test.junit.jogl.swt.TestNewtCanvasSWTGLn hits the line of code in com.jogamp.nativewindow.swt.SWTAccessor that accesses GTK_VERSION.

The next step is to update JOGL's copy of the SWT JARs to the latest version, then verify that the OP's bug is replicated. After that, it should be a straightforward fix as Julien explained in the bug ticket.
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Wade Walker
Administrator
I replicated the OP's bug, and started on the fix. It's a little more involved than just getting GTK_VERSION from org.eclipse.swt.internal.gtk.GTK instead of org.eclipse.swt.internal.gtk.OS. There are also several functions that migrated from org.eclipse.swt.internal.gtk.OS to a new class org.eclipse.swt.internal.gtk.GDK in newer versions of SWT, and the function gdk_window_set_back_pixmap() seems to have disappeared altogether in newer versions of GTK. Luckily, we're only ever setting the back pixmap to null, so we can replace that call with gdk_window_set_background_pattern() as they did in  https://git.eclipse.org/r/#/c/56351/3/bundles/org.eclipse.swt/Eclipse+SWT/gtk/org/eclipse/swt/widgets/Composite.java.
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Marcel
In reply to this post by gouessej
Yes, I would do so but I need an account for that.
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Marcel
Here my regeistered account.
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

gouessej
Administrator
In reply to this post by Marcel
Do you want me to create an account for you in our bug tracker?
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Wade Walker
Administrator
I put in the fixes, and the unit tests seem to work, but they throw some GTK errors/exceptions at shutdown:

SWT:14860): Gdk-CRITICAL **: 17:47:49.569: The window 0x7f71e51ac640 already has a drawing context. You cannot call gdk_window_begin_draw_frame() without calling gdk_window_end_draw_frame() first.

(SWT:14860): Gdk-CRITICAL **: 17:47:49.569: gdk_drawing_context_get_cairo_context: assertion 'GDK_IS_DRAWING_CONTEXT (context)' failed

I'll need to dig into this a little and see what's going on. It seems like GTK 3+ throws a lot of these (judging from what I've seen in the logs of other programs), but I'm not sure how serious it is or how hard it will be to fix.
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Marcel
In reply to this post by gouessej
I f that is necessary to file a bug here then I need ann account.
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Wade Walker
Administrator
In reply to this post by Wade Walker
I confirmed that the new bugs I see only occur with SWT 4.10/GTK 3.22.30. The previous version we used, SWT 4.3/GTK 2.24.32, didn't show this behavior. They may be due to my fix for the deprecated GTK gdk_window_set_back_pixmap(), which I replaced with gdk_window_set_background_pattern() after finding that Eclipse did the same thing. I'll dig in here and see if there's another solution.
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Wade Walker
Administrator
OK, it doesn't seem like the gdk_window_set_back_pixmap() --> gdk_window_set_background_pattern() replacement is causing the problem. It looks like the test is somehow consuming Cairo handles as its window proc is called, so if I let it run freely, it eats up all the handles and dies. But if I put in a breakpoint and step through slowly, it works :) So I need to figure out where in the chain a handle isn't being freed. This could be a tough error, since NewtCanvasSWT was probably never tested on GTK 3, but I'll keep poking and see what comes up.
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Wade Walker
Administrator
This bug may require Sven to do a fix. To summarize the work so far:

- When users try to use JOGL with GTK 3, they get an exception when JOGL tries to read GTK_VERSION from org.eclipse.swt.internal.gtk.OS (its location in GTK 2)

- If you fix JOGL to read GTK_VERSION from its new location in org.eclipse.swt.internal.gtk.GTK (where it moved in GTK 3), then you get another problem:

(SWT:14860): Gdk-CRITICAL **: 17:47:49.569: The window 0x7f71e51ac640 already has a drawing context. You cannot call gdk_window_begin_draw_frame() without calling gdk_window_end_draw_frame() first.

(SWT:14860): Gdk-CRITICAL **: 17:47:49.569: gdk_drawing_context_get_cairo_context: assertion 'GDK_IS_DRAWING_CONTEXT (context)' failed
org.eclipse.swt.SWTError: No more handles

- The problem shows up in JOGL unit tests like com.jogamp.opengl.test.junit.jogl.swt.preAttach_NoAnimator()

- The problem seems to be caused by a significant change to the way drawing works between GTK 2 and GTK 3. Somehow the NewtCanvasSWT is skipping a gdk_window_end_draw_frame() somewhere. But JOGL never directly calls gdk_window_begin_draw_frame(), gdk_window_end_draw_frame(), or gdk_drawing_context_get_cairo_context(), and it's never erroneously disposing a Cairo context, so I don't know how this could be happening.

- The problem doesn't occur if you step through the code slowly in the debugger, seemingly because it can't switch frames fast enough to run out of contexts before the test ends. The gdk_window_begin_draw_frame()/gdk_window_end_draw_frame() calls are always properly paired when you set breakpoints on them.

Next I'll try running the test for a longer time in the debugger, to see if I can make it fail after more cycles, but it's not looking promising. If anyone else wants to give this a try, I can give you the GTK 3 patch that I've written so far.
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

gouessej
Administrator
Please can you post the patch somewhere? I'd like to look at it. I appreciate your dedication, thank you for the excellent job :)
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Marcel
In reply to this post by Wade Walker
I think I found a solution for the problem.

Some of the methods have been moved to the GDK class. So I defined a new variable in the SWTAccessor class:

private static final String str_OS_gdk_class = "org.eclipse.swt.internal.gtk.GDK";

Also on GTK 3 (now default on SWT) the following method is non existent anymore and has been replaced:

OS.gdk_window_set_back_pixmap (window, 0, false) replace by

OS.gdk_window_set_background_pattern(window, 0);

which is now:

GDK.gdk_window_set_background_pattern(window, 0);

See:
https://git.eclipse.org/c/platform/eclipse.platform.swt.git/commit/?id=81b32128f07db650bc1cc9e0cc9f1272b6fa65f6

So I defined a new reflection access:

d = ReflectionUtil.getClass(str_OS_gdk_class, false, SWTAccessor.class.getClassLoader());

I could now start the JOGL window.

What I now get is a no handle exeception on Ubuntu 18.10 when I switch the view. But I also found a workaround here which should solve the problem, too:

https://bugs.eclipse.org/bugs/show_bug.cgi?id=542675

If you have the time please test it. Here the complete SWTAccessor class (sorry for posting here):







----------------------------------------------------------------------------------------------------------------------------------------------




import com.jogamp.common.os.Platform;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;

import org.eclipse.swt.graphics.GCData;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;

import com.jogamp.nativewindow.AbstractGraphicsScreen;
import com.jogamp.nativewindow.NativeWindowException;
import com.jogamp.nativewindow.AbstractGraphicsDevice;
import com.jogamp.nativewindow.NativeWindowFactory;
import com.jogamp.nativewindow.VisualIDHolder;

import com.jogamp.common.util.ReflectionUtil;
import com.jogamp.common.util.VersionNumber;
import com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice;
import com.jogamp.nativewindow.windows.WindowsGraphicsDevice;
import com.jogamp.nativewindow.x11.X11GraphicsDevice;

import jogamp.nativewindow.macosx.OSXUtil;
import jogamp.nativewindow.x11.X11Lib;

public class SWTAccessor {
    private static final boolean DEBUG = false;

    private static final Field swt_control_handle;
    private static final boolean swt_uses_long_handles;

    private static Object swt_osx_init = new Object();
    private static Field swt_osx_control_view = null;
    private static Field swt_osx_view_id = null;

    private static final String nwt;
    public static final boolean isOSX;
    public static final boolean isWindows;
    public static final boolean isX11;
    public static final boolean isX11GTK;

    // X11/GTK, Windows/GDI, ..
    private static final String str_handle = "handle";

    // OSX/Cocoa
    private static final String str_osx_view = "view";  // OSX
    private static final String str_osx_id = "id";    // OSX
    // static final String str_NSView = "org.eclipse.swt.internal.cocoa.NSView";

    private static final Method swt_control_internal_new_GC;
    private static final Method swt_control_internal_dispose_GC;
    private static final String str_internal_new_GC = "internal_new_GC";
    private static final String str_internal_dispose_GC = "internal_dispose_GC";

    private static final String str_OS_gtk_class = "org.eclipse.swt.internal.gtk.GTK";
    private static final String str_OS_gdk_class = "org.eclipse.swt.internal.gtk.GDK";
    public static final Class<?> OS_gtk_class;
    private static final String str_OS_gtk_version = "GTK_VERSION";
    public static final VersionNumber OS_gtk_version;

    private static final Method OS_gtk_widget_realize;
    private static final Method OS_gtk_widget_unrealize; // optional (removed in SWT 4.3)
    private static final Method OS_GTK_WIDGET_WINDOW;
    private static final Method OS_gtk_widget_get_window;
    private static final Method OS_gdk_x11_drawable_get_xdisplay;
    private static final Method OS_gdk_x11_display_get_xdisplay;
    private static final Method OS_gdk_window_get_display;
    private static final Method OS_gdk_x11_drawable_get_xid;
    private static final Method OS_gdk_x11_window_get_xid;
    private static final Method OS_gdk_window_set_back_pixmap;

    private static final String str_gtk_widget_realize = "gtk_widget_realize";
    private static final String str_gtk_widget_unrealize = "gtk_widget_unrealize";
    private static final String str_GTK_WIDGET_WINDOW = "GTK_WIDGET_WINDOW";
    private static final String str_gtk_widget_get_window = "gtk_widget_get_window";
    private static final String str_gdk_x11_drawable_get_xdisplay = "gdk_x11_drawable_get_xdisplay";
    private static final String str_gdk_x11_display_get_xdisplay = "gdk_x11_display_get_xdisplay";
    private static final String str_gdk_window_get_display = "gdk_window_get_display";
    private static final String str_gdk_x11_drawable_get_xid = "gdk_x11_drawable_get_xid";
    private static final String str_gdk_x11_window_get_xid = "gdk_x11_window_get_xid";
    private static final String str_gdk_window_set_back_pixmap = "gdk_window_set_background_pattern";

    private static final VersionNumber GTK_VERSION_2_14_0 = new VersionNumber(2, 14, 0);
    private static final VersionNumber GTK_VERSION_2_24_0 = new VersionNumber(2, 24, 0);
    private static final VersionNumber GTK_VERSION_3_0_0  = new VersionNumber(3,  0, 0);

    private static VersionNumber GTK_VERSION(final int version) {
        // return (major << 16) + (minor << 8) + micro;
        final int micro = ( version       ) & 0xff;
        final int minor = ( version >>  8 ) & 0xff;
        final int major = ( version >> 16 ) & 0xff;
        return new VersionNumber(major, minor, micro);
    }

    static {
        AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public Object run() {
                NativeWindowFactory.initSingleton(); // last resort ..
                return null;
            } } );

        nwt = NativeWindowFactory.getNativeWindowType(false);
        isOSX = NativeWindowFactory.TYPE_MACOSX == nwt;
        isWindows = NativeWindowFactory.TYPE_WINDOWS == nwt;
        isX11 = NativeWindowFactory.TYPE_X11 == nwt;

        Field f = null;
        if( !isOSX ) {
            try {
                f = Control.class.getField(str_handle);
            } catch (final Exception ex) {
                throw new NativeWindowException(ex);
            }
        }
        swt_control_handle = f; // maybe null !

        boolean ulh;
        if (null != swt_control_handle) {
            ulh = swt_control_handle.getGenericType().toString().equals(long.class.toString());
        } else {
            ulh = Platform.is64Bit();
        }
        swt_uses_long_handles = ulh;
        // System.err.println("SWT long handles: " + swt_uses_long_handles);
        // System.err.println("Platform 64bit: "+Platform.is64Bit());

        Method m=null;
        try {
            m = ReflectionUtil.getMethod(Control.class, str_internal_new_GC, new Class[] { GCData.class });
        } catch (final Exception ex) {
            throw new NativeWindowException(ex);
        }
        swt_control_internal_new_GC = m;

        try {
            if(swt_uses_long_handles) {
                m = Control.class.getDeclaredMethod(str_internal_dispose_GC, new Class[] { long.class, GCData.class });
            } else {
                m = Control.class.getDeclaredMethod(str_internal_dispose_GC, new Class[] { int.class, GCData.class });
            }
        } catch (final NoSuchMethodException ex) {
            throw new NativeWindowException(ex);
        }
        swt_control_internal_dispose_GC = m;

        Class<?> c=null;
        Class<?> d=null;
        VersionNumber _gtk_version = new VersionNumber(0, 0, 0);
        Method m1=null, m2=null, m3=null, m4=null, m5=null, m6=null, m7=null, m8=null, m9=null, ma=null;
        final Class<?> handleType = swt_uses_long_handles  ? long.class : int.class ;
        if( isX11 ) {
            // mandatory
            try {
                ClassLoader classLoader = SWTAccessor.class.getClassLoader();
                                c = ReflectionUtil.getClass(str_OS_gtk_class, false, classLoader);
                d = ReflectionUtil.getClass(str_OS_gdk_class, false, classLoader);
                final Field field_OS_gtk_version = c.getField(str_OS_gtk_version);
                _gtk_version = GTK_VERSION(field_OS_gtk_version.getInt(null));
                m1 = c.getDeclaredMethod(str_gtk_widget_realize, handleType);
                if (_gtk_version.compareTo(GTK_VERSION_2_14_0) >= 0) {
                    m4 = c.getDeclaredMethod(str_gtk_widget_get_window, handleType);
                } else {
                    m3 = c.getDeclaredMethod(str_GTK_WIDGET_WINDOW, handleType);
                }
                if (_gtk_version.compareTo(GTK_VERSION_2_24_0) >= 0) {
                    m6 = d.getDeclaredMethod(str_gdk_x11_display_get_xdisplay, handleType);
                    m7 = d.getDeclaredMethod(str_gdk_window_get_display, handleType);
                } else {
                    m5 = d.getDeclaredMethod(str_gdk_x11_drawable_get_xdisplay, handleType);
                }
                if (_gtk_version.compareTo(GTK_VERSION_3_0_0) >= 0) {
                    m9 = d.getDeclaredMethod(str_gdk_x11_window_get_xid, handleType);
                } else {
                    m8 = d.getDeclaredMethod(str_gdk_x11_drawable_get_xid, handleType);
                }
                ma = d.getDeclaredMethod(str_gdk_window_set_back_pixmap, handleType, handleType);
            } catch (final Exception ex) { throw new NativeWindowException(ex); }
            // optional
            try {
                m2 = c.getDeclaredMethod(str_gtk_widget_unrealize, handleType);
            } catch (final Exception ex) { }
        }
        OS_gtk_class = c;
        OS_gtk_version = _gtk_version;
        OS_gtk_widget_realize = m1;
        OS_gtk_widget_unrealize = m2;
        OS_GTK_WIDGET_WINDOW = m3;
        OS_gtk_widget_get_window = m4;
        OS_gdk_x11_drawable_get_xdisplay = m5;
        OS_gdk_x11_display_get_xdisplay = m6;
        OS_gdk_window_get_display = m7;
        OS_gdk_x11_drawable_get_xid = m8;
        OS_gdk_x11_window_get_xid = m9;
        OS_gdk_window_set_back_pixmap = ma;

        isX11GTK = isX11 && null != OS_gtk_class;

        if(DEBUG) {
            System.err.println("SWTAccessor.<init>: GTK Version: "+OS_gtk_version);
        }
    }

    private static Number getIntOrLong(final long arg) {
        if(swt_uses_long_handles) {
            return Long.valueOf(arg);
        }
        return Integer.valueOf((int) arg);
    }

    private static void callStaticMethodL2V(final Method m, final long arg) {
        ReflectionUtil.callMethod(null, m, new Object[] { getIntOrLong(arg) });
    }

    private static void callStaticMethodLLZ2V(final Method m, final long arg0, final long arg1, final boolean arg3) {
        ReflectionUtil.callMethod(null, m, new Object[] { getIntOrLong(arg0), getIntOrLong(arg1), Boolean.valueOf(arg3) });
    }

    private static long callStaticMethodL2L(final Method m, final long arg) {
        final Object o = ReflectionUtil.callMethod(null, m, new Object[] { getIntOrLong(arg) });
        if(o instanceof Number) {
            return ((Number)o).longValue();
        } else {
            throw new InternalError("SWT method "+m.getName()+" didn't return int or long but "+o.getClass());
        }
    }

    //
    // Public properties
    //

    public static boolean isUsingLongHandles() {
        return swt_uses_long_handles;
    }

    public static boolean useX11GTK() { return isX11GTK; }
    public static VersionNumber GTK_VERSION() { return OS_gtk_version; }

    //
    // Common GTK
    //

    public static long gdk_widget_get_window(final long handle) {
        final long window;
        if (OS_gtk_version.compareTo(GTK_VERSION_2_14_0) >= 0) {
            window = callStaticMethodL2L(OS_gtk_widget_get_window, handle);
        } else {
            window = callStaticMethodL2L(OS_GTK_WIDGET_WINDOW, handle);
        }
        if(0 == window) {
            throw new NativeWindowException("Null gtk-window-handle of SWT handle 0x"+Long.toHexString(handle));
        }
        return window;
    }

    public static long gdk_window_get_xdisplay(final long window) {
        final long xdisplay;
        if (OS_gtk_version.compareTo(GTK_VERSION_2_24_0) >= 0) {
            final long display = callStaticMethodL2L(OS_gdk_window_get_display, window);
            if(0 == display) {
                throw new NativeWindowException("Null display-handle of gtk-window-handle 0x"+Long.toHexString(window));
            }
            xdisplay = callStaticMethodL2L(OS_gdk_x11_display_get_xdisplay, display);
        } else {
            xdisplay = callStaticMethodL2L(OS_gdk_x11_drawable_get_xdisplay, window);
        }
        if(0 == xdisplay) {
            throw new NativeWindowException("Null x11-display-handle of gtk-window-handle 0x"+Long.toHexString(window));
        }
        return xdisplay;
    }

    public static long gdk_window_get_xwindow(final long window) {
        final long xWindow;
        if (OS_gtk_version.compareTo(GTK_VERSION_3_0_0) >= 0) {
            xWindow = callStaticMethodL2L(OS_gdk_x11_window_get_xid, window);
        } else {
            xWindow = callStaticMethodL2L(OS_gdk_x11_drawable_get_xid, window);
        }
        if(0 == xWindow) {
            throw new NativeWindowException("Null x11-window-handle of gtk-window-handle 0x"+Long.toHexString(window));
        }
        return xWindow;
    }

    public static void gdk_window_set_back_pixmap(final long window, final long pixmap, final boolean parent_relative) {
        callStaticMethodLLZ2V(OS_gdk_window_set_back_pixmap, window, pixmap, parent_relative);
    }

    //
    // Common any toolkit
    //

    /**
     * @param swtControl the SWT Control to retrieve the native widget-handle from
     * @return the native widget-handle
     * @throws NativeWindowException if the widget handle is null
     */
    public static long getHandle(final Control swtControl) throws NativeWindowException {
        long h = 0;
        if( isOSX ) {
            synchronized(swt_osx_init) {
                try {
                    if(null == swt_osx_view_id) {
                        swt_osx_control_view = Control.class.getField(str_osx_view);
                        final Object view = swt_osx_control_view.get(swtControl);
                        swt_osx_view_id = view.getClass().getField(str_osx_id);
                        h = swt_osx_view_id.getLong(view);
                    } else {
                        h = swt_osx_view_id.getLong( swt_osx_control_view.get(swtControl) );
                    }
                } catch (final Exception ex) {
                    throw new NativeWindowException(ex);
                }
            }
        } else {
            try {
                h = swt_control_handle.getLong(swtControl);
            } catch (final Exception ex) {
                throw new NativeWindowException(ex);
            }
        }
        if(0 == h) {
            throw new NativeWindowException("Null widget-handle of SWT "+swtControl.getClass().getName()+": "+swtControl.toString());
        }
        return h;
    }

    public static void setRealized(final Control swtControl, final boolean realize)
            throws NativeWindowException
    {
        if(!realize && swtControl.isDisposed()) {
            return;
        }
        final long handle = getHandle(swtControl);

        if(null != OS_gtk_class) {
            invoke(true, new Runnable() {
                @Override
                public void run() {
                    if(realize) {
                        callStaticMethodL2V(OS_gtk_widget_realize, handle);
                    } else if(null != OS_gtk_widget_unrealize) {
                        callStaticMethodL2V(OS_gtk_widget_unrealize, handle);
                    }
                }
            });
        }
    }

    /**
     * @param swtControl the SWT Control to retrieve the native device handle from
     * @return the AbstractGraphicsDevice w/ the native device handle
     * @throws NativeWindowException if the widget handle is null
     * @throws UnsupportedOperationException if the windowing system is not supported
     */
    public static AbstractGraphicsDevice getDevice(final Control swtControl) throws NativeWindowException, UnsupportedOperationException {
        final long handle = getHandle(swtControl);
        if( isX11GTK ) {
            final long xdisplay0 = gdk_window_get_xdisplay( gdk_widget_get_window( handle ) );
            return new X11GraphicsDevice(xdisplay0, AbstractGraphicsDevice.DEFAULT_UNIT, false /* owner */);
        }
        if( isWindows ) {
            return new WindowsGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT);
        }
        if( isOSX ) {
            return new MacOSXGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT);
        }
        throw new UnsupportedOperationException("n/a for this windowing system: "+nwt);
    }

    /**
     * @param device
     * @param screen -1 is default screen of the given device, e.g. maybe 0 or determined by native API. >= 0 is specific screen
     * @return
     */
    public static AbstractGraphicsScreen getScreen(final AbstractGraphicsDevice device, final int screen) {
        return NativeWindowFactory.createScreen(device, screen);
    }

    public static int getNativeVisualID(final AbstractGraphicsDevice device, final long windowHandle) {
        if( isX11 ) {
            return X11Lib.GetVisualIDFromWindow(device.getHandle(), windowHandle);
        }
        if( isWindows || isOSX ) {
            return VisualIDHolder.VID_UNDEFINED;
        }
        throw new UnsupportedOperationException("n/a for this windowing system: "+nwt);
    }

    /**
     * @param swtControl the SWT Control to retrieve the native window handle from
     * @return the native window handle
     * @throws NativeWindowException if the widget handle is null
     * @throws UnsupportedOperationException if the windowing system is not supported
     */
    public static long getWindowHandle(final Control swtControl) throws NativeWindowException, UnsupportedOperationException {
        final long handle = getHandle(swtControl);
        if(0 == handle) {
            throw new NativeWindowException("Null SWT handle of SWT control "+swtControl);
        }
        if( isX11GTK ) {
            return gdk_window_get_xwindow( gdk_widget_get_window( handle ) );
        }
        if( isWindows || isOSX ) {
            return handle;
        }
        throw new UnsupportedOperationException("n/a for this windowing system: "+nwt);
    }

    public static long newGC(final Control swtControl, final GCData gcData) {
        final Object[] o = new Object[1];
        invoke(true, new Runnable() {
            @Override
            public void run() {
                o[0] = ReflectionUtil.callMethod(swtControl, swt_control_internal_new_GC, new Object[] { gcData });
            }
        });
        if(o[0] instanceof Number) {
            return ((Number)o[0]).longValue();
        } else {
            throw new InternalError("SWT internal_new_GC did not return int or long but "+o[0].getClass());
        }
    }

    public static void disposeGC(final Control swtControl, final long gc, final GCData gcData) {
        invoke(true, new Runnable() {
            @Override
            public void run() {
                if(swt_uses_long_handles) {
                    ReflectionUtil.callMethod(swtControl, swt_control_internal_dispose_GC, new Object[] { Long.valueOf(gc), gcData });
                }  else {
                    ReflectionUtil.callMethod(swtControl, swt_control_internal_dispose_GC, new Object[] { Integer.valueOf((int)gc), gcData });
                }
            }
        });
    }

   /**
    * Runs the specified action in an SWT compatible thread, which is:
    * <ul>
    *   <li>Mac OSX
    *   <ul>
    *    
    *     <li>Main Thread: Run on OSX UI main thread. 'wait' is implemented on Java site via lock/wait on {@link RunnableTask} to not freeze OSX main thread.</li>
    *   </ul></li>
    *   <li>Linux, Windows, ..
    *   <ul>
    *     <li>Current thread.</li>
    *   </ul></li>
    * </ul>
    * @see Platform#AWT_AVAILABLE
    * @see Platform#getOSType()
    */
    public static void invoke(final boolean wait, final Runnable runnable) {
        if( isOSX ) {
            // Use SWT main thread! Only reliable config w/ -XStartOnMainThread !?
            OSXUtil.RunOnMainThread(wait, false, runnable);
        } else {
            runnable.run();
        }
    }

   /**
    * Runs the specified action on the SWT UI thread.
    * <p>
    * If <code>display</code> is disposed or the current thread is the SWT UI thread
    * {@link #invoke(boolean, Runnable)} is being used.
    * @see #invoke(boolean, Runnable)
    */
    public static void invoke(final org.eclipse.swt.widgets.Display display, final boolean wait, final Runnable runnable) {
        if( display.isDisposed() || Thread.currentThread() == display.getThread() ) {
            invoke(wait, runnable);
        } else if( wait ) {
            display.syncExec(runnable);
        } else {
            display.asyncExec(runnable);
        }
    }

    //
    // Specific X11 GTK ChildWindow - Using plain X11 native parenting (works well)
    //

    public static long createCompatibleX11ChildWindow(final AbstractGraphicsScreen screen, final Control swtControl, final int visualID, final int width, final int height) {
        final long handle = getHandle(swtControl);
        final long parentWindow = gdk_widget_get_window( handle );
        gdk_window_set_back_pixmap (parentWindow, 0, false);

        final long x11ParentHandle = gdk_window_get_xwindow(parentWindow);
        final long x11WindowHandle = X11Lib.CreateWindow(x11ParentHandle, screen.getDevice().getHandle(), screen.getIndex(), visualID, width, height, true, true);

        return x11WindowHandle;
    }

    public static void resizeX11Window(final AbstractGraphicsDevice device, final Rectangle clientArea, final long x11Window) {
        X11Lib.SetWindowPosSize(device.getHandle(), x11Window, clientArea.x, clientArea.y, clientArea.width, clientArea.height);
    }
    public static void destroyX11Window(final AbstractGraphicsDevice device, final long x11Window) {
        X11Lib.DestroyWindow(device.getHandle(), x11Window);
    }

    //
    // Specific X11 SWT/GTK ChildWindow - Using SWT/GTK native parenting (buggy - sporadic resize flickering, sporadic drop of rendering)
    //
    // FIXME: Need to use reflection for 32bit access as well !
    //

    // public static final int GDK_WA_TYPE_HINT = 1 << 9;
    // public static final int GDK_WA_VISUAL = 1 << 6;

    public static long createCompatibleGDKChildWindow(final Control swtControl, final int visualID, final int width, final int height) {
        return 0;
        /**
        final long handle = SWTAccessor.getHandle(swtControl);
        final long parentWindow = gdk_widget_get_window( handle );

        final long screen = OS.gdk_screen_get_default ();
        final long gdkvisual = OS.gdk_x11_screen_lookup_visual (screen, visualID);

        final GdkWindowAttr attrs = new GdkWindowAttr();
        attrs.width = width > 0 ? width : 1;
        attrs.height = height > 0 ? height : 1;
        attrs.event_mask = OS.GDK_KEY_PRESS_MASK | OS.GDK_KEY_RELEASE_MASK |
                           OS.GDK_FOCUS_CHANGE_MASK | OS.GDK_POINTER_MOTION_MASK |
                           OS.GDK_BUTTON_PRESS_MASK | OS.GDK_BUTTON_RELEASE_MASK |
                           OS.GDK_ENTER_NOTIFY_MASK | OS.GDK_LEAVE_NOTIFY_MASK |
                           OS.GDK_EXPOSURE_MASK | OS.GDK_VISIBILITY_NOTIFY_MASK |
                           OS.GDK_POINTER_MOTION_HINT_MASK;
        attrs.window_type = OS.GDK_WINDOW_CHILD;
        attrs.visual = gdkvisual;

        final long childWindow = OS.gdk_window_new (parentWindow, attrs, OS.GDK_WA_VISUAL|GDK_WA_TYPE_HINT);
        OS.gdk_window_set_user_data (childWindow, handle);
        OS.gdk_window_set_back_pixmap (parentWindow, 0, false);

        OS.gdk_window_show (childWindow);
        OS.gdk_flush();
        return childWindow; */
    }

    public static void showGDKWindow(final long gdkWindow) {
        /* OS.gdk_window_show (gdkWindow);
        OS.gdk_flush(); */
    }
    public static void focusGDKWindow(final long gdkWindow) {
        /*
        OS.gdk_window_show (gdkWindow);
        OS.gdk_window_focus(gdkWindow, 0);
        OS.gdk_flush(); */
    }
    public static void resizeGDKWindow(final Rectangle clientArea, final long gdkWindow) {
        /**
        OS.gdk_window_move (gdkWindow, clientArea.x, clientArea.y);
        OS.gdk_window_resize (gdkWindow, clientArea.width, clientArea.height);
        OS.gdk_flush(); */
    }

    public static void destroyGDKWindow(final long gdkWindow) {
        // OS.gdk_window_destroy (gdkWindow);
    }
}







12