com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.java Source code

Java tutorial

Introduction

Here is the source code for com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.java

Source

/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.badlogic.gdx.backends.lwjgl3;

import java.io.File;

import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;

import com.badlogic.gdx.Application;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Audio;
import com.badlogic.gdx.Files;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Graphics;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.LifecycleListener;
import com.badlogic.gdx.Net;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.backends.lwjgl3.audio.OpenALAudio;
import com.badlogic.gdx.backends.lwjgl3.audio.mock.MockAudio;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Clipboard;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.ObjectMap;

public class Lwjgl3Application implements Application {
    private final Lwjgl3ApplicationConfiguration config;
    private final Array<Lwjgl3Window> windows = new Array<Lwjgl3Window>();
    private volatile Lwjgl3Window currentWindow;
    private Audio audio;
    private final Files files;
    private final Net net;
    private final ObjectMap<String, Preferences> preferences = new ObjectMap<String, Preferences>();
    private final Lwjgl3Clipboard clipboard;
    private int logLevel = LOG_INFO;
    private volatile boolean running = true;
    private final Array<Runnable> runnables = new Array<Runnable>();
    private final Array<Runnable> executedRunnables = new Array<Runnable>();
    private final Array<LifecycleListener> lifecycleListeners = new Array<LifecycleListener>();
    private static GLFWErrorCallback errorCallback;
    private static int versionMajor, versionMinor, versionRelease;

    static void initializeGlfw() {
        if (errorCallback == null) {
            Lwjgl3NativesLoader.load();
            errorCallback = GLFWErrorCallback.createPrint(System.err);
            GLFW.glfwSetErrorCallback(errorCallback);
            if (GLFW.glfwInit() != GLFW.GLFW_TRUE) {
                throw new GdxRuntimeException("Unable to initialize GLFW");
            }
        }
    }

    public Lwjgl3Application(ApplicationListener listener, Lwjgl3ApplicationConfiguration config) {
        initializeGlfw();
        this.config = Lwjgl3ApplicationConfiguration.copy(config);
        if (this.config.title == null)
            this.config.title = listener.getClass().getSimpleName();
        Gdx.app = this;
        if (!config.disableAudio) {
            try {
                this.audio = Gdx.audio = new OpenALAudio(config.audioDeviceSimultaneousSources,
                        config.audioDeviceBufferCount, config.audioDeviceBufferSize);
            } catch (Throwable t) {
                log("Lwjgl3Application", "Couldn't initialize audio, disabling audio", t);
                this.audio = Gdx.audio = new MockAudio();
            }
        } else {
            this.audio = Gdx.audio = new MockAudio();
        }
        this.files = Gdx.files = new Lwjgl3Files();
        this.net = Gdx.net = new Lwjgl3Net();
        this.clipboard = new Lwjgl3Clipboard();

        Lwjgl3Window window = createWindow(config, listener, 0);
        windows.add(window);
        try {
            loop();
        } catch (Throwable t) {
            t.printStackTrace();
        } finally {
            cleanup();
        }
    }

    private void loop() {
        Array<Lwjgl3Window> closedWindows = new Array<Lwjgl3Window>();
        while (running && windows.size > 0) {
            // FIXME put it on a separate thread
            if (audio instanceof OpenALAudio) {
                ((OpenALAudio) audio).update();
            }

            closedWindows.clear();
            for (Lwjgl3Window window : windows) {
                Gdx.graphics = window.getGraphics();
                Gdx.gl30 = window.getGraphics().getGL30();
                Gdx.gl20 = Gdx.gl30 != null ? Gdx.gl30 : window.getGraphics().getGL20();
                Gdx.gl = Gdx.gl30 != null ? Gdx.gl30 : Gdx.gl20;
                Gdx.input = window.getInput();

                GLFW.glfwMakeContextCurrent(window.getWindowHandle());
                currentWindow = window;
                synchronized (lifecycleListeners) {
                    window.update(lifecycleListeners);
                }
                if (window.shouldClose()) {
                    closedWindows.add(window);
                }
            }
            GLFW.glfwPollEvents();

            synchronized (runnables) {
                executedRunnables.clear();
                executedRunnables.addAll(runnables);
                runnables.clear();
            }
            for (Runnable runnable : executedRunnables) {
                runnable.run();
            }

            for (Lwjgl3Window closedWindow : closedWindows) {
                closedWindow.dispose();
                windows.removeValue(closedWindow, false);
            }
        }
    }

    private void cleanup() {
        for (Lwjgl3Window window : windows) {
            window.dispose();
        }
        if (audio instanceof OpenALAudio) {
            ((OpenALAudio) audio).dispose();
        }
        errorCallback.release();
        GLFW.glfwTerminate();
    }

    @Override
    public ApplicationListener getApplicationListener() {
        return currentWindow.getListener();
    }

    @Override
    public Graphics getGraphics() {
        return currentWindow.getGraphics();
    }

    @Override
    public Audio getAudio() {
        return audio;
    }

    @Override
    public Input getInput() {
        return currentWindow.getInput();
    }

    @Override
    public Files getFiles() {
        return files;
    }

    @Override
    public Net getNet() {
        return net;
    }

    @Override
    public void debug(String tag, String message) {
        if (logLevel >= LOG_DEBUG) {
            System.out.println(tag + ": " + message);
        }
    }

    @Override
    public void debug(String tag, String message, Throwable exception) {
        if (logLevel >= LOG_DEBUG) {
            System.out.println(tag + ": " + message);
            exception.printStackTrace(System.out);
        }
    }

    @Override
    public void log(String tag, String message) {
        if (logLevel >= LOG_INFO) {
            System.out.println(tag + ": " + message);
        }
    }

    @Override
    public void log(String tag, String message, Throwable exception) {
        if (logLevel >= LOG_INFO) {
            System.out.println(tag + ": " + message);
            exception.printStackTrace(System.out);
        }
    }

    @Override
    public void error(String tag, String message) {
        if (logLevel >= LOG_ERROR) {
            System.err.println(tag + ": " + message);
        }
    }

    @Override
    public void error(String tag, String message, Throwable exception) {
        if (logLevel >= LOG_ERROR) {
            System.err.println(tag + ": " + message);
            exception.printStackTrace(System.err);
        }
    }

    @Override
    public void setLogLevel(int logLevel) {
        this.logLevel = logLevel;
    }

    @Override
    public int getLogLevel() {
        return logLevel;
    }

    @Override
    public ApplicationType getType() {
        return ApplicationType.Desktop;
    }

    @Override
    public int getVersion() {
        return 0;
    }

    @Override
    public long getJavaHeap() {
        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
    }

    @Override
    public long getNativeHeap() {
        return getJavaHeap();
    }

    @Override
    public Preferences getPreferences(String name) {
        if (preferences.containsKey(name)) {
            return preferences.get(name);
        } else {
            Preferences prefs = new Lwjgl3Preferences(
                    new Lwjgl3FileHandle(new File(config.preferencesDirectory, name), config.preferencesFileType));
            preferences.put(name, prefs);
            return prefs;
        }
    }

    @Override
    public Clipboard getClipboard() {
        return clipboard;
    }

    @Override
    public void postRunnable(Runnable runnable) {
        synchronized (runnables) {
            runnables.add(runnable);
        }
    }

    @Override
    public void exit() {
        running = false;
    }

    @Override
    public void addLifecycleListener(LifecycleListener listener) {
        synchronized (lifecycleListeners) {
            lifecycleListeners.add(listener);
        }
    }

    @Override
    public void removeLifecycleListener(LifecycleListener listener) {
        synchronized (lifecycleListeners) {
            lifecycleListeners.add(listener);
        }
    }

    /**
     * Creates a new {@link Lwjgl3Window} using the provided listener and {@link Lwjgl3WindowConfiguration}.
     */
    public Lwjgl3Window newWindow(ApplicationListener listener, Lwjgl3WindowConfiguration config) {
        Lwjgl3ApplicationConfiguration appConfig = Lwjgl3ApplicationConfiguration.copy(this.config);
        appConfig.setWindowedMode(config.windowWidth, config.windowHeight);
        appConfig.setWindowPosition(config.windowX, config.windowY);
        appConfig.setResizable(config.windowResizable);
        appConfig.setDecorated(config.windowDecorated);
        appConfig.setWindowListener(config.windowListener);
        appConfig.setFullscreenMode(config.fullscreenMode);
        appConfig.setTitle(config.title);
        appConfig.setInitialBackgroundColor(config.initialBackgroundColor);
        Lwjgl3Window window = createWindow(appConfig, listener, windows.get(0).getWindowHandle());
        windows.add(window);
        return window;
    }

    private Lwjgl3Window createWindow(Lwjgl3ApplicationConfiguration config, ApplicationListener listener,
            long sharedContext) {
        long windowHandle = createGlfwWindow(config, sharedContext);
        Lwjgl3Window window = new Lwjgl3Window(windowHandle, listener, config);
        window.setVisible(true);
        return window;
    }

    static long createGlfwWindow(Lwjgl3ApplicationConfiguration config, long sharedContextWindow) {
        GLFW.glfwDefaultWindowHints();
        GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
        GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, config.windowResizable ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);

        if (sharedContextWindow == 0) {
            GLFW.glfwWindowHint(GLFW.GLFW_RED_BITS, config.r);
            GLFW.glfwWindowHint(GLFW.GLFW_GREEN_BITS, config.g);
            GLFW.glfwWindowHint(GLFW.GLFW_BLUE_BITS, config.b);
            GLFW.glfwWindowHint(GLFW.GLFW_ALPHA_BITS, config.a);
            GLFW.glfwWindowHint(GLFW.GLFW_STENCIL_BITS, config.stencil);
            GLFW.glfwWindowHint(GLFW.GLFW_DEPTH_BITS, config.depth);
            GLFW.glfwWindowHint(GLFW.GLFW_SAMPLES, config.samples);
        }

        if (config.useGL30) {
            //GLFW.glfwWindowHint(GLFW.GLFW_CLIENT_API, GLFW.GLFW_OPENGL_API);
            GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, config.gles30ContextMajorVersion);
            GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, config.gles30ContextMinorVersion);
            GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE);
            GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);
        }

        long windowHandle = 0;

        if (config.fullscreenMode != null) {
            // glfwWindowHint(GLFW.GLFW_REFRESH_RATE, config.fullscreenMode.refreshRate);
            windowHandle = GLFW.glfwCreateWindow(config.fullscreenMode.width, config.fullscreenMode.height,
                    config.title, config.fullscreenMode.getMonitor(), sharedContextWindow);
        } else {
            GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, config.windowDecorated ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
            windowHandle = GLFW.glfwCreateWindow(config.windowWidth, config.windowHeight, config.title, 0,
                    sharedContextWindow);
        }
        if (windowHandle == 0) {
            throw new GdxRuntimeException("Couldn't create window");
        }
        if (config.fullscreenMode == null) {
            if (config.windowX == -1 && config.windowY == -1) {
                GLFWVidMode vidMode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
                GLFW.glfwSetWindowPos(windowHandle, vidMode.width() / 2 - config.windowWidth / 2,
                        vidMode.height() / 2 - config.windowHeight / 2);
            } else {
                GLFW.glfwSetWindowPos(windowHandle, config.windowX, config.windowY);
            }
        }
        GLFW.glfwMakeContextCurrent(windowHandle);
        GLFW.glfwSwapInterval(config.vSyncEnabled ? 1 : 0);
        GL.createCapabilities();

        extractVersion();
        if (!isOpenGLOrHigher(2, 0))
            throw new GdxRuntimeException(
                    "OpenGL 2.0 or higher with the FBO extension is required. OpenGL version: "
                            + GL11.glGetString(GL11.GL_VERSION) + "\n" + glInfo());

        if (!supportsFBO()) {
            throw new GdxRuntimeException(
                    "OpenGL 2.0 or higher with the FBO extension is required. OpenGL version: "
                            + GL11.glGetString(GL11.GL_VERSION) + ", FBO extension: false\n" + glInfo());
        }

        for (int i = 0; i < 2; i++) {
            GL11.glClearColor(config.initialBackgroundColor.r, config.initialBackgroundColor.g,
                    config.initialBackgroundColor.b, config.initialBackgroundColor.a);
            GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
            GLFW.glfwSwapBuffers(windowHandle);
        }
        return windowHandle;
    }

    private static void extractVersion() {
        // See https://www.opengl.org/wiki/GLAPI/glGetString, format is:
        // <major> "." <minor> ("." <release>) (<space> (<vendor_specific_info>))
        String version = GL11.glGetString(GL11.GL_VERSION);
        try {
            String[] v = version.split(" ", 2)[0].split("\\.", 3);
            versionMajor = Integer.parseInt(v[0]);
            versionMinor = Integer.parseInt(v[1]);
            versionRelease = v.length > 2 ? Integer.parseInt(v[2]) : 0;
        } catch (Throwable t) {
            throw new GdxRuntimeException("Error extracting the OpenGL version: " + version, t);
        }
    }

    private static boolean isOpenGLOrHigher(int major, int minor) {
        return versionMajor > major || (versionMajor == major && versionMinor >= minor);
    }

    private static boolean supportsFBO() {
        // FBO is in core since OpenGL 3.0, see https://www.opengl.org/wiki/Framebuffer_Object
        return isOpenGLOrHigher(3, 0) || GLFW.glfwExtensionSupported("GL_EXT_framebuffer_object") == GLFW.GLFW_TRUE
                || GLFW.glfwExtensionSupported("GL_ARB_framebuffer_object") == GLFW.GLFW_TRUE;
    }

    private static String glInfo() {
        try {
            return GL11.glGetString(GL11.GL_VENDOR) + "\n" //
                    + GL11.glGetString(GL11.GL_RENDERER) + "\n" //
                    + GL11.glGetString(GL11.GL_VERSION);
        } catch (Throwable ignored) {
        }
        return "";
    }
}