diff options
author | Xerxes Rånby <[email protected]> | 2015-07-22 02:08:41 +0200 |
---|---|---|
committer | Xerxes Rånby <[email protected]> | 2015-07-28 23:47:39 +0200 |
commit | f84af439535640e1072b6cd670aa44cbe91ef052 (patch) | |
tree | d48c71ece12af9083717f1125cebe674f03142f5 /src/newt | |
parent | 6e68e6a9d2472d556c7cab69b2ee9e55aeb60f36 (diff) |
Bug 1178: Implement X11UnderlayTracker
driver/x11/X11UnderlayTracker
Using NEWT to initialize an X11 window for use by Raspberry Pi users
to handle mouse and keyboard input when using the bcm.vc.iv driver inside xorg.
newt/driver/bcm/vc/iv/WindowDriver
Try use X11UnderlayTracker as input for bcm.vc.iv
If X11 fail to initialize then
track using the /dev/event files directly
using the LinuxMouseTracker.
Input source is switched inside bcm/vc/iv/WindowDriver
by using the new
newt/driver/KeyTracker
newt/driver/MouseTracker
interfaces.
Signed-off-by: Xerxes Rånby <[email protected]>
Diffstat (limited to 'src/newt')
6 files changed, 446 insertions, 12 deletions
diff --git a/src/newt/classes/jogamp/newt/driver/KeyTracker.java b/src/newt/classes/jogamp/newt/driver/KeyTracker.java new file mode 100644 index 000000000..ab91d48aa --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/KeyTracker.java @@ -0,0 +1,35 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver; + +import com.jogamp.newt.event.WindowListener; + +public interface KeyTracker extends WindowListener{ + +} diff --git a/src/newt/classes/jogamp/newt/driver/MouseTracker.java b/src/newt/classes/jogamp/newt/driver/MouseTracker.java new file mode 100644 index 000000000..3a0cb2db4 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/MouseTracker.java @@ -0,0 +1,39 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver; + +import com.jogamp.newt.event.WindowListener; + +public interface MouseTracker extends WindowListener { + + int getLastY(); + + int getLastX(); + +} diff --git a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java index 93c28d370..02076348b 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java @@ -34,6 +34,7 @@ import com.jogamp.nativewindow.Capabilities; import com.jogamp.nativewindow.DefaultGraphicsScreen; import com.jogamp.nativewindow.GraphicsConfigurationFactory; import com.jogamp.nativewindow.NativeWindowException; + import com.jogamp.nativewindow.VisualIDHolder; import com.jogamp.nativewindow.util.Insets; import com.jogamp.nativewindow.util.Point; @@ -41,12 +42,16 @@ import com.jogamp.nativewindow.util.Rectangle; import com.jogamp.nativewindow.util.RectangleImmutable; import com.jogamp.common.util.IntBitfield; import com.jogamp.nativewindow.egl.EGLGraphicsDevice; + import com.jogamp.newt.event.MouseEvent; import jogamp.newt.PointerIconImpl; import jogamp.newt.WindowImpl; +import jogamp.newt.driver.KeyTracker; +import jogamp.newt.driver.MouseTracker; import jogamp.newt.driver.linux.LinuxEventDeviceTracker; import jogamp.newt.driver.linux.LinuxMouseTracker; +import jogamp.newt.driver.x11.X11UnderlayTracker; import jogamp.opengl.egl.EGLDisplayUtil; public class WindowDriver extends WindowImpl { @@ -57,8 +62,26 @@ public class WindowDriver extends WindowImpl { } public WindowDriver() { - linuxMouseTracker = LinuxMouseTracker.getSingleton(); - linuxEventDeviceTracker = LinuxEventDeviceTracker.getSingleton(); + + /* Try use X11 as input for bcm.vc.iv + * if X11 fail to initialize then + * track using the /dev/event files directly + * using the LinuxMouseTracker + */ + try{ + x11UnderlayTracker = X11UnderlayTracker.getSingleton(); + + mouseTracker = x11UnderlayTracker; + keyTracker = x11UnderlayTracker; + } catch(ExceptionInInitializerError e){ + linuxMouseTracker = LinuxMouseTracker.getSingleton(); + linuxEventDeviceTracker = LinuxEventDeviceTracker.getSingleton(); + + mouseTracker = linuxMouseTracker; + keyTracker = linuxEventDeviceTracker; + } + + layer = -1; nativeWindowHandle = 0; windowHandleClose = 0; @@ -192,8 +215,10 @@ public class WindowDriver extends WindowImpl { } windowHandleClose = nativeWindowHandle; - addWindowListener(linuxEventDeviceTracker); - addWindowListener(linuxMouseTracker); + addWindowListener(keyTracker); + addWindowListener(mouseTracker); + + focusChanged(false, true); } @@ -202,8 +227,8 @@ public class WindowDriver extends WindowImpl { final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) getGraphicsConfiguration().getScreen().getDevice(); - removeWindowListener(linuxMouseTracker); - removeWindowListener(linuxEventDeviceTracker); + removeWindowListener(mouseTracker); + removeWindowListener(keyTracker); if(0!=windowHandleClose) { CloseWindow0(display.getBCMHandle(), windowHandleClose); @@ -229,6 +254,7 @@ public class WindowDriver extends WindowImpl { final RectangleImmutable rect = clampRect((ScreenDriver) getScreen(), new Rectangle(x, y, width, height), false); // reconfigure0 will issue position/size changed events if required reconfigure0(nativeWindowHandle, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight(), flags); + return true; } @@ -255,21 +281,25 @@ public class WindowDriver extends WindowImpl { @Override protected void setPointerIconImpl(final PointerIconImpl pi) { final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); - display.setPointerIconActive(null != pi ? pi.validatedHandle() : 0, linuxMouseTracker.getLastX(), linuxMouseTracker.getLastY()); + display.setPointerIconActive(null != pi ? pi.validatedHandle() : 0, mouseTracker.getLastX(), mouseTracker.getLastY()); } @Override protected boolean setPointerVisibleImpl(final boolean pointerVisible) { final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); - display.setActivePointerIconVisible(pointerVisible, linuxMouseTracker.getLastX(), linuxMouseTracker.getLastY()); + display.setActivePointerIconVisible(pointerVisible, mouseTracker.getLastX(), mouseTracker.getLastY()); return true; } //---------------------------------------------------------------------- // Internals only // - private final LinuxMouseTracker linuxMouseTracker; - private final LinuxEventDeviceTracker linuxEventDeviceTracker; + private LinuxMouseTracker linuxMouseTracker; + private LinuxEventDeviceTracker linuxEventDeviceTracker; + private X11UnderlayTracker x11UnderlayTracker; + + private MouseTracker mouseTracker; + private KeyTracker keyTracker; protected static native boolean initIDs(); private native long CreateWindow0(long bcmDisplay, int layer, int x, int y, int width, int height, boolean opaque, int alphaBits); diff --git a/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java b/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java index 49a815cb6..bc0bfaa16 100644 --- a/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java +++ b/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java @@ -40,6 +40,7 @@ import java.lang.Thread; import java.nio.ByteBuffer; import jogamp.newt.WindowImpl; +import jogamp.newt.driver.KeyTracker; import com.jogamp.common.nio.StructAccessor; import com.jogamp.newt.Window; @@ -55,7 +56,7 @@ import com.jogamp.newt.event.KeyEvent; * within it's own polling thread. */ -public class LinuxEventDeviceTracker implements WindowListener { +public class LinuxEventDeviceTracker implements WindowListener, KeyTracker { private static final LinuxEventDeviceTracker ledt; diff --git a/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java b/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java index 1d9377563..f40728da0 100644 --- a/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java +++ b/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.io.InputStream; import jogamp.newt.WindowImpl; +import jogamp.newt.driver.MouseTracker; import com.jogamp.newt.Screen; import com.jogamp.newt.Window; @@ -48,7 +49,7 @@ import com.jogamp.newt.event.WindowUpdateEvent; * just reading <code>/dev/input/mice</code> * within it's own polling thread. */ -public class LinuxMouseTracker implements WindowListener { +public class LinuxMouseTracker implements WindowListener, MouseTracker { private static final LinuxMouseTracker lmt; diff --git a/src/newt/classes/jogamp/newt/driver/x11/X11UnderlayTracker.java b/src/newt/classes/jogamp/newt/driver/x11/X11UnderlayTracker.java new file mode 100644 index 000000000..d0c5ed2c0 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/x11/X11UnderlayTracker.java @@ -0,0 +1,328 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.x11; + +import jogamp.nativewindow.x11.X11Util; +import jogamp.newt.WindowImpl; +import jogamp.newt.driver.MouseTracker; +import jogamp.newt.driver.KeyTracker; + +import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.nativewindow.Capabilities; +import com.jogamp.nativewindow.GraphicsConfigurationFactory; +import com.jogamp.nativewindow.NativeWindowFactory; +import com.jogamp.newt.Display; +import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.Screen; +import com.jogamp.newt.Window; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.MouseListener; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.event.WindowListener; +import com.jogamp.newt.event.WindowUpdateEvent; + +/** + * UnderlayTracker can be used as input for + * WM that only provide undecorated overlays. + * + * The UnderlayTracker enable move and resize + * manipulation of the overlays. + * + * A NEWT window may use the UnderlayTracker by calling + * <code>addWindowListener(X11UnderlayTracker.getSingleton())</code> + */ +public class X11UnderlayTracker implements WindowListener, KeyListener, MouseListener, MouseTracker, KeyTracker { + + private static final X11UnderlayTracker tracker; + private static Window window; + private volatile WindowImpl focusedWindow = null; + private volatile MouseEvent lastMouse; + + static { + tracker = new X11UnderlayTracker(); + } + + public static X11UnderlayTracker getSingleton() { + return tracker; + } + + private X11UnderlayTracker() { + + /* + * X11UnderlayTracker is intended to be used on systems where + * X11 is not the default WM. + * We must explicitly initialize all X11 dependencies + * to make sure they are available. + */ + X11Util.initSingleton(); + GraphicsConfigurationFactory.initSingleton(); + try { + ReflectionUtil.callStaticMethod("jogamp.nativewindow.x11.X11GraphicsConfigurationFactory", + "registerFactory", null, null, GraphicsConfigurationFactory.class.getClassLoader()); + } catch (final Exception e) { + throw new RuntimeException(e); + } + + /* + * Initialize the X11 window. + */ + Capabilities caps = new Capabilities(); + final Display display = NewtFactory.createDisplay(NativeWindowFactory.TYPE_X11, null, false); + final Screen screen = NewtFactory.createScreen(display, 0); + + window = WindowImpl.create(null, 0, screen, caps); + //window.setSize(200, 140); + //window.setPosition(300, 400); + window.setVisible(false, true); + + window.addKeyListener(this); + window.addMouseListener(this); + window.addWindowListener(this); + } + + + + @Override + public void windowResized(final WindowEvent e) { + final Object s = e.getSource(); + + if(s instanceof WindowImpl) { + if(window == s) { + if(focusedWindow!=null){ + focusedWindow.setSize(window.getSurfaceWidth(),window.getSurfaceHeight()); + // workaround bvm.vc.iv possition gets moved during setSize + focusedWindow.setPosition(window.getX(),window.getY()); + } + } else { + // FIXME null check here used as a workaround to prevent event avalance + // fixing it will allow the user to resize the NEWT window using code + // after it has gained focus. + if(focusedWindow==null){ + WindowImpl sourceWindow = (WindowImpl) s; + window.setSize(sourceWindow.getSurfaceWidth(),sourceWindow.getSurfaceHeight()); + } + } + } + } + + @Override + public void windowMoved(final WindowEvent e) { + final Object s = e.getSource(); + if(s instanceof WindowImpl) { + if(window == s) { + if(focusedWindow!=null){ + //focusedWindow.setSize(window.getSurfaceWidth(),window.getSurfaceHeight()); + focusedWindow.setPosition(window.getX(), window.getY()); + } + } else { + // FIXME null check here used as a workaround to prevent event avalance + // fixing it will allow the user to move the NEWT window using code + // after it has gained focus. + if(focusedWindow==null){ + WindowImpl sourceWindow = (WindowImpl) s; + window.setPosition(sourceWindow.getX(), sourceWindow.getY()); + } + } + } + } + + @Override + public void windowDestroyNotify(final WindowEvent e) { + final Object s = e.getSource(); + + if(window == s) { + if(focusedWindow!=null){ + focusedWindow.destroy(); + focusedWindow = null; + } + } else { + if(focusedWindow == s) { + focusedWindow = null; + window.setVisible(false, false); + window.destroy(); + } + } + } + + @Override + public void windowDestroyed(final WindowEvent e) { } + + @Override + public void windowGainedFocus(final WindowEvent e) { + final Object s = e.getSource(); + if(s instanceof WindowImpl) { + if(window == s) { + // do nothing + } else { + if(focusedWindow==null) { + // hack that initially make the tracking window the same size as the overlay + WindowImpl sourceWindow = (WindowImpl) s; + window.setSize(sourceWindow.getSurfaceWidth(),sourceWindow.getSurfaceHeight()); + window.setPosition(sourceWindow.getX(), sourceWindow.getY()); + } + focusedWindow = (WindowImpl) s; + } + } + } + + @Override + public void windowLostFocus(final WindowEvent e) { + final Object s = e.getSource(); + if(window == s) { + // do nothing + } else { + if(focusedWindow == s) { + focusedWindow = null; + } + } + } + + @Override + public void windowRepaint(final WindowUpdateEvent e) { } + + public static void main(String[] args) throws InterruptedException{ + X11UnderlayTracker.getSingleton(); + + Thread.sleep(25000); + } + + @Override + public void mouseClicked(MouseEvent e) { + lastMouse = e; + if(focusedWindow != null){ + //e.setConsumed(false); + //focusedWindow.consumeEvent(e); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_CLICKED, 0, e.getX(), e.getY(), (short)0, 0 ); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + lastMouse = e; + if(focusedWindow != null){ + //e.setConsumed(false); + //focusedWindow.consumeEvent(e); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_ENTERED, 0, e.getX(), e.getY(), (short)0, 0 ); + } + } + + @Override + public void mouseExited(MouseEvent e) { + lastMouse = e; + if(focusedWindow != null){ + //e.setConsumed(false); + //focusedWindow.consumeEvent(e); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_EXITED, 0, e.getX(), e.getY(), (short)0, 0 ); + } + } + + @Override + public void mousePressed(MouseEvent e) { + lastMouse = e; + if(focusedWindow != null){ + //e.setConsumed(false); + //focusedWindow.consumeEvent(e); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_PRESSED, 0, e.getX(), e.getY(), (short)0, 0 ); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + lastMouse = e; + if(focusedWindow != null){ + //e.setConsumed(false); + //focusedWindow.consumeEvent(e); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_RELEASED, 0, e.getX(), e.getY(), (short)0, 0 ); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + lastMouse = e; + if(focusedWindow != null){ + //e.setConsumed(false); + //focusedWindow.consumeEvent(e); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_MOVED, 0, e.getX(), e.getY(), (short)0, 0 ); + } + } + + @Override + public void mouseDragged(MouseEvent e) { + lastMouse = e; + if(focusedWindow != null){ + //e.setConsumed(false); + //focusedWindow.consumeEvent(e); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_DRAGGED, 0, e.getX(), e.getY(), (short)0, 0 ); + } + } + + @Override + public void mouseWheelMoved(MouseEvent e) { + lastMouse = e; + if(focusedWindow != null){ + //e.setConsumed(false); + //focusedWindow.consumeEvent(e); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_WHEEL_MOVED, 0, e.getX(), e.getY(), (short)0, 0 ); + } + } + + @Override + public void keyPressed(KeyEvent e) { + if(focusedWindow != null){ + //e.setConsumed(false); + //focusedWindow.consumeEvent(e); + focusedWindow.sendKeyEvent(e.getEventType(), e.getModifiers(), e.getKeyCode(), e.getKeyCode(), (char)e.getKeySymbol()); + } + } + + @Override + public void keyReleased(KeyEvent e) { + if(focusedWindow != null){ + //e.setConsumed(false); + //focusedWindow.consumeEvent(e); + focusedWindow.sendKeyEvent(e.getEventType(), e.getModifiers(), e.getKeyCode(), e.getKeyCode(), (char)e.getKeySymbol()); + } + } + + @Override + public int getLastY() { + if(lastMouse!=null) + return lastMouse.getY(); + return 0; + } + + @Override + public int getLastX() { + if(lastMouse!=null) + return lastMouse.getX(); + return 0; + } +} |