Pārlūkot izejas kodu

Add support for Multiple Monitors in jme-LWJGL3 #2030

Add support for multiple monitors.
Add a feature so that when a "Full Screen" window is created, that you can tell it which monitor to create the window on.
Add a feature so that the application can call context to get a list of monitors that OPENGL found. It returns them in an ArrayList so that the programmers can select a monitor from the list. JME will take the pos of the monitor from the arraylist to get its handle. So if you have 2 monitors, you will have 2 in the list. So to tell JME which monitor to create the window on it would be 0 or 1. The array position in the list.

The thought behind this is the program gets a list of monitors and then they can use that list in their settings for the user to select which monitor to us.
Since the ID of the monitor changes between each launch, I went with the position from the arraylist that it returned. So many if user changes the order of the monitors then the program will launch on a different screen. Minor.

Added in AppSettings a way to get/set Monitor. Monitor value is used only when creating a Full Screen.
KEVIN-DESKTOP\kevinba 2 gadi atpakaļ
vecāks
revīzija
acd2cb304d

+ 14 - 0
jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java

@@ -554,4 +554,18 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
         Rect result = holder.getSurfaceFrame();
         return result;
     }
+
+	@Override
+	public Monitors getMonitors()
+	{
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public int getPrimaryMonitor()
+	{
+		// TODO Auto-generated method stub
+		return 0;
+	}
 }

+ 29 - 0
jme3-core/src/main/java/com/jme3/system/AppSettings.java

@@ -266,6 +266,7 @@ public final class AppSettings extends HashMap<String, Object> {
     public static final String JOAL = "JOAL";
 
     static {
+        defaults.put("Monitor", 0);
         defaults.put("CenterWindow", true);
         defaults.put("Width", 640);
         defaults.put("Height", 480);
@@ -1479,4 +1480,32 @@ public final class AppSettings extends HashMap<String, Object> {
     public void setWindowYPosition(int pos) {
         putInteger("WindowYPosition", pos);
     }
+    
+    
+    /**
+     * Gets the monitor number used when creating a window.
+     *
+     * <p>This setting is used only with LWJGL3, it defines which monitor
+     * to use when creating a OpenGL window.
+     *
+     * @return the desired monitor used when creating a OpenGL window
+     * @see #setMonitor(long)
+     */
+    public int getMonitor() {
+        return getInteger("Monitor");
+    }
+
+    /**
+     * Sets the monitor number used when creating a window.  The position
+     * number is the number in the list of monitors GlfwGetMonitors returns.
+     *
+     * <p>This setting is used only with LWJGL3, it defines which monitor
+     * to use when creating a OpenGL window. its default value is -1.
+     *
+     * @param mon the desired monitor used when creating a OpenGL window
+     * 
+     */
+    public void setMonitor(int mon) {
+        putInteger("Monitor", mon);
+    }
 }

+ 18 - 0
jme3-core/src/main/java/com/jme3/system/JmeContext.java

@@ -225,4 +225,22 @@ public interface JmeContext {
      * @throws IllegalStateException for a headless or null context
      */
     public int getWindowYPosition();
+
+    /**
+     * 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 Monitors getMonitors();
+
+    /**
+     * 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 getPrimaryMonitor();
 }

+ 73 - 0
jme3-core/src/main/java/com/jme3/system/MonitorInfo.java

@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009-2022 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;
+
+/**
+ * This class holds information about the monitor that was
+ * returned by glfwGetMonitors() calls in the context
+ * class
+ * 
+ * @author Kevin Bales
+ */
+public class MonitorInfo {
+   
+   /**
+    * monitorID - monitor id that was return from Lwjgl3.
+    */
+   public long monitorID = 0;
+
+   /**
+    * width - width that was return from Lwjgl3.
+    */
+   public int width = 1080;
+
+   /**
+    * height - height that was return from Lwjgl3.
+    */
+   public int height = 1920;
+
+   /**
+    * rate - refresh rate that was return from Lwjgl3.
+    */
+   public int rate = 60;
+   
+   /**
+    * primary - indicates if the monitor is the primary monitor.
+    */
+   public boolean primary = false;
+   
+   /**
+    * name - monitor name that was return from Lwjgl3.
+    */
+   public String name = "Generic Monitor";
+
+}

+ 119 - 0
jme3-core/src/main/java/com/jme3/system/Monitors.java

@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2009-2022 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;
+
+import java.util.ArrayList;
+
+/**
+ * This class holds all information about all monitors that where
+ * return from the glfwGetMonitors() call.  It stores them into
+ * an <ArrayList>
+ * 
+ * @author Kevin Bales
+ */
+public class Monitors {
+
+   private ArrayList<MonitorInfo> monitors = new ArrayList<MonitorInfo>();
+   
+   public int addNewMonitor(long monitorID)
+   {
+      MonitorInfo info = new MonitorInfo();
+      info.monitorID = monitorID;
+      monitors.add(info);
+      return monitors.size() - 1;
+   }
+   
+   /**
+    * This function returns the size of the monitor ArrayList
+    * @return the 
+    */
+   public int size()
+   {
+      return monitors.size();
+   }
+   
+   /**
+    * Call to get monitor information on a certain monitor.
+    * 
+    * @param pos the position in the arraylist of the monitor
+    *        information that you want to get.
+    * @return  returns the MonitorInfo data for the monitor
+    *          called for.
+    */
+   public MonitorInfo get(int pos)
+   {
+      if (pos < monitors.size())
+         return monitors.get(pos);
+      
+      return null;
+   }
+
+   /**
+    * Set information about this monitor stored in monPos position
+    * in the array list.
+    * 
+    * @param monPos  arraylist position of monitor to update
+    * @param width   the current width the monitor is displaying
+    * @param height  the current height the monitor is displaying
+    * @param rate    the current refresh rate the monitor is set to
+    */
+   public void setInfo(int monPos, String name, int width, int height, int rate) {
+      if (monPos < monitors.size() ) {
+         MonitorInfo info = monitors.get(monPos);
+         if (info != null) {
+            info.width = width;
+            info.height = height;
+            info.rate = rate;
+            info.name = name;
+         }
+      }
+   }
+
+   /**
+    * This function will mark a certain monitor as the
+    * primary monitor.
+    *  
+    * @param monPos the position in the arraylist of which
+    *        monitor is the primary monitor
+    */
+   public void setPrimaryMonitor(int monPos) {
+      if (monPos < monitors.size() ) {
+         MonitorInfo info = monitors.get(monPos);
+         if (info != null)
+            info.primary = true;
+      }
+      
+   }
+   
+   
+
+}

+ 12 - 0
jme3-core/src/main/java/com/jme3/system/NullContext.java

@@ -306,4 +306,16 @@ public class NullContext implements JmeContext, Runnable {
     public int getWindowYPosition() {
         throw new UnsupportedOperationException("null context");
     }
+
+   @Override
+   public Monitors getMonitors() {
+      // TODO Auto-generated method stub
+      return null;
+   }
+
+   @Override
+   public int getPrimaryMonitor() {
+      // TODO Auto-generated method stub
+      return 0;
+   }
 }

+ 12 - 0
jme3-desktop/src/main/java/com/jme3/system/AWTContext.java

@@ -275,4 +275,16 @@ public class AWTContext implements JmeContext {
     public int getWindowYPosition() {
         throw new UnsupportedOperationException("not implemented yet");
     }
+
+   @Override
+   public Monitors getMonitors() {
+      // TODO Auto-generated method stub
+      return null;
+   }
+
+   @Override
+   public int getPrimaryMonitor() {
+      // TODO Auto-generated method stub
+      return 0;
+   }
 }

+ 12 - 0
jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanelsContext.java

@@ -328,4 +328,16 @@ public class AwtPanelsContext implements JmeContext {
     public int getWindowYPosition() {
         return inputSource.getY();
     }
+
+   @Override
+   public Monitors getMonitors() {
+      // TODO Auto-generated method stub
+      return null;
+   }
+
+   @Override
+   public int getPrimaryMonitor() {
+      // TODO Auto-generated method stub
+      return 0;
+   }
 }

+ 14 - 0
jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java

@@ -267,4 +267,18 @@ public class IGLESContext implements JmeContext {
     public int getWindowYPosition() {
         throw new UnsupportedOperationException("not implemented yet");
     }
+
+	@Override
+	public Monitors getMonitors()
+	{
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public int getPrimaryMonitor()
+	{
+		// TODO Auto-generated method stub
+		return 0;
+	}
 }

+ 15 - 0
jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java

@@ -36,6 +36,7 @@ import com.jme3.system.AppSettings;
 import com.jme3.system.JmeCanvasContext;
 import com.jme3.system.JmeContext.Type;
 import com.jme3.system.JmeSystem;
+import com.jme3.system.Monitors;
 import com.jme3.system.Platform;
 import java.awt.Canvas;
 import java.util.logging.Level;
@@ -502,4 +503,18 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
             // TODO: Fix deadlock that happens after the error (throw runtime exception?)
         }
     }
+
+	@Override
+	public Monitors getMonitors()
+	{
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public int getPrimaryMonitor()
+	{
+		// TODO Auto-generated method stub
+		return 0;
+	}
 }

+ 16 - 0
jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java

@@ -34,6 +34,8 @@ package com.jme3.system.lwjgl;
 
 import com.jme3.system.AppSettings;
 import com.jme3.system.JmeContext.Type;
+import com.jme3.system.Monitors;
+
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 import java.nio.ByteBuffer;
@@ -282,4 +284,18 @@ public class LwjglDisplay extends LwjglAbstractDisplay {
         return ByteBuffer.wrap(imageBuffer);
     }
 
+	@Override
+	public Monitors getMonitors()
+	{
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public int getPrimaryMonitor()
+	{
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
 }

+ 16 - 0
jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java

@@ -38,6 +38,8 @@ 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.Monitors;
+
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -218,4 +220,18 @@ public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
     public void setTitle(String title) {
     }
 
+	@Override
+	public Monitors getMonitors()
+	{
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public int getPrimaryMonitor()
+	{
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
 }

+ 96 - 5
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
@@ -43,9 +43,12 @@ import com.jme3.math.Vector2f;
 import com.jme3.system.AppSettings;
 import com.jme3.system.JmeContext;
 import com.jme3.system.JmeSystem;
+import com.jme3.system.Monitors;
 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;
@@ -58,9 +61,11 @@ import org.lwjgl.system.Platform;
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -283,21 +288,34 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
 
         glfwWindowHint(GLFW_ALPHA_BITS, settings.getAlphaBits());
 
-        // TODO: Add support for monitor selection
         long monitor = NULL;
 
+        /** 
+         * Let's grab the monitor selected, if not found it will return primaryMonitor.
+         * if not full screen just use primary monitor data.
+         */
         if (settings.isFullscreen()) {
-            monitor = glfwGetPrimaryMonitor();
+           monitor = getMonitor(settings.getMonitor());
+        } else {
+           monitor = glfwGetPrimaryMonitor();
         }
 
-        final GLFWVidMode videoMode = glfwGetVideoMode(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();
         }
-        window = glfwCreateWindow(requestWidth, requestHeight, settings.getTitle(), monitor, NULL);
+        
+        //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 (window == NULL) {
             throw new RuntimeException("Failed to create the GLFW window");
         }
@@ -853,4 +871,77 @@ 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 getPrimaryMonitor()
+    {
+       long prim = glfwGetPrimaryMonitor();
+       Monitors monitors = getMonitors();
+       for ( int i = 0; i < monitors.size(); i++ ) {
+          long monitorI = monitors.get(i).monitorID;
+          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 monitor ID by position in an array of monitors returned
+     * by glfwGetMonitors().
+     * 
+     * @param pos  the position of the monitor in the list of monitors returned.
+     * @return return the monitorID if found otherwise return Primary Monitor
+     */
+    private long getMonitor(int pos) {
+       Monitors monitors = getMonitors();
+       if (pos < monitors.size())
+          return monitors.get(pos).monitorID;
+       
+       LOGGER.log(Level.SEVERE,"Couldn't locate Monitor requested in the list of Monitors. pos:"+pos+" size: "+ monitors.size());
+       return glfwGetPrimaryMonitor();
+    }
+    
+    /**
+     * This returns an arraylist of all the monitors returned by OpenGL get Monitor
+     * call.  It will also has some limited information about each monitor, like:
+     * width, height and refresh rate.
+     * 
+     * @return returns an ArrayList of all Monitors returned by glfwGetMonitors()
+     */
+    
+    @Override
+    public Monitors getMonitors()
+    {
+       PointerBuffer monitors = glfwGetMonitors();
+       long primary = glfwGetPrimaryMonitor();
+       Monitors monitorList = new Monitors();
+       
+       for ( int i = 0; i < monitors.limit(); i++ ) {
+           long monitorI = monitors.get(i);
+           int monPos = monitorList.addNewMonitor(monitorI);
+           //lets check if this monitor is the primary monitor. If use mark it as such.
+           if (primary == monitorI)
+              monitorList.setPrimaryMonitor(monPos);
+           
+           final GLFWVidMode modes = glfwGetVideoMode(monitorI);
+           String name = glfwGetMonitorName(monitorI);
+
+           int width = modes.width();
+           int height = modes.height();
+           int rate = modes.refreshRate();
+           monitorList.setInfo(monPos, name, width, height, rate);
+           LOGGER.log(Level.INFO, "Monitor id: "+monitorI+" Resolution: " + width + " x " + height + " @ " + rate);
+        }
+        return monitorList;    
+    }
 }

+ 15 - 0
jme3-vr/src/main/java/com/jme3/system/lwjgl/LwjglDisplayVR.java

@@ -32,6 +32,7 @@
 package com.jme3.system.lwjgl;
 
 import com.jme3.opencl.Context;
+import com.jme3.system.Monitors;
 
 /**
  * A VR oriented LWJGL display.
@@ -51,4 +52,18 @@ public class LwjglDisplayVR extends LwjglWindowVR {
     public Context getOpenCLContext() {
         return null;
     }
+
+	@Override
+	public Monitors getMonitors()
+	{
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public int getPrimaryMonitor()
+	{
+		// TODO Auto-generated method stub
+		return 0;
+	}
 }