jmonkeyengine
jmonkeyengine copied to clipboard
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.
Thank you for implementing the feature for us.
I don't have a multi-monitor setup myself, so it didn't occur to me that this might be wanted. I assumed it would usually be easier to create windows in the primary display and use a window manager to move them to where they're wanted. Is that not the case?
I don't understand why this PR modifies TestResizableApp. Please explain.
There are style violations in the added files: javadoc issues, braces missing or positioned unconventionally, 3-space indentation of code blocks, extra blank lines in import sections, and so on. Please review our coding style and revise accordingly.
Also I think the added files should have 2023 copyright dates.
I don't have a multi-monitor setup myself, so it didn't occur to me that this might be wanted. I assumed it would usually be easier to create windows in the primary display and use a window manager to move them to where they're wanted. Is that not the case?
I think it is impossible in many windowing systems. This is the way to go.
Do modern games usually let you select the monitor from the game settings?
Edit: Also, from a quick look at this PR seems the selected monitor will be used only when running in fullscreen while using the primary monitor for non-fullscreen modes?
Yes, this is for full screen only. You can't select a monitor for "WINDOW", it is forced to "primary OS monitor". When doing full screen, if you don't select it will do it on primary. If your primary is the window you want, then it is fine. But for it to launch and then move a FULL SCREEN is not nice. When you can cause it to launch on the correct window.
I'll update the copyright.
Forgot.. TestResizableMonitor was failing after creating a window in full screen when using a different monitor. "reshape" function was called before "SimpleInitApp()" was called when creating a window other than "NULL". I put a check in there to not update label. So 'txt' variable was "NULL" because it had not called simpleInitApp(), yet.
I didn't look into way JME was calling reship before it did the initialization.
I updated the comments. Let me know if you find anything else.
I missed the LegacyApplication update for getMonitors()
As a general comment, this new API feels a little weird to me. Having a Monitors class that is essentially just a list of monitor infos with one odd control method feels off. Even weirder if this is ever extended to provide display information about the monitors other than "what it is right now".
What I would have expected from this feature is some way to ask JME what monitors are available that returns a List of info objects... then some method to set the current monitor on the same thing I asked for the list of monitors.
something like:
JmeSystem.getMonitorInfos() : List<MonitorInfo>
JmeSystem.setCurrentMonitor(MonitorInfo)
And even that might be a dead-end design if we ever want to support changing things about a particular monitor (like current display settings or whatever).
Instead of MonitorInfo we could have a Monitor interface with the getters (no setters necessary) then different system implementations can implement the Monitor interface as they see fit. (An abstract version could be provided to make this easier for the system implementations that only support one display... and perhaps Display is a better name than Monitor.)
So:
JmeSystem.getAvailableDisplays() : List<Display>
JmeSystem.setCurrentDisplay(Display)
Regardless of all that, a MonitorInfo class with all public fields becomes a maintenance burden forever and should be avoided.
@pspeed42,
I thought about just capturing the list of all the available mode that monitor support and add them to the list. I didn't do this because when I run it on my both of my 2k monitors, it shows the max being 800x600. when it is 2k. It doesn't report back (At least my monitors) accurately.
Also, the monitor names come from Windows and even though I have Acer monitors, the driver comes up generic monitor.
The monitors do 2k 170hz on both of them, but they report is not even close. So I thought if we listed resolution, people might think that is the only option because it doesn't report correctly.
I can change the Monitor classes, I could create getter/setters and even a interface class. That would all be easy.
Doing a setCurrentDisplay() would be very hard to do. The monitor selection is a "WINDOW CREATION" so this is before you get into the App simpleInitApp(). So the only option to set which monitor in full screen is through the ini. A restart of the application is require after you allow user to select a monitor. I didn't see an LWJGL command to move monitor to another monitor, just during window creation.
Let me know if someone knows something I don't and I can look into it and change it.
The point about the resolution settings is that someday the OS/drivers/etc. may fix those issues and those APIs would work like we expect... so it's better not to automatically design dead-ends into JME's API.
As to setCurrentDisplay(), I was confused by this which seemed to be essentially doing the same thing: public void setPrimaryMonitor(int monPos)
But maybe change what I said to setPrimaryDisplay() and it makes more sense? Maybe it should be on AppSettings?
Either way, I prefer a "request the available options" and "set the option I want" API versus a "get the available options and somehow call something on those options to set my option"
And having setters on MonitorInfo is probably confusing when they don't really do anything except let callers mess up the values.
Thanks for the work so far. I don't mean to be picking on every thing. I'm just trying to forestall some future maintenance burden.
@pspeed42, Those things make sense, The Monitors::setPrimaryMonitor() is misleading. It is more "Mark monitor as the active monitor". I can change it to be setActiveMonitor(). Which is more accurate.
There is no way to do a "setPrimaryDisplay()" sense it is before the simpleInitApp().
All the calls to getMonitors() and such is "AFTER" the fact. It only use is for programmers to get a list of monitors if they wish to use it in "settings" gui to list the monitor to use on "Next" launch.
I have no problem with you making suggestions or even demands to fit JME. I don't write JME and don't know the true layout.
So I can change Monitors:setPrimaryMonitor() to Monitors::setActiveMonitor(). That way if the game grabs monitors again, it will reflect the current monitor that game uses on boot and they could display that if they wish.
Calling AppSettings::setMonitor() updates the "App Ini settings". The game could then capture the close and write out ini so it is updated, or could write the ini in the gui control when it updates the monitor.
The objects returned from getMonitors/Displays/Whatever should be immutable objects with no setters on them. There should be no Monitors class as it is unnecessary. A list is fine. "Monitor" is also a limiting name since a Monitor is a Display but not all Displays are "monitors".
We should find a way to fix JME so that we can make platform specific queries before starting JME. Else we end up with cases where the user never gets to see the app because the OS chose to put it on a display that isn't even turned on.
Setting which display to use should be done on AppSettings. That is the "JME way", I guess... but we need a platform-independent way of reliably querying the available displays (and even the available resolutions... which we currently do not have either). That last bit is kind of a larger issue with how JME is setup.
I used "Monitor" because that is the reference inside OPENGL. It is called a monitor in the code.
I could change everything related to "Monitor" to "Display" to fix that idea of "Not a Monitor" Selecting which display to use is through "AppSettings", calling setMonitor() (change it to setDisplay). Since it is part of the AppSettings those values can be saved and reused on launch.
I have a check inside jme3-lwjgl3::LwjglWindow::createContext(). it checks if the number of the display doesn't show up in the list of available displays then use the "primaryMonitor" instead.
Now since OPENGL lwjgl3 call to get monitors sends a new number each time that application is launched and programmers will never be able to guarantee it is the same monitor. So if a person changed their primary monitor. Then next time, it will open on another monitor. We only get a list of monitor and the ID changes all the time. So we can't determine which monitor it is.
Probably even OpenGL doesn't call it a monitor on mobile devices... but I haven't looked.
Is the last paragraph new information or in response to something we were discussing?
OpenGL does call it monitors.
https://www.glfw.org/docs/3.3/group__window.html#ga3555a418df92ad53f917597fe2f64aeb
The comment is related to you comments about a window opening up on a monitor that is not there anymore or turned on. The best we could do is if you select monitor 1 (0-1) and when game launches, we could default it to 0. But if you have 3 monitors 0-2 and you select 1. Then remove your 2nd monitor, so the 3rd monitor becomes the 2nd monitor. There is no way in code, to determine that is a different monitor now.
GLFW = "for windows" not mobile. So is still going to be very PC-monitor specific. FWIW, OpenGL-ES calls it a "surface".
Interesting, but this is not for Android. The only support is for PC. I do not code for mobile and don't know enough to write something that would support mobile.
But again, I do not see support for multiple monitors in mobiles. So this feature is basically for PC not other devices. That is why if you call monitor functions, you get either NULL or 0, because it is not supported.
What interface are you putting this method on? I thought it was shared across all platforms JME supports and so it would be nice if it didn't have PC-specific stuff hard-coded onto it.
If you want to make some specific windows-style-os-specific new class to handle "the window-bearing subset" of platforms that JME supports then so be it. Otherwise, we should code the "all platforms" API as if someone someday will do something other than UnsupportedOperationException for the platforms you don't develop implementations for.
As API developers, we need to think about the future. Maybe in two years flip phones have two screens again and treat them as separate displays.
Then again, as already alluded to, JME provides no way to query anything about the current display settings, available display settings, or anything prior to starting the application... and that would be a really useful thing to have and maybe IS windows-style-os-specific.
But again, I do not see support for multiple monitors in mobiles. So this feature is basically for PC not other devices.
Android already supports Multiple Displays, but it's a part of Android core hardware not GLES specifically, you have to be careful developing this API to build it across the JME Cross platform interface, I suggest we continue on forums.
EDIT:
I suggest to change Monitor keyword to Display to be more compatible with Mobile Devices supporting HDMI and everyone will be happy I guess 😁.
@Scrappers-glitch, just got to ask. How is handling it the way Mobile devices more compatible, when desktops call it Monitors under OpenGL. There is not a common verbiage.
How about "screen" it something that is not really in either. Desktop uses Monitor, Mobile uses Displays.
Let's come at this from a different direction: why do you have such a huge problem with the word "display"?
(Mobile calls them 'surfaces', by the way. The trick is to come up with a word that can apply to all things that 'display' graphics... whether monitor, screen, surface, projector, laser array, HMD, whatever.)
@Scrappers-glitch, just got to ask. How is handling it the way Mobile devices more compatible, when desktops call it Monitors under OpenGL. There is not a common verbiage.
Display is an abstract keyword, both Graphics Monitor and Screen are types of Display, that's why I suggested Display too at the first place.
Give me a couple of days, life, and I'll get it changed to display.
Sorry, been away for sometime. Work and the farm taking up all the spare time. I just uploaded the changes to "Devices".
I didn't mean to close this.
My branch was old, and merged it to current, but not sure if I missed something. Please let me know, and I'll fix it.
What is the difference between Display and Monitor? They look copy paste to me.
A Display is something capable of displaying graphics. A Monitor is the big giant display sitting on your desk... which is by definition a subset of Display.
Was this about code or the naming conversation?
Did github autoformat everything again? Going to take some extra time to review with all of the non-changes mixed in.