]> gerrit.simantics Code Review - simantics/3d.git/blobdiff - org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SWTCanvas.java
Alpha-version of jME-bindings for g3d.
[simantics/3d.git] / org.simantics.g3d.jme / src / org / simantics / g3d / jme / system / SWTCanvas.java
diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SWTCanvas.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SWTCanvas.java
new file mode 100644 (file)
index 0000000..637d53e
--- /dev/null
@@ -0,0 +1,1099 @@
+package org.simantics.g3d.jme.system;\r
+\r
+\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.events.KeyEvent;\r
+import org.eclipse.swt.events.KeyListener;\r
+import org.eclipse.swt.events.MouseEvent;\r
+import org.eclipse.swt.events.MouseListener;\r
+import org.eclipse.swt.events.MouseMoveListener;\r
+import org.eclipse.swt.events.MouseTrackListener;\r
+import org.eclipse.swt.events.MouseWheelListener;\r
+import org.eclipse.swt.events.PaintEvent;\r
+import org.eclipse.swt.events.PaintListener;\r
+import org.eclipse.swt.graphics.Point;\r
+import org.eclipse.swt.layout.FillLayout;\r
+import org.eclipse.swt.opengl.GLCanvas;\r
+import org.eclipse.swt.opengl.GLData;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.lwjgl.LWJGLException;\r
+import org.lwjgl.Sys;\r
+import org.lwjgl.input.Keyboard;\r
+import org.lwjgl.input.Mouse;\r
+import org.lwjgl.opengl.GLContext;\r
+import org.lwjgl.opengl.Pbuffer;\r
+import org.lwjgl.opengl.PixelFormat;\r
+\r
+import com.jme3.cursors.plugins.JmeCursor;\r
+import com.jme3.input.JoyInput;\r
+import com.jme3.input.KeyInput;\r
+import com.jme3.input.MouseInput;\r
+import com.jme3.input.RawInputListener;\r
+import com.jme3.input.event.InputEvent;\r
+import com.jme3.input.event.KeyInputEvent;\r
+import com.jme3.input.event.MouseButtonEvent;\r
+import com.jme3.input.event.MouseMotionEvent;\r
+import com.jme3.system.AppSettings;\r
+import com.jme3.system.JmeSystem;\r
+import com.jme3.system.Platform;\r
+import com.jme3.system.lwjgl.LwjglAbstractDisplay;\r
+import com.jme3.system.lwjgl.LwjglDisplay;\r
+import com.jme3.system.lwjgl.LwjglTimer;\r
+\r
+/**\r
+ * SWT OpenGL canvas for jME\r
+ * \r
+ * Note: this class is experimental, and contains questionable code.\r
+ * \r
+ * \r
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
+ *\r
+ */\r
+public class SWTCanvas extends LwjglAbstractDisplay {\r
+\r
+       protected static final boolean USE_SHARED_CONTEXT = false;\r
+\r
+       \r
+       private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName());\r
+       \r
+       GLCanvas canvas;\r
+       \r
+       private int width;\r
+    private int height;\r
+    \r
+    private boolean runningFirstTime = true;\r
+    private boolean mouseWasGrabbed = false;\r
+    \r
+    private boolean mouseWasCreated = false;\r
+    private boolean keyboardWasCreated = false;\r
+\r
+    private Pbuffer pbuffer;\r
+    private PixelFormat pbufferFormat;\r
+    private PixelFormat canvasFormat;\r
+    \r
+    private boolean disposed = false;\r
+    \r
+       public SWTCanvas(Composite parent) {\r
+               parent.setLayout(new FillLayout());\r
+               \r
+               GLData data = new GLData();\r
+               data.doubleBuffer = true;\r
+               data.blueSize = 8;\r
+               data.redSize = 8;\r
+               data.greenSize = 8;\r
+               data.depthSize = 32;\r
+               data.alphaSize = 8;\r
+               data.stencilSize = 8;\r
+               data.stereo = false;\r
+               data.accumAlphaSize = 0;\r
+               data.accumBlueSize = 0;\r
+               data.accumGreenSize = 0;\r
+               data.accumRedSize = 0;\r
+               canvas = new GLCanvas(parent, SWT.EMBEDDED|SWT.NO_REDRAW_RESIZE, data);\r
+               //canvas = new GLCanvas(parent, SWT.NONE, data);\r
+               \r
+               \r
+       }\r
+       \r
+       public void dispose() {\r
+               if (disposed)\r
+                       return;\r
+               \r
+               setCurrent();\r
+               deinitInThread();\r
+//             pauseCanvas();\r
+               canvas.dispose();\r
+               disposed = true;\r
+       }\r
+       \r
+       public boolean isDisposed() {\r
+               return disposed;\r
+       }\r
+       \r
+       @Override\r
+       public Type getType() {\r
+               return Type.Canvas; // TODO : is Canvas Correct?\r
+       }\r
+       \r
+       @Override\r
+       public void setTitle(String title) {\r
+               // do nothing\r
+       }\r
+       \r
+       @Override\r
+       public void restart() {\r
+                frameRate = settings.getFrameRate();\r
+       }\r
+       \r
+\r
+       private boolean c = false;\r
+       \r
+       public void create(boolean waitFor){\r
+               if (c)\r
+                       return;\r
+               c = true;\r
+               \r
+               setCurrent();\r
+               initInThread();\r
+               renderable.set(true);\r
+               //restoreCanvas();\r
+               \r
+//             canvas.addPaintListener(new PaintListener() {\r
+//                     \r
+//                     @Override\r
+//                     public void paintControl(PaintEvent arg0) {\r
+//                             repaint();\r
+//                     }\r
+//             });\r
+               \r
+               org.eclipse.swt.widgets.Display.getCurrent().asyncExec(new Runnable() {\r
+                       \r
+                       @Override\r
+                       public void run() {\r
+                               if (canvas.isDisposed())\r
+                                       return;\r
+                               repaint();\r
+                               org.eclipse.swt.widgets.Display.getCurrent().asyncExec(this);\r
+                               \r
+                       }\r
+               });\r
+\r
+       }\r
+       \r
+       @Override\r
+       protected void runLoop() {\r
+\r
+               if (renderable.get()){\r
+                       Point size = canvas.getSize();\r
+                       //System.out.println("Render " + size);\r
+            int newWidth = Math.max(size.x, 1);\r
+            int newHeight = Math.max(size.y, 1);\r
+            if (width != newWidth || height != newHeight){\r
+               System.out.println("Render resize " + size);\r
+                width = newWidth;\r
+                height = newHeight;\r
+                if (listener != null){\r
+                    listener.reshape(width, height);\r
+                }\r
+            }\r
+        }\r
+                if (!created.get())\r
+                   throw new IllegalStateException();\r
+                listener.update();\r
+                if (renderable.get()){\r
+                        assert checkGLError();\r
+                        \r
+                        // calls swap buffers, etc.\r
+                   try {\r
+                       canvas.swapBuffers();\r
+//                     Display.processMessages();\r
+//                     if (autoFlush){\r
+//                         Display.update(false);\r
+//                     }else{\r
+//                         Display.processMessages();\r
+//                         Thread.sleep(50);\r
+//                         // add a small wait\r
+//                         // to reduce CPU usage\r
+//                     }\r
+                   } catch (Throwable ex){\r
+                       listener.handleError("Error while swapping buffers", ex);\r
+                   }\r
+                }\r
+//              if (frameRate > 0)\r
+//                 Display.sync(frameRate);\r
+\r
+//             if (renderable.get()){\r
+//                 if (autoFlush){\r
+//                     // check input after we synchronize with framerate.\r
+//                     // this reduces input lag.\r
+//                     Display.processMessages();\r
+//                 }\r
+//             }\r
+       }\r
+       \r
+       public void repaint() {\r
+               setCurrent();\r
+//             if (renderable.get()){\r
+//            if (Display.isCloseRequested())\r
+//                listener.requestClose(false);\r
+                       \r
+//            if (wasActive != Display.isActive()) {\r
+//                if (!wasActive) {\r
+//                    listener.gainFocus();\r
+//                    timer.reset();\r
+//                    wasActive = true;\r
+//                } else {\r
+//                    listener.loseFocus();\r
+//                    wasActive = false;\r
+//                }\r
+//            }\r
+//        }\r
+\r
+        runLoop();\r
+       }\r
+       \r
+       @Override\r
+       public void run() {\r
+               // do nothing\r
+               //super.run();\r
+       }\r
+       \r
+       @Override\r
+       protected void destroyContext() {\r
+                try {\r
+                   // invalidate the state so renderer can resume operation\r
+                   if (!USE_SHARED_CONTEXT){\r
+                       renderer.cleanup();\r
+                   }\r
+                   \r
+                   //if (Display.isCreated()){\r
+                   if (!canvas.isDisposed()) {\r
+                       /* FIXES:\r
+                        * org.lwjgl.LWJGLException: X Error\r
+                        * BadWindow (invalid Window parameter) request_code: 2 minor_code: 0\r
+                        * \r
+                        * Destroying keyboard early prevents the error above, triggered\r
+                        * by destroying keyboard in by Display.destroy() or Display.setParent(null).\r
+                        * Therefore Keyboard.destroy() should precede any of these calls.\r
+                        */\r
+                       if (Keyboard.isCreated()){\r
+                           // Should only happen if called in \r
+                           // LwjglAbstractDisplay.deinitInThread().\r
+                           Keyboard.destroy();\r
+                       }\r
+\r
+                       //try {\r
+                           // NOTE: On Windows XP, not calling setParent(null)\r
+                           // freezes the application.\r
+                           // On Mac it freezes the application.\r
+                           // On Linux it fixes a crash with X Window System.\r
+                           if (JmeSystem.getPlatform() == Platform.Windows32\r
+                            || JmeSystem.getPlatform() == Platform.Windows64){\r
+                               //Display.setParent(null);\r
+                           }\r
+                       //} catch (LWJGLException ex) {\r
+                       //    logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex);\r
+                       //}\r
+\r
+//                     Display.destroy();\r
+                   }\r
+                   \r
+                   // The canvas is no longer visible,\r
+                   // but the context thread is still running.\r
+                   if (!needClose.get()){\r
+                       // MUST make sure there's still a context current here ..\r
+                       // Display is dead, make pbuffer available to the system\r
+                       makePbufferAvailable();\r
+                       \r
+                       renderer.invalidateState();\r
+                   }else{\r
+                       // The context thread is no longer running.\r
+                       // Destroy pbuffer.\r
+                       destroyPbuffer();\r
+                   }\r
+               } catch (LWJGLException ex) {\r
+                   listener.handleError("Failed make pbuffer available", ex);\r
+               }\r
+               \r
+       }\r
+       \r
+\r
+       \r
+       public void setFocus() {\r
+               canvas.setFocus();              \r
+       }\r
+       \r
+       public void setCurrent(GLCanvas canvas) {\r
+               // delegate to the GLContext\r
+               canvas.setCurrent();\r
+               try {\r
+                       GLContext.useContext(canvas);\r
+               } catch (LWJGLException e) {\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+    \r
+    public void setCurrent() {\r
+       setCurrent(canvas);\r
+    }\r
+    \r
+    \r
+    \r
+    private void pauseCanvas(){\r
+        if (Mouse.isCreated()){\r
+            if (Mouse.isGrabbed()){\r
+                Mouse.setGrabbed(false);\r
+                mouseWasGrabbed = true;\r
+            }\r
+            mouseWasCreated = true;\r
+            Mouse.destroy();\r
+        }\r
+        if (Keyboard.isCreated()){\r
+            keyboardWasCreated = true;\r
+            Keyboard.destroy();\r
+        }\r
+\r
+        renderable.set(false);\r
+        destroyContext();\r
+    }\r
+\r
+    /**\r
+     * Called to restore the canvas.\r
+     */\r
+    private void restoreCanvas(){\r
+       setCurrent();\r
+       logger.log(Level.INFO, "OGL: Waiting for canvas to become displayable..");\r
+        while (!canvas.getVisible()){\r
+            try {\r
+                Thread.sleep(10);\r
+            } catch (InterruptedException ex) {\r
+                logger.log(Level.SEVERE, "OGL: Interrupted! ", ex);\r
+            }\r
+        }\r
+        \r
+        logger.log(Level.INFO, "OGL: Creating display context ..");\r
+\r
+        // Set renderable to true, since canvas is now displayable.\r
+        renderable.set(true);\r
+        createContext(settings);\r
+\r
+        logger.log(Level.INFO, "OGL: Display is active!");\r
+\r
+        try {\r
+            if (mouseWasCreated){\r
+                Mouse.create();\r
+                if (mouseWasGrabbed){\r
+                    Mouse.setGrabbed(true);\r
+                    mouseWasGrabbed = false;\r
+                }\r
+            }\r
+            if (keyboardWasCreated){\r
+                Keyboard.create();\r
+                keyboardWasCreated = false;\r
+            }\r
+        } catch (LWJGLException ex){\r
+            logger.log(Level.SEVERE, "Encountered exception when restoring input", ex);\r
+        }\r
+\r
+        \r
+    }\r
+    /**\r
+     * It seems it is best to use one pixel format for all shared contexts.\r
+     * @see <a href="http://developer.apple.com/library/mac/#qa/qa1248/_index.html">http://developer.apple.com/library/mac/#qa/qa1248/_index.html</a>\r
+     */\r
+    protected PixelFormat acquirePixelFormat(boolean forPbuffer){\r
+        if (forPbuffer){\r
+            // Use 0 samples for pbuffer format, prevents\r
+            // crashes on bad drivers\r
+            if (pbufferFormat == null){\r
+                pbufferFormat = new PixelFormat(settings.getBitsPerPixel(),\r
+                                                0,\r
+                                                settings.getDepthBits(),\r
+                                                settings.getStencilBits(),\r
+                                                0);\r
+            }\r
+            return pbufferFormat;\r
+        }else{\r
+            if (canvasFormat == null){\r
+                int samples = getNumSamplesToUse();\r
+                canvasFormat = new PixelFormat(settings.getBitsPerPixel(),\r
+                                               0,\r
+                                               settings.getDepthBits(),\r
+                                               settings.getStencilBits(),\r
+                                               samples);\r
+            }\r
+            return canvasFormat;\r
+        }\r
+    }\r
+    \r
+    /**\r
+     * Makes sure the pbuffer is available and ready for use\r
+     */\r
+    protected void makePbufferAvailable() throws LWJGLException{\r
+        if (pbuffer != null && pbuffer.isBufferLost()){\r
+            logger.log(Level.WARNING, "PBuffer was lost!");\r
+            pbuffer.destroy();\r
+            pbuffer = null;\r
+        }\r
+        \r
+        if (pbuffer == null) {\r
+            pbuffer = new Pbuffer(1, 1, acquirePixelFormat(true), null);\r
+            pbuffer.makeCurrent();\r
+            logger.log(Level.INFO, "OGL: Pbuffer has been created");\r
+            \r
+            // Any created objects are no longer valid\r
+            if (!runningFirstTime){\r
+                renderer.resetGLObjects();\r
+            }\r
+        }\r
+        \r
+        pbuffer.makeCurrent();\r
+        if (!pbuffer.isCurrent()){\r
+            throw new LWJGLException("Pbuffer cannot be made current");\r
+        }\r
+    }\r
+    \r
+    protected void destroyPbuffer(){\r
+        if (pbuffer != null){\r
+            if (!pbuffer.isBufferLost()){\r
+                pbuffer.destroy();\r
+            }\r
+            pbuffer = null;\r
+        }\r
+    }\r
+    \r
+    @Override\r
+    protected void createContext(AppSettings settings) {\r
+       setCurrent();\r
+       \r
+        // In case canvas is not visible, we still take framerate\r
+        // from settings to prevent "100% CPU usage"\r
+        frameRate = settings.getFrameRate();\r
+        \r
+        try {\r
+            if (renderable.get()){\r
+                if (!runningFirstTime){\r
+                    // because the display is a different opengl context\r
+                    // must reset the context state.\r
+                    if (!USE_SHARED_CONTEXT){\r
+                        renderer.cleanup();\r
+                    }\r
+                }\r
+                \r
+                // if the pbuffer is currently active, \r
+                // make sure to deactivate it\r
+                destroyPbuffer();\r
+                \r
+                if (Keyboard.isCreated()){\r
+                    Keyboard.destroy();\r
+                }\r
+                \r
+                try {\r
+                    Thread.sleep(1000);\r
+                } catch (InterruptedException ex) {\r
+                }\r
+                \r
+//                Display.setVSyncEnabled(settings.isVSync());\r
+                //Display.setParent(canvas);\r
+                \r
+//                if (USE_SHARED_CONTEXT){\r
+//                    Display.create(acquirePixelFormat(false), pbuffer);\r
+//                }else{\r
+//                    Display.create(acquirePixelFormat(false));\r
+//                }\r
+                \r
+                renderer.invalidateState();\r
+            }else{\r
+                // First create the pbuffer, if it is needed.\r
+                makePbufferAvailable();\r
+            }\r
+\r
+            // At this point, the OpenGL context is active.\r
+            if (runningFirstTime){\r
+                // THIS is the part that creates the renderer.\r
+                // It must always be called, now that we have the pbuffer workaround.\r
+                initContextFirstTime();\r
+                runningFirstTime = false;\r
+            }\r
+        } catch (LWJGLException ex) {\r
+            listener.handleError("Failed to initialize OpenGL context", ex);\r
+            // TODO: Fix deadlock that happens after the error (throw runtime exception?)\r
+        }\r
+    }\r
+\r
+    \r
+    public JoyInput getJoyInput() {\r
+       return null;\r
+    }\r
+\r
+    \r
+    SWTMouseInput mouseInput;\r
+    \r
+    public MouseInput getMouseInput() {\r
+        if (mouseInput == null){\r
+            mouseInput = new SWTMouseInput(this);\r
+        }\r
+        return mouseInput;\r
+    }\r
+\r
+    \r
+    SWTKeyInput keyInput;\r
+    \r
+    public KeyInput getKeyInput() {\r
+        if (keyInput == null){\r
+            keyInput = new SWTKeyInput(this);\r
+        }\r
+        return keyInput;\r
+    }\r
+    \r
+\r
+    \r
+    public class SWTMouseInput implements MouseInput, MouseListener, MouseMoveListener, MouseWheelListener, MouseTrackListener {\r
+       SWTCanvas canvas;\r
+       boolean init = false;\r
+       RawInputListener listener;\r
+       \r
+       public SWTMouseInput(SWTCanvas canvas) {\r
+               this.canvas = canvas;\r
+               \r
+       }\r
+       \r
+       @Override\r
+       public void destroy() {\r
+               this.canvas.canvas.removeMouseListener(this);\r
+               this.canvas.canvas.removeMouseMoveListener(this);\r
+               this.canvas.canvas.removeMouseWheelListener(this);\r
+               this.canvas.canvas.removeMouseTrackListener(this);\r
+       }\r
+       \r
+       @Override\r
+       public void initialize() {\r
+               this.canvas.canvas.addMouseListener(this);\r
+               this.canvas.canvas.addMouseMoveListener(this);\r
+               this.canvas.canvas.addMouseWheelListener(this);\r
+               this.canvas.canvas.addMouseTrackListener(this);\r
+               init = true;\r
+       }\r
+       \r
+       @Override\r
+       public int getButtonCount() {\r
+               return 3;\r
+       }\r
+       \r
+       @Override\r
+       public boolean isInitialized() {\r
+               return init;\r
+       }\r
+       \r
+       @Override\r
+       public void setInputListener(RawInputListener listener) {\r
+               this.listener = listener;\r
+\r
+       }\r
+       \r
+       public long getInputTimeNanos() {\r
+            return Sys.getTime() * LwjglTimer.LWJGL_TIME_TO_NANOS;\r
+        }\r
+       \r
+       @Override\r
+       public void setCursorVisible(boolean visible) {\r
+               \r
+       }\r
+       \r
+       @Override\r
+       public void setNativeCursor(JmeCursor cursor) {\r
+               \r
+       }\r
+       \r
+       @Override\r
+       public void update() {\r
+               if (listener == null || events.size() == 0)\r
+                       return;\r
+               listener.beginInput();\r
+               for (InputEvent e : events) {\r
+                       if (e instanceof MouseButtonEvent)\r
+                               listener.onMouseButtonEvent((MouseButtonEvent)e);\r
+                       else\r
+                               listener.onMouseMotionEvent((MouseMotionEvent)e);\r
+               }\r
+               events.clear();\r
+               listener.endInput();\r
+       }\r
+       \r
+       private List<InputEvent> events = new ArrayList<InputEvent>();\r
+       \r
+       @Override\r
+       public void mouseDoubleClick(MouseEvent e) {\r
+               //events.add(new MouseButtonEvent(e.button,true, e.x, e.y));\r
+               \r
+       }\r
+       \r
+       @Override\r
+       public void mouseDown(MouseEvent e) {\r
+               events.add(new MouseButtonEvent(e.button,true, e.x, e.y));\r
+               \r
+       }\r
+       \r
+       @Override\r
+       public void mouseUp(MouseEvent e) {\r
+               events.add(new MouseButtonEvent(e.button,false, e.x, e.y));\r
+               \r
+       }\r
+       \r
+       int px = 0;\r
+       int py = 0;\r
+       \r
+       @Override\r
+       public void mouseMove(MouseEvent e) {\r
+               events.add(new MouseMotionEvent(e.x, e.y, e.x - px, e.y - py, 0, 0));\r
+               px = e.x;\r
+               py = e.y;\r
+               \r
+       }\r
+       \r
+       @Override\r
+       public void mouseScrolled(MouseEvent e) {\r
+               events.add(new MouseMotionEvent(e.x, e.y, 0, 0, e.button, e.count));\r
+       }\r
+       \r
+       @Override\r
+       public void mouseEnter(MouseEvent e) {\r
+               px = e.x;\r
+               py = e.y;\r
+       }\r
+       \r
+       @Override\r
+       public void mouseExit(MouseEvent e) {\r
+               \r
+       }\r
+       \r
+       @Override\r
+       public void mouseHover(MouseEvent e) {\r
+               \r
+       }\r
+    }\r
+    \r
+    public class SWTKeyInput implements KeyInput, KeyListener {\r
+       SWTCanvas canvas;\r
+       boolean init = false;\r
+       RawInputListener listener;\r
+       \r
+       public SWTKeyInput(SWTCanvas canvas) {\r
+               this.canvas = canvas;\r
+       }\r
+       \r
+       @Override\r
+       public void destroy() {\r
+               this.canvas.canvas.removeKeyListener(this);\r
+       }\r
+       \r
+       @Override\r
+       public void initialize() {\r
+               this.canvas.canvas.addKeyListener(this);\r
+               init = true;\r
+       }\r
+       \r
+       @Override\r
+       public boolean isInitialized() {\r
+               return init;\r
+       }\r
+       \r
+       @Override\r
+       public void setInputListener(RawInputListener listener) {\r
+               this.listener = listener;\r
+       }\r
+       \r
+       public long getInputTimeNanos() {\r
+            return Sys.getTime() * LwjglTimer.LWJGL_TIME_TO_NANOS;\r
+        }\r
+       \r
+       private List<KeyInputEvent> events = new ArrayList<KeyInputEvent>();\r
+       \r
+       KeyInputEvent prevEvent = new KeyInputEvent(0, (char)0, false, false);\r
+       \r
+       @Override\r
+       public void update() {\r
+               if (listener == null || events.size() == 0)\r
+                       return;\r
+               listener.beginInput();\r
+               \r
+               for (KeyInputEvent e : events) {\r
+                       //System.out.println("Key " + e.getKeyCode() + " " + e.getKeyChar() + " " + e.isPressed() + " " + e.isRepeating());\r
+                       \r
+                       // SWT reports wrong key code in released event for a key, if multiple keys are down at the same time\r
+                       // example:\r
+                       // press 'a'   -> event press a\r
+                       // press 'b'   -> event press b\r
+                       // release 'a' -> event release b\r
+                       // keep 'b' pressed -> event press b (with slight pause before events starts coming by)\r
+                       // release 'b' -> event release b\r
+                       \r
+                       // press 'a'   -> event press a\r
+                       // press 'b'   -> event press b\r
+                       // release 'b' -> event release b\r
+                       // keep 'a' pressed -> nothing happens.\r
+                       // release 'a' -> nothing happens\r
+                       \r
+                       if (e.isPressed()) {\r
+                               if (e.getKeyCode() != prevEvent.getKeyCode() && prevEvent.isPressed()) {\r
+                                       listener.onKeyEvent(new KeyInputEvent(prevEvent.getKeyCode(), prevEvent.getKeyChar(), false, false));\r
+                               }\r
+                       }\r
+                       \r
+                       listener.onKeyEvent(e);\r
+                       prevEvent = e;\r
+               }\r
+               events.clear();\r
+               listener.endInput();\r
+               \r
+       }\r
+       \r
+       @Override\r
+       public void keyPressed(KeyEvent e) {\r
+               events.add(new KeyInputEvent(getLWJGLKeyCode(e.keyCode), e.character, true, false));\r
+               \r
+       }\r
+       \r
+       @Override\r
+       public void keyReleased(KeyEvent e) {\r
+               \r
+               events.add(new KeyInputEvent(getLWJGLKeyCode(e.keyCode), e.character, false, false));\r
+       }\r
+       \r
+       private int getLWJGLKeyCode(int swtKeyCode) {\r
+               // TODO : LWJGL uses mapping/layout of US keyboard, these have to be checked   \r
+               if(swtKeyCode > 1024) {\r
+                   int keyCode = 0;\r
+                   switch (swtKeyCode) {\r
+                   case SWT.CTRL:\r
+                       keyCode = Keyboard.KEY_LCONTROL;\r
+                       break;\r
+                   case SWT.ALT:\r
+                       keyCode = Keyboard.KEY_LMETA;\r
+                       break;\r
+                   \r
+                   case SWT.SHIFT:\r
+                       keyCode = Keyboard.KEY_LSHIFT;\r
+                       break;\r
+                   case SWT.ARROW_LEFT:\r
+                       keyCode = Keyboard.KEY_LEFT;\r
+                       break;\r
+                   case SWT.ARROW_RIGHT:\r
+                       keyCode = Keyboard.KEY_RIGHT;\r
+                       break;\r
+                   case SWT.ARROW_UP:\r
+                       keyCode = Keyboard.KEY_UP;\r
+                       break;\r
+                   case SWT.ARROW_DOWN:\r
+                       keyCode = Keyboard.KEY_DOWN;\r
+                       break;\r
+                   case SWT.KEYPAD_0:\r
+                       keyCode = Keyboard.KEY_NUMPAD0;\r
+                       break;\r
+                   case SWT.KEYPAD_1:\r
+                       keyCode = Keyboard.KEY_NUMPAD1;\r
+                       break;\r
+                   case SWT.KEYPAD_2:\r
+                       keyCode = Keyboard.KEY_NUMPAD2;\r
+                       break;\r
+                   case SWT.KEYPAD_3:\r
+                       keyCode = Keyboard.KEY_NUMPAD3;\r
+                       break;\r
+                   case SWT.KEYPAD_4:\r
+                       keyCode = Keyboard.KEY_NUMPAD4;\r
+                       break;\r
+                   case SWT.KEYPAD_5:\r
+                       keyCode = Keyboard.KEY_NUMPAD5;\r
+                       break;\r
+                   case SWT.KEYPAD_6:\r
+                       keyCode = Keyboard.KEY_NUMPAD6;\r
+                       break;\r
+                   case SWT.KEYPAD_7:\r
+                       keyCode = Keyboard.KEY_NUMPAD7;\r
+                       break;\r
+                   case SWT.KEYPAD_8:\r
+                       keyCode = Keyboard.KEY_NUMPAD8;\r
+                       break;\r
+                   case SWT.KEYPAD_9:\r
+                       keyCode = Keyboard.KEY_NUMPAD9;\r
+                       break;\r
+                   case SWT.KEYPAD_CR:\r
+                       keyCode = Keyboard.KEY_NUMPADENTER;\r
+                       break;\r
+                   case SWT.NUM_LOCK:\r
+                       keyCode = Keyboard.KEY_NUMLOCK;\r
+                       break;\r
+                   case SWT.SCROLL_LOCK:\r
+                       keyCode = Keyboard.KEY_SCROLL;\r
+                       break;\r
+                   case SWT.CAPS_LOCK:\r
+                       keyCode = Keyboard.KEY_CAPITAL;\r
+                       break;\r
+                   case SWT.INSERT:\r
+                       keyCode = Keyboard.KEY_INSERT;\r
+                       break;\r
+                   case SWT.HOME:\r
+                       keyCode = Keyboard.KEY_HOME;\r
+                       break;\r
+                   case SWT.END:\r
+                       keyCode = Keyboard.KEY_END;\r
+                       break;\r
+                   case SWT.PAGE_UP:\r
+                       keyCode = Keyboard.KEY_NEXT;\r
+                       break;\r
+                   case SWT.PAGE_DOWN:\r
+                       keyCode = Keyboard.KEY_PRIOR;\r
+                       break;\r
+                   case SWT.PAUSE:\r
+                       keyCode = Keyboard.KEY_PAUSE;   \r
+                       break;\r
+                   case SWT.BREAK:\r
+                       keyCode = Keyboard.KEY_PAUSE;   \r
+                       break;\r
+                   case SWT.PRINT_SCREEN:\r
+                       keyCode = Keyboard.KEY_SYSRQ;   \r
+                       break;   \r
+                   case SWT.HELP:\r
+                       keyCode = 0;\r
+                       break;\r
+                   case SWT.KEYPAD_MULTIPLY:\r
+                       keyCode = Keyboard.KEY_MULTIPLY;\r
+                       break;\r
+                   case SWT.KEYPAD_DIVIDE:\r
+                       keyCode = Keyboard.KEY_DIVIDE;\r
+                       break;\r
+                   case SWT.KEYPAD_DECIMAL:\r
+                       keyCode = Keyboard.KEY_DECIMAL;\r
+                       break;\r
+                   case SWT.F1:\r
+                       keyCode = Keyboard.KEY_F1;\r
+                       break;\r
+                   case SWT.F2:\r
+                       keyCode = Keyboard.KEY_F2;\r
+                       break;\r
+                   case SWT.F3:\r
+                       keyCode = Keyboard.KEY_F3;\r
+                       break;\r
+                   case SWT.F4:\r
+                       keyCode = Keyboard.KEY_F4;\r
+                       break;\r
+                   case SWT.F5:\r
+                       keyCode = Keyboard.KEY_F5;\r
+                       break;\r
+                   case SWT.F6:\r
+                       keyCode = Keyboard.KEY_F6;\r
+                       break;\r
+                   case SWT.F7:\r
+                       keyCode = Keyboard.KEY_F7;\r
+                       break;\r
+                   case SWT.F8:\r
+                       keyCode = Keyboard.KEY_F8;\r
+                       break;\r
+                   case SWT.F9:\r
+                       keyCode = Keyboard.KEY_F9;\r
+                       break;\r
+                   case SWT.F10:\r
+                       keyCode = Keyboard.KEY_F10;\r
+                       break;\r
+                   case SWT.F11:\r
+                       keyCode = Keyboard.KEY_F11;\r
+                       break;\r
+                   case SWT.F12:\r
+                       keyCode = Keyboard.KEY_F12;\r
+                       break;\r
+                   default :\r
+                       keyCode = Keyboard.KEY_NONE;\r
+                       break;\r
+                   }\r
+                   \r
+                   return keyCode;\r
+               } else if (swtKeyCode == 8) {\r
+                       return Keyboard.KEY_BACK;\r
+               } else if (swtKeyCode == SWT.ESC) {\r
+                       return Keyboard.KEY_ESCAPE;\r
+               } else if (swtKeyCode == SWT.SPACE) {\r
+                       return Keyboard.KEY_SPACE;\r
+               } else if (swtKeyCode >= 49 && swtKeyCode < 58) { // 1 - 9\r
+                       return swtKeyCode - 47; \r
+               } else if (swtKeyCode == 48) {\r
+                       return Keyboard.KEY_0;\r
+               } else if (swtKeyCode == SWT.TAB) {\r
+                       return Keyboard.KEY_TAB;\r
+               } else if (swtKeyCode == 46) {\r
+                       return Keyboard.KEY_PERIOD;\r
+               } else if (swtKeyCode == 44) {\r
+                       return Keyboard.KEY_COMMA;\r
+               } else if (swtKeyCode == 39) { // '/* on SWE/FI keyboard\r
+                       return Keyboard.KEY_SLASH;\r
+               } else if (swtKeyCode == 45) {\r
+                       return Keyboard.KEY_MINUS;\r
+               } else if (swtKeyCode == 43) {\r
+                       return Keyboard.KEY_EQUALS; // +/? on SWE/FI keyboard\r
+               } else if (swtKeyCode == SWT.DEL) {\r
+                       return Keyboard.KEY_DELETE;\r
+               } else if (swtKeyCode == SWT.CR) {\r
+                       return Keyboard.KEY_RETURN;\r
+               } else if (swtKeyCode == 167) { // ยง on SWE/FI keyboard\r
+                       return Keyboard.KEY_BACKSLASH;\r
+               }\r
+               else if (swtKeyCode >= 97 )\r
+                       swtKeyCode -= 32;\r
+               if (swtKeyCode >= 65 && swtKeyCode <= 90) {\r
+                       switch (swtKeyCode) {\r
+                               case 65:\r
+                                       return Keyboard.KEY_A;\r
+                               case 66:\r
+                                       return Keyboard.KEY_B;\r
+                               case 67:\r
+                                       return Keyboard.KEY_C;\r
+                               case 68:\r
+                                       return Keyboard.KEY_D;\r
+                               case 69:\r
+                                       return Keyboard.KEY_E;\r
+                               case 70:\r
+                                       return Keyboard.KEY_F;\r
+                               case 71:\r
+                                       return Keyboard.KEY_G;\r
+                               case 72:\r
+                                       return Keyboard.KEY_H;\r
+                               case 73:\r
+                                       return Keyboard.KEY_I;\r
+                               case 74:\r
+                                       return Keyboard.KEY_J;\r
+                               case 75:\r
+                                       return Keyboard.KEY_K;\r
+                               case 76:\r
+                                       return Keyboard.KEY_L;\r
+                               case 77:\r
+                                       return Keyboard.KEY_M;\r
+                               case 78:\r
+                                       return Keyboard.KEY_N;\r
+                               case 79:\r
+                                       return Keyboard.KEY_O;\r
+                               case 80:\r
+                                       return Keyboard.KEY_P;\r
+                               case 81:\r
+                                       return Keyboard.KEY_Q;\r
+                               case 82:\r
+                                       return Keyboard.KEY_R;\r
+                               case 83:\r
+                                       return Keyboard.KEY_S;\r
+                               case 84:\r
+                                       return Keyboard.KEY_T;\r
+                               case 85:\r
+                                       return Keyboard.KEY_U;\r
+                               case 86:\r
+                                       return Keyboard.KEY_V;\r
+                               case 87:\r
+                                       return Keyboard.KEY_W;\r
+                               case 88:\r
+                                       return Keyboard.KEY_X;\r
+                               case 89:\r
+                                       return Keyboard.KEY_Y;\r
+                               case 90:\r
+                                       return Keyboard.KEY_Z;\r
+                                       \r
+                       }\r
+               }\r
+               return Keyboard.KEY_UNLABELED;\r
+           }\r
+\r
+       \r
+        private int getAWTKeyCode(int swtKeyCode) {\r
+               if(swtKeyCode > 1024) {\r
+                   int keyCode = 0;\r
+                   switch (swtKeyCode) {\r
+                   case SWT.CTRL:\r
+                       keyCode = java.awt.event.KeyEvent.VK_CONTROL;\r
+                       break;\r
+                   case SWT.ALT:\r
+                       keyCode = java.awt.event.KeyEvent.VK_ALT;\r
+                       break;\r
+                   \r
+                   case SWT.SHIFT:\r
+                       keyCode = java.awt.event.KeyEvent.VK_SHIFT;\r
+                       break;\r
+                   case SWT.ARROW_LEFT:\r
+                       keyCode = java.awt.event.KeyEvent.VK_LEFT;\r
+                       break;\r
+                   case SWT.ARROW_RIGHT:\r
+                       keyCode = java.awt.event.KeyEvent.VK_RIGHT;\r
+                       break;\r
+                   case SWT.ARROW_UP:\r
+                       keyCode = java.awt.event.KeyEvent.VK_UP;\r
+                       break;\r
+                   case SWT.ARROW_DOWN:\r
+                       keyCode = java.awt.event.KeyEvent.VK_DOWN;\r
+                       break;\r
+                   case SWT.KEYPAD_0:\r
+                       keyCode = java.awt.event.KeyEvent.VK_NUMPAD0;\r
+                       break;\r
+                   case SWT.KEYPAD_1:\r
+                       keyCode = java.awt.event.KeyEvent.VK_NUMPAD1;\r
+                       break;\r
+                   case SWT.KEYPAD_2:\r
+                       keyCode = java.awt.event.KeyEvent.VK_NUMPAD2;\r
+                       break;\r
+                   case SWT.KEYPAD_3:\r
+                       keyCode = java.awt.event.KeyEvent.VK_NUMPAD3;\r
+                       break;\r
+                   case SWT.KEYPAD_4:\r
+                       keyCode = java.awt.event.KeyEvent.VK_NUMPAD4;\r
+                       break;\r
+                   case SWT.KEYPAD_5:\r
+                       keyCode = java.awt.event.KeyEvent.VK_NUMPAD5;\r
+                       break;\r
+                   case SWT.KEYPAD_6:\r
+                       keyCode = java.awt.event.KeyEvent.VK_NUMPAD6;\r
+                       break;\r
+                   case SWT.KEYPAD_7:\r
+                       keyCode = java.awt.event.KeyEvent.VK_NUMPAD7;\r
+                       break;\r
+                   case SWT.KEYPAD_8:\r
+                       keyCode = java.awt.event.KeyEvent.VK_NUMPAD8;\r
+                       break;\r
+                   case SWT.KEYPAD_9:\r
+                       keyCode = java.awt.event.KeyEvent.VK_NUMPAD9;\r
+                       break;\r
+                   case SWT.KEYPAD_CR:\r
+                       keyCode = java.awt.event.KeyEvent.VK_ENTER;\r
+                       break;\r
+                   case SWT.NUM_LOCK:\r
+                       keyCode = java.awt.event.KeyEvent.VK_NUM_LOCK;\r
+                       break;\r
+                   case SWT.SCROLL_LOCK:\r
+                       keyCode = java.awt.event.KeyEvent.VK_SCROLL_LOCK;\r
+                       break;\r
+                   case SWT.CAPS_LOCK:\r
+                       keyCode = java.awt.event.KeyEvent.VK_CAPS_LOCK;\r
+                       break;\r
+                   case SWT.INSERT:\r
+                       keyCode = java.awt.event.KeyEvent.VK_INSERT;\r
+                       break;\r
+                   case SWT.HOME:\r
+                       keyCode = java.awt.event.KeyEvent.VK_HOME;\r
+                       break;\r
+                   case SWT.END:\r
+                       keyCode = java.awt.event.KeyEvent.VK_END;\r
+                       break;\r
+                   case SWT.PAGE_UP:\r
+                       keyCode = java.awt.event.KeyEvent.VK_PAGE_UP;\r
+                       break;\r
+                   case SWT.PAGE_DOWN:\r
+                       keyCode = java.awt.event.KeyEvent.VK_PAGE_DOWN;\r
+                       break;\r
+                   case SWT.PAUSE:\r
+                       keyCode = java.awt.event.KeyEvent.VK_PAUSE;   \r
+                       break;\r
+                   case SWT.BREAK:\r
+                       keyCode = java.awt.event.KeyEvent.VK_PAUSE;   \r
+                       break;\r
+                   case SWT.PRINT_SCREEN:\r
+                       keyCode = java.awt.event.KeyEvent.VK_PRINTSCREEN;   \r
+                       break;   \r
+                   case SWT.HELP:\r
+                       keyCode = java.awt.event.KeyEvent.VK_HELP;\r
+                       break;\r
+                   default :\r
+                       keyCode = 0;\r
+                       break;\r
+                   }\r
+                   \r
+                   return keyCode;\r
+               } else if (swtKeyCode == 8) {\r
+                       return java.awt.event.KeyEvent.VK_BACK_SPACE;\r
+               }\r
+               else if (swtKeyCode >= 97 )\r
+                       return swtKeyCode - 32;\r
+               else\r
+                       return swtKeyCode;\r
+           }\r
+    }\r
+\r
+}\r