Browse Source

LwjglCanvas using lwjgl-jawt (#2153)

Canvas rendering solution using lwjgl-jawt
JNightRide 1 năm trước cách đây
mục cha
commit
22bf58ad80

+ 2 - 2
jme3-examples/build.gradle

@@ -19,8 +19,8 @@ dependencies {
     implementation project(':jme3-effects')
     implementation project(':jme3-jbullet')
     implementation project(':jme3-jogg')
-    implementation project(':jme3-lwjgl')
-//    implementation project(':jme3-lwjgl3')
+//    implementation project(':jme3-lwjgl')
+    implementation project(':jme3-lwjgl3')
     implementation project(':jme3-networking')
     implementation project(':jme3-niftygui')
     implementation project(':jme3-plugins')

+ 1 - 2
jme3-examples/src/main/java/jme3test/awt/TestCanvas.java

@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package jme3test.awt;
 
 import com.jme3.app.LegacyApplication;
@@ -171,7 +170,7 @@ public class TestCanvas {
             public void actionPerformed(ActionEvent e) {
                 currentPanel.remove(canvas);
                 app.stop(true);
-
+                
                 createCanvas(appClass);
                 currentPanel.add(canvas, BorderLayout.CENTER);
                 frame.pack();

+ 3 - 0
jme3-lwjgl3/build.gradle

@@ -1,9 +1,12 @@
 dependencies {
     api project(':jme3-core')
     api project(':jme3-desktop')
+    
+    api "org.lwjglx:lwjgl3-awt:0.1.8"
 
     api "org.lwjgl:lwjgl:${lwjgl3Version}"
     api "org.lwjgl:lwjgl-glfw:${lwjgl3Version}"
+    api "org.lwjgl:lwjgl-jawt:${lwjgl3Version}"
     api "org.lwjgl:lwjgl-jemalloc:${lwjgl3Version}"
     api "org.lwjgl:lwjgl-openal:${lwjgl3Version}"
     api "org.lwjgl:lwjgl-opencl:${lwjgl3Version}"

+ 623 - 168
jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2022 jMonkeyEngine
+ * Copyright (c) 2009-2024 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,151 +37,538 @@ import com.jme3.input.awt.AwtKeyInput;
 import com.jme3.input.awt.AwtMouseInput;
 import com.jme3.system.AppSettings;
 import com.jme3.system.JmeCanvasContext;
-import com.jme3.system.JmeContext.Type;
-import com.jme3.texture.FrameBuffer;
-import com.jme3.texture.FrameBuffer.FrameBufferTarget;
-import com.jme3.texture.Image;
-import com.jme3.util.BufferUtils;
-import com.jme3.util.Screenshots;
+import com.jme3.system.lwjglx.LwjglxGLPlatform;
+
 import java.awt.AWTException;
-import java.awt.BufferCapabilities;
 import java.awt.Canvas;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
-import java.awt.ImageCapabilities;
-import java.awt.RenderingHints;
+import java.awt.GraphicsConfiguration;
+import java.awt.Toolkit;
 import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
 import java.awt.geom.AffineTransform;
-import java.awt.image.AffineTransformOp;
-import java.awt.image.BufferStrategy;
-import java.awt.image.BufferedImage;
-import java.nio.ByteBuffer;
-import java.nio.IntBuffer;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.lwjgl.awthacks.NonClearGraphics;
+import org.lwjgl.awthacks.NonClearGraphics2D;
+import org.lwjgl.opengl.awt.GLData;
+import org.lwjgl.system.Platform;
+
+import static org.lwjgl.system.MemoryUtil.*;
+import static com.jme3.system.lwjglx.LwjglxDefaultGLPlatform.*;
+
+/**
+ * Class <code>LwjglCanvas</code> that integrates <a href="https://github.com/LWJGLX/lwjgl3-awt">LWJGLX</a>
+ * which allows using AWT-Swing components.
+ * 
+ * <p>
+ * If <b>LwjglCanvas</b> throws an exception due to configuration problems, we can debug as follows:
+ * <br>
+ * - In <code>AppSettings</code>, set this property to enable a debug that displays
+ * the effective data for the context.
+ * <pre><code>
+ * ....
+ *  AppSettings settings = new AppSettings(true);
+ *  settings.putBoolean("GLDataEffectiveDebug", true);
+ * ...
+ * </code></pre>
+ * 
+ * <p>
+ * <b>NOTE:</b> If running <code>LwjglCanvas</code> on older machines, the <code>SRGB | Gamma Correction</code> option 
+ * will raise an exception, so it should be disabled.
+ * <pre><code>
+ * ....
+ *  AppSettings settings = new AppSettings(true);
+ *  settings.setGammaCorrection(false);
+ * ...
+ * </code></pre>
+ * 
+ * @author wil
+ */
 public class LwjglCanvas extends LwjglWindow implements JmeCanvasContext, Runnable {
 
-    private static final Logger logger = Logger.getLogger(LwjglCanvas.class.getName());
-
-    private final Canvas canvas;
-
-    private BufferedImage img;
-    private FrameBuffer fb;
-
-    private ByteBuffer byteBuf;
-    private IntBuffer intBuf;
-
-    private BufferStrategy strategy;
-    private AffineTransformOp transformOp;
+    /** Logger class. */
+    private static final Logger LOGGER = Logger.getLogger(LwjglCanvas.class.getName());
+    
+    /** GL versions map. */
+    private static final Map<String, Consumer<GLData>> RENDER_CONFIGS = new HashMap<>();
+    
+    /** Type of operating system where this context is running. */
+    private static final Platform OS = Platform.get();
+    
+    /*
+        Register the different versions.
+    */
+    static {
+        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL30, (data) -> {
+            data.majorVersion = 3;
+            data.minorVersion = 0;
+        });
+        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL31, (data) -> {
+            data.majorVersion = 3;
+            data.minorVersion = 1;
+        });
+        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL32, (data) -> {
+            data.majorVersion = 3;
+            data.minorVersion = 2;
+            data.profile = GLData.Profile.COMPATIBILITY;
+        });
+        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL33, (data) -> {
+            data.majorVersion = 3;
+            data.minorVersion = 3;
+            data.profile = GLData.Profile.COMPATIBILITY;
+        });
+        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL40, (data) -> {
+            data.majorVersion = 4;
+            data.minorVersion = 0;
+            data.profile = GLData.Profile.COMPATIBILITY;
+        });
+        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL41, (data) -> {
+            data.majorVersion = 4;
+            data.minorVersion = 1;
+            data.profile = GLData.Profile.COMPATIBILITY;
+        });
+        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL42, (data) -> {
+            data.majorVersion = 4;
+            data.minorVersion = 2;
+            data.profile = GLData.Profile.COMPATIBILITY;
+        });
+        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL43, (data) -> {
+            data.majorVersion = 4;
+            data.minorVersion = 3;
+            data.profile = GLData.Profile.COMPATIBILITY;
+        });
+        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL44, (data) -> {
+            data.majorVersion = 4;
+            data.minorVersion = 4;
+            data.profile = GLData.Profile.COMPATIBILITY;
+        });
+        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL45, (data) -> {
+            data.majorVersion = 4;
+            data.minorVersion = 5;
+            data.profile = GLData.Profile.COMPATIBILITY;
+        });
+    }
+    
+    /**
+     * An AWT <code>java.awt.Canvas</code> that supports to be drawn on using OpenGL.
+     */
+    private class LwjglAWTGLCanvas extends Canvas {
+        
+        /** 
+         * A {@link com.jme3.system.lwjglx.LwjglxGLPlatform} object. 
+         * @see org.lwjgl.opengl.awt.PlatformGLCanvas
+         */
+        private LwjglxGLPlatform platformCanvas;
+        
+        /**  The OpenGL context (LWJGL3-AWT). */
+        private long context;
+        
+        /**
+         * Information object used to create the OpenGL context.
+         */
+        private GLData data;
+        
+        /** Effective data to initialize the context. */
+        private GLData effective;
+
+        /**
+         * Constructor of the <code>LwjglAWTGLCanva</code> class where objects are
+         * initialized for OpenGL-AWT rendering
+         * 
+         * @param data A {@link org.lwjgl.opengl.awt.GLData} object
+         */
+        public LwjglAWTGLCanvas(GLData data) {
+            this.effective = new GLData();
+            this.context   = NULL;
+            this.data      = data;
+            
+            try {
+                platformCanvas = createLwjglxGLPlatform();
+            } catch (UnsupportedOperationException e) {
+                listener.handleError(e.getLocalizedMessage(), e);
+            }
+        }
+        
+        /**
+         * (non-Javadoc)
+         * @see java.awt.Component#addComponentListener(java.awt.event.ComponentListener) 
+         * @param l object-listener
+         */
+        @Override
+        public synchronized void addComponentListener(ComponentListener l) {
+            super.addComponentListener(l);
+        }
+        
+        /**
+         * Returns the effective data (recommended or ideal) to initialize the 
+         * LWJGL3-AWT context.
+         * 
+         * @return A {@link org.lwjgl.opengl.awt.GLData} object
+         */
+        public GLData getGLDataEffective() {
+            return effective;
+        }
+        
+        /**
+         * Called after <code>beforeRender()</code> to release the threads (unlock); 
+         * so that AWT can update its threads normally.
+         * <p>
+         * <b>NOTE:</b> It is very important to call this method and not leave AWT 
+         * hanging (breaking it) regardless of whether an error occurs during OpenGL
+         * rendering.
+         */
+        public void afterRender() {
+            // release the rendering context
+            platformCanvas.makeCurrent(NULL);
+            try {
+                platformCanvas.unlock(); // <- MUST unlock on Linux
+            } catch (AWTException e) {
+                listener.handleError("Failed to unlock Canvas", e);
+            }
+        }
+        
+        /**
+         * Called before <code>afterRender()</code> to prepare the AWT drawing surface.
+         */
+        public void beforeRender() {
+            // this is where the OpenGL rendering context is generated.
+            if (context == NULL) {
+                try {
+                    context = platformCanvas.create(this, data, effective);
+                } catch (AWTException e) {
+                    listener.handleError("Exception while creating the OpenGL context", e);
+                    return;
+                }
+            }
+            
+            /*
+             * To start drawing on the AWT surface, the AWT threads must be locked to
+             * avoid conflicts when drawing on the canvas.
+             */
+            try {
+                platformCanvas.lock(); // <- MUST lock on Linux
+            } catch (AWTException e) {
+                listener.handleError("Failed to lock Canvas", e);
+            }
+            
+            /*
+             * The 'makeCurrent(long)' method converts the specified OpenGL rendering 
+             * context to the current rendering context.
+             */
+            platformCanvas.makeCurrent(context);
+        }
+        
+        /**
+         * Frees up the drawing surface (only on Windows and MacOSX).
+         */
+        public void doDisposeCanvas() {
+            if (OS != Platform.LINUX) {
+                platformCanvas.dispose();
+            }
+        }
+        
+        /**
+         * This is where you actually draw on the canvas (framebuffer).
+         */
+        public void swapBuffers() {
+            platformCanvas.swapBuffers();
+        }
+        
+        /**
+         * (non-Javadoc)
+         * @see java.awt.Component#addNotify() 
+         */
+        @Override
+        public void addNotify() {
+            super.addNotify();
+            /* you have to notify if the canvas is visible to draw on it. */
+            synchronized (lock) {
+                hasNativePeer.set(true);
+            }
+            requestFocusInWindow();
+        }
+        
+        /**
+         * (non-Javadoc)
+         * @see java.awt.Component#removeNotify() 
+         */
+        @Override
+        public void removeNotify() {
+            synchronized (lock) {
+                // prepare for a possible re-adding
+                if ((OS != Platform.LINUX) && (context != NULL)) {
+                    platformCanvas.deleteContext(context);
+                    context = NULL;
+                }
+                hasNativePeer.set(false);
+            }            
+            super.removeNotify();
+            if (OS == Platform.WINDOWS) {
+                LOGGER.log(Level.WARNING, "Windows does not support this functionality: remove(__canvas__)");
+            }
+        }
+        
+        /**
+         * (non-Javadoc)
+         * @see com.jme3.system.lwjglx.LwjglxGLPlatform#destroy() 
+         */
+        public void destroy() {
+            platformCanvas.destroy();
+        }
+        
+        /**
+         * Returns Graphics object that ignores {@link java.awt.Graphics#clearRect(int, int, int, int)}
+         * calls.
+         * <p>
+         * This is done so that the frame buffer will not be cleared by AWT/Swing internals.
+         * 
+         * @see org.lwjgl.awthacks.NonClearGraphics2D
+         * @see org.lwjgl.awthacks.NonClearGraphics
+         * @return Graphics
+         */
+        @Override
+        public Graphics getGraphics() {
+            Graphics graphics = super.getGraphics();
+            if (graphics instanceof Graphics2D) {
+                return new NonClearGraphics2D((Graphics2D) graphics);
+            }
+            return new NonClearGraphics(graphics);
+        }
+    }
+    
+    /** Canvas-AWT. */
+    private final LwjglAWTGLCanvas canvas;
+    
+    /**
+     * Configuration data to start the AWT context, this is used by the
+     * {@code lwjgl-awt} library.
+     */
+    private GLData glData;
+    
+    /** Used to display the effective data for the {@code AWT-Swing} drawing surface per console. */
+    private final AtomicBoolean showGLDataEffective = new AtomicBoolean(false);
+    
+    /** Used to notify the canvas status ({@code remove()/add()}). */
     private final AtomicBoolean hasNativePeer = new AtomicBoolean(false);
+    
+    /** Notify if the canvas is visible and has a parent.*/
     private final AtomicBoolean showing = new AtomicBoolean(false);
-
-    private int width = 1;
-    private int height = 1;
+    
+    /** Notify if there is a change in canvas dimensions. */
     private AtomicBoolean needResize = new AtomicBoolean(false);
-    private final Object lock = new Object();
+    
+    /**
+     * Flag that uses the context to check if it is initialized or not, this prevents
+     * it from being initialized multiple times and potentially breaking the JVM.
+     */
+    private AtomicBoolean contextFlag = new AtomicBoolean(false);
+
+    /** Semaphort used to check the "terminate" signal. */
+    private final Semaphore signalTerminate = new Semaphore(0);
 
+    /** lock-object. */
+    private final Object lock = new Object();
+    
+    /** Framebuffer width. */
+    private int framebufferWidth = 1;
+    
+    /** Framebuffer height. */
+    private int framebufferHeight = 1;
+
+    /** AWT keyboard input manager. */
     private AwtKeyInput keyInput;
+    
+    /** AWT mouse input manager. */
     private AwtMouseInput mouseInput;
-
+    
+    /**
+     * Generate a new OpenGL context (<code>LwjglCanvas</code>) to integrate 
+     * AWT/Swing with JME3 in your desktop applications.
+     */
     public LwjglCanvas() {
         super(Type.Canvas);
-
-        canvas = new Canvas() {
-            @Override
-            public void paint(Graphics g) {
-                Graphics2D g2d = (Graphics2D) g;
-                synchronized (lock) {
-                    g2d.drawImage(img, transformOp, 0, 0);
-                }
-            }
-
-            @Override
-            public void addNotify() {
-                super.addNotify();
-
-                synchronized (lock) {
-                    hasNativePeer.set(true);
-                }
-
-                requestFocusInWindow();
-            }
-
+        glData = new GLData();
+        canvas = new LwjglAWTGLCanvas(glData);
+        canvas.setIgnoreRepaint(true);
+        
+        // To determine the size of the framebuffer every time the user resizes
+        // the canvas (this works if the component has a parent)
+        canvas.addComponentListener(new ComponentAdapter() {
             @Override
-            public void removeNotify() {
+            public void componentResized(ComponentEvent e) {                
                 synchronized (lock) {
-                    hasNativePeer.set(false);
-                }
+                    GraphicsConfiguration gc = canvas.getGraphicsConfiguration();
+                    if (gc == null) {
+                        return;
+                    }
+                    
+                    AffineTransform at = gc.getDefaultTransform();
+                    float sx = (float) at.getScaleX(),
+                          sy = (float) at.getScaleY();
 
-                super.removeNotify();
-            }
-        };
-        canvas.addComponentListener(new ComponentAdapter() {
+                    int fw = (int) (canvas.getWidth() * sx);
+                    int fh = (int) (canvas.getHeight() * sy);
 
-            @Override
-            public void componentResized(ComponentEvent e) {
-                synchronized (lock) {
-                    int newWidth = Math.max(canvas.getWidth(), 1);
-                    int newHeight = Math.max(canvas.getHeight(), 1);
-                    if (width != newWidth || height != newHeight) {
-                        width = newWidth;
-                        height = newHeight;
+                    if (fw != framebufferWidth || fh != framebufferHeight) {
+                        framebufferWidth = Math.max(fw, 1);
+                        framebufferHeight = Math.max(fh, 1);
                         needResize.set(true);
                     }
                 }
-            }
+            }            
         });
-        canvas.setFocusable(true);
-        canvas.setIgnoreRepaint(true);
     }
 
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.JmeContext#destroy(boolean) 
+     * @param waitFor boolean
+     */
     @Override
-    public Canvas getCanvas() {
-        return canvas;
+    public void destroy(boolean waitFor) {
+        super.destroy(waitFor);
+        this.contextFlag.set(false);
     }
 
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.JmeContext#create(boolean) 
+     * @param waitFor boolean
+     */
     @Override
-    protected void showWindow() {
+    public void create(boolean waitFor) {
+        if (this.contextFlag.get()) {
+            return;
+        }
+        // create context
+        super.create(waitFor);
+        this.contextFlag.set(true);
     }
-
+    
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.lwjgl.LwjglWindow#createContext(com.jme3.system.AppSettings) 
+     * @param settings A {@link com.jme3.system.AppSettings} object
+     */
     @Override
-    protected void setWindowIcon(final AppSettings settings) {
+    protected void createContext(AppSettings settings)  {
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException ex) {
+            LOGGER.log(Level.SEVERE, "LWJGL3-AWT: Interrupted!", ex);
+        }
+        
+        super.createContext(settings);
+        RENDER_CONFIGS.computeIfAbsent(settings.getRenderer(), (t) -> {
+            return (data) -> {
+                data.majorVersion = 2;
+                data.minorVersion = 0;
+            };
+        }).accept(glData);
+        
+        if (settings.getBitsPerPixel() == 24) {
+            glData.redSize = 8;
+            glData.greenSize = 8;
+            glData.blueSize = 8;            
+        } else if (settings.getBitsPerPixel() == 16) {
+            glData.redSize = 5;
+            glData.greenSize = 6;
+            glData.blueSize = 5;            
+        }
+        
+        // Enable vsync for LWJGL3-AWT
+        if (settings.isVSync()) {
+            glData.swapInterval = 1;
+        } else {
+            glData.swapInterval = 0;
+        }
+        
+        // This will activate the "effective data" scrubber.
+        showGLDataEffective.set(settings.getBoolean("GLDataEffectiveDebug"));
+        
+        glData.depthSize = settings.getBitsPerPixel();
+        glData.alphaSize = settings.getAlphaBits();
+        glData.sRGB = settings.isGammaCorrection(); // Not compatible with very old devices
+        
+        glData.depthSize = settings.getDepthBits();
+        glData.stencilSize = settings.getStencilBits();
+        glData.samples = settings.getSamples();
+        glData.stereo = settings.useStereo3D();  
+       
+        glData.debug = settings.isGraphicsDebug();
+        glData.api = GLData.API.GL;
     }
-
+    
+    /**
+     * Returns the AWT component where it is drawn (canvas).
+     * @return Canvas
+     */
     @Override
-    public void setTitle(String title) {
+    public Canvas getCanvas() {
+        return canvas;
     }
 
+    /** (non-Javadoc) */
+    @Override
+    protected void showWindow() { }
+    /** (non-Javadoc) */
+    @Override 
+    protected void setWindowIcon(final AppSettings settings) { }
+    /** (non-Javadoc) */
+    @Override
+    public void setTitle(String title) { }
+
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.lwjgl.LwjglWindow#getKeyInput() 
+     * @return returns a {@link com.jme3.input.awt.AwtKeyInput} object
+     */
     @Override
     public KeyInput getKeyInput() {
         if (keyInput == null) {
             keyInput = new AwtKeyInput();
             keyInput.setInputSource(canvas);
         }
-
         return keyInput;
     }
 
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.lwjgl.LwjglWindow#getMouseInput() 
+     * @return returns a {@link com.jme3.input.awt.AwtMouseInput} object
+     */
     @Override
     public MouseInput getMouseInput() {
         if (mouseInput == null) {
             mouseInput = new AwtMouseInput();
             mouseInput.setInputSource(canvas);
         }
-
         return mouseInput;
     }
 
+    /**
+     * Check if the canvas is displayed, that is, if it has a parent that has set it up.
+     * <p>
+     * It is very important that this verification be done so that LWJGL3-AWT works correctly.
+     * 
+     * @return returns <code>true</code> if the canvas is ready to draw; otherwise 
+     * returns <code>false</code>
+     */
     public boolean checkVisibilityState() {
         if (!hasNativePeer.get()) {
             synchronized (lock) {
-                if (strategy != null) {
-                    strategy.dispose();
-                    strategy = null;
-                }
+                canvas.doDisposeCanvas();
             }
             return false;
         }
@@ -191,111 +578,179 @@ public class LwjglCanvas extends LwjglWindow implements JmeCanvasContext, Runnab
         return currentShowing;
     }
 
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.lwjgl.LwjglWindow#destroyContext() 
+     */
     @Override
     protected void destroyContext() {
         synchronized (lock) {
-            destroyFrameBuffer();
-            img = null;
-            byteBuf = null;
-            intBuf = null;
+            canvas.destroy();
         }
 
+        // request the cleanup
+        signalTerminate.release();
         super.destroyContext();
     }
 
-    @Override
-    protected void createContext(AppSettings settings) {
-        super.createContext(settings);
-
-        if (renderer != null) {
-            createFrameBuffer(width, height);
-        }
-    }
-
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.lwjgl.LwjglWindow#runLoop() 
+     */
     @Override
     protected void runLoop() {
         if (needResize.get()) {
             needResize.set(false);
-            listener.reshape(width, height);
-            createFrameBuffer(width, height);
+            settings.setResolution(framebufferWidth, framebufferHeight);
+            listener.reshape(framebufferWidth, framebufferHeight);
         }
-
+        
+        // check component status
         if (!checkVisibilityState()) {
             return;
         }
 
-        super.runLoop();
-
-        drawFrameInThread();
-    }
-
-    public void drawFrameInThread() {
-
-        // Convert screenshot
-        byteBuf.clear();
-        renderer.readFrameBuffer(fb, byteBuf);
-        Screenshots.convertScreenShot2(intBuf, img);
-
-        synchronized (lock) {
-            // All operations on strategy should be synchronized (?)
-            if (strategy == null) {
-                try {
-                    canvas.createBufferStrategy(1,
-                            new BufferCapabilities(
-                                    new ImageCapabilities(true),
-                                    new ImageCapabilities(true),
-                                    BufferCapabilities.FlipContents.UNDEFINED)
-                    );
-                } catch (AWTException ex) {
-                    logger.log(Level.SEVERE, "Failed to create buffer strategy!", ex);
-                }
-                strategy = canvas.getBufferStrategy();
+        //----------------------------------------------------------------------
+        //                          AWT - RENDERER
+        //----------------------------------------------------------------------
+        /*
+         * The same logic as AWTGLCanvas is used to draw on the awt drawing surface:
+         * 
+         * 1. Lock any thread to avoid any conflict.
+         * 2. Buffer swap (this is where the framebuffer is actually drawn): swapBuffers()
+         * 3. Unlock so that the AWT thread can work normally. IF NOT DONE, IT WILL 
+         *    BE WAITING AND BREAK ANY AWT/Swing APP.
+         */
+        canvas.beforeRender();
+        try {
+            super.runLoop();
+            if (allowSwapBuffers && autoFlush) {
+                canvas.swapBuffers();
             }
-
-            // Draw screenshot
-            do {
-                do {
-                    Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
-                    if (g2d == null) {
-                        logger.log(Level.WARNING, "OGL: DrawGraphics was null.");
-                        return;
-                    }
-
-                    g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
-                            RenderingHints.VALUE_RENDER_QUALITY);
-
-                    g2d.drawImage(img, transformOp, 0, 0);
-                    g2d.dispose();
-                    strategy.show();
-                } while (strategy.contentsRestored());
-            } while (strategy.contentsLost());
+        } finally {
+            canvas.afterRender();
+        }
+        
+        // Sync the display on some systems.
+        Toolkit.getDefaultToolkit().sync();
+        
+        //----------------------------------------------------------------------
+        /*
+         * Whether it is necessary to know the effective attributes to 
+         * initialize the LWJGL3-AWT context
+         */
+        //----------------------------------------------------------------------
+        if (showGLDataEffective.get()) {
+            showGLDataEffective.set(false);
+            System.out.println(MessageFormat.format("[ DEBUGGER ] :Effective data to initialize the LWJGL3-AWT context\n{0}", 
+                                                getPrintContextInitInfo(canvas.getGLDataEffective())));
         }
-    }
-
-    private void createFrameBuffer(int width, int height) {
-        byteBuf = BufferUtils.ensureLargeEnough(byteBuf, width * height * 4);
-        intBuf = byteBuf.asIntBuffer();
-
-        destroyFrameBuffer();
-
-        fb = new FrameBuffer(width, height, settings.getSamples());
-        fb.setDepthTarget(FrameBufferTarget.newTarget(Image.Format.Depth));
-        fb.addColorTarget(FrameBufferTarget.newTarget(Image.Format.RGB8));
-        fb.setSrgb(settings.isGammaCorrection());
-
-        renderer.setMainFrameBufferOverride(fb);
-
-        img = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
 
-        AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
-        tx.translate(0, -img.getHeight());
-        transformOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+        try {
+            if (signalTerminate.tryAcquire(10, TimeUnit.MILLISECONDS)) {
+                canvas.doDisposeCanvas();
+            }
+        } catch (InterruptedException ignored) { }
+    }
+    
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.lwjgl.LwjglContext#printContextInitInfo() 
+     */
+    @Override
+    protected void printContextInitInfo() {
+        super.printContextInitInfo();
+        LOGGER.log(Level.INFO, "Initializing LWJGL3-AWT with jMonkeyEngine\n{0}", getPrintContextInitInfo(glData));
+    }
+    
+    /**
+     * Returns a string with the information obtained from <code>GLData</code>
+     * so that it can be displayed.
+     * 
+     * @param glData context information
+     * @return String
+     */
+    protected String getPrintContextInitInfo(GLData glData) {
+        StringBuilder sb = new StringBuilder();
+            sb.append(" *  Double Buffer: ").append(glData.doubleBuffer);
+            sb.append('\n')
+              .append(" *  Stereo: ").append(glData.stereo);
+            sb.append('\n')
+              .append(" *  Red Size: ").append(glData.redSize);
+            sb.append('\n')
+              .append(" *  Rreen Size: ").append(glData.greenSize);
+            sb.append('\n')
+              .append(" *  Blue Size: ").append(glData.blueSize);
+            sb.append('\n')
+              .append(" *  Alpha Size: ").append(glData.alphaSize);
+            sb.append('\n')
+              .append(" *  Depth Size: ").append(glData.depthSize);
+            sb.append('\n')
+              .append(" *  Stencil Size: ").append(glData.stencilSize);
+            sb.append('\n')
+              .append(" *  Accum Red Size: ").append(glData.accumRedSize);
+            sb.append('\n')
+              .append(" *  Accum Green Size: ").append(glData.accumGreenSize);
+            sb.append('\n')
+              .append(" *  Accum Blue Size: ").append(glData.accumBlueSize);
+            sb.append('\n')
+              .append(" *  Accum Alpha Size: ").append(glData.accumAlphaSize);
+            sb.append('\n')
+              .append(" *  Sample Buffers: ").append(glData.sampleBuffers);
+            sb.append('\n')
+              .append(" *  Share Context: ").append(glData.shareContext);
+            sb.append('\n')
+              .append(" *  Major Version: ").append(glData.majorVersion);
+            sb.append('\n')
+              .append(" *  Minor Version: ").append(glData.minorVersion);
+            sb.append('\n')
+              .append(" *  Forward Compatible: ").append(glData.forwardCompatible);
+            sb.append('\n')
+              .append(" *  Profile: ").append(glData.profile);
+            sb.append('\n')
+              .append(" *  API: ").append(glData.api);
+            sb.append('\n')
+              .append(" *  Debug: ").append(glData.debug);
+            sb.append('\n')
+              .append(" *  Swap Interval: ").append(glData.swapInterval);
+            sb.append('\n')
+              .append(" *  SRGB (Gamma Correction): ").append(glData.sRGB);
+            sb.append('\n')
+              .append(" *  Pixel Format Float: ").append(glData.pixelFormatFloat);
+            sb.append('\n')
+              .append(" *  Context Release Behavior: ").append(glData.contextReleaseBehavior);
+            sb.append('\n')
+              .append(" *  Color Samples NV: ").append(glData.colorSamplesNV);
+            sb.append('\n')
+              .append(" *  Swap Group NV: ").append(glData.swapGroupNV);
+            sb.append('\n')
+              .append(" *  Swap Barrier NV: ").append(glData.swapBarrierNV);
+            sb.append('\n')
+              .append(" *  Robustness: ").append(glData.robustness);
+            sb.append('\n')
+              .append(" *  Lose Context On Reset: ").append(glData.loseContextOnReset);
+            sb.append('\n')
+              .append(" *  Context Reset Isolation: ").append(glData.contextResetIsolation);
+        return String.valueOf(sb);
+    }
+    
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.lwjgl.LwjglWindow#getFramebufferHeight() 
+     * @return int
+     */
+    @Override
+    public int getFramebufferHeight() {
+        return this.framebufferHeight;
     }
 
-    public void destroyFrameBuffer() {
-        if (fb != null) {
-            fb.dispose();
-            fb = null;
-        }
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.lwjgl.LwjglWindow#getFramebufferWidth() 
+     * @return int
+     */
+    @Override
+    public int getFramebufferWidth() {
+        return this.framebufferWidth;
     }
 }

+ 14 - 10
jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java

@@ -46,6 +46,16 @@ import com.jme3.system.JmeSystem;
 import com.jme3.system.NanoTimer;
 import com.jme3.util.BufferUtils;
 import com.jme3.util.SafeArrayList;
+
+import org.lwjgl.Version;
+import org.lwjgl.glfw.GLFWErrorCallback;
+import org.lwjgl.glfw.GLFWFramebufferSizeCallback;
+import org.lwjgl.glfw.GLFWImage;
+import org.lwjgl.glfw.GLFWVidMode;
+import org.lwjgl.glfw.GLFWWindowFocusCallback;
+import org.lwjgl.glfw.GLFWWindowSizeCallback;
+import org.lwjgl.system.Platform;
+
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 import java.nio.ByteBuffer;
@@ -55,17 +65,10 @@ import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import org.lwjgl.Version;
+
 import static org.lwjgl.glfw.GLFW.*;
-import org.lwjgl.glfw.GLFWErrorCallback;
-import org.lwjgl.glfw.GLFWFramebufferSizeCallback;
-import org.lwjgl.glfw.GLFWImage;
-import org.lwjgl.glfw.GLFWVidMode;
-import org.lwjgl.glfw.GLFWWindowFocusCallback;
-import org.lwjgl.glfw.GLFWWindowSizeCallback;
 import static org.lwjgl.opengl.GL11.GL_FALSE;
 import static org.lwjgl.system.MemoryUtil.NULL;
-import org.lwjgl.system.Platform;
 
 /**
  * A wrapper class over the GLFW framework in LWJGL 3.
@@ -631,9 +634,10 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         // If the canvas is not active, there's no need to waste time
         // doing that.
         if (renderable.get()) {
-            // calls swap buffers, etc.
             try {
-                if (allowSwapBuffers && autoFlush) {
+                // If type is 'Canvas'; lwjgl-awt takes care of swap buffers.
+                if ((type != Type.Canvas) && allowSwapBuffers && autoFlush) {
+                    // calls swap buffers, etc.
                     glfwSwapBuffers(window);
                 }
             } catch (Throwable ex) {

+ 0 - 3
jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/Sync.java

@@ -31,7 +31,6 @@
  */
 package com.jme3.system.lwjgl;
 
-
 /**
 * A highly accurate sync method that continually adapts to the system 
 * it runs on to provide reliable results.
@@ -41,8 +40,6 @@ package com.jme3.system.lwjgl;
 */
 class Sync {
 
-
-
     /** number of nanoseconds in a second */
     private static final long NANOS_IN_SECOND = 1000L * 1000L * 1000L;
 

+ 66 - 0
jme3-lwjgl3/src/main/java/com/jme3/system/lwjglx/LwjglxDefaultGLPlatform.java

@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT OWNER 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.
+ */
+package com.jme3.system.lwjglx;
+
+import org.lwjgl.system.Platform;
+import static org.lwjgl.system.Platform.*;
+
+/**
+ * Class <code>wjglxDefaultGLPlatform</code> used to create a drawing platform.
+ * @author wil
+ */
+public final class LwjglxDefaultGLPlatform {
+    
+    /**
+     * Returns a drawing platform based on the platform it is running on.
+     * @return LwjglxGLPlatform
+     * @throws UnsupportedOperationException throws exception if platform is not supported
+     */
+    public static LwjglxGLPlatform createLwjglxGLPlatform() throws UnsupportedOperationException {
+        switch (Platform.get()) {
+            case WINDOWS:
+                return new Win32GLPlatform();
+            //case FREEBSD:  -> In future versions of lwjgl3 (possibly)
+            case LINUX:
+                return new X11GLPlatform();
+            case MACOSX:
+                return new MacOSXGLPlatform();
+            default:
+                throw new UnsupportedOperationException("Platform " + Platform.get() + " not yet supported");
+        }
+    }
+    
+    /**
+     * private constructor.
+     */
+    private LwjglxDefaultGLPlatform() {}
+}

+ 48 - 0
jme3-lwjgl3/src/main/java/com/jme3/system/lwjglx/LwjglxGLPlatform.java

@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT OWNER 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.
+ */
+package com.jme3.system.lwjglx;
+
+import org.lwjgl.opengl.awt.PlatformGLCanvas;
+
+/**
+ * Interface <code>LwjglxGLPlatform</code>; It is used to implement and manage
+ * the context of a specific platform.
+ * 
+ * @author wil
+ */
+public interface LwjglxGLPlatform extends PlatformGLCanvas {
+
+    /**
+     * Free the drawing surface.
+     */
+    public void destroy();    
+}

+ 56 - 0
jme3-lwjgl3/src/main/java/com/jme3/system/lwjglx/MacOSXGLPlatform.java

@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT OWNER 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.
+ */
+package com.jme3.system.lwjglx;
+
+import org.lwjgl.opengl.awt.PlatformMacOSXGLCanvas;
+import static org.lwjgl.system.jawt.JAWTFunctions.*;
+
+/**
+ * <code>MacOSXGLPlatform</code> class that implements the {@link com.jme3.system.lwjglx.LwjglxGLPlatform} 
+ * interface for the MacOS platform.
+ * 
+ * @author wil
+ */
+final class MacOSXGLPlatform extends PlatformMacOSXGLCanvas implements LwjglxGLPlatform {
+
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.lwjglx.LwjglxGLPlatform#destroy() 
+     */
+    @Override
+    public void destroy() {
+        if (ds != null) {
+            JAWT_FreeDrawingSurface(ds, awt.FreeDrawingSurface());
+            awt.free();
+        }
+    }
+ }

+ 56 - 0
jme3-lwjgl3/src/main/java/com/jme3/system/lwjglx/Win32GLPlatform.java

@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT OWNER 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.
+ */
+package com.jme3.system.lwjglx;
+
+import static org.lwjgl.system.jawt.JAWTFunctions.*;
+import org.lwjgl.opengl.awt.PlatformWin32GLCanvas;
+
+/**
+ * <code>Win32GLPlatform</code> class that implements the {@link com.jme3.system.lwjglx.LwjglxGLPlatform} 
+ * interface for the Windows (Win32) platform.
+ * 
+ * @author wil
+ */
+final class Win32GLPlatform extends PlatformWin32GLCanvas implements LwjglxGLPlatform {
+
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.lwjglx.LwjglxGLPlatform#destroy() 
+     */
+    @Override
+    public void destroy() {
+        if (ds != null) {
+            JAWT_FreeDrawingSurface(ds, awt.FreeDrawingSurface());
+            awt.free();
+        }
+    }
+}

+ 123 - 0
jme3-lwjgl3/src/main/java/com/jme3/system/lwjglx/X11GLPlatform.java

@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2009-2023 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT OWNER 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.
+ */
+package com.jme3.system.lwjglx;
+
+import org.lwjgl.opengl.awt.PlatformLinuxGLCanvas;
+import org.lwjgl.system.jawt.*;
+
+import static org.lwjgl.system.MemoryUtil.*;
+import static org.lwjgl.system.jawt.JAWTFunctions.*;
+
+/**
+ * Class <code>X11GLPlatform</code>; overrides the following methods: <code>swapBuffers()</code> 
+ * and <code>makeCurrent(long context)</code>. So that the canvas can be removed and 
+ * added back from its parent component.
+ * 
+ * <p>
+ * Works only for <b>Linux</b> based platforms
+ * 
+ * @author wil
+ */
+final class X11GLPlatform extends PlatformLinuxGLCanvas implements LwjglxGLPlatform {
+    
+    /**
+     * (non-Javadoc)
+     * @see org.lwjgl.opengl.awt.PlatformGLCanvas#swapBuffers() 
+     * @return boolean
+     */
+    @Override
+    public boolean swapBuffers() {
+         // Get the drawing surface info
+        JAWTDrawingSurfaceInfo dsi = JAWT_DrawingSurface_GetDrawingSurfaceInfo(ds, ds.GetDrawingSurfaceInfo());
+        if (dsi == null) {
+            throw new IllegalStateException("JAWT_DrawingSurface_GetDrawingSurfaceInfo() failed");
+        }
+        
+        try {
+            // Get the platform-specific drawing info
+            JAWTX11DrawingSurfaceInfo dsi_x11 = JAWTX11DrawingSurfaceInfo.create(dsi.platformInfo());
+
+            // Set new values
+            display  = dsi_x11.display();
+            drawable = dsi_x11.drawable();
+            
+            // Swap-Buffers            
+            return super.swapBuffers();
+        } finally {
+            JAWT_DrawingSurface_FreeDrawingSurfaceInfo(dsi, ds.FreeDrawingSurfaceInfo());
+        }
+    }
+
+    /**
+     * (non-Javadoc)
+     * @see org.lwjgl.opengl.awt.PlatformGLCanvas#makeCurrent(long) 
+     * 
+     * @param context long
+     * @return boolean
+     */
+    @Override
+    public boolean makeCurrent(long context) {
+        // Get the drawing surface info
+        JAWTDrawingSurfaceInfo dsi = JAWT_DrawingSurface_GetDrawingSurfaceInfo(ds, ds.GetDrawingSurfaceInfo());
+        if (dsi == null) {
+            throw new IllegalStateException("JAWT_DrawingSurface_GetDrawingSurfaceInfo() failed");
+        }
+
+        try {
+            // Get the platform-specific drawing info
+            JAWTX11DrawingSurfaceInfo dsi_x11 = JAWTX11DrawingSurfaceInfo.create(dsi.platformInfo());
+            
+            // Set new values
+            display  = dsi_x11.display();
+            drawable = dsi_x11.drawable();
+            
+            if (drawable == NULL) {
+                return false;
+            }
+            return super.makeCurrent(context);
+        } finally {
+            JAWT_DrawingSurface_FreeDrawingSurfaceInfo(dsi, ds.FreeDrawingSurfaceInfo());
+        }
+    }
+
+    /**
+     * (non-Javadoc)
+     * @see com.jme3.system.lwjglx.LwjglxGLPlatform#destroy() 
+     */
+    @Override
+    public void destroy() {
+        if (ds != null) {
+            JAWT_FreeDrawingSurface(ds, awt.FreeDrawingSurface());
+            awt.free();
+        }
+    }
+}

+ 3 - 2
jme3-lwjgl3/src/main/java/com/jme3/util/LWJGLBufferAllocator.java

@@ -8,6 +8,7 @@ import java.nio.*;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.StampedLock;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
@@ -164,7 +165,7 @@ public class LWJGLBufferAllocator implements BufferAllocator {
         final long address = getAddress(buffer);
 
         if (address == -1) {
-            LOGGER.warning("Not found address of the " + buffer);
+            LOGGER.log(Level.WARNING, "Not found address of the {0}", buffer);
             return;
         }
 
@@ -172,7 +173,7 @@ public class LWJGLBufferAllocator implements BufferAllocator {
         final Deallocator deallocator = DEALLOCATORS.remove(address);
 
         if (deallocator == null) {
-            LOGGER.warning("Not found a deallocator for address " + address);
+            LOGGER.log(Level.WARNING, "Not found a deallocator for address {0}", address);
             return;
         }