Login  Register

Re: NewtCanvasSWT Linux Exception with GTK_VERSION

Posted by Marcel on Feb 14, 2019; 9:07am
URL: https://forum.jogamp.org/NewtCanvasSWT-Linux-Exception-with-GTK-VERSION-tp4039442p4039536.html

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);
    }
}