Querying and Configuring Graphics (monitors, display modes, vsync, display cutouts)

Configuration & querying

libGDX has an elaborate API that lets you query monitors and display modes, and toggle vertical synchronization (vsync). This can be done either when configuring your application, or at runtime. Note: Display mode changes are not supported by Android, iOS or HTML5.

Querying and setting monitors & display modes at configuration time

Querying monitors and display modes at configuration time is platform specific. The following subsections illustrate what you can do on each platform with regards to monitors and display modes.

LWJGL backend

Using the default LWJGL 2 backend, you can get the available display modes for the primary monitor as follows:

DisplayMode[] modes = LwjglApplicationConfiguration.getDisplayModes();

You can get the current display mode of the primary monitor (also known as desktop mode) as follows:

DisplayMode desktopMode = LwjglApplicationConfiguration.getDesktopDisplayMode();

Once you have a DisplayMode, you can set it on the LwjglApplicationConfiguration:

DisplayMode displayMode = LwjglApplicationConfiguration.getDesktopDisplayMode();
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.setFromDisplayMode(displayMode);
new LwjglApplication(new MyAppListener(), config);

Your app will be started in full-screen mode, using the resolution found in the DisplayMode object.

To start your app in windowed mode, simply specify the width and height of the window:

LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.width = 800;
config.height = 600;
new LwjglApplication(new MyAppListener(), config);

You can also position the window on the primary monitor by specifying its top-left corner coordinate relative to the monitors current display mode:

config.x = 100;
config.y = 100;

To center your window, use -1 for the coordinates (the default).

Finally, you can also specify whether your app should start with vsync enabled or not:

config.vSyncEnabled = true;

Disabling vsync will allow your your app to run above your monitor’s refresh rate, provided foregroundFPS has been set accordingly.

LWJGL 3 backend

The LWJGL 3 backend is much more elaborate when it comes to monitors and display modes. Unlike the LWJGL 2 backend, it supports multi-monitor setups.

Querying all available monitors at configuration time works like this:

Monitor[] monitors = Lwjgl3ApplicationConfiguration.getMonitors();

To get the primary monitor, call:

Monitor primary = Lwjgl3ApplicationConfiguration.getPrimaryMonitor();

To get all supported display modes of a monitor, call:

DisplayMode[] displayModes = Lwjgl3ApplicationConfiguration.getDisplayModes(monitor);

To get the current display mode of a monitor, call:

DisplayMode desktopMode = Lwjgl3ApplicationConfiguration.getDisplayMode(monitor);

There are shorthands for getting the display modes of the primary monitor as well:

DisplayMode[] primaryDisplayModes = Lwjgl3ApplicationConfiguration.getDisplayModes();
DisplayMode primaryDesktopMode = Lwjgl3ApplicationConfiguration.getDisplayMode();

With a display mode in hand, you can set it on the Lwjgl3ApplicationConfiguration:

DisplayMode primaryMode = Lwjgl3ApplicationConfiguration.getDisplayMode();
Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setFullscreenMode(primaryMode);
new Lwjgl3ApplicationConfiguration(new MyAppListener(), config);

This will start your app in full-screen mode on the primary monitor, using that monitor’s current resolution. If you pass a display mode from a different monitor, the app will be started in full-screen mode on that montior. Note: it is recommended to always use the current display mode of a monitor. Other display modes may fail.

To start your app in windowed mode, call this method on the configuration:

Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setWindowedMode(800, 600);

This will start your app in windowed mode on the primary monitor. To set the position of your window call:

config.setWindowPosition(100, 100);

This will position the window’s top-left corner at the coordinates 100,100 relative to the virtual surface all your monitors span. To check the position of a monitor, simply access it’s virtualX and virtualY members. These coordinates are also relative to the virtual surface your monitors span. By positioning your window relative to these coordinates, you can move a window to that specific monitor.

You can also specify if your window should be resizable and whether it has decoration (window title bar, borders):

config.setResizable(false);
config.setDecorated(false);

The LWJGL 3 backend also allows you to specify how to deal with HDPI monitors. The operating system may report logical sizes and coordinates instead of pixel-based coordinates to you for drawing surface sizes or mouse events. E.g. on a Macbook Pro with a retina display running Mac OS X, the OS reports only half the width/height of the underlying pixel surface. The LWJGL 3 backend can report drawing surface sizes as returned by Gdx.graphics.getWidth()/getHeight() and mouse coordinates either in those logical coordinates, or in pixel coordinates. To configure this behaviour, set a HDPI mode:

config.setHdpiMode(HdpiMode.Logical);

This will report mouse coordinates and drawing surface sizes in logical coordinates. It is also the default for this backend. If you want to work in raw pixels, use HdpiMode.Pixels. Note that when using logical coordinates, you will have to convert these to pixel coordinates for OpenGL functions like glScissor, glViewport or glReadPixels. All libGDX classes calling these functions will take into account the HdpiMode you set. If you call these functions yourself, use HdpiUtils.

Querying and setting monitors & display modes at runtime

libGDX provides an API via the Graphics interface that lets you query monitors, display modes and other related aspects at runtime. Once you know about possible configurations, you can set them, e.g. switch to full-screen mode, or toggle vsync.

Checking if display mode changes are supported

Only a subset of platforms supports display mode changes. Notably, Android and iOS do not support switching to arbitrary full-screen display modes. It is therefor good practice to check if the platform your application currently runs on supports display mode changes:

if(Gdx.graphics.supportsDisplayModeChange()) {
   // change display mode if necessary
}

Note that all display mode related functions in Graphics will simply not do anything on platforms that don’t support display mode changes.

Querying monitors

To query all connected monitors, use this method:

Monitor[] monitors = Gdx.graphics.getMonitors();

On Android, iOS, GWT and the LWJGL 2 backend, only the primary monitor will be reported. The LWJGL 3 backend reports all connected monitors.

To query the primary monitor, use:

Monitor primary = Gdx.graphics.getPrimaryMonitor();

To query the monitor the window is currently on, use:

Monitor currMonitor = Gdx.graphics.getMonitor();

It is good practice to toggle full-screen on the monitor the window is on, instead of say the primary monitor. This allows users to move the application window to another monitor, and then enable full-screen mode there.

Querying display modes

Once you have a Monitor instance, you can query its supported display modes:

DisplayMode[] modes = Gdx.graphics.getDisplayModes(monitor);

To get the current display mode, use this method:

DisplayMode currMode = Gdx.graphics.getDisplayMode(monitor);

Switching to full-screen mode

With a DisplayMode from a specific Monitor, you can switch to full-screen as follows:

Monitor currMonitor = Gdx.graphics.getMonitor();
DisplayMode displayMode = Gdx.graphics.getDisplayMode(currMonitor);
if(!Gdx.graphics.setFullscreenMode(displayMode)) {
   // switching to full-screen mode failed
}

If the switch to full-screen mode failed, the backend will restore the last windowed-mode configuration.

Switching to windowed mode

To change the size of a window, or to switch from full-screen mode to windowed mode, use this method:

Gdx.graphics.setWindowedMode(800, 600);

This will set the window to windowed mode, centering it on the monitor it was on before the call to this method.

Querying display cutouts on mobile

On Android and iOS, displays don’t have to be a perfect rectangle but might have cutouts, round edges or overlaying status bars. You can query the areas that are not safe to use for your important game content with the Gdx.graphics.getSafeInsetLeft(), Gdx.graphics.getSafeInsetRight(), Gdx.graphics.getSafeInsetTop() and Gdx.graphics.getSafeInsetBottom() methods.

Desktop & multi-window API of the LWJGL 3 backend

Some applications like editors or other desktop-only tools can benefit from multi-window setups. The LWJGL 3 backend provides an additional, non-cross-platform API to create multiple windows.

Every application starts with one window that is set up during configuration time:

Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setWindowedMode(800, 600);
new Lwjgl3ApplicationConfiguration(new MyMainWindowListener(), config);

In this example, the window is driven by MyMainWindowListener, a standard ApplicationListener. libGDX does not report events like iconification or focus loss directly. For this, the LWJGL 3 backend introduces a desktop specific interface called Lwjgl3WindowListener. You can provide an implementation of this interface to receive and react to such events:

config.setWindowListener(new Lwjgl3WindowListener() {
   @Override
   public void iconified() {
      // the window is not visible anymore, potentially stop background
      // work, mute audio, etc.
   }

   @Override
   public void deiconified() {
      // the window is visible again, start-up background work, unmute
      // audio, etc.
   }

   @Override
   public void focusLost() {
      // the window lost focus, pause the game
   }

   @Override
   public void focusGained() {
      // the window received input focus, unpause the game
   }

   @Override
   public boolean windowIsClosing() {
      // if there's unsaved stuff, we may not want to close
      // the window, but ask the user to save her work
      if(isStuffUnsaved) {
         // tell our app listener to show a save dialog
         return false;
      } else {
         // OK, the window may close
         return true;
      }
});

The LWJGL 3 backend does not report pause and resume events if the window loses focus. It will only report pause and resume events in case the app is iconfified/deiconified, or if the app is being closed.

To spawn additional windows, your code needs to cast Gdx.app to Lwjgl3Application. This is only possible if your project directly depends on the LWJGL 3 backend. You will not be able to share such code with other platforms. Once you have an Lwjgl3Application, you can create a new window like this:

Lwjgl3Application lwjgl3App = (Lwjgl3Application)Gdx.app;
Lwjgl3WindowConfiguration windowConfig = new Lwjgl3WindowConfiguration();
windowConfig.setWindowListener(new MyWindowListener());
windowConfig.setTitle("My other window");
Lwjgl3Window window = Lwjgl3App.newWindow(new MyOtherWindowAppListener(), windowConfig);

It is recommended to let every window have its own ApplicationListener. All windows of an application are updated on the same thread, one after the other. The LWJGL 3 backend will ensure that the statics Gdx.graphics and Gdx.input are setup for the window that’s currently being updated. This means that your ApplicationListener can essentially ignore the other windows, and pretend it’s the only listener in town.

There is one exception to this. When using Gdx.app.postRunnable(), the LWJGL 3 backend can not decide for which window the Runnable has been posted. E.g. the method may have been called from a worker thread, and by the time the Runnable is posted, a different window may currently be updated. To fix this, it is recommended to post window-specific Runnable instances directly to the window:

// e.g. in a worker thread
window.postRunnable(new MyRunnable());

This ensures that the statics Gdx.graphics and Gdx.input will be setup for the specific window when the Runnable is executed.

The Lwjgl3Window class has additional methods that let you modify the window’s properties. You can fetch the current window in your ApplicationListener, then proceed to modify it:

// in ApplicationListener#render()
Lwjgl3Window window = ((Lwjgl3Graphics)Gdx.graphics).getWindow();
window.setVisible(false); // hide the window
window.iconifyWindow(); // iconify the window
window.deiconifyWindow(); // deiconify window
window.closeWindow(); // close the window, also disposes the ApplicationListener