bob0bob hai 1 ano
pai
achega
2f39669ab5

+ 132 - 96
jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java

@@ -59,7 +59,6 @@ import com.jme3.renderer.opengl.*;
 import com.jme3.system.*;
 import com.jme3.util.BufferAllocatorFactory;
 import com.jme3.util.PrimitiveAllocator;
-
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -79,7 +78,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
     protected SystemListener listener;
     protected boolean autoFlush = true;
     protected AndroidInputHandler androidInput;
-    protected long minFrameDuration = 0;                   // No FPS cap
+    protected long minFrameDuration = 0; // No FPS cap
     protected long lastUpdateTime = 0;
 
     static {
@@ -90,8 +89,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
         }
     }
 
-    public OGLESContext() {
-    }
+    public OGLESContext() {}
 
     @Override
     public Type getType() {
@@ -115,9 +113,11 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
             // below 4.0, check OpenGL ES 2.0 support.
             if (info.reqGlEsVersion < 0x20000) {
-                throw new UnsupportedOperationException("OpenGL ES 2.0 or better is not supported on this device");
+                throw new UnsupportedOperationException(
+                    "OpenGL ES 2.0 or better is not supported on this device"
+                );
             }
-        } else if (Build.VERSION.SDK_INT < 9){
+        } else if (Build.VERSION.SDK_INT < 9) {
             throw new UnsupportedOperationException("jME3 requires Android 2.3 or later");
         }
 
@@ -127,7 +127,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
         if (androidInput == null) {
             if (Build.VERSION.SDK_INT >= 14) {
                 androidInput = new AndroidInputHandler14();
-            } else if (Build.VERSION.SDK_INT >= 9){
+            } else if (Build.VERSION.SDK_INT >= 9) {
                 androidInput = new AndroidInputHandler();
             }
         }
@@ -137,7 +137,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
         // setEGLContextClientVersion must be set before calling setRenderer
         // this means it cannot be set in AndroidConfigChooser (too late)
         // use proper openGL ES version
-        view.setEGLContextClientVersion(info.reqGlEsVersion>>16);
+        view.setEGLContextClientVersion(info.reqGlEsVersion >> 16);
 
         view.setFocusableInTouchMode(true);
         view.setFocusable(true);
@@ -200,22 +200,35 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
         logger.log(Level.FINE, "Running on thread: {0}", Thread.currentThread().getName());
 
         // Setup unhandled Exception Handler
-        Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
-            @Override
-            public void uncaughtException(Thread thread, Throwable thrown) {
-                listener.handleError("Exception thrown in " + thread.toString(), thrown);
-            }
-        });
+        Thread
+            .currentThread()
+            .setUncaughtExceptionHandler(
+                new Thread.UncaughtExceptionHandler() {
+                    @Override
+                    public void uncaughtException(Thread thread, Throwable thrown) {
+                        listener.handleError("Exception thrown in " + thread.toString(), thrown);
+                    }
+                }
+            );
 
         timer = new NanoTimer();
         GL gl = new AndroidGL();
         if (settings.getBoolean("GraphicsDebug")) {
-            gl = (GL) GLDebug.createProxy(gl, gl, GL.class, GL2.class, GLES_30.class, GLFbo.class, GLExt.class);
+            gl =
+                (GL) GLDebug.createProxy(
+                    gl,
+                    gl,
+                    GL.class,
+                    GL2.class,
+                    GLES_30.class,
+                    GLFbo.class,
+                    GLExt.class
+                );
         }
         if (settings.getBoolean("GraphicsTrace")) {
-            gl = (GL)GLTracer.createGlesTracer(gl, GL.class, GLES_30.class, GLFbo.class, GLExt.class);
+            gl = (GL) GLTracer.createGlesTracer(gl, GL.class, GLES_30.class, GLFbo.class, GLExt.class);
         }
-        renderer = new GLRenderer(gl, (GLExt)gl, (GLFbo)gl);
+        renderer = new GLRenderer(gl, (GLExt) gl, (GLFbo) gl);
         renderer.initialize();
 
         JmeSystem.setSoftTextDialogInput(this);
@@ -254,7 +267,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
         }
 
         if (settings.getFrameRate() > 0) {
-            minFrameDuration = (long)(1000d / settings.getFrameRate()); // ms
+            minFrameDuration = (long) (1000d / settings.getFrameRate()); // ms
             logger.log(Level.FINE, "Setting min tpf: {0}ms", minFrameDuration);
         } else {
             minFrameDuration = 0;
@@ -312,8 +325,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
     }
 
     @Override
-    public void setTitle(String title) {
-    }
+    public void setTitle(String title) {}
 
     @Override
     public boolean isCreated() {
@@ -329,7 +341,11 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
     @Override
     public void onSurfaceChanged(GL10 gl, int width, int height) {
         if (logger.isLoggable(Level.FINE)) {
-            logger.log(Level.FINE, "GL Surface changed, width: {0} height: {1}", new Object[]{width, height});
+            logger.log(
+                Level.FINE,
+                "GL Surface changed, width: {0} height: {1}",
+                new Object[] { width, height }
+            );
         }
         // update the application settings with the new resolution
         settings.setResolution(width, height);
@@ -372,16 +388,14 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
 
             // Enforce a FPS cap
             if (updateDelta < minFrameDuration) {
-//                    logger.log(Level.INFO, "lastUpdateTime: {0}, updateDelta: {1}, minTimePerFrame: {2}",
-//                            new Object[]{lastUpdateTime, updateDelta, minTimePerFrame});
+                //                    logger.log(Level.INFO, "lastUpdateTime: {0}, updateDelta: {1}, minTimePerFrame: {2}",
+                //                            new Object[]{lastUpdateTime, updateDelta, minTimePerFrame});
                 try {
                     Thread.sleep(minFrameDuration - updateDelta);
-                } catch (InterruptedException e) {
-                }
+                } catch (InterruptedException e) {}
             }
 
             lastUpdateTime = System.currentTimeMillis();
-
         }
     }
 
@@ -402,8 +416,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
     }
 
     @Override
-    public void restart() {
-    }
+    public void restart() {}
 
     @Override
     public void destroy(boolean waitFor) {
@@ -421,76 +434,99 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
         while (renderable.get() != createdVal) {
             try {
                 Thread.sleep(10);
-            } catch (InterruptedException ex) {
-            }
+            } catch (InterruptedException ex) {}
         }
     }
 
     @Override
-    public void requestDialog(final int id, final String title, final String initialValue, final SoftTextDialogInputListener listener) {
+    public void requestDialog(
+        final int id,
+        final String title,
+        final String initialValue,
+        final SoftTextDialogInputListener listener
+    ) {
         if (logger.isLoggable(Level.FINE)) {
-            logger.log(Level.FINE, "requestDialog: title: {0}, initialValue: {1}",
-                    new Object[]{title, initialValue});
+            logger.log(
+                Level.FINE,
+                "requestDialog: title: {0}, initialValue: {1}",
+                new Object[] { title, initialValue }
+            );
         }
 
         final View view = JmeAndroidSystem.getView();
-        view.getHandler().post(new Runnable() {
-            @Override
-            public void run() {
-
-                final FrameLayout layoutTextDialogInput = new FrameLayout(view.getContext());
-                final EditText editTextDialogInput = new EditText(view.getContext());
-                editTextDialogInput.setWidth(LayoutParams.FILL_PARENT);
-                editTextDialogInput.setHeight(LayoutParams.FILL_PARENT);
-                editTextDialogInput.setPadding(20, 20, 20, 20);
-                editTextDialogInput.setGravity(Gravity.FILL_HORIZONTAL);
-                //editTextDialogInput.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
-
-                editTextDialogInput.setText(initialValue);
-
-                switch (id) {
-                    case SoftTextDialogInput.TEXT_ENTRY_DIALOG:
-
-                        editTextDialogInput.setInputType(InputType.TYPE_CLASS_TEXT);
-                        break;
-
-                    case SoftTextDialogInput.NUMERIC_ENTRY_DIALOG:
-
-                        editTextDialogInput.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL | InputType.TYPE_NUMBER_FLAG_SIGNED);
-                        break;
-
-                    case SoftTextDialogInput.NUMERIC_KEYPAD_DIALOG:
-
-                        editTextDialogInput.setInputType(InputType.TYPE_CLASS_PHONE);
-                        break;
-
-                    default:
-                        break;
+        view
+            .getHandler()
+            .post(
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        final FrameLayout layoutTextDialogInput = new FrameLayout(view.getContext());
+                        final EditText editTextDialogInput = new EditText(view.getContext());
+                        editTextDialogInput.setWidth(LayoutParams.FILL_PARENT);
+                        editTextDialogInput.setHeight(LayoutParams.FILL_PARENT);
+                        editTextDialogInput.setPadding(20, 20, 20, 20);
+                        editTextDialogInput.setGravity(Gravity.FILL_HORIZONTAL);
+                        //editTextDialogInput.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
+
+                        editTextDialogInput.setText(initialValue);
+
+                        switch (id) {
+                            case SoftTextDialogInput.TEXT_ENTRY_DIALOG:
+                                editTextDialogInput.setInputType(InputType.TYPE_CLASS_TEXT);
+                                break;
+                            case SoftTextDialogInput.NUMERIC_ENTRY_DIALOG:
+                                editTextDialogInput.setInputType(
+                                    InputType.TYPE_CLASS_NUMBER |
+                                    InputType.TYPE_NUMBER_FLAG_DECIMAL |
+                                    InputType.TYPE_NUMBER_FLAG_SIGNED
+                                );
+                                break;
+                            case SoftTextDialogInput.NUMERIC_KEYPAD_DIALOG:
+                                editTextDialogInput.setInputType(InputType.TYPE_CLASS_PHONE);
+                                break;
+                            default:
+                                break;
+                        }
+
+                        layoutTextDialogInput.addView(editTextDialogInput);
+
+                        AlertDialog dialogTextInput = new AlertDialog.Builder(view.getContext())
+                            .setTitle(title)
+                            .setView(layoutTextDialogInput)
+                            .setPositiveButton(
+                                "OK",
+                                new DialogInterface.OnClickListener() {
+                                    @Override
+                                    public void onClick(DialogInterface dialog, int whichButton) {
+                                        /* User clicked OK, send COMPLETE action
+                                         * and text */
+                                        listener.onSoftText(
+                                            SoftTextDialogInputListener.COMPLETE,
+                                            editTextDialogInput.getText().toString()
+                                        );
+                                    }
+                                }
+                            )
+                            .setNegativeButton(
+                                "Cancel",
+                                new DialogInterface.OnClickListener() {
+                                    @Override
+                                    public void onClick(DialogInterface dialog, int whichButton) {
+                                        /* User clicked CANCEL, send CANCEL action
+                                         * and text */
+                                        listener.onSoftText(
+                                            SoftTextDialogInputListener.CANCEL,
+                                            editTextDialogInput.getText().toString()
+                                        );
+                                    }
+                                }
+                            )
+                            .create();
+
+                        dialogTextInput.show();
+                    }
                 }
-
-                layoutTextDialogInput.addView(editTextDialogInput);
-
-                AlertDialog dialogTextInput = new AlertDialog.Builder(view.getContext()).setTitle(title).setView(layoutTextDialogInput).setPositiveButton("OK",
-                        new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int whichButton) {
-                                /* User clicked OK, send COMPLETE action
-                                 * and text */
-                                listener.onSoftText(SoftTextDialogInputListener.COMPLETE, editTextDialogInput.getText().toString());
-                            }
-                        }).setNegativeButton("Cancel",
-                        new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int whichButton) {
-                                /* User clicked CANCEL, send CANCEL action
-                                 * and text */
-                                listener.onSoftText(SoftTextDialogInputListener.CANCEL, editTextDialogInput.getText().toString());
-                            }
-                        }).create();
-
-                dialogTextInput.show();
-            }
-        });
+            );
     }
 
     @Override
@@ -542,11 +578,11 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
     public int getWindowYPosition() {
         throw new UnsupportedOperationException("not implemented yet");
     }
-    
+
     /**
      * Retrieves the dimensions of the input surface. Note: do not modify the
      * returned object.
-     * 
+     *
      * @return the dimensions (in pixels, left and top are 0)
      */
     private Rect getSurfaceFrame() {
@@ -558,13 +594,13 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
 
     @Override
     public Displays getDisplays() {
-      // TODO Auto-generated method stub
-      return null;
+        // TODO Auto-generated method stub
+        return null;
     }
 
     @Override
     public int getPrimaryDisplay() {
-      // TODO Auto-generated method stub
-      return 0;
+        // TODO Auto-generated method stub
+        return 0;
     }
-  }
+}

+ 9 - 12
jme3-core/src/main/java/com/jme3/app/LegacyApplication.java

@@ -883,29 +883,26 @@ public class LegacyApplication implements Application, SystemListener {
             return null;
         }
     }
-    
-    
+
     /**
      * This call will return a list of Monitors that glfwGetMonitors()
-     * returns and information about the monitor, like width, height, 
+     * returns and information about the monitor, like width, height,
      * and refresh rate.
-     * 
+     *
      * @return returns a list of monitors and their information.
      */
-    public Displays getDisplays()
-    {
-       return context.getDisplays();
+    public Displays getDisplays() {
+        return context.getDisplays();
     }
-    
+
     /**
      * Use this to get the positional number of the primary
      * monitor from the glfwGetMonitors() function call.
-     * 
+     *
      * @return the position of the value in the arraylist of
      *         the primary monitor.
      */
-    public int getPrimaryDisplay()
-    {
-       return context.getPrimaryDisplay();
+    public int getPrimaryDisplay() {
+        return context.getPrimaryDisplay();
     }
 }

+ 16 - 18
jme3-core/src/main/java/com/jme3/system/AppSettings.java

@@ -86,7 +86,6 @@ public final class AppSettings extends HashMap<String, Object> {
     @Deprecated
     public static final String LWJGL_OPENGL3 = "LWJGL-OpenGL3";
 
-
     /**
      * Use LWJGL as the display system and force using the core OpenGL3.0 renderer.
      * <p>
@@ -437,7 +436,9 @@ public final class AppSettings extends HashMap<String, Object> {
                             put(key.substring(2), prefs.getBoolean(key, false));
                             break;
                         default:
-                            throw new UnsupportedOperationException("Undefined setting type: " + key.charAt(0));
+                            throw new UnsupportedOperationException(
+                                "Undefined setting type: " + key.charAt(0)
+                            );
                     }
                 } else {
                     // Use old method for compatibility with older preferences
@@ -719,7 +720,7 @@ public final class AppSettings extends HashMap<String, Object> {
      * @param clazz The custom context class.
      * (Default: not set)
      */
-    public void setCustomRenderer(Class<? extends JmeContext> clazz){
+    public void setCustomRenderer(Class<? extends JmeContext> clazz) {
         put("Renderer", "CUSTOM" + clazz.getName());
     }
 
@@ -767,7 +768,7 @@ public final class AppSettings extends HashMap<String, Object> {
 
     /**
      * Set the size of the window
-     * 
+     *
      * @param width The width in pixels (default = width of the default framebuffer)
      * @param height The height in pixels (default = height of the default framebuffer)
      */
@@ -803,8 +804,6 @@ public final class AppSettings extends HashMap<String, Object> {
         setMinHeight(height);
     }
 
-
-
     /**
      * Set the frequency, also known as refresh rate, for the
      * rendering display.
@@ -826,7 +825,7 @@ public final class AppSettings extends HashMap<String, Object> {
      *
      * @param value The depth bits
      */
-    public void setDepthBits(int value){
+    public void setDepthBits(int value) {
         putInteger("DepthBits", value);
     }
 
@@ -845,7 +844,7 @@ public final class AppSettings extends HashMap<String, Object> {
      *
      * @param value The alpha bits
      */
-    public void setAlphaBits(int value){
+    public void setAlphaBits(int value) {
         putInteger("AlphaBits", value);
     }
 
@@ -860,7 +859,7 @@ public final class AppSettings extends HashMap<String, Object> {
      *
      * @param value Number of stencil bits
      */
-    public void setStencilBits(int value){
+    public void setStencilBits(int value) {
         putInteger("StencilBits", value);
     }
 
@@ -922,7 +921,7 @@ public final class AppSettings extends HashMap<String, Object> {
      *
      * @param value true to enable 3-D stereo, false to disable (default=false)
      */
-    public void setStereo3D(boolean value){
+    public void setStereo3D(boolean value) {
         putBoolean("Stereo3D", value);
     }
 
@@ -1181,7 +1180,7 @@ public final class AppSettings extends HashMap<String, Object> {
      * @return true if 3-D stereo is enabled, otherwise false
      * @see #setStereo3D(boolean)
      */
-    public boolean useStereo3D(){
+    public boolean useStereo3D() {
         return getBoolean("Stereo3D");
     }
 
@@ -1302,7 +1301,7 @@ public final class AppSettings extends HashMap<String, Object> {
      * Without this, many openGL calls might fail without notice, so turning it on is recommended for development.
      * Graphics Debug mode will also label native objects and group calls on supported renderers. Compatible
      * graphics debuggers will be able to use this data to show a better outlook of your application
-     * 
+     *
      * @return whether the context will be run in Graphics Debug Mode or not
      * @see #setGraphicsDebug(boolean)
      */
@@ -1480,8 +1479,7 @@ public final class AppSettings extends HashMap<String, Object> {
     public void setWindowYPosition(int pos) {
         putInteger("WindowYPosition", pos);
     }
-    
-    
+
     /**
      * Gets the display number used when creating a window.
      *
@@ -1492,7 +1490,7 @@ public final class AppSettings extends HashMap<String, Object> {
      * @return the desired display used when creating a OpenGL window
      */
     public int getDisplay() {
-      return getInteger("Display");
+        return getInteger("Display");
     }
 
     /**
@@ -1504,9 +1502,9 @@ public final class AppSettings extends HashMap<String, Object> {
      * window. its default value is 0.
      *
      * @param mon the desired display used when creating a OpenGL window
-     * 
+     *
      */
     public void setDisplay(int mon) {
-      putInteger("Display", mon);
+        putInteger("Display", mon);
     }
-  }
+}

+ 25 - 26
jme3-core/src/main/java/com/jme3/system/DisplayInfo.java

@@ -28,39 +28,38 @@ package com.jme3.system;
 /**
  * This class holds information about the display that was returned by glfwGetMonitors() calls in
  * the context class
- * 
+ *
  * @author Kevin Bales
  */
 public class DisplayInfo {
 
-  /**
-   * displayID - display id that was return from Lwjgl3.
-   */
-  public long displayID = 0;
-
-  /**
-   * width - width that was return from Lwjgl3.
-   */
-  public int width = 1080;
+    /**
+     * displayID - display id that was return from Lwjgl3.
+     */
+    public long displayID = 0;
 
-  /**
-   * height - height that was return from Lwjgl3.
-   */
-  public int height = 1920;
+    /**
+     * width - width that was return from Lwjgl3.
+     */
+    public int width = 1080;
 
-  /**
-   * rate - refresh rate that was return from Lwjgl3.
-   */
-  public int rate = 60;
+    /**
+     * height - height that was return from Lwjgl3.
+     */
+    public int height = 1920;
 
-  /**
-   * primary - indicates if the display is the primary monitor.
-   */
-  public boolean primary = false;
+    /**
+     * rate - refresh rate that was return from Lwjgl3.
+     */
+    public int rate = 60;
 
-  /**
-   * name - display name that was return from Lwjgl3.
-   */
-  public String name = "Generic Monitor";
+    /**
+     * primary - indicates if the display is the primary monitor.
+     */
+    public boolean primary = false;
 
+    /**
+     * name - display name that was return from Lwjgl3.
+     */
+    public String name = "Generic Monitor";
 }

+ 55 - 61
jme3-core/src/main/java/com/jme3/system/Displays.java

@@ -30,77 +30,71 @@ import java.util.ArrayList;
 /**
  * This class holds all information about all displays that where return from the glfwGetMonitors()
  * call. It stores them into an ArrayList
- * 
+ *
  * @author Kevin Bales
  */
 public class Displays {
 
-  private ArrayList<DisplayInfo> displays = new ArrayList<DisplayInfo>();
-
-  public int addNewMonitor(long displaysID) {
-    DisplayInfo info = new DisplayInfo();
-    info.displayID = displaysID;
-    displays.add(info);
-    return displays.size() - 1;
-  }
-
-  /**
-   * This function returns the size of the display ArrayList
-   * 
-   * @return the
-   */
-  public int size() {
-    return displays.size();
-  }
-
-  /**
-   * Call to get display information on a certain display.
-   * 
-   * @param pos the position in the ArrayList of the display information that you want to get.
-   * @return returns the DisplayInfo data for the display called for.
-   */
-  public DisplayInfo get(int pos) {
-    if (pos < displays.size())
-      return displays.get(pos);
+    private ArrayList<DisplayInfo> displays = new ArrayList<DisplayInfo>();
 
-    return null;
-  }
-
-  /**
-   * Set information about this display stored in displayPos display in the array list.
-   * 
-   * @param displayPos ArrayList position of display to update
-   * @param name name of the display 
-   * @param width the current width the display is displaying
-   * @param height the current height the display is displaying
-   * @param rate the current refresh rate the display is set to
-   */
-  public void setInfo(int displayPos, String name, int width, int height, int rate) {
-    if (displayPos < displays.size()) {
-      DisplayInfo info = displays.get(displayPos);
-      if (info != null) {
-        info.width = width;
-        info.height = height;
-        info.rate = rate;
-        info.name = name;
-      }
+    public int addNewMonitor(long displaysID) {
+        DisplayInfo info = new DisplayInfo();
+        info.displayID = displaysID;
+        displays.add(info);
+        return displays.size() - 1;
     }
-  }
 
-  /**
-   * This function will mark a certain display as the primary display.
-   * 
-   * @param displayPos the position in the ArrayList of which display is the primary display
-   */
-  public void setPrimaryDisplay(int displayPos) {
-    if (displayPos < displays.size()) {
-      DisplayInfo info = displays.get(displayPos);
-      if (info != null)
-        info.primary = true;
+    /**
+     * This function returns the size of the display ArrayList
+     *
+     * @return the
+     */
+    public int size() {
+        return displays.size();
     }
 
-  }
+    /**
+     * Call to get display information on a certain display.
+     *
+     * @param pos the position in the ArrayList of the display information that you want to get.
+     * @return returns the DisplayInfo data for the display called for.
+     */
+    public DisplayInfo get(int pos) {
+        if (pos < displays.size()) return displays.get(pos);
 
+        return null;
+    }
 
+    /**
+     * Set information about this display stored in displayPos display in the array list.
+     *
+     * @param displayPos ArrayList position of display to update
+     * @param name name of the display
+     * @param width the current width the display is displaying
+     * @param height the current height the display is displaying
+     * @param rate the current refresh rate the display is set to
+     */
+    public void setInfo(int displayPos, String name, int width, int height, int rate) {
+        if (displayPos < displays.size()) {
+            DisplayInfo info = displays.get(displayPos);
+            if (info != null) {
+                info.width = width;
+                info.height = height;
+                info.rate = rate;
+                info.name = name;
+            }
+        }
+    }
 
+    /**
+     * This function will mark a certain display as the primary display.
+     *
+     * @param displayPos the position in the ArrayList of which display is the primary display
+     */
+    public void setPrimaryDisplay(int displayPos) {
+        if (displayPos < displays.size()) {
+            DisplayInfo info = displays.get(displayPos);
+            if (info != null) info.primary = true;
+        }
+    }
 }

+ 5 - 6
jme3-core/src/main/java/com/jme3/system/JmeContext.java

@@ -41,7 +41,6 @@ import com.jme3.renderer.Renderer;
  * Represents a rendering context within the engine.
  */
 public interface JmeContext {
-
     /**
      * The type of context.
      */
@@ -77,7 +76,7 @@ public interface JmeContext {
          * any drawable surface. The implementation does not provide any
          * display, input, or sound support.
          */
-        Headless;
+        Headless,
     }
 
     /**
@@ -102,7 +101,7 @@ public interface JmeContext {
     /**
      * Sets the listener that will receive events relating to context
      * creation, update, and destroy.
-     * 
+     *
      * @param listener the desired listener
      */
     public void setSystemListener(SystemListener listener);
@@ -229,7 +228,7 @@ public interface JmeContext {
     /**
      * This call will return a list of Monitors that glfwGetMonitors() returns and information about
      * the monitor, like width, height, and refresh rate.
-     * 
+     *
      * @return returns a list of monitors and their information.
      */
     public Displays getDisplays();
@@ -237,8 +236,8 @@ public interface JmeContext {
     /**
      * Use this to get the positional number of the primary monitor from the glfwGetMonitors()
      * function call.
-     * 
+     *
      * @return the position of the value in the arraylist of the primary monitor.
      */
     public int getPrimaryDisplay();
-  }
+}

+ 36 - 41
jme3-core/src/main/java/com/jme3/system/NullContext.java

@@ -48,7 +48,7 @@ public class NullContext implements JmeContext, Runnable {
     protected static final Logger logger = Logger.getLogger(NullContext.class.getName());
 
     protected static final String THREAD_NAME = "jME3 Headless Main";
-    
+
     protected AtomicBoolean created = new AtomicBoolean(false);
     protected AtomicBoolean needClose = new AtomicBoolean(false);
     protected final Object createdLock = new Object();
@@ -75,26 +75,28 @@ public class NullContext implements JmeContext, Runnable {
     }
 
     @Override
-    public void setSystemListener(SystemListener listener){
+    public void setSystemListener(SystemListener listener) {
         this.listener = listener;
     }
 
-    protected void initInThread(){
+    protected void initInThread() {
         logger.fine("NullContext created.");
         if (logger.isLoggable(Level.FINE)) {
             logger.log(Level.FINE, "Running on thread: {0}", Thread.currentThread().getName());
         }
 
-        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
-            @Override
-            public void uncaughtException(Thread thread, Throwable thrown) {
-                listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
+        Thread.setDefaultUncaughtExceptionHandler(
+            new Thread.UncaughtExceptionHandler() {
+                @Override
+                public void uncaughtException(Thread thread, Throwable thrown) {
+                    listener.handleError("Uncaught exception thrown in " + thread.toString(), thrown);
+                }
             }
-        });
+        );
 
         timer = new NanoTimer();
         renderer = new NullRenderer();
-        synchronized (createdLock){
+        synchronized (createdLock) {
             created.set(true);
             createdLock.notifyAll();
         }
@@ -102,10 +104,10 @@ public class NullContext implements JmeContext, Runnable {
         listener.initialize();
     }
 
-    protected void deinitInThread(){
+    protected void deinitInThread() {
         listener.destroy();
         timer = null;
-        synchronized (createdLock){
+        synchronized (createdLock) {
             created.set(false);
             createdLock.notifyAll();
         }
@@ -142,7 +144,7 @@ public class NullContext implements JmeContext, Runnable {
     }
 
     @Override
-    public void run(){
+    public void run() {
         initInThread();
 
         do {
@@ -159,31 +161,27 @@ public class NullContext implements JmeContext, Runnable {
     }
 
     @Override
-    public void destroy(boolean waitFor){
+    public void destroy(boolean waitFor) {
         needClose.set(true);
-        if (waitFor)
-            waitFor(false);
+        if (waitFor) waitFor(false);
     }
 
     @Override
-    public void create(boolean waitFor){
-        if (created.get()){
+    public void create(boolean waitFor) {
+        if (created.get()) {
             logger.warning("create() called when NullContext is already created!");
             return;
         }
 
         new Thread(this, THREAD_NAME).start();
-        if (waitFor)
-            waitFor(true);
+        if (waitFor) waitFor(true);
     }
 
     @Override
-    public void restart() {
-    }
+    public void restart() {}
 
     @Override
-    public void setAutoFlushFrames(boolean enabled){
-    }
+    public void setAutoFlushFrames(boolean enabled) {}
 
     @Override
     public MouseInput getMouseInput() {
@@ -206,30 +204,28 @@ public class NullContext implements JmeContext, Runnable {
     }
 
     @Override
-    public void setTitle(String title) {
-    }
+    public void setTitle(String title) {}
 
-    public void create(){
+    public void create() {
         create(false);
     }
 
-    public void destroy(){
+    public void destroy() {
         destroy(false);
     }
 
-    protected void waitFor(boolean createdVal){
-        synchronized (createdLock){
-            while (created.get() != createdVal){
+    protected void waitFor(boolean createdVal) {
+        synchronized (createdLock) {
+            while (created.get() != createdVal) {
                 try {
                     createdLock.wait();
-                } catch (InterruptedException ex) {
-                }
+                } catch (InterruptedException ex) {}
             }
         }
     }
 
     @Override
-    public boolean isCreated(){
+    public boolean isCreated() {
         return created.get();
     }
 
@@ -237,12 +233,11 @@ public class NullContext implements JmeContext, Runnable {
     public void setSettings(AppSettings settings) {
         this.settings.copyFrom(settings);
         frameRate = settings.getFrameRate();
-        if (frameRate <= 0)
-            frameRate = 60; // use default update rate.
+        if (frameRate <= 0) frameRate = 60; // use default update rate.
     }
 
     @Override
-    public AppSettings getSettings(){
+    public AppSettings getSettings() {
         return settings;
     }
 
@@ -259,7 +254,7 @@ public class NullContext implements JmeContext, Runnable {
     @Override
     public boolean isRenderable() {
         return true; // Doesn't really matter if true or false. Either way
-                     // RenderManager won't render anything.
+        // RenderManager won't render anything.
     }
 
     @Override
@@ -309,13 +304,13 @@ public class NullContext implements JmeContext, Runnable {
 
     @Override
     public Displays getDisplays() {
-      // TODO Auto-generated method stub
-      return null;
+        // TODO Auto-generated method stub
+        return null;
     }
 
     @Override
     public int getPrimaryDisplay() {
-      // TODO Auto-generated method stub
-      return 0;
+        // TODO Auto-generated method stub
+        return 0;
     }
 }

+ 180 - 183
jme3-desktop/src/main/java/com/jme3/system/AWTContext.java

@@ -31,7 +31,6 @@
  */
 package com.jme3.system;
 
-
 import com.jme3.input.AWTKeyInput;
 import com.jme3.input.AWTMouseInput;
 import com.jme3.input.JoyInput;
@@ -49,104 +48,104 @@ import com.jme3.renderer.Renderer;
  */
 public class AWTContext implements JmeContext {
 
-  /**
-   * The settings.
-   */
-  protected final AppSettings settings;
-
-  /**
-   * The key input.
-   */
-  protected final AWTKeyInput keyInput;
-
-  /**
-   * The mouse input.
-   */
-  protected final AWTMouseInput mouseInput;
-
-  /**
-   * The current width.
-   */
-  private volatile int width;
-
-  /**
-   * The current height.
-   */
-  private volatile int height;
-
-  /**
-   * The background context.
-   */
-  protected JmeContext backgroundContext;
-
-  public AWTContext() {
-      this.keyInput = new AWTKeyInput(this);
-      this.mouseInput = new AWTMouseInput(this);
-      this.settings = createSettings();
-      this.backgroundContext = createBackgroundContext();
-      this.height = 1;
-      this.width = 1;
-  }
-
-  /**
-   * @return the current height.
-   */
-  public int getHeight() {
-      return height;
-  }
-
-  /**
-   * @param height the current height.
-   */
-  public void setHeight(final int height) {
-      this.height = height;
-  }
-
-  /**
-   * @return the current width.
-   */
-  public int getWidth() {
-      return width;
-  }
-
-  /**
-   * @param width the current width.
-   */
-  public void setWidth(final int width) {
-      this.width = width;
-  }
-
-  /**
-   * @return new settings.
-   */
-  protected AppSettings createSettings() {
-      final AppSettings settings = new AppSettings(true);
-      settings.setRenderer(AppSettings.LWJGL_OPENGL32);
-      return settings;
-  }
-
-  /**
-   * @return new context/
-   */
-  protected JmeContext createBackgroundContext() {
-      return JmeSystem.newContext(settings, Type.OffscreenSurface);
-  }
-
-  @Override
-  public Type getType() {
-      return Type.OffscreenSurface;
-  }
-
-  @Override
-  public void setSettings(AppSettings settings) {
-      this.settings.copyFrom(settings);
-      this.settings.setRenderer(AppSettings.LWJGL_OPENGL32);
-      this.backgroundContext.setSettings(settings);
-  }
+    /**
+     * The settings.
+     */
+    protected final AppSettings settings;
+
+    /**
+     * The key input.
+     */
+    protected final AWTKeyInput keyInput;
+
+    /**
+     * The mouse input.
+     */
+    protected final AWTMouseInput mouseInput;
+
+    /**
+     * The current width.
+     */
+    private volatile int width;
+
+    /**
+     * The current height.
+     */
+    private volatile int height;
+
+    /**
+     * The background context.
+     */
+    protected JmeContext backgroundContext;
+
+    public AWTContext() {
+        this.keyInput = new AWTKeyInput(this);
+        this.mouseInput = new AWTMouseInput(this);
+        this.settings = createSettings();
+        this.backgroundContext = createBackgroundContext();
+        this.height = 1;
+        this.width = 1;
+    }
+
+    /**
+     * @return the current height.
+     */
+    public int getHeight() {
+        return height;
+    }
+
+    /**
+     * @param height the current height.
+     */
+    public void setHeight(final int height) {
+        this.height = height;
+    }
+
+    /**
+     * @return the current width.
+     */
+    public int getWidth() {
+        return width;
+    }
+
+    /**
+     * @param width the current width.
+     */
+    public void setWidth(final int width) {
+        this.width = width;
+    }
+
+    /**
+     * @return new settings.
+     */
+    protected AppSettings createSettings() {
+        final AppSettings settings = new AppSettings(true);
+        settings.setRenderer(AppSettings.LWJGL_OPENGL32);
+        return settings;
+    }
+
+    /**
+     * @return new context/
+     */
+    protected JmeContext createBackgroundContext() {
+        return JmeSystem.newContext(settings, Type.OffscreenSurface);
+    }
+
+    @Override
+    public Type getType() {
+        return Type.OffscreenSurface;
+    }
+
+    @Override
+    public void setSettings(AppSettings settings) {
+        this.settings.copyFrom(settings);
+        this.settings.setRenderer(AppSettings.LWJGL_OPENGL32);
+        this.backgroundContext.setSettings(settings);
+    }
 
     /**
      * Accesses the listener that receives events related to this context.
-    *
+     *
      * @return the pre-existing instance
      */
     @Override
@@ -154,87 +153,85 @@ public class AWTContext implements JmeContext {
         return backgroundContext.getSystemListener();
     }
 
-  @Override
-  public void setSystemListener(final SystemListener listener) {
-      backgroundContext.setSystemListener(listener);
-  }
-
-  @Override
-  public AppSettings getSettings() {
-      return settings;
-  }
-
-  @Override
-  public Renderer getRenderer() {
-      return backgroundContext.getRenderer();
-  }
-
-  @Override
-  public Context getOpenCLContext() {
-      return null;
-  }
-
-  @Override
-  public AWTMouseInput getMouseInput() {
-      return mouseInput;
-  }
-
-  @Override
-  public AWTKeyInput getKeyInput() {
-      return keyInput;
-  }
-
-  @Override
-  public JoyInput getJoyInput() {
-      return null;
-  }
-
-  @Override
-  public TouchInput getTouchInput() {
-      return null;
-  }
-
-  @Override
-  public Timer getTimer() {
-      return backgroundContext.getTimer();
-  }
-
-  @Override
-  public void setTitle(final String title) {
-  }
-
-  @Override
-  public boolean isCreated() {
-      return backgroundContext != null && backgroundContext.isCreated();
-  }
-
-  @Override
-  public boolean isRenderable() {
-      return backgroundContext != null && backgroundContext.isRenderable();
-  }
-
-  @Override
-  public void setAutoFlushFrames(final boolean enabled) {
-      // TODO Auto-generated method stub
-  }
-
-  @Override
-  public void create(final boolean waitFor) {
+    @Override
+    public void setSystemListener(final SystemListener listener) {
+        backgroundContext.setSystemListener(listener);
+    }
+
+    @Override
+    public AppSettings getSettings() {
+        return settings;
+    }
+
+    @Override
+    public Renderer getRenderer() {
+        return backgroundContext.getRenderer();
+    }
+
+    @Override
+    public Context getOpenCLContext() {
+        return null;
+    }
+
+    @Override
+    public AWTMouseInput getMouseInput() {
+        return mouseInput;
+    }
+
+    @Override
+    public AWTKeyInput getKeyInput() {
+        return keyInput;
+    }
+
+    @Override
+    public JoyInput getJoyInput() {
+        return null;
+    }
+
+    @Override
+    public TouchInput getTouchInput() {
+        return null;
+    }
+
+    @Override
+    public Timer getTimer() {
+        return backgroundContext.getTimer();
+    }
+
+    @Override
+    public void setTitle(final String title) {}
+
+    @Override
+    public boolean isCreated() {
+        return backgroundContext != null && backgroundContext.isCreated();
+    }
+
+    @Override
+    public boolean isRenderable() {
+        return backgroundContext != null && backgroundContext.isRenderable();
+    }
+
+    @Override
+    public void setAutoFlushFrames(final boolean enabled) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void create(final boolean waitFor) {
         String render = System.getProperty("awt.background.render", AppSettings.LWJGL_OPENGL33);
         backgroundContext.getSettings().setRenderer(render);
         backgroundContext.create(waitFor);
-  }
+    }
 
-  @Override
-  public void restart() {
-  }
+    @Override
+    public void restart() {}
 
-  @Override
-  public void destroy(final boolean waitFor) {
-      if (backgroundContext == null) throw new IllegalStateException("Not created");
-      // destroy wrapped context
-      backgroundContext.destroy(waitFor);
-}
+    @Override
+    public void destroy(final boolean waitFor) {
+        if (backgroundContext == null) throw new IllegalStateException("Not created");
+        // destroy wrapped context
+        backgroundContext.destroy(waitFor);
+    }
 
     /**
      * Returns the height of the framebuffer.
@@ -276,15 +273,15 @@ public class AWTContext implements JmeContext {
         throw new UnsupportedOperationException("not implemented yet");
     }
 
-   @Override
-   public Displays getDisplays() {
-      // TODO Auto-generated method stub
-      return null;
-   }
-
-   @Override
-   public int getPrimaryDisplay() {
-      // TODO Auto-generated method stub
-      return 0;
-   }
+    @Override
+    public Displays getDisplays() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public int getPrimaryDisplay() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
 }

+ 25 - 29
jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanelsContext.java

@@ -109,9 +109,8 @@ public class AwtPanelsContext implements JmeContext {
         }
     }
 
-    public void setInputSource(AwtPanel panel){
-        if (!panels.contains(panel))
-            throw new IllegalArgumentException();
+    public void setInputSource(AwtPanel panel) {
+        if (!panels.contains(panel)) throw new IllegalArgumentException();
 
         inputSource = panel;
         mouseInput.setInputSource(panel);
@@ -187,42 +186,41 @@ public class AwtPanelsContext implements JmeContext {
     public Context getOpenCLContext() {
         return actualContext.getOpenCLContext();
     }
-    
-    public AwtPanelsContext(){
-    }
 
-    public AwtPanel createPanel(PaintMode paintMode){
+    public AwtPanelsContext() {}
+
+    public AwtPanel createPanel(PaintMode paintMode) {
         AwtPanel panel = new AwtPanel(paintMode);
         panels.add(panel);
         return panel;
     }
-    
-    public AwtPanel createPanel(PaintMode paintMode, boolean srgb){
+
+    public AwtPanel createPanel(PaintMode paintMode, boolean srgb) {
         AwtPanel panel = new AwtPanel(paintMode, srgb);
         panels.add(panel);
         return panel;
     }
 
-    private void initInThread(){
+    private void initInThread() {
         listener.initialize();
     }
 
-    private void updateInThread(){
+    private void updateInThread() {
         // Check if throttle required
         boolean needThrottle = true;
 
-        for (AwtPanel panel : panels){
-            if (panel.isActiveDrawing()){
+        for (AwtPanel panel : panels) {
+            if (panel.isActiveDrawing()) {
                 needThrottle = false;
                 break;
             }
         }
 
-        if (lastThrottleState != needThrottle){
+        if (lastThrottleState != needThrottle) {
             lastThrottleState = needThrottle;
-            if (lastThrottleState){
+            if (lastThrottleState) {
                 System.out.println("OGL: Throttling update loop.");
-            }else{
+            } else {
                 System.out.println("OGL: Ceased throttling update loop.");
             }
         }
@@ -230,18 +228,17 @@ public class AwtPanelsContext implements JmeContext {
         if (needThrottle) {
             try {
                 Thread.sleep(100);
-            } catch (InterruptedException ex) {
-            }
+            } catch (InterruptedException ex) {}
         }
 
         listener.update();
-        
-        for (AwtPanel panel : panels){
+
+        for (AwtPanel panel : panels) {
             panel.onFrameEnd();
         }
     }
 
-    private void destroyInThread(){
+    private void destroyInThread() {
         listener.destroy();
     }
 
@@ -249,14 +246,14 @@ public class AwtPanelsContext implements JmeContext {
     public void setSettings(AppSettings settings) {
         this.settings.copyFrom(settings);
         this.settings.setRenderer(AppSettings.LWJGL_OPENGL2);
-        if (actualContext != null){
+        if (actualContext != null) {
             actualContext.setSettings(settings);
         }
     }
 
     @Override
     public void create(boolean waitFor) {
-        if (actualContext != null){
+        if (actualContext != null) {
             throw new IllegalStateException("Already created");
         }
 
@@ -267,8 +264,7 @@ public class AwtPanelsContext implements JmeContext {
 
     @Override
     public void destroy(boolean waitFor) {
-        if (actualContext == null)
-            throw new IllegalStateException("Not created");
+        if (actualContext == null) throw new IllegalStateException("Not created");
 
         // destroy parent context
         actualContext.destroy(waitFor);
@@ -331,13 +327,13 @@ public class AwtPanelsContext implements JmeContext {
 
     @Override
     public Displays getDisplays() {
-       // TODO Auto-generated method stub
-       return null;
+        // TODO Auto-generated method stub
+        return null;
     }
 
     @Override
     public int getPrimaryDisplay() {
-       // TODO Auto-generated method stub
-       return 0;
+        // TODO Auto-generated method stub
+        return 0;
     }
 }

+ 191 - 187
jme3-examples/src/main/java/jme3test/app/TestMonitorApp.java

@@ -31,15 +31,6 @@
  */
 package jme3test.app;
 
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
 import com.jme3.app.SimpleApplication;
 import com.jme3.font.BitmapText;
 import com.jme3.input.KeyInput;
@@ -51,190 +42,203 @@ import com.jme3.scene.shape.Box;
 import com.jme3.system.AppSettings;
 import com.jme3.system.DisplayInfo;
 import com.jme3.system.Displays;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * Tests the capability to change which monitor the window will be created on.
  * Also, shows that you can force JME to center the window. Also, it shows to to
  * force JME to set the window to x,y coords. Center window and window position
  * doesn't apply if in fullscreen.
- * 
+ *
  * @author Kevin Bales
  */
-public class TestMonitorApp extends SimpleApplication
-         implements ActionListener {
-
-   private BitmapText txt;
-   private BitmapText selectedMonitorTxt;
-   private BitmapText fullScreenTxt;
-   private int monitorSelected = 0;
-   private Displays monitors = null;
-
-   public static void main(String[] args) {
-      TestMonitorApp app = new TestMonitorApp();
-      AppSettings settings = new AppSettings(true);
-      settings.setResizable(false);
-      app.setShowSettings(true);
-      settings.setRenderer(AppSettings.LWJGL_OPENGL33);
-      settings.setDisplay(0);
-      settings.setResolution(800, 600);
-
-      settings.setFullscreen(true);
-
-      // Force JME to center the window, this only applies if it is
-      // not fullscreen.
-      settings.setCenterWindow(true);
-
-      // If center window is not turned on, you can force JME to
-      // open the window at certain x,y coords. These are ignored
-      // if the screen is set to "fullscreen".
-      settings.setWindowXPosition(0);
-      settings.setWindowYPosition(0);
-
-      try {
-         // Let's try and load the AppSetting parameters back into memory
-         InputStream out = new FileInputStream("TestMonitorApp.prefs");
-         settings.load(out);
-      } catch (IOException e) {
-         System.out.println("failed to load settings, reverting to defaults");
-      }
-      app.setSettings(settings);
-
-      app.start();
-   }
-
-   @Override
-   public void simpleInitApp() {
-      flyCam.setDragToRotate(true);
-      int numMonitors = 1;
-
-      // If monitor is define, Jme supports multiple monitors. Setup to keys
-      if (monitors == null) {
-         inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_DOWN));
-         inputManager.addMapping("fullscreen", new KeyTrigger(KeyInput.KEY_F));
-         inputManager.addListener(this, "down", "fullscreen");
-      }
-
-      // Get the selected monitor
-      monitorSelected = settings.getDisplay();
-      monitors = context.getDisplays();
-      if (monitors != null)
-         numMonitors = monitors.size();
-
-      // Let's define the labels for users to see what is going on with Multiple
-      // Monitor
-      String labelValue = "";
-      labelValue = "There are " + numMonitors
-               + " monitor(s) hooked up to this computer.";
-      txt = new BitmapText(loadGuiFont());
-      txt.setText(labelValue);
-      txt.setLocalTranslation(0, settings.getHeight(), 0);
-      guiNode.attachChild(txt);
-
-      txt = new BitmapText(loadGuiFont());
-      if (!settings.isFullscreen())
-         txt.setText("Window is on Monitor N/A (fullscreen only feature)");
-      else
-         txt.setText("Window is on Monitor " + settings.getDisplay());
-
-      txt.setLocalTranslation(0, settings.getHeight() - 40, 0);
-      guiNode.attachChild(txt);
-
-      if (monitors != null) {
-         selectedMonitorTxt = new BitmapText(loadGuiFont());
-         // Lets display information about selected monitor
-         String label = "Selected Monitor " + "Name: "
-                  + monitors.get(settings.getDisplay()).name + " "
-                  + monitorSelected + " Res: "
-                  + monitors.get(settings.getDisplay()).width + ","
-                  + monitors.get(settings.getDisplay()).height + " refresh: "
-                  + monitors.get(settings.getDisplay()).rate;
-         selectedMonitorTxt.setText(label);
-         selectedMonitorTxt.setLocalTranslation(0, settings.getHeight() - 80,
-                  0);
-         guiNode.attachChild(selectedMonitorTxt);
-
-         // Let's loop through all the monitors and display on the screen
-         for (int i = 0; i < monitors.size(); i++) {
-            DisplayInfo monitor = monitors.get(i);
-            labelValue = "Mon : " + i + " " + monitor.name + " " + monitor.width
-                     + "," + monitor.height + " refresh: " + monitor.rate;
-            txt = new BitmapText(loadGuiFont());
-            txt.setText(labelValue);
-            txt.setLocalTranslation(0, settings.getHeight() - 160 - (40 * i),
-                     0);
-            guiNode.attachChild(txt);
-         }
-      }
-
-      // Lets put a label up there for FullScreen/Window toggle
-      fullScreenTxt = new BitmapText(loadGuiFont());
-      if (!settings.isFullscreen())
-         fullScreenTxt.setText("(f) Window Screen");
-      else
-         fullScreenTxt.setText("(f) Fullscreen");
-
-      fullScreenTxt.setLocalTranslation(00, settings.getHeight() - 240, 0);
-      guiNode.attachChild(fullScreenTxt);
-
-      BitmapText infoTxt = new BitmapText(loadGuiFont());
-      infoTxt.setText("Restart is required to activate changes in settings.");
-      infoTxt.setLocalTranslation(0, settings.getHeight() - 300, 0);
-      guiNode.attachChild(infoTxt);
-
-   }
-
-   @Override
-   public void onAction(String name, boolean isPressed, float tpf) {
-
-      if (monitors == null)
-         return;
-
-      if (name.equals("down") && isPressed) {
-         monitorSelected++;
-         if (monitorSelected >= monitors.size())
-            monitorSelected = 0;
-         saveSettings();
-      } else if (name.equals("up") && isPressed) {
-         monitorSelected--;
-         if (monitorSelected < 0)
-            monitorSelected = monitors.size() - 1;
-         saveSettings();
-      } else if (name.equals("fullscreen") && isPressed) {
-         settings.setFullscreen(!settings.isFullscreen());
-         saveSettings();
-      }
-   }
-
-   /**
-    * This function saves out the AppSettings into a file to be loaded back in
-    * on start of application.
-    */
-   public void saveSettings() {
-
-      try {
-         settings.setDisplay(monitorSelected);
-         OutputStream out = new FileOutputStream("TestMonitorApp.prefs");
-         settings.save(out);
-
-         int monitorSelected = settings.getDisplay();
-         String label = "Selected Monitor " + monitorSelected + " "
-                  + monitors.get(monitorSelected).name + " Res: "
-                  + monitors.get(monitorSelected).width + ","
-                  + monitors.get(monitorSelected).height + "refresh: "
-                  + monitors.get(monitorSelected).rate;
-         selectedMonitorTxt.setText(label);
-         if (!settings.isFullscreen())
-            fullScreenTxt.setText("(f) Window Screen");
-         else
-            fullScreenTxt.setText("(f) Fullscreen");
-      } catch (FileNotFoundException e) {
-         // TODO Auto-generated catch block
-         e.printStackTrace();
-      } catch (IOException e) {
-         // TODO Auto-generated catch block
-         e.printStackTrace();
-      }
-
-   }
-
+public class TestMonitorApp extends SimpleApplication implements ActionListener {
+
+    private BitmapText txt;
+    private BitmapText selectedMonitorTxt;
+    private BitmapText fullScreenTxt;
+    private int monitorSelected = 0;
+    private Displays monitors = null;
+
+    public static void main(String[] args) {
+        TestMonitorApp app = new TestMonitorApp();
+        AppSettings settings = new AppSettings(true);
+        settings.setResizable(false);
+        app.setShowSettings(true);
+        settings.setRenderer(AppSettings.LWJGL_OPENGL33);
+        settings.setDisplay(0);
+        settings.setResolution(800, 600);
+
+        settings.setFullscreen(true);
+
+        // Force JME to center the window, this only applies if it is
+        // not fullscreen.
+        settings.setCenterWindow(true);
+
+        // If center window is not turned on, you can force JME to
+        // open the window at certain x,y coords. These are ignored
+        // if the screen is set to "fullscreen".
+        settings.setWindowXPosition(0);
+        settings.setWindowYPosition(0);
+
+        try {
+            // Let's try and load the AppSetting parameters back into memory
+            InputStream out = new FileInputStream("TestMonitorApp.prefs");
+            settings.load(out);
+        } catch (IOException e) {
+            System.out.println("failed to load settings, reverting to defaults");
+        }
+        app.setSettings(settings);
+
+        app.start();
+    }
+
+    @Override
+    public void simpleInitApp() {
+        flyCam.setDragToRotate(true);
+        int numMonitors = 1;
+
+        // If monitor is define, Jme supports multiple monitors. Setup to keys
+        if (monitors == null) {
+            inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_DOWN));
+            inputManager.addMapping("fullscreen", new KeyTrigger(KeyInput.KEY_F));
+            inputManager.addListener(this, "down", "fullscreen");
+        }
+
+        // Get the selected monitor
+        monitorSelected = settings.getDisplay();
+        monitors = context.getDisplays();
+        if (monitors != null) numMonitors = monitors.size();
+
+        // Let's define the labels for users to see what is going on with Multiple
+        // Monitor
+        String labelValue = "";
+        labelValue = "There are " + numMonitors + " monitor(s) hooked up to this computer.";
+        txt = new BitmapText(loadGuiFont());
+        txt.setText(labelValue);
+        txt.setLocalTranslation(0, settings.getHeight(), 0);
+        guiNode.attachChild(txt);
+
+        txt = new BitmapText(loadGuiFont());
+        if (!settings.isFullscreen()) txt.setText(
+            "Window is on Monitor N/A (fullscreen only feature)"
+        ); else txt.setText("Window is on Monitor " + settings.getDisplay());
+
+        txt.setLocalTranslation(0, settings.getHeight() - 40, 0);
+        guiNode.attachChild(txt);
+
+        if (monitors != null) {
+            selectedMonitorTxt = new BitmapText(loadGuiFont());
+            // Lets display information about selected monitor
+            String label =
+                "Selected Monitor " +
+                "Name: " +
+                monitors.get(settings.getDisplay()).name +
+                " " +
+                monitorSelected +
+                " Res: " +
+                monitors.get(settings.getDisplay()).width +
+                "," +
+                monitors.get(settings.getDisplay()).height +
+                " refresh: " +
+                monitors.get(settings.getDisplay()).rate;
+            selectedMonitorTxt.setText(label);
+            selectedMonitorTxt.setLocalTranslation(0, settings.getHeight() - 80, 0);
+            guiNode.attachChild(selectedMonitorTxt);
+
+            // Let's loop through all the monitors and display on the screen
+            for (int i = 0; i < monitors.size(); i++) {
+                DisplayInfo monitor = monitors.get(i);
+                labelValue =
+                    "Mon : " +
+                    i +
+                    " " +
+                    monitor.name +
+                    " " +
+                    monitor.width +
+                    "," +
+                    monitor.height +
+                    " refresh: " +
+                    monitor.rate;
+                txt = new BitmapText(loadGuiFont());
+                txt.setText(labelValue);
+                txt.setLocalTranslation(0, settings.getHeight() - 160 - (40 * i), 0);
+                guiNode.attachChild(txt);
+            }
+        }
+
+        // Lets put a label up there for FullScreen/Window toggle
+        fullScreenTxt = new BitmapText(loadGuiFont());
+        if (!settings.isFullscreen()) fullScreenTxt.setText("(f) Window Screen"); else fullScreenTxt.setText(
+            "(f) Fullscreen"
+        );
+
+        fullScreenTxt.setLocalTranslation(00, settings.getHeight() - 240, 0);
+        guiNode.attachChild(fullScreenTxt);
+
+        BitmapText infoTxt = new BitmapText(loadGuiFont());
+        infoTxt.setText("Restart is required to activate changes in settings.");
+        infoTxt.setLocalTranslation(0, settings.getHeight() - 300, 0);
+        guiNode.attachChild(infoTxt);
+    }
+
+    @Override
+    public void onAction(String name, boolean isPressed, float tpf) {
+        if (monitors == null) return;
+
+        if (name.equals("down") && isPressed) {
+            monitorSelected++;
+            if (monitorSelected >= monitors.size()) monitorSelected = 0;
+            saveSettings();
+        } else if (name.equals("up") && isPressed) {
+            monitorSelected--;
+            if (monitorSelected < 0) monitorSelected = monitors.size() - 1;
+            saveSettings();
+        } else if (name.equals("fullscreen") && isPressed) {
+            settings.setFullscreen(!settings.isFullscreen());
+            saveSettings();
+        }
+    }
+
+    /**
+     * This function saves out the AppSettings into a file to be loaded back in
+     * on start of application.
+     */
+    public void saveSettings() {
+        try {
+            settings.setDisplay(monitorSelected);
+            OutputStream out = new FileOutputStream("TestMonitorApp.prefs");
+            settings.save(out);
+
+            int monitorSelected = settings.getDisplay();
+            String label =
+                "Selected Monitor " +
+                monitorSelected +
+                " " +
+                monitors.get(monitorSelected).name +
+                " Res: " +
+                monitors.get(monitorSelected).width +
+                "," +
+                monitors.get(monitorSelected).height +
+                "refresh: " +
+                monitors.get(monitorSelected).rate;
+            selectedMonitorTxt.setText(label);
+            if (!settings.isFullscreen()) fullScreenTxt.setText(
+                "(f) Window Screen"
+            ); else fullScreenTxt.setText("(f) Fullscreen");
+        } catch (FileNotFoundException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
 }

+ 18 - 22
jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java

@@ -42,7 +42,6 @@ import com.jme3.opencl.Context;
 import com.jme3.renderer.ios.IosGL;
 import com.jme3.renderer.opengl.*;
 import com.jme3.system.*;
-
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -63,10 +62,10 @@ public class IGLESContext implements JmeContext {
     protected Timer timer;
     protected SystemListener listener;
     protected IosInputHandler input;
-    protected int minFrameDuration = 0;                   // No FPS cap
+    protected int minFrameDuration = 0; // No FPS cap
 
     public IGLESContext() {
-           logger.log(Level.FINE, "IGLESContext constructor");
+        logger.log(Level.FINE, "IGLESContext constructor");
     }
 
     @Override
@@ -123,13 +122,13 @@ public class IGLESContext implements JmeContext {
 
     @Override
     public JoyInput getJoyInput() {
-    /*
+        /*
         if (androidSensorJoyInput == null) {
             androidSensorJoyInput = new AndroidSensorJoyInput();
         }
         return androidSensorJoyInput;
         */
-        return null;//  new DummySensorJoyInput();
+        return null; //  new DummySensorJoyInput();
     }
 
     @Override
@@ -143,8 +142,7 @@ public class IGLESContext implements JmeContext {
     }
 
     @Override
-    public void setTitle(String title) {
-    }
+    public void setTitle(String title) {}
 
     @Override
     public boolean isCreated() {
@@ -160,7 +158,7 @@ public class IGLESContext implements JmeContext {
     @Override
     public boolean isRenderable() {
         logger.log(Level.FINE, "IGLESContext isRenderable");
-        return true;// renderable.get();
+        return true; // renderable.get();
     }
 
     @Override
@@ -169,18 +167,18 @@ public class IGLESContext implements JmeContext {
         IosGL gl = new IosGL();
 
         if (settings.getBoolean("GraphicsDebug")) {
-            gl = (IosGL)GLDebug.createProxy(gl, gl, GL.class, GLExt.class, GLFbo.class);
+            gl = (IosGL) GLDebug.createProxy(gl, gl, GL.class, GLExt.class, GLFbo.class);
         }
 
         renderer = new GLRenderer(gl, gl, gl);
         renderer.initialize();
-        
+
         input = new IosInputHandler();
         timer = new NanoTimer();
 
-//synchronized (createdLock){
-            created.set(true);
-            //createdLock.notifyAll();
+        //synchronized (createdLock){
+        created.set(true);
+        //createdLock.notifyAll();
         //}
 
         listener.initialize();
@@ -196,8 +194,7 @@ public class IGLESContext implements JmeContext {
     }
 
     @Override
-    public void restart() {
-    }
+    public void restart() {}
 
     @Override
     public void destroy(boolean waitFor) {
@@ -217,8 +214,7 @@ public class IGLESContext implements JmeContext {
         while (renderable.get() != createdVal) {
             try {
                 Thread.sleep(10);
-            } catch (InterruptedException ex) {
-            }
+            } catch (InterruptedException ex) {}
         }
     }
 
@@ -270,13 +266,13 @@ public class IGLESContext implements JmeContext {
 
     @Override
     public Displays getDisplays() {
-      // TODO Auto-generated method stub
-      return null;
+        // TODO Auto-generated method stub
+        return null;
     }
 
     @Override
     public int getPrimaryDisplay() {
-      // TODO Auto-generated method stub
-      return 0;
+        // TODO Auto-generated method stub
+        return 0;
     }
-}
+}

+ 519 - 497
jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java

@@ -1,497 +1,519 @@
-/*
- * Copyright (c) 2009-2021 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.lwjgl;
-
-import com.jme3.system.AppSettings;
-import com.jme3.system.Displays;
-import com.jme3.system.JmeCanvasContext;
-import com.jme3.system.JmeContext.Type;
-import com.jme3.system.JmeSystem;
-import com.jme3.system.Platform;
-import java.awt.Canvas;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.swing.SwingUtilities;
-import org.lwjgl.LWJGLException;
-import org.lwjgl.input.Keyboard;
-import org.lwjgl.input.Mouse;
-import org.lwjgl.opengl.Display;
-import org.lwjgl.opengl.Pbuffer;
-import org.lwjgl.opengl.PixelFormat;
-
-public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContext {
-
-  protected static final int TASK_NOTHING = 0, TASK_DESTROY_DISPLAY = 1, TASK_CREATE_DISPLAY = 2,
-      TASK_COMPLETE = 3;
-
-  // protected static final boolean USE_SHARED_CONTEXT =
-  // Boolean.parseBoolean(System.getProperty("jme3.canvas.sharedctx", "true"));
-
-  protected static final boolean USE_SHARED_CONTEXT = false;
-
-  private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName());
-  private Canvas canvas;
-  private int width;
-  private int height;
-
-  private final Object taskLock = new Object();
-  private int desiredTask = TASK_NOTHING;
-
-  private Thread renderThread;
-  private boolean runningFirstTime = true;
-  private boolean mouseWasGrabbed = false;
-
-  private boolean mouseWasCreated = false;
-  private boolean keyboardWasCreated = false;
-
-  private Pbuffer pbuffer;
-  private PixelFormat pbufferFormat;
-  private PixelFormat canvasFormat;
-
-  private class GLCanvas extends Canvas {
-    @Override
-    public void addNotify() {
-      super.addNotify();
-
-      if (renderThread != null && renderThread.getState() == Thread.State.TERMINATED) {
-        return; // already destroyed.
-      }
-
-      if (renderThread == null) {
-        logger.log(Level.FINE, "EDT: Creating OGL thread.");
-
-        // Also set some settings on the canvas here.
-        // So we don't do it outside the AWT thread.
-        canvas.setFocusable(true);
-        canvas.setIgnoreRepaint(true);
-
-        renderThread = new Thread(LwjglCanvas.this, THREAD_NAME);
-        renderThread.start();
-      } else if (needClose.get()) {
-        return;
-      }
-
-      logger.log(Level.FINE, "EDT: Telling OGL to create display ..");
-      synchronized (taskLock) {
-        desiredTask = TASK_CREATE_DISPLAY;
-        // while (desiredTask != TASK_COMPLETE){
-        // try {
-        // taskLock.wait();
-        // } catch (InterruptedException ex) {
-        // return;
-        // }
-        // }
-        // desiredTask = TASK_NOTHING;
-      }
-      // logger.log(Level.FINE, "EDT: OGL has created the display");
-    }
-
-    @Override
-    public void removeNotify() {
-      if (needClose.get()) {
-        logger.log(Level.FINE, "EDT: Application is stopped. Not restoring canvas.");
-        super.removeNotify();
-        return;
-      }
-
-      // We must tell GL context to shut down and wait for it to
-      // shut down. Otherwise, issues will occur.
-      logger.log(Level.FINE, "EDT: Telling OGL to destroy display ..");
-      synchronized (taskLock) {
-        desiredTask = TASK_DESTROY_DISPLAY;
-        while (desiredTask != TASK_COMPLETE) {
-          try {
-            taskLock.wait();
-          } catch (InterruptedException ex) {
-            super.removeNotify();
-            return;
-          }
-        }
-        desiredTask = TASK_NOTHING;
-      }
-
-      logger.log(Level.FINE, "EDT: Acknowledged receipt of canvas death");
-      // GL context is dead at this point
-
-      super.removeNotify();
-    }
-  }
-
-  public LwjglCanvas() {
-    super();
-    canvas = new GLCanvas();
-  }
-
-  @Override
-  public Type getType() {
-    return Type.Canvas;
-  }
-
-  @Override
-  public void create(boolean waitFor) {
-    if (renderThread == null) {
-      logger.log(Level.FINE, "MAIN: Creating OGL thread.");
-
-      renderThread = new Thread(LwjglCanvas.this, THREAD_NAME);
-      renderThread.start();
-    }
-    // do not do anything.
-    // superclass's create() will be called at initInThread()
-    if (waitFor) {
-      waitFor(true);
-    }
-  }
-
-  @Override
-  public void setTitle(String title) {}
-
-  @Override
-  public void restart() {
-    frameRate = settings.getFrameRate();
-    // TODO: Handle other cases, like change of pixel format, etc.
-  }
-
-  @Override
-  public Canvas getCanvas() {
-    return canvas;
-  }
-
-  @Override
-  protected void runLoop() {
-    if (desiredTask != TASK_NOTHING) {
-      synchronized (taskLock) {
-        switch (desiredTask) {
-          case TASK_CREATE_DISPLAY:
-            logger.log(Level.FINE, "OGL: Creating display ..");
-            restoreCanvas();
-            listener.gainFocus();
-            desiredTask = TASK_NOTHING;
-            break;
-          case TASK_DESTROY_DISPLAY:
-            logger.log(Level.FINE, "OGL: Destroying display ..");
-            listener.loseFocus();
-            pauseCanvas();
-            break;
-        }
-        desiredTask = TASK_COMPLETE;
-        taskLock.notifyAll();
-      }
-    }
-
-    if (renderable.get()) {
-      int newWidth = Math.max(canvas.getWidth(), 1);
-      int newHeight = Math.max(canvas.getHeight(), 1);
-      if (width != newWidth || height != newHeight) {
-        width = newWidth;
-        height = newHeight;
-        if (listener != null) {
-          listener.reshape(width, height);
-        }
-      }
-    } else {
-      if (frameRate <= 0) {
-        // NOTE: MUST be done otherwise
-        // Windows OS will freeze
-        Display.sync(30);
-      }
-    }
-
-    super.runLoop();
-  }
-
-  private void pauseCanvas() {
-    if (Mouse.isCreated()) {
-      if (Mouse.isGrabbed()) {
-        Mouse.setGrabbed(false);
-        mouseWasGrabbed = true;
-      }
-      mouseWasCreated = true;
-      Mouse.destroy();
-    }
-    if (Keyboard.isCreated()) {
-      keyboardWasCreated = true;
-      Keyboard.destroy();
-    }
-
-    renderable.set(false);
-    destroyContext();
-  }
-
-  /**
-   * Called to restore the canvas.
-   */
-  private void restoreCanvas() {
-    logger.log(Level.FINE, "OGL: Waiting for canvas to become displayable..");
-    while (!canvas.isDisplayable()) {
-      try {
-        Thread.sleep(10);
-      } catch (InterruptedException ex) {
-        logger.log(Level.SEVERE, "OGL: Interrupted! ", ex);
-      }
-    }
-
-    logger.log(Level.FINE, "OGL: Creating display context ..");
-
-    // Set renderable to true, since canvas is now displayable.
-    renderable.set(true);
-    createContext(settings);
-
-    logger.log(Level.FINE, "OGL: Display is active!");
-
-    try {
-      if (mouseWasCreated) {
-        Mouse.create();
-        if (mouseWasGrabbed) {
-          Mouse.setGrabbed(true);
-          mouseWasGrabbed = false;
-        }
-      }
-      if (keyboardWasCreated) {
-        Keyboard.create();
-        keyboardWasCreated = false;
-      }
-    } catch (LWJGLException ex) {
-      logger.log(Level.SEVERE, "Encountered exception when restoring input", ex);
-    }
-
-    SwingUtilities.invokeLater(new Runnable() {
-      @Override
-      public void run() {
-        canvas.requestFocus();
-      }
-    });
-  }
-
-  /**
-   * It seems it is best to use one pixel format for all shared contexts.
-   * 
-   * @see <a href=
-   *      "http://developer.apple.com/library/mac/#qa/qa1248/_index.html">http://developer.apple.com/library/mac/#qa/qa1248/_index.html</a>
-   * 
-   * @param forPbuffer true&rarr;zero samples, false&rarr;correct number of samples
-   * @return a new instance
-   */
-  protected PixelFormat acquirePixelFormat(boolean forPbuffer) {
-    if (forPbuffer) {
-      // Use 0 samples for pbuffer format, prevents
-      // crashes on bad drivers
-      if (pbufferFormat == null) {
-        pbufferFormat = new PixelFormat(settings.getBitsPerPixel(), settings.getAlphaBits(),
-            settings.getDepthBits(), settings.getStencilBits(), 0, // samples
-            0, 0, 0, settings.useStereo3D());
-      }
-      return pbufferFormat;
-    } else {
-      if (canvasFormat == null) {
-        int samples = getNumSamplesToUse();
-        canvasFormat = new PixelFormat(settings.getBitsPerPixel(), settings.getAlphaBits(),
-            settings.getDepthBits(), settings.getStencilBits(), samples, 0, 0, 0,
-            settings.useStereo3D());
-      }
-      return canvasFormat;
-    }
-  }
-
-  /**
-   * Makes sure the pbuffer is available and ready for use
-   * 
-   * @throws LWJGLException if the buffer can't be made current
-   */
-  protected void makePbufferAvailable() throws LWJGLException {
-    if (pbuffer != null && pbuffer.isBufferLost()) {
-      logger.log(Level.WARNING, "PBuffer was lost!");
-      pbuffer.destroy();
-      pbuffer = null;
-    }
-
-    if (pbuffer == null) {
-      pbuffer = new Pbuffer(1, 1, acquirePixelFormat(true), null);
-      pbuffer.makeCurrent();
-      logger.log(Level.FINE, "OGL: Pbuffer has been created");
-
-      // Any created objects are no longer valid
-      if (!runningFirstTime) {
-        renderer.resetGLObjects();
-      }
-    }
-
-    pbuffer.makeCurrent();
-    if (!pbuffer.isCurrent()) {
-      throw new LWJGLException("Pbuffer cannot be made current");
-    }
-  }
-
-  protected void destroyPbuffer() {
-    if (pbuffer != null) {
-      if (!pbuffer.isBufferLost()) {
-        pbuffer.destroy();
-      }
-      pbuffer = null;
-    }
-  }
-
-  /**
-   * This is called: 1) When the context thread ends 2) Any time the canvas becomes non-displayable
-   */
-  @Override
-  protected void destroyContext() {
-    try {
-      // invalidate the state so renderer can resume operation
-      if (!USE_SHARED_CONTEXT) {
-        renderer.cleanup();
-      }
-
-      if (Display.isCreated()) {
-        /*
-         * FIXES: org.lwjgl.LWJGLException: X Error BadWindow (invalid Window parameter)
-         * request_code: 2 minor_code: 0
-         * 
-         * Destroying keyboard early prevents the error above, triggered by destroying keyboard in
-         * by Display.destroy() or Display.setParent(null). Therefore, Keyboard.destroy() should
-         * precede any of these calls.
-         */
-        if (Keyboard.isCreated()) {
-          // Should only happen if called in
-          // LwjglAbstractDisplay.deinitInThread().
-          Keyboard.destroy();
-        }
-
-        // try {
-        // NOTE: On Windows XP, not calling setParent(null)
-        // freezes the application.
-        // On Mac it freezes the application.
-        // On Linux it fixes a crash with X Window System.
-        if (JmeSystem.getPlatform() == Platform.Windows32
-            || JmeSystem.getPlatform() == Platform.Windows64) {
-          // Display.setParent(null);
-        }
-        // } catch (LWJGLException ex) {
-        // logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex);
-        // }
-
-        Display.destroy();
-      }
-
-      // The canvas is no longer visible,
-      // but the context thread is still running.
-      if (!needClose.get()) {
-        // MUST make sure there's still a context current here.
-        // Display is dead, make PBuffer available to the system.
-        makePbufferAvailable();
-
-        renderer.invalidateState();
-      } else {
-        // The context thread is no longer running.
-        // Destroy pbuffer.
-        destroyPbuffer();
-      }
-    } catch (LWJGLException ex) {
-      listener.handleError("Failed make pbuffer available", ex);
-    }
-  }
-
-  /**
-   * This is called: 1) When the context thread starts 2) Any time the canvas becomes displayable
-   * again. In the first call of this method, OpenGL context is not ready yet. Therefore, OpenCL
-   * context cannot be created. The second call of this method is done after "simpleInitApp" is
-   * called. Therefore, OpenCL won't be available in "simpleInitApp" if Canvas/Swing is used. To use
-   * OpenCL with Canvas/Swing, you need to use OpenCL in the rendering loop "simpleUpdate" and check
-   * for "context.getOpenCLContext()!=null".
-   */
-  @Override
-  protected void createContext(AppSettings settings) {
-    // In case canvas is not visible, we still take framerate
-    // from settings to prevent "100% CPU usage"
-    frameRate = settings.getFrameRate();
-    allowSwapBuffers = settings.isSwapBuffers();
-
-    try {
-      if (renderable.get()) {
-        if (!runningFirstTime) {
-          // because the display is a different opengl context
-          // must reset the context state.
-          if (!USE_SHARED_CONTEXT) {
-            renderer.cleanup();
-          }
-        }
-
-        // if the pbuffer is currently active,
-        // make sure to deactivate it
-        destroyPbuffer();
-
-        if (Keyboard.isCreated()) {
-          Keyboard.destroy();
-        }
-
-        try {
-          Thread.sleep(1000);
-        } catch (InterruptedException ex) {
-        }
-
-        Display.setVSyncEnabled(settings.isVSync());
-        Display.setParent(canvas);
-
-        if (USE_SHARED_CONTEXT) {
-          Display.create(acquirePixelFormat(false), pbuffer);
-        } else {
-          Display.create(acquirePixelFormat(false));
-        }
-        if (settings.isOpenCLSupport()) {
-          initOpenCL();
-        }
-
-        renderer.invalidateState();
-      } else {
-        // First create the pbuffer, if it is needed.
-        makePbufferAvailable();
-      }
-
-      // At this point, the OpenGL context is active.
-      if (runningFirstTime) {
-        // THIS is the part that creates the renderer.
-        // It must always be called, now that we have the pbuffer workaround.
-        initContextFirstTime();
-        runningFirstTime = false;
-      }
-    } catch (LWJGLException ex) {
-      listener.handleError("Failed to initialize OpenGL context", ex);
-      // TODO: Fix deadlock that happens after the error (throw runtime exception?)
-    }
-  }
-
-  @Override
-  public Displays getDisplays() {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  @Override
-  public int getPrimaryDisplay() {
-    // TODO Auto-generated method stub
-    return 0;
-  }
-}
+/*
+ * Copyright (c) 2009-2021 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.lwjgl;
+
+import com.jme3.system.AppSettings;
+import com.jme3.system.Displays;
+import com.jme3.system.JmeCanvasContext;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.system.JmeSystem;
+import com.jme3.system.Platform;
+import java.awt.Canvas;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.SwingUtilities;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.Display;
+import org.lwjgl.opengl.Pbuffer;
+import org.lwjgl.opengl.PixelFormat;
+
+public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContext {
+
+    protected static final int TASK_NOTHING = 0, TASK_DESTROY_DISPLAY = 1, TASK_CREATE_DISPLAY =
+        2, TASK_COMPLETE = 3;
+
+    // protected static final boolean USE_SHARED_CONTEXT =
+    // Boolean.parseBoolean(System.getProperty("jme3.canvas.sharedctx", "true"));
+
+    protected static final boolean USE_SHARED_CONTEXT = false;
+
+    private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName());
+    private Canvas canvas;
+    private int width;
+    private int height;
+
+    private final Object taskLock = new Object();
+    private int desiredTask = TASK_NOTHING;
+
+    private Thread renderThread;
+    private boolean runningFirstTime = true;
+    private boolean mouseWasGrabbed = false;
+
+    private boolean mouseWasCreated = false;
+    private boolean keyboardWasCreated = false;
+
+    private Pbuffer pbuffer;
+    private PixelFormat pbufferFormat;
+    private PixelFormat canvasFormat;
+
+    private class GLCanvas extends Canvas {
+
+        @Override
+        public void addNotify() {
+            super.addNotify();
+
+            if (renderThread != null && renderThread.getState() == Thread.State.TERMINATED) {
+                return; // already destroyed.
+            }
+
+            if (renderThread == null) {
+                logger.log(Level.FINE, "EDT: Creating OGL thread.");
+
+                // Also set some settings on the canvas here.
+                // So we don't do it outside the AWT thread.
+                canvas.setFocusable(true);
+                canvas.setIgnoreRepaint(true);
+
+                renderThread = new Thread(LwjglCanvas.this, THREAD_NAME);
+                renderThread.start();
+            } else if (needClose.get()) {
+                return;
+            }
+
+            logger.log(Level.FINE, "EDT: Telling OGL to create display ..");
+            synchronized (taskLock) {
+                desiredTask = TASK_CREATE_DISPLAY;
+                // while (desiredTask != TASK_COMPLETE){
+                // try {
+                // taskLock.wait();
+                // } catch (InterruptedException ex) {
+                // return;
+                // }
+                // }
+                // desiredTask = TASK_NOTHING;
+            }
+            // logger.log(Level.FINE, "EDT: OGL has created the display");
+        }
+
+        @Override
+        public void removeNotify() {
+            if (needClose.get()) {
+                logger.log(Level.FINE, "EDT: Application is stopped. Not restoring canvas.");
+                super.removeNotify();
+                return;
+            }
+
+            // We must tell GL context to shut down and wait for it to
+            // shut down. Otherwise, issues will occur.
+            logger.log(Level.FINE, "EDT: Telling OGL to destroy display ..");
+            synchronized (taskLock) {
+                desiredTask = TASK_DESTROY_DISPLAY;
+                while (desiredTask != TASK_COMPLETE) {
+                    try {
+                        taskLock.wait();
+                    } catch (InterruptedException ex) {
+                        super.removeNotify();
+                        return;
+                    }
+                }
+                desiredTask = TASK_NOTHING;
+            }
+
+            logger.log(Level.FINE, "EDT: Acknowledged receipt of canvas death");
+            // GL context is dead at this point
+
+            super.removeNotify();
+        }
+    }
+
+    public LwjglCanvas() {
+        super();
+        canvas = new GLCanvas();
+    }
+
+    @Override
+    public Type getType() {
+        return Type.Canvas;
+    }
+
+    @Override
+    public void create(boolean waitFor) {
+        if (renderThread == null) {
+            logger.log(Level.FINE, "MAIN: Creating OGL thread.");
+
+            renderThread = new Thread(LwjglCanvas.this, THREAD_NAME);
+            renderThread.start();
+        }
+        // do not do anything.
+        // superclass's create() will be called at initInThread()
+        if (waitFor) {
+            waitFor(true);
+        }
+    }
+
+    @Override
+    public void setTitle(String title) {}
+
+    @Override
+    public void restart() {
+        frameRate = settings.getFrameRate();
+        // TODO: Handle other cases, like change of pixel format, etc.
+    }
+
+    @Override
+    public Canvas getCanvas() {
+        return canvas;
+    }
+
+    @Override
+    protected void runLoop() {
+        if (desiredTask != TASK_NOTHING) {
+            synchronized (taskLock) {
+                switch (desiredTask) {
+                    case TASK_CREATE_DISPLAY:
+                        logger.log(Level.FINE, "OGL: Creating display ..");
+                        restoreCanvas();
+                        listener.gainFocus();
+                        desiredTask = TASK_NOTHING;
+                        break;
+                    case TASK_DESTROY_DISPLAY:
+                        logger.log(Level.FINE, "OGL: Destroying display ..");
+                        listener.loseFocus();
+                        pauseCanvas();
+                        break;
+                }
+                desiredTask = TASK_COMPLETE;
+                taskLock.notifyAll();
+            }
+        }
+
+        if (renderable.get()) {
+            int newWidth = Math.max(canvas.getWidth(), 1);
+            int newHeight = Math.max(canvas.getHeight(), 1);
+            if (width != newWidth || height != newHeight) {
+                width = newWidth;
+                height = newHeight;
+                if (listener != null) {
+                    listener.reshape(width, height);
+                }
+            }
+        } else {
+            if (frameRate <= 0) {
+                // NOTE: MUST be done otherwise
+                // Windows OS will freeze
+                Display.sync(30);
+            }
+        }
+
+        super.runLoop();
+    }
+
+    private void pauseCanvas() {
+        if (Mouse.isCreated()) {
+            if (Mouse.isGrabbed()) {
+                Mouse.setGrabbed(false);
+                mouseWasGrabbed = true;
+            }
+            mouseWasCreated = true;
+            Mouse.destroy();
+        }
+        if (Keyboard.isCreated()) {
+            keyboardWasCreated = true;
+            Keyboard.destroy();
+        }
+
+        renderable.set(false);
+        destroyContext();
+    }
+
+    /**
+     * Called to restore the canvas.
+     */
+    private void restoreCanvas() {
+        logger.log(Level.FINE, "OGL: Waiting for canvas to become displayable..");
+        while (!canvas.isDisplayable()) {
+            try {
+                Thread.sleep(10);
+            } catch (InterruptedException ex) {
+                logger.log(Level.SEVERE, "OGL: Interrupted! ", ex);
+            }
+        }
+
+        logger.log(Level.FINE, "OGL: Creating display context ..");
+
+        // Set renderable to true, since canvas is now displayable.
+        renderable.set(true);
+        createContext(settings);
+
+        logger.log(Level.FINE, "OGL: Display is active!");
+
+        try {
+            if (mouseWasCreated) {
+                Mouse.create();
+                if (mouseWasGrabbed) {
+                    Mouse.setGrabbed(true);
+                    mouseWasGrabbed = false;
+                }
+            }
+            if (keyboardWasCreated) {
+                Keyboard.create();
+                keyboardWasCreated = false;
+            }
+        } catch (LWJGLException ex) {
+            logger.log(Level.SEVERE, "Encountered exception when restoring input", ex);
+        }
+
+        SwingUtilities.invokeLater(
+            new Runnable() {
+                @Override
+                public void run() {
+                    canvas.requestFocus();
+                }
+            }
+        );
+    }
+
+    /**
+     * It seems it is best to use one pixel format for all shared contexts.
+     *
+     * @see <a href=
+     *      "http://developer.apple.com/library/mac/#qa/qa1248/_index.html">http://developer.apple.com/library/mac/#qa/qa1248/_index.html</a>
+     *
+     * @param forPbuffer true&rarr;zero samples, false&rarr;correct number of samples
+     * @return a new instance
+     */
+    protected PixelFormat acquirePixelFormat(boolean forPbuffer) {
+        if (forPbuffer) {
+            // Use 0 samples for pbuffer format, prevents
+            // crashes on bad drivers
+            if (pbufferFormat == null) {
+                pbufferFormat =
+                    new PixelFormat(
+                        settings.getBitsPerPixel(),
+                        settings.getAlphaBits(),
+                        settings.getDepthBits(),
+                        settings.getStencilBits(),
+                        0, // samples
+                        0,
+                        0,
+                        0,
+                        settings.useStereo3D()
+                    );
+            }
+            return pbufferFormat;
+        } else {
+            if (canvasFormat == null) {
+                int samples = getNumSamplesToUse();
+                canvasFormat =
+                    new PixelFormat(
+                        settings.getBitsPerPixel(),
+                        settings.getAlphaBits(),
+                        settings.getDepthBits(),
+                        settings.getStencilBits(),
+                        samples,
+                        0,
+                        0,
+                        0,
+                        settings.useStereo3D()
+                    );
+            }
+            return canvasFormat;
+        }
+    }
+
+    /**
+     * Makes sure the pbuffer is available and ready for use
+     *
+     * @throws LWJGLException if the buffer can't be made current
+     */
+    protected void makePbufferAvailable() throws LWJGLException {
+        if (pbuffer != null && pbuffer.isBufferLost()) {
+            logger.log(Level.WARNING, "PBuffer was lost!");
+            pbuffer.destroy();
+            pbuffer = null;
+        }
+
+        if (pbuffer == null) {
+            pbuffer = new Pbuffer(1, 1, acquirePixelFormat(true), null);
+            pbuffer.makeCurrent();
+            logger.log(Level.FINE, "OGL: Pbuffer has been created");
+
+            // Any created objects are no longer valid
+            if (!runningFirstTime) {
+                renderer.resetGLObjects();
+            }
+        }
+
+        pbuffer.makeCurrent();
+        if (!pbuffer.isCurrent()) {
+            throw new LWJGLException("Pbuffer cannot be made current");
+        }
+    }
+
+    protected void destroyPbuffer() {
+        if (pbuffer != null) {
+            if (!pbuffer.isBufferLost()) {
+                pbuffer.destroy();
+            }
+            pbuffer = null;
+        }
+    }
+
+    /**
+     * This is called: 1) When the context thread ends 2) Any time the canvas becomes non-displayable
+     */
+    @Override
+    protected void destroyContext() {
+        try {
+            // invalidate the state so renderer can resume operation
+            if (!USE_SHARED_CONTEXT) {
+                renderer.cleanup();
+            }
+
+            if (Display.isCreated()) {
+                /*
+                 * FIXES: org.lwjgl.LWJGLException: X Error BadWindow (invalid Window parameter)
+                 * request_code: 2 minor_code: 0
+                 *
+                 * Destroying keyboard early prevents the error above, triggered by destroying keyboard in
+                 * by Display.destroy() or Display.setParent(null). Therefore, Keyboard.destroy() should
+                 * precede any of these calls.
+                 */
+                if (Keyboard.isCreated()) {
+                    // Should only happen if called in
+                    // LwjglAbstractDisplay.deinitInThread().
+                    Keyboard.destroy();
+                }
+
+                // try {
+                // NOTE: On Windows XP, not calling setParent(null)
+                // freezes the application.
+                // On Mac it freezes the application.
+                // On Linux it fixes a crash with X Window System.
+                if (
+                    JmeSystem.getPlatform() == Platform.Windows32 ||
+                    JmeSystem.getPlatform() == Platform.Windows64
+                ) {
+                    // Display.setParent(null);
+                }
+                // } catch (LWJGLException ex) {
+                // logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex);
+                // }
+
+                Display.destroy();
+            }
+
+            // The canvas is no longer visible,
+            // but the context thread is still running.
+            if (!needClose.get()) {
+                // MUST make sure there's still a context current here.
+                // Display is dead, make PBuffer available to the system.
+                makePbufferAvailable();
+
+                renderer.invalidateState();
+            } else {
+                // The context thread is no longer running.
+                // Destroy pbuffer.
+                destroyPbuffer();
+            }
+        } catch (LWJGLException ex) {
+            listener.handleError("Failed make pbuffer available", ex);
+        }
+    }
+
+    /**
+     * This is called: 1) When the context thread starts 2) Any time the canvas becomes displayable
+     * again. In the first call of this method, OpenGL context is not ready yet. Therefore, OpenCL
+     * context cannot be created. The second call of this method is done after "simpleInitApp" is
+     * called. Therefore, OpenCL won't be available in "simpleInitApp" if Canvas/Swing is used. To use
+     * OpenCL with Canvas/Swing, you need to use OpenCL in the rendering loop "simpleUpdate" and check
+     * for "context.getOpenCLContext()!=null".
+     */
+    @Override
+    protected void createContext(AppSettings settings) {
+        // In case canvas is not visible, we still take framerate
+        // from settings to prevent "100% CPU usage"
+        frameRate = settings.getFrameRate();
+        allowSwapBuffers = settings.isSwapBuffers();
+
+        try {
+            if (renderable.get()) {
+                if (!runningFirstTime) {
+                    // because the display is a different opengl context
+                    // must reset the context state.
+                    if (!USE_SHARED_CONTEXT) {
+                        renderer.cleanup();
+                    }
+                }
+
+                // if the pbuffer is currently active,
+                // make sure to deactivate it
+                destroyPbuffer();
+
+                if (Keyboard.isCreated()) {
+                    Keyboard.destroy();
+                }
+
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException ex) {}
+
+                Display.setVSyncEnabled(settings.isVSync());
+                Display.setParent(canvas);
+
+                if (USE_SHARED_CONTEXT) {
+                    Display.create(acquirePixelFormat(false), pbuffer);
+                } else {
+                    Display.create(acquirePixelFormat(false));
+                }
+                if (settings.isOpenCLSupport()) {
+                    initOpenCL();
+                }
+
+                renderer.invalidateState();
+            } else {
+                // First create the pbuffer, if it is needed.
+                makePbufferAvailable();
+            }
+
+            // At this point, the OpenGL context is active.
+            if (runningFirstTime) {
+                // THIS is the part that creates the renderer.
+                // It must always be called, now that we have the pbuffer workaround.
+                initContextFirstTime();
+                runningFirstTime = false;
+            }
+        } catch (LWJGLException ex) {
+            listener.handleError("Failed to initialize OpenGL context", ex);
+            // TODO: Fix deadlock that happens after the error (throw runtime exception?)
+        }
+    }
+
+    @Override
+    public Displays getDisplays() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public int getPrimaryDisplay() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+}

+ 325 - 299
jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java

@@ -1,299 +1,325 @@
-/*
- * 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.lwjgl;
-
-import com.jme3.system.AppSettings;
-import com.jme3.system.Displays;
-import com.jme3.system.JmeContext.Type;
-
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import org.lwjgl.LWJGLException;
-import org.lwjgl.opengl.*;
-
-public class LwjglDisplay extends LwjglAbstractDisplay {
-
-    private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName());
-
-    private final AtomicBoolean needRestart = new AtomicBoolean(false);
-    private PixelFormat pixelFormat;
-
-    /**
-     * @param width The required display width
-     * @param height The required display height
-     * @param bpp The required bits per pixel. If -1 is passed it will return
-     *           whatever bpp is found
-     * @param freq The required frequency, if -1 is passed it will return
-     *             whatever frequency is found
-     * @return The {@link DisplayMode} matches with specified settings or
-     *         return null if no matching display mode is found
-     */
-    protected DisplayMode getFullscreenDisplayMode(int width, int height, int bpp, int freq){
-        try {
-            DisplayMode[] modes = Display.getAvailableDisplayModes();
-            for (DisplayMode mode : modes) {
-                if (mode.getWidth() == width
-                        && mode.getHeight() == height
-                        && (mode.getBitsPerPixel() == bpp || (bpp == 24 && mode.getBitsPerPixel() == 32) || bpp == -1)
-                        // Looks like AWT uses mathematical round to convert floating point
-                        // frequency values to int while lwjgl 2 uses mathematical floor.
-                        // For example if frequency is 59.83, AWT will return 60 but lwjgl2
-                        // will return 59. This is what I observed on Linux.  - Ali-RS 2023-1-10
-                        && (Math.abs(mode.getFrequency() - freq) <= 1 || freq == -1)) {
-                    return mode;
-                }
-            }
-        } catch (LWJGLException ex) {
-            listener.handleError("Failed to acquire fullscreen display mode!", ex);
-        }
-        return null;
-    }
-
-    @Override
-    protected void createContext(AppSettings settings) throws LWJGLException{
-        DisplayMode displayMode;
-        if (settings.getWidth() <= 0 || settings.getHeight() <= 0) {
-            displayMode = Display.getDesktopDisplayMode();
-            settings.setResolution(displayMode.getWidth(), displayMode.getHeight());
-        } else if (settings.isFullscreen()) {
-            displayMode = getFullscreenDisplayMode(settings.getWidth(), settings.getHeight(),
-                    settings.getBitsPerPixel(), settings.getFrequency());
-            if (displayMode == null) {
-                // Fall back to whatever mode is available at the specified width & height
-                displayMode = getFullscreenDisplayMode(settings.getWidth(), settings.getHeight(), -1, -1);
-                if (displayMode == null) {
-                    throw new RuntimeException("Unable to find fullscreen display mode matching settings");
-                } else {
-                    logger.log(Level.WARNING, "Unable to find fullscreen display mode matching settings, falling back to: {0}", displayMode);
-                }
-            }
-        } else {
-            displayMode = new DisplayMode(settings.getWidth(), settings.getHeight());
-        }
-
-        int samples = getNumSamplesToUse();
-        PixelFormat pf = new PixelFormat(settings.getBitsPerPixel(),
-                                         settings.getAlphaBits(),
-                                         settings.getDepthBits(),
-                                         settings.getStencilBits(),
-                                         samples, 
-                                         0, 
-                                         0, 
-                                         0, 
-                                         settings.useStereo3D());
-        
-        frameRate = settings.getFrameRate();
-        allowSwapBuffers = settings.isSwapBuffers();
-        logger.log(Level.FINE, "Selected display mode: {0}", displayMode);
-
-        boolean pixelFormatChanged = false;
-        if (created.get() && (pixelFormat.getBitsPerPixel() != pf.getBitsPerPixel()
-                            ||pixelFormat.getAlphaBits() != pf.getAlphaBits()
-                            ||pixelFormat.getDepthBits() != pf.getDepthBits()
-                            ||pixelFormat.getStencilBits() != pf.getStencilBits()
-                            ||pixelFormat.getSamples() != pf.getSamples())){
-            renderer.resetGLObjects();
-            Display.destroy();
-            pixelFormatChanged = true;
-        }
-        pixelFormat = pf;
-        
-        Display.setTitle(settings.getTitle());
-        Display.setResizable(settings.isResizable());
-        
-        if (settings.isFullscreen()) {
-            Display.setDisplayModeAndFullscreen(displayMode);
-        } else {
-            Display.setFullscreen(false);
-            Display.setDisplayMode(displayMode);
-        }
-
-        if (settings.getIcons() != null) {
-            Display.setIcon(imagesToByteBuffers(settings.getIcons()));
-        }
-        
-        Display.setVSyncEnabled(settings.isVSync());
-        
-        if (created.get() && !pixelFormatChanged) {
-            renderer.resetGLObjects();
-            Display.releaseContext();
-            Display.makeCurrent();
-            Display.update();
-        }
-
-        if (!created.get() || pixelFormatChanged){
-            ContextAttribs attr = createContextAttribs();
-            if (attr != null) {
-                Display.create(pixelFormat, attr);
-            } else {
-                Display.create(pixelFormat);
-            }
-            renderable.set(true);
-            
-            if (pixelFormatChanged && pixelFormat.getSamples() > 1
-             && GLContext.getCapabilities().GL_ARB_multisample){
-                GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
-            }
-        }
-        
-        if (settings.isOpenCLSupport()) {
-            initOpenCL();
-        }
-    }
-    
-    @Override
-    protected void destroyContext(){
-        try {
-            renderer.cleanup();
-            Display.releaseContext();
-            Display.destroy();
-        } catch (LWJGLException ex) {
-            listener.handleError("Failed to destroy context", ex);
-        }
-    }
-
-    @Override
-    public void create(boolean waitFor){
-        if (created.get()){
-            logger.warning("create() called when display is already created!");
-            return;
-        }
-
-        new Thread(this, THREAD_NAME).start();
-        if (waitFor)
-            waitFor(true);
-    }
-
-    @Override
-    public void runLoop(){
-        // This method is overridden to do restart
-        if (needRestart.getAndSet(false)) {
-            try {
-                createContext(settings);
-            } catch (LWJGLException ex) {
-                logger.log(Level.SEVERE, "Failed to set display settings!", ex);
-            }
-            listener.reshape(settings.getWidth(), settings.getHeight());
-            if (renderable.get()) {
-                reinitContext();
-            } else {
-                assert getType() == Type.Canvas;
-            }
-            logger.fine("Display restarted.");
-        } else if (Display.wasResized()) {
-            int newWidth = Display.getWidth();
-            int newHeight = Display.getHeight();
-            settings.setResolution(newWidth, newHeight);
-            listener.reshape(newWidth, newHeight);
-        }
-
-        super.runLoop();
-    }
-
-    @Override
-    public void restart() {
-        if (created.get()){
-            needRestart.set(true);
-        }else{
-            logger.warning("Display is not created, cannot restart window.");
-        }
-    }
-
-    @Override
-    public Type getType() {
-        return Type.Display;
-    }
-
-    @Override
-    public void setTitle(String title){
-        if (created.get())
-            Display.setTitle(title);
-    }
-    
-    private ByteBuffer[] imagesToByteBuffers(Object[] images) {
-        ByteBuffer[] out = new ByteBuffer[images.length];
-        for (int i = 0; i < images.length; i++) {
-            BufferedImage image = (BufferedImage) images[i];
-            out[i] = imageToByteBuffer(image);
-        }
-        return out;
-    }
-
-    private ByteBuffer imageToByteBuffer(BufferedImage image) {
-        if (image.getType() != BufferedImage.TYPE_INT_ARGB_PRE) {
-            BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
-            Graphics2D g = convertedImage.createGraphics();
-            double width = image.getWidth() * (double) 1;
-            double height = image.getHeight() * (double) 1;
-            g.drawImage(image, (int) ((convertedImage.getWidth() - width) / 2),
-                    (int) ((convertedImage.getHeight() - height) / 2),
-                    (int) (width), (int) (height), null);
-            g.dispose();
-            image = convertedImage;
-        }
-
-        byte[] imageBuffer = new byte[image.getWidth() * image.getHeight() * 4];
-        int counter = 0;
-        for (int i = 0; i < image.getHeight(); i++) {
-            for (int j = 0; j < image.getWidth(); j++) {
-                int colorSpace = image.getRGB(j, i);
-                imageBuffer[counter + 0] = (byte) ((colorSpace << 8) >> 24);
-                imageBuffer[counter + 1] = (byte) ((colorSpace << 16) >> 24);
-                imageBuffer[counter + 2] = (byte) ((colorSpace << 24) >> 24);
-                imageBuffer[counter + 3] = (byte) (colorSpace >> 24);
-                counter += 4;
-            }
-        }
-        return ByteBuffer.wrap(imageBuffer);
-    }
-
-    @Override
-    public Displays getDisplays() {
-      // TODO Auto-generated method stub
-      return null;
-    }
-
-    @Override
-    public int getPrimaryDisplay() {
-      // TODO Auto-generated method stub
-      return 0;
-    }
-
-}
+/*
+ * 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.lwjgl;
+
+import com.jme3.system.AppSettings;
+import com.jme3.system.Displays;
+import com.jme3.system.JmeContext.Type;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.opengl.*;
+
+public class LwjglDisplay extends LwjglAbstractDisplay {
+
+    private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName());
+
+    private final AtomicBoolean needRestart = new AtomicBoolean(false);
+    private PixelFormat pixelFormat;
+
+    /**
+     * @param width The required display width
+     * @param height The required display height
+     * @param bpp The required bits per pixel. If -1 is passed it will return
+     *           whatever bpp is found
+     * @param freq The required frequency, if -1 is passed it will return
+     *             whatever frequency is found
+     * @return The {@link DisplayMode} matches with specified settings or
+     *         return null if no matching display mode is found
+     */
+    protected DisplayMode getFullscreenDisplayMode(int width, int height, int bpp, int freq) {
+        try {
+            DisplayMode[] modes = Display.getAvailableDisplayModes();
+            for (DisplayMode mode : modes) {
+                if (
+                    mode.getWidth() == width &&
+                    mode.getHeight() == height &&
+                    (mode.getBitsPerPixel() == bpp ||
+                        (bpp == 24 && mode.getBitsPerPixel() == 32) ||
+                        bpp == -1) &&
+                    // Looks like AWT uses mathematical round to convert floating point
+                    // frequency values to int while lwjgl 2 uses mathematical floor.
+                    // For example if frequency is 59.83, AWT will return 60 but lwjgl2
+                    // will return 59. This is what I observed on Linux.  - Ali-RS 2023-1-10
+                    (Math.abs(mode.getFrequency() - freq) <= 1 || freq == -1)
+                ) {
+                    return mode;
+                }
+            }
+        } catch (LWJGLException ex) {
+            listener.handleError("Failed to acquire fullscreen display mode!", ex);
+        }
+        return null;
+    }
+
+    @Override
+    protected void createContext(AppSettings settings) throws LWJGLException {
+        DisplayMode displayMode;
+        if (settings.getWidth() <= 0 || settings.getHeight() <= 0) {
+            displayMode = Display.getDesktopDisplayMode();
+            settings.setResolution(displayMode.getWidth(), displayMode.getHeight());
+        } else if (settings.isFullscreen()) {
+            displayMode =
+                getFullscreenDisplayMode(
+                    settings.getWidth(),
+                    settings.getHeight(),
+                    settings.getBitsPerPixel(),
+                    settings.getFrequency()
+                );
+            if (displayMode == null) {
+                // Fall back to whatever mode is available at the specified width & height
+                displayMode = getFullscreenDisplayMode(settings.getWidth(), settings.getHeight(), -1, -1);
+                if (displayMode == null) {
+                    throw new RuntimeException("Unable to find fullscreen display mode matching settings");
+                } else {
+                    logger.log(
+                        Level.WARNING,
+                        "Unable to find fullscreen display mode matching settings, falling back to: {0}",
+                        displayMode
+                    );
+                }
+            }
+        } else {
+            displayMode = new DisplayMode(settings.getWidth(), settings.getHeight());
+        }
+
+        int samples = getNumSamplesToUse();
+        PixelFormat pf = new PixelFormat(
+            settings.getBitsPerPixel(),
+            settings.getAlphaBits(),
+            settings.getDepthBits(),
+            settings.getStencilBits(),
+            samples,
+            0,
+            0,
+            0,
+            settings.useStereo3D()
+        );
+
+        frameRate = settings.getFrameRate();
+        allowSwapBuffers = settings.isSwapBuffers();
+        logger.log(Level.FINE, "Selected display mode: {0}", displayMode);
+
+        boolean pixelFormatChanged = false;
+        if (
+            created.get() &&
+            (pixelFormat.getBitsPerPixel() != pf.getBitsPerPixel() ||
+                pixelFormat.getAlphaBits() != pf.getAlphaBits() ||
+                pixelFormat.getDepthBits() != pf.getDepthBits() ||
+                pixelFormat.getStencilBits() != pf.getStencilBits() ||
+                pixelFormat.getSamples() != pf.getSamples())
+        ) {
+            renderer.resetGLObjects();
+            Display.destroy();
+            pixelFormatChanged = true;
+        }
+        pixelFormat = pf;
+
+        Display.setTitle(settings.getTitle());
+        Display.setResizable(settings.isResizable());
+
+        if (settings.isFullscreen()) {
+            Display.setDisplayModeAndFullscreen(displayMode);
+        } else {
+            Display.setFullscreen(false);
+            Display.setDisplayMode(displayMode);
+        }
+
+        if (settings.getIcons() != null) {
+            Display.setIcon(imagesToByteBuffers(settings.getIcons()));
+        }
+
+        Display.setVSyncEnabled(settings.isVSync());
+
+        if (created.get() && !pixelFormatChanged) {
+            renderer.resetGLObjects();
+            Display.releaseContext();
+            Display.makeCurrent();
+            Display.update();
+        }
+
+        if (!created.get() || pixelFormatChanged) {
+            ContextAttribs attr = createContextAttribs();
+            if (attr != null) {
+                Display.create(pixelFormat, attr);
+            } else {
+                Display.create(pixelFormat);
+            }
+            renderable.set(true);
+
+            if (
+                pixelFormatChanged &&
+                pixelFormat.getSamples() > 1 &&
+                GLContext.getCapabilities().GL_ARB_multisample
+            ) {
+                GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
+            }
+        }
+
+        if (settings.isOpenCLSupport()) {
+            initOpenCL();
+        }
+    }
+
+    @Override
+    protected void destroyContext() {
+        try {
+            renderer.cleanup();
+            Display.releaseContext();
+            Display.destroy();
+        } catch (LWJGLException ex) {
+            listener.handleError("Failed to destroy context", ex);
+        }
+    }
+
+    @Override
+    public void create(boolean waitFor) {
+        if (created.get()) {
+            logger.warning("create() called when display is already created!");
+            return;
+        }
+
+        new Thread(this, THREAD_NAME).start();
+        if (waitFor) waitFor(true);
+    }
+
+    @Override
+    public void runLoop() {
+        // This method is overridden to do restart
+        if (needRestart.getAndSet(false)) {
+            try {
+                createContext(settings);
+            } catch (LWJGLException ex) {
+                logger.log(Level.SEVERE, "Failed to set display settings!", ex);
+            }
+            listener.reshape(settings.getWidth(), settings.getHeight());
+            if (renderable.get()) {
+                reinitContext();
+            } else {
+                assert getType() == Type.Canvas;
+            }
+            logger.fine("Display restarted.");
+        } else if (Display.wasResized()) {
+            int newWidth = Display.getWidth();
+            int newHeight = Display.getHeight();
+            settings.setResolution(newWidth, newHeight);
+            listener.reshape(newWidth, newHeight);
+        }
+
+        super.runLoop();
+    }
+
+    @Override
+    public void restart() {
+        if (created.get()) {
+            needRestart.set(true);
+        } else {
+            logger.warning("Display is not created, cannot restart window.");
+        }
+    }
+
+    @Override
+    public Type getType() {
+        return Type.Display;
+    }
+
+    @Override
+    public void setTitle(String title) {
+        if (created.get()) Display.setTitle(title);
+    }
+
+    private ByteBuffer[] imagesToByteBuffers(Object[] images) {
+        ByteBuffer[] out = new ByteBuffer[images.length];
+        for (int i = 0; i < images.length; i++) {
+            BufferedImage image = (BufferedImage) images[i];
+            out[i] = imageToByteBuffer(image);
+        }
+        return out;
+    }
+
+    private ByteBuffer imageToByteBuffer(BufferedImage image) {
+        if (image.getType() != BufferedImage.TYPE_INT_ARGB_PRE) {
+            BufferedImage convertedImage = new BufferedImage(
+                image.getWidth(),
+                image.getHeight(),
+                BufferedImage.TYPE_INT_ARGB_PRE
+            );
+            Graphics2D g = convertedImage.createGraphics();
+            double width = image.getWidth() * (double) 1;
+            double height = image.getHeight() * (double) 1;
+            g.drawImage(
+                image,
+                (int) ((convertedImage.getWidth() - width) / 2),
+                (int) ((convertedImage.getHeight() - height) / 2),
+                (int) (width),
+                (int) (height),
+                null
+            );
+            g.dispose();
+            image = convertedImage;
+        }
+
+        byte[] imageBuffer = new byte[image.getWidth() * image.getHeight() * 4];
+        int counter = 0;
+        for (int i = 0; i < image.getHeight(); i++) {
+            for (int j = 0; j < image.getWidth(); j++) {
+                int colorSpace = image.getRGB(j, i);
+                imageBuffer[counter + 0] = (byte) ((colorSpace << 8) >> 24);
+                imageBuffer[counter + 1] = (byte) ((colorSpace << 16) >> 24);
+                imageBuffer[counter + 2] = (byte) ((colorSpace << 24) >> 24);
+                imageBuffer[counter + 3] = (byte) (colorSpace >> 24);
+                counter += 4;
+            }
+        }
+        return ByteBuffer.wrap(imageBuffer);
+    }
+
+    @Override
+    public Displays getDisplays() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public int getPrimaryDisplay() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+}

+ 233 - 235
jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java

@@ -1,235 +1,233 @@
-/*
- * Copyright (c) 2009-2020 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.lwjgl;
-
-import com.jme3.input.JoyInput;
-import com.jme3.input.KeyInput;
-import com.jme3.input.MouseInput;
-import com.jme3.input.TouchInput;
-import com.jme3.input.dummy.DummyKeyInput;
-import com.jme3.input.dummy.DummyMouseInput;
-import com.jme3.system.Displays;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import org.lwjgl.LWJGLException;
-import org.lwjgl.Sys;
-import org.lwjgl.opengl.*;
-
-public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
-
-    private static final Logger logger = Logger.getLogger(LwjglOffscreenBuffer.class.getName());
-    private Pbuffer pbuffer;
-    protected AtomicBoolean needClose = new AtomicBoolean(false);
-    private int width;
-    private int height;
-    private PixelFormat pixelFormat;
-
-    protected void initInThread(){
-        if ((Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0){
-            logger.severe("Offscreen surfaces are not supported.");
-            return;
-        }
-
-        int samples = getNumSamplesToUse();
-        pixelFormat = new PixelFormat(settings.getBitsPerPixel(),
-                                      settings.getAlphaBits(),
-                                      settings.getDepthBits(),
-                                      settings.getStencilBits(),
-                                      samples);
-        
-        width = settings.getWidth();
-        height = settings.getHeight();
-        try{
-            Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
-                @Override
-                public void uncaughtException(Thread thread, Throwable thrown) {
-                    listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
-                }
-            });
-
-            pbuffer = new Pbuffer(width, height, pixelFormat, null, null, createContextAttribs());
-            pbuffer.makeCurrent();
-
-            renderable.set(true);
-
-            logger.fine("Offscreen buffer created.");
-            printContextInitInfo();
-        } catch (LWJGLException ex){
-            listener.handleError("Failed to create display", ex);
-        } finally {
-            // TODO: It is possible to avoid "Failed to find pixel format"
-            // error here by creating a default display.
-        }
-        super.internalCreate();
-        listener.initialize();
-    }
-
-    protected boolean checkGLError(){
-        try {
-            Util.checkGLError();
-        } catch (OpenGLException ex){
-            listener.handleError("An OpenGL error has occurred!", ex);
-        }
-        // NOTE: Always return true since this is used in an "assert" statement
-        return true;
-    }
-
-    protected void runLoop(){
-        if (!created.get()) {
-            throw new IllegalStateException();
-        }
-
-        if (pbuffer.isBufferLost()) {
-            pbuffer.destroy();
-
-            try {
-                pbuffer = new Pbuffer(width, height, pixelFormat, null);
-                pbuffer.makeCurrent();
-                
-                // Context MUST be reset here to avoid invalid objects!
-                renderer.invalidateState();
-            } catch (LWJGLException ex) {
-                listener.handleError("Failed to restore PBuffer content", ex);
-            }
-        }
-
-        listener.update();
-        assert checkGLError();
-
-        renderer.postFrame();
-        
-        // Need to flush GL commands 
-        // to see any result on the pbuffer's front buffer.
-        GL11.glFlush();
-
-        int frameRate = settings.getFrameRate();
-        if (frameRate >= 1) {
-            Display.sync(frameRate);
-        }
-    }
-
-    protected void deinitInThread(){
-        renderable.set(false);
-
-        listener.destroy();
-        renderer.cleanup();
-        pbuffer.destroy();
-        logger.fine("Offscreen buffer destroyed.");
-        
-        super.internalDestroy();
-    }
-
-    @Override
-    public void run(){
-        loadNatives();
-        if (logger.isLoggable(Level.FINE)) {
-            logger.log(Level.FINE, "Using LWJGL {0}", Sys.getVersion());
-        }
-        initInThread();
-        while (!needClose.get()){
-            runLoop();
-        }
-        deinitInThread();
-    }
-
-    @Override
-    public void destroy(boolean waitFor){
-        needClose.set(true);
-        if (waitFor)
-            waitFor(false);
-    }
-
-    @Override
-    public void create(boolean waitFor){
-        if (created.get()){
-            logger.warning("create() called when pbuffer is already created!");
-            return;
-        }
-
-        new Thread(this, THREAD_NAME).start();
-        if (waitFor)
-            waitFor(true);
-    }
-
-    @Override
-    public void restart() {
-    }
-
-    @Override
-    public void setAutoFlushFrames(boolean enabled){
-    }
-
-    @Override
-    public Type getType() {
-        return Type.OffscreenSurface;
-    }
-
-    @Override
-    public MouseInput getMouseInput() {
-        return new DummyMouseInput();
-    }
-
-    @Override
-    public KeyInput getKeyInput() {
-        return new DummyKeyInput();
-    }
-
-    @Override
-    public JoyInput getJoyInput() {
-        return null;
-    }
-
-    @Override
-    public TouchInput getTouchInput() {
-        return null;
-    }
-
-    @Override
-    public void setTitle(String title) {
-    }
-
-    @Override
-    public Displays getDisplays() {
-      // TODO Auto-generated method stub
-      return null;
-    }
-
-    @Override
-    public int getPrimaryDisplay() {
-      // TODO Auto-generated method stub
-      return 0;
-    }
-
-}
+/*
+ * Copyright (c) 2009-2020 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.lwjgl;
+
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.TouchInput;
+import com.jme3.input.dummy.DummyKeyInput;
+import com.jme3.input.dummy.DummyMouseInput;
+import com.jme3.system.Displays;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.Sys;
+import org.lwjgl.opengl.*;
+
+public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
+
+    private static final Logger logger = Logger.getLogger(LwjglOffscreenBuffer.class.getName());
+    private Pbuffer pbuffer;
+    protected AtomicBoolean needClose = new AtomicBoolean(false);
+    private int width;
+    private int height;
+    private PixelFormat pixelFormat;
+
+    protected void initInThread() {
+        if ((Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0) {
+            logger.severe("Offscreen surfaces are not supported.");
+            return;
+        }
+
+        int samples = getNumSamplesToUse();
+        pixelFormat =
+            new PixelFormat(
+                settings.getBitsPerPixel(),
+                settings.getAlphaBits(),
+                settings.getDepthBits(),
+                settings.getStencilBits(),
+                samples
+            );
+
+        width = settings.getWidth();
+        height = settings.getHeight();
+        try {
+            Thread.setDefaultUncaughtExceptionHandler(
+                new Thread.UncaughtExceptionHandler() {
+                    @Override
+                    public void uncaughtException(Thread thread, Throwable thrown) {
+                        listener.handleError("Uncaught exception thrown in " + thread.toString(), thrown);
+                    }
+                }
+            );
+
+            pbuffer = new Pbuffer(width, height, pixelFormat, null, null, createContextAttribs());
+            pbuffer.makeCurrent();
+
+            renderable.set(true);
+
+            logger.fine("Offscreen buffer created.");
+            printContextInitInfo();
+        } catch (LWJGLException ex) {
+            listener.handleError("Failed to create display", ex);
+        } finally {
+            // TODO: It is possible to avoid "Failed to find pixel format"
+            // error here by creating a default display.
+        }
+        super.internalCreate();
+        listener.initialize();
+    }
+
+    protected boolean checkGLError() {
+        try {
+            Util.checkGLError();
+        } catch (OpenGLException ex) {
+            listener.handleError("An OpenGL error has occurred!", ex);
+        }
+        // NOTE: Always return true since this is used in an "assert" statement
+        return true;
+    }
+
+    protected void runLoop() {
+        if (!created.get()) {
+            throw new IllegalStateException();
+        }
+
+        if (pbuffer.isBufferLost()) {
+            pbuffer.destroy();
+
+            try {
+                pbuffer = new Pbuffer(width, height, pixelFormat, null);
+                pbuffer.makeCurrent();
+
+                // Context MUST be reset here to avoid invalid objects!
+                renderer.invalidateState();
+            } catch (LWJGLException ex) {
+                listener.handleError("Failed to restore PBuffer content", ex);
+            }
+        }
+
+        listener.update();
+        assert checkGLError();
+
+        renderer.postFrame();
+
+        // Need to flush GL commands
+        // to see any result on the pbuffer's front buffer.
+        GL11.glFlush();
+
+        int frameRate = settings.getFrameRate();
+        if (frameRate >= 1) {
+            Display.sync(frameRate);
+        }
+    }
+
+    protected void deinitInThread() {
+        renderable.set(false);
+
+        listener.destroy();
+        renderer.cleanup();
+        pbuffer.destroy();
+        logger.fine("Offscreen buffer destroyed.");
+
+        super.internalDestroy();
+    }
+
+    @Override
+    public void run() {
+        loadNatives();
+        if (logger.isLoggable(Level.FINE)) {
+            logger.log(Level.FINE, "Using LWJGL {0}", Sys.getVersion());
+        }
+        initInThread();
+        while (!needClose.get()) {
+            runLoop();
+        }
+        deinitInThread();
+    }
+
+    @Override
+    public void destroy(boolean waitFor) {
+        needClose.set(true);
+        if (waitFor) waitFor(false);
+    }
+
+    @Override
+    public void create(boolean waitFor) {
+        if (created.get()) {
+            logger.warning("create() called when pbuffer is already created!");
+            return;
+        }
+
+        new Thread(this, THREAD_NAME).start();
+        if (waitFor) waitFor(true);
+    }
+
+    @Override
+    public void restart() {}
+
+    @Override
+    public void setAutoFlushFrames(boolean enabled) {}
+
+    @Override
+    public Type getType() {
+        return Type.OffscreenSurface;
+    }
+
+    @Override
+    public MouseInput getMouseInput() {
+        return new DummyMouseInput();
+    }
+
+    @Override
+    public KeyInput getKeyInput() {
+        return new DummyKeyInput();
+    }
+
+    @Override
+    public JoyInput getJoyInput() {
+        return null;
+    }
+
+    @Override
+    public TouchInput getTouchInput() {
+        return null;
+    }
+
+    @Override
+    public void setTitle(String title) {}
+
+    @Override
+    public Displays getDisplays() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public int getPrimaryDisplay() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+}

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

@@ -42,15 +42,15 @@ public class LwjglDisplay extends LwjglWindow {
         super(Type.Display);
     }
 
-   @Override
-   public Displays getDisplays() {
-      // TODO Auto-generated method stub
-      return null;
-   }
+    @Override
+    public Displays getDisplays() {
+        // TODO Auto-generated method stub
+        return null;
+    }
 
-   @Override
-   public int getPrimaryDisplay() {
-      // TODO Auto-generated method stub
-      return 0;
-   }
+    @Override
+    public int getPrimaryDisplay() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
 }

+ 253 - 204
jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java

@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2009-2023 jMonkeyEngine
+ * Copyright (c) 2009-2023 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,6 +32,10 @@
 
 package com.jme3.system.lwjgl;
 
+import static org.lwjgl.glfw.GLFW.*;
+import static org.lwjgl.opengl.GL11.GL_FALSE;
+import static org.lwjgl.system.MemoryUtil.NULL;
+
 import com.jme3.input.JoyInput;
 import com.jme3.input.KeyInput;
 import com.jme3.input.MouseInput;
@@ -47,17 +51,6 @@ import com.jme3.system.JmeSystem;
 import com.jme3.system.NanoTimer;
 import com.jme3.util.BufferUtils;
 import com.jme3.util.SafeArrayList;
-
-import org.lwjgl.PointerBuffer;
-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;
@@ -69,10 +62,15 @@ import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-
-import static org.lwjgl.glfw.GLFW.*;
-import static org.lwjgl.opengl.GL11.GL_FALSE;
-import static org.lwjgl.system.MemoryUtil.NULL;
+import org.lwjgl.PointerBuffer;
+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;
 
 /**
  * A wrapper class over the GLFW framework in LWJGL 3.
@@ -84,66 +82,99 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
     private static final Logger LOGGER = Logger.getLogger(LwjglWindow.class.getName());
 
     private static final EnumSet<JmeContext.Type> SUPPORTED_TYPES = EnumSet.of(
-            JmeContext.Type.Display,
-            JmeContext.Type.Canvas,
-            JmeContext.Type.OffscreenSurface);
+        JmeContext.Type.Display,
+        JmeContext.Type.Canvas,
+        JmeContext.Type.OffscreenSurface
+    );
 
     private static final Map<String, Runnable> RENDER_CONFIGS = new HashMap<>();
 
     static {
-        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL30, () -> {
-            // Based on GLFW docs for OpenGL version below 3.2,
-            // GLFW_OPENGL_ANY_PROFILE must be used.
-            glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
-        });
-        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL31, () -> {
-            // Based on GLFW docs for OpenGL version below 3.2,
-            // GLFW_OPENGL_ANY_PROFILE must be used.
-            glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
-        });
-        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL32, () -> {
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
-        });
-        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL33, () -> {
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
-        });
-        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL40, () -> {
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
-        });
-        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL41, () -> {
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
-        });
-        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL42, () -> {
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
-        });
-        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL43, () -> {
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
-        });
-        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL44, () -> {
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
-        });
-        RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL45, () -> {
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
-        });
+        RENDER_CONFIGS.put(
+            AppSettings.LWJGL_OPENGL30,
+            () -> {
+                // Based on GLFW docs for OpenGL version below 3.2,
+                // GLFW_OPENGL_ANY_PROFILE must be used.
+                glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+            }
+        );
+        RENDER_CONFIGS.put(
+            AppSettings.LWJGL_OPENGL31,
+            () -> {
+                // Based on GLFW docs for OpenGL version below 3.2,
+                // GLFW_OPENGL_ANY_PROFILE must be used.
+                glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
+            }
+        );
+        RENDER_CONFIGS.put(
+            AppSettings.LWJGL_OPENGL32,
+            () -> {
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+            }
+        );
+        RENDER_CONFIGS.put(
+            AppSettings.LWJGL_OPENGL33,
+            () -> {
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+            }
+        );
+        RENDER_CONFIGS.put(
+            AppSettings.LWJGL_OPENGL40,
+            () -> {
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+            }
+        );
+        RENDER_CONFIGS.put(
+            AppSettings.LWJGL_OPENGL41,
+            () -> {
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
+            }
+        );
+        RENDER_CONFIGS.put(
+            AppSettings.LWJGL_OPENGL42,
+            () -> {
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+            }
+        );
+        RENDER_CONFIGS.put(
+            AppSettings.LWJGL_OPENGL43,
+            () -> {
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+            }
+        );
+        RENDER_CONFIGS.put(
+            AppSettings.LWJGL_OPENGL44,
+            () -> {
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
+            }
+        );
+        RENDER_CONFIGS.put(
+            AppSettings.LWJGL_OPENGL45,
+            () -> {
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
+            }
+        );
     }
 
     protected final AtomicBoolean needClose = new AtomicBoolean(false);
     protected final AtomicBoolean needRestart = new AtomicBoolean(false);
 
     private final JmeContext.Type type;
-    private final SafeArrayList<WindowSizeListener> windowSizeListeners = new SafeArrayList<>(WindowSizeListener.class);
+    private final SafeArrayList<WindowSizeListener> windowSizeListeners = new SafeArrayList<>(
+        WindowSizeListener.class
+    );
 
     private GLFWErrorCallback errorCallback;
     private GLFWWindowSizeCallback windowSizeCallback;
@@ -170,7 +201,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
     private final Vector2f oldScale = new Vector2f(1, 1);
 
     public LwjglWindow(final JmeContext.Type type) {
-
         if (!SUPPORTED_TYPES.contains(type)) {
             throw new IllegalArgumentException("Unsupported type '" + type.name() + "' provided");
         }
@@ -234,13 +264,16 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
      * @param settings the settings to apply when creating the context.
      */
     protected void createContext(final AppSettings settings) {
-        glfwSetErrorCallback(errorCallback = new GLFWErrorCallback() {
-            @Override
-            public void invoke(int error, long description) {
-                final String message = GLFWErrorCallback.getDescription(description);
-                listener.handleError(message, new Exception(message));
-            }
-        });
+        glfwSetErrorCallback(
+            errorCallback =
+                new GLFWErrorCallback() {
+                    @Override
+                    public void invoke(int error, long description) {
+                        final String message = GLFWErrorCallback.getDescription(description);
+                        listener.handleError(message, new Exception(message));
+                    }
+                }
+        );
 
         if (!glfwInit()) {
             throw new IllegalStateException("Unable to initialize GLFW");
@@ -253,12 +286,18 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
         glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 
-        RENDER_CONFIGS.computeIfAbsent(renderer, s -> () -> {
-            glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE);
-            glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
-            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
-        }).run();
+        RENDER_CONFIGS
+            .computeIfAbsent(
+                renderer,
+                s ->
+                    () -> {
+                        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE);
+                        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
+                        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+                        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+                    }
+            )
+            .run();
 
         if (settings.getBoolean("RendererDebug")) {
             glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
@@ -274,8 +313,14 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         glfwWindowHint(GLFW_STENCIL_BITS, settings.getStencilBits());
         glfwWindowHint(GLFW_SAMPLES, settings.getSamples());
         glfwWindowHint(GLFW_STEREO, settings.useStereo3D() ? GLFW_TRUE : GLFW_FALSE);
-        glfwWindowHint(GLFW_REFRESH_RATE, settings.getFrequency()<=0?GLFW_DONT_CARE:settings.getFrequency());
-        glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, settings.isUseRetinaFrameBuffer() ? GLFW_TRUE : GLFW_FALSE);
+        glfwWindowHint(
+            GLFW_REFRESH_RATE,
+            settings.getFrequency() <= 0 ? GLFW_DONT_CARE : settings.getFrequency()
+        );
+        glfwWindowHint(
+            GLFW_COCOA_RETINA_FRAMEBUFFER,
+            settings.isUseRetinaFrameBuffer() ? GLFW_TRUE : GLFW_FALSE
+        );
 
         if (settings.getBitsPerPixel() == 24) {
             glfwWindowHint(GLFW_RED_BITS, 8);
@@ -289,65 +334,65 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
 
         glfwWindowHint(GLFW_ALPHA_BITS, settings.getAlphaBits());
 
-//        long monitor = NULL;
+        //        long monitor = NULL;
 
         /**
          * Let's grab the display selected, if not found it will return
          * primaryMonitor. if not full screen just use primary display data.
          */
         if (settings.isFullscreen()) {
-           monitor = getDisplay(settings.getDisplay());
+            monitor = getDisplay(settings.getDisplay());
         } else {
-           monitor = glfwGetPrimaryMonitor();
+            monitor = glfwGetPrimaryMonitor();
         }
 
         final GLFWVidMode videoMode = glfwGetVideoMode(monitor);
         int requestWidth = settings.getWindowWidth();
         int requestHeight = settings.getWindowHeight();
         if (requestWidth <= 0 || requestHeight <= 0) {
-           requestWidth = videoMode.width();
-           requestHeight = videoMode.height();
+            requestWidth = videoMode.width();
+            requestHeight = videoMode.height();
         }
-        
+
         // Lets use the monitor selected from AppSettings if FullScreen is
         // set.
-        if (settings.isFullscreen())
-           window = glfwCreateWindow(requestWidth, requestHeight,
-                    settings.getTitle(), monitor, NULL);
-        else
-           window = glfwCreateWindow(requestWidth, requestHeight,
-                    settings.getTitle(), NULL, NULL);
+        if (settings.isFullscreen()) window =
+            glfwCreateWindow(requestWidth, requestHeight, settings.getTitle(), monitor, NULL); else window =
+            glfwCreateWindow(requestWidth, requestHeight, settings.getTitle(), NULL, NULL);
 
         if (window == NULL) {
             throw new RuntimeException("Failed to create the GLFW window");
         }
 
-        glfwSetWindowFocusCallback(window, windowFocusCallback = new GLFWWindowFocusCallback() {
-
-            @Override
-            public void invoke(final long window, final boolean focus) {
-                if (wasActive != focus) {
-                    if (!wasActive) {
-                        listener.gainFocus();
-                        timer.reset();
-                    } else {
-                        listener.loseFocus();
+        glfwSetWindowFocusCallback(
+            window,
+            windowFocusCallback =
+                new GLFWWindowFocusCallback() {
+                    @Override
+                    public void invoke(final long window, final boolean focus) {
+                        if (wasActive != focus) {
+                            if (!wasActive) {
+                                listener.gainFocus();
+                                timer.reset();
+                            } else {
+                                listener.loseFocus();
+                            }
+                            wasActive = !wasActive;
+                        }
                     }
-                    wasActive = !wasActive;
                 }
-            }
-        });
+        );
 
         if (!settings.isFullscreen()) {
             if (settings.getCenterWindow()) {
                 // Center the window
-                glfwSetWindowPos(window,
-                        (videoMode.width() - requestWidth) / 2,
-                        (videoMode.height() - requestHeight) / 2);
+                glfwSetWindowPos(
+                    window,
+                    (videoMode.width() - requestWidth) / 2,
+                    (videoMode.height() - requestHeight) / 2
+                );
             } else {
-                glfwSetWindowPos(window,
-                        settings.getWindowXPosition(),
-                        settings.getWindowYPosition());
+                glfwSetWindowPos(window, settings.getWindowXPosition(), settings.getWindowYPosition());
             }
         }
 
@@ -367,24 +412,30 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         // HACK: the framebuffer seems to be initialized with the wrong size
         // on some HiDPI platforms until glfwPollEvents is called 2 or 3 times
         for (int i = 0; i < 4; i++) glfwPollEvents();
-        
-        // Windows resize callback
-        glfwSetWindowSizeCallback(window, windowSizeCallback = new GLFWWindowSizeCallback() {
 
-            @Override
-            public void invoke(final long window, final int width, final int height) {
-                updateSizes();
-            }
-        });
+        // Windows resize callback
+        glfwSetWindowSizeCallback(
+            window,
+            windowSizeCallback =
+                new GLFWWindowSizeCallback() {
+                    @Override
+                    public void invoke(final long window, final int width, final int height) {
+                        updateSizes();
+                    }
+                }
+        );
 
         // Add a framebuffer resize callback which delegates to the listener
-        glfwSetFramebufferSizeCallback(window, framebufferSizeCallback = new GLFWFramebufferSizeCallback() {
-
-            @Override
-            public void invoke(final long window, final int width, final int height) {
-                updateSizes();
-            }
-        });
+        glfwSetFramebufferSizeCallback(
+            window,
+            framebufferSizeCallback =
+                new GLFWFramebufferSizeCallback() {
+                    @Override
+                    public void invoke(final long window, final int width, final int height) {
+                        updateSizes();
+                    }
+                }
+        );
 
         allowSwapBuffers = settings.isSwapBuffers();
 
@@ -402,8 +453,7 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         glfwGetWindowSize(window, width, height);
         int windowWidth = width[0] < 1 ? 1 : width[0];
         int windowHeight = height[0] < 1 ? 1 : height[0];
-        if (settings.getWindowWidth() != windowWidth
-                || settings.getWindowHeight() != windowHeight) {
+        if (settings.getWindowWidth() != windowWidth || settings.getWindowHeight() != windowHeight) {
             settings.setWindowSize(windowWidth, windowHeight);
             for (WindowSizeListener wsListener : windowSizeListeners.getArray()) {
                 wsListener.onWindowSizeChanged(windowWidth, windowHeight);
@@ -413,8 +463,7 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         glfwGetFramebufferSize(window, width, height);
         int framebufferWidth = width[0];
         int framebufferHeight = height[0];
-        if (framebufferWidth != oldFramebufferWidth
-                || framebufferHeight != oldFramebufferHeight) {
+        if (framebufferWidth != oldFramebufferWidth || framebufferHeight != oldFramebufferHeight) {
             settings.setResolution(framebufferWidth, framebufferHeight);
             listener.reshape(framebufferWidth, framebufferHeight);
 
@@ -441,14 +490,12 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
      * @param settings settings for getting the icons
      */
     protected void setWindowIcon(final AppSettings settings) {
-
         final Object[] icons = settings.getIcons();
         if (icons == null) return;
 
         final GLFWImage[] images = imagesToGLFWImages(icons);
 
         try (final GLFWImage.Buffer iconSet = GLFWImage.malloc(images.length)) {
-
             for (int i = images.length - 1; i >= 0; i--) {
                 final GLFWImage image = images[i];
                 iconSet.put(i, image);
@@ -462,7 +509,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
      * Convert array of images to array of {@link GLFWImage}.
      */
     private GLFWImage[] imagesToGLFWImages(final Object[] images) {
-
         final GLFWImage[] out = new GLFWImage[images.length];
 
         for (int i = 0; i < images.length; i++) {
@@ -477,10 +523,12 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
      * Convert the {@link BufferedImage} to the {@link GLFWImage}.
      */
     private GLFWImage imageToGLFWImage(BufferedImage image) {
-
         if (image.getType() != BufferedImage.TYPE_INT_ARGB_PRE) {
-
-            final BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
+            final BufferedImage convertedImage = new BufferedImage(
+                image.getWidth(),
+                image.getHeight(),
+                BufferedImage.TYPE_INT_ARGB_PRE
+            );
             final Graphics2D graphics = convertedImage.createGraphics();
 
             final int targetWidth = image.getWidth();
@@ -522,7 +570,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
             }
 
             if (errorCallback != null) {
-
                 // We need to specifically set this to null as we might set a new callback before we reinit GLFW
                 glfwSetErrorCallback(null);
 
@@ -549,7 +596,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
                 glfwDestroyWindow(window);
                 window = NULL;
             }
-
         } catch (final Exception ex) {
             listener.handleError("Failed to destroy context", ex);
         }
@@ -577,7 +623,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
                 waitFor(true);
             }
         }
-
     }
 
     /**
@@ -589,14 +634,16 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         try {
             if (!JmeSystem.isLowPermissions()) {
                 // Enable uncaught exception handler only for current thread
-                Thread.currentThread().setUncaughtExceptionHandler((thread, thrown) -> {
-                    listener.handleError("Uncaught exception thrown in " + thread.toString(), thrown);
-                    if (needClose.get()) {
-                        // listener.handleError() has requested the
-                        // context to close. Satisfy request.
-                        deinitInThread();
-                    }
-                });
+                Thread
+                    .currentThread()
+                    .setUncaughtExceptionHandler((thread, thrown) -> {
+                        listener.handleError("Uncaught exception thrown in " + thread.toString(), thrown);
+                        if (needClose.get()) {
+                            // listener.handleError() has requested the
+                            // context to close. Satisfy request.
+                            deinitInThread();
+                        }
+                    });
             }
 
             timer = new NanoTimer();
@@ -630,7 +677,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         return true;
     }
 
-
     /**
      * execute one iteration of the render loop in the OpenGL thread
      */
@@ -644,7 +690,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
             throw new IllegalStateException();
         }
 
-
         listener.update();
 
         // All this does is call glfwSwapBuffers().
@@ -721,8 +766,9 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
     @Override
     public void run() {
         if (listener == null) {
-            throw new IllegalStateException("SystemListener is not set on context!"
-                    + "Must set with JmeContext.setSystemListener().");
+            throw new IllegalStateException(
+                "SystemListener is not set on context!" + "Must set with JmeContext.setSystemListener()."
+            );
         }
 
         LOGGER.log(Level.FINE, "Using LWJGL {0}", Version.getVersion());
@@ -733,7 +779,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         }
 
         while (true) {
-
             runLoop();
 
             if (needClose.get()) {
@@ -873,76 +918,80 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
         int result = height[0];
         return result;
     }
-    
+
     /**
      * Returns the Primary Monitor position number from the list of monitors
      * returned by glfwGetPrimaryMonitor().  If primary monitor not found
      * it will return -1 and report the error.
-     * 
+     *
      * @return returns the Primary Monitor Position.
      */
     @Override
-    public int getPrimaryDisplay()
-    {
-       long prim = glfwGetPrimaryMonitor();
-       Displays monitors = getDisplays();
-       for ( int i = 0; i < monitors.size(); i++ ) {
-          long monitorI = monitors.get(i).displayID;
-          if (monitorI == prim)
-             return i;
-       }
-       
-       LOGGER.log(Level.SEVERE,"Couldn't locate Primary Monitor in the list of Monitors.");
-       return -1;
-    }
-    
-    
+    public int getPrimaryDisplay() {
+        long prim = glfwGetPrimaryMonitor();
+        Displays monitors = getDisplays();
+        for (int i = 0; i < monitors.size(); i++) {
+            long monitorI = monitors.get(i).displayID;
+            if (monitorI == prim) return i;
+        }
+
+        LOGGER.log(Level.SEVERE, "Couldn't locate Primary Monitor in the list of Monitors.");
+        return -1;
+    }
+
     /**
      * This routines return the display ID by position in an array of display returned
      * by glfwGetMonitors().
-     * 
+     *
      * @param pos  the position of the display in the list of displays returned.
      * @return return the displayID if found otherwise return Primary display
      */
     private long getDisplay(int pos) {
-       Displays displays = getDisplays();
-       if (pos < displays.size())
-          return displays.get(pos).displayID;
-       
-       LOGGER.log(Level.SEVERE,"Couldn't locate Display requested in the list of Displays. pos:"+pos+" size: "+ displays.size());
-       return glfwGetPrimaryMonitor();
-    }
-    
+        Displays displays = getDisplays();
+        if (pos < displays.size()) return displays.get(pos).displayID;
+
+        LOGGER.log(
+            Level.SEVERE,
+            "Couldn't locate Display requested in the list of Displays. pos:" +
+            pos +
+            " size: " +
+            displays.size()
+        );
+        return glfwGetPrimaryMonitor();
+    }
+
     /**
      * This returns an arraylist of all the Display returned by OpenGL get Monitor
      * call.  It will also has some limited information about each display, like:
      * width, height and refresh rate.
-     * 
+     *
      * @return returns an ArrayList of all Display returned by glfwGetMonitors()
      */
-    
+
     @Override
-    public Displays getDisplays()  {
-       PointerBuffer displays = glfwGetMonitors();
-       long primary = glfwGetPrimaryMonitor();
-       Displays displayList = new Displays();
-       
-       for ( int i = 0; i < displays.limit(); i++ ) {
-           long monitorI = displays.get(i);
-           int monPos = displayList.addNewMonitor(monitorI);
-           //lets check if this display is the primary display. If use mark it as such.
-           if (primary == monitorI)
-              displayList.setPrimaryDisplay(monPos);
-           
-           final GLFWVidMode modes = glfwGetVideoMode(monitorI);
-           String name = glfwGetMonitorName(monitorI);
-
-           int width = modes.width();
-           int height = modes.height();
-           int rate = modes.refreshRate();
-           displayList.setInfo(monPos, name, width, height, rate);
-           LOGGER.log(Level.INFO, "Display id: "+monitorI+" Resolution: " + width + " x " + height + " @ " + rate);
-        }
-        return displayList;    
+    public Displays getDisplays() {
+        PointerBuffer displays = glfwGetMonitors();
+        long primary = glfwGetPrimaryMonitor();
+        Displays displayList = new Displays();
+
+        for (int i = 0; i < displays.limit(); i++) {
+            long monitorI = displays.get(i);
+            int monPos = displayList.addNewMonitor(monitorI);
+            //lets check if this display is the primary display. If use mark it as such.
+            if (primary == monitorI) displayList.setPrimaryDisplay(monPos);
+
+            final GLFWVidMode modes = glfwGetVideoMode(monitorI);
+            String name = glfwGetMonitorName(monitorI);
+
+            int width = modes.width();
+            int height = modes.height();
+            int rate = modes.refreshRate();
+            displayList.setInfo(monPos, name, width, height, rate);
+            LOGGER.log(
+                Level.INFO,
+                "Display id: " + monitorI + " Resolution: " + width + " x " + height + " @ " + rate
+            );
+        }
+        return displayList;
     }
 }

+ 68 - 67
jme3-vr/src/main/java/com/jme3/system/lwjgl/LwjglDisplayVR.java

@@ -1,67 +1,68 @@
-/*
- * Copyright (c) 2009-2012 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.lwjgl;
-
-import com.jme3.opencl.Context;
-import com.jme3.system.Displays;
-
-/**
- * A VR oriented LWJGL display.
- * @author Daniel Johansson
- * @author reden - phr00t - https://github.com/phr00t
- * @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
- */
-public class LwjglDisplayVR extends LwjglWindowVR {
-    /**
-     * Create a new VR oriented LWJGL display.
-     */
-    public LwjglDisplayVR() {
-        super(Type.Display);
-    }
-
-    @Override
-    public Context getOpenCLContext() {
-        return null;
-    }
-
-    @Override
-    public Displays getDisplays() {
-      // TODO Auto-generated method stub
-      return null;
-    }
-
-    @Override
-    public int getPrimaryDisplay() {
-      // TODO Auto-generated method stub
-      return 0;
-    }
-}
+/*
+ * Copyright (c) 2009-2012 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.lwjgl;
+
+import com.jme3.opencl.Context;
+import com.jme3.system.Displays;
+
+/**
+ * A VR oriented LWJGL display.
+ * @author Daniel Johansson
+ * @author reden - phr00t - https://github.com/phr00t
+ * @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
+ */
+public class LwjglDisplayVR extends LwjglWindowVR {
+
+    /**
+     * Create a new VR oriented LWJGL display.
+     */
+    public LwjglDisplayVR() {
+        super(Type.Display);
+    }
+
+    @Override
+    public Context getOpenCLContext() {
+        return null;
+    }
+
+    @Override
+    public Displays getDisplays() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public int getPrimaryDisplay() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+}