net.smert.frameworkgl.Window.java Source code

Java tutorial

Introduction

Here is the source code for net.smert.frameworkgl.Window.java

Source

/**
 * Copyright 2012 Jason Sorensen (sorensenj@smert.net)
 *
 * 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 net.smert.frameworkgl;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.Callbacks;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWCursorPosCallback;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWKeyCallback;
import org.lwjgl.glfw.GLFWMouseButtonCallback;
import org.lwjgl.glfw.GLFWScrollCallback;
import org.lwjgl.glfw.GLFWWindowSizeCallback;
import org.lwjgl.glfw.GLFWvidmode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.system.MemoryUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Jason Sorensen <sorensenj@smert.net>
 */
public class Window {

    private final static Logger log = LoggerFactory.getLogger(Window.class);

    private boolean fullscreen;
    private boolean initialized;
    private boolean vSync;
    private boolean wasResized;
    private long lastSyncTime;
    private long variableYieldTime;
    private long window;
    private GLFWCursorPosCallback cursorPosCallback;
    private GLFWErrorCallback errorCallback;
    private GLFWKeyCallback keyCallback;
    private GLFWMouseButtonCallback mouseButtonCallback;
    private GLFWScrollCallback scrollCallback;
    private GLFWWindowSizeCallback windowSizeCallback;
    private final IntBuffer heightBuffer;
    private final IntBuffer widthBuffer;

    public Window() {
        fullscreen = Fw.config.fullscreenRequested;
        vSync = Fw.config.vSyncRequested;
        heightBuffer = BufferUtils.createIntBuffer(1);
        widthBuffer = BufferUtils.createIntBuffer(1);
    }

    private GLFWvidmode findVideoMode(long monitor, int width, int height, int refreshRate, int redBits,
            int greenBits, int blueBits) {
        GLFWvidmode glfwVidMode = null;
        GLFWvidmode[] modes = getVideoModes(monitor);
        for (GLFWvidmode mode : modes) {

            // If the display mode matches save it
            if ((mode.getWidth() == width) && (mode.getHeight() == height) && (mode.getRefreshRate() == refreshRate)
                    && (mode.getRedBits() == redBits) && (mode.getGreenBits() == greenBits)
                    && (mode.getBlueBits() == blueBits)) {
                glfwVidMode = mode; // Don't break in case of logging
            }

            log.debug("Found fullscreen compatible mode: "
                    + "Width: {}px Height: {}px Refresh Rate: {}hz Red Bits: {} Green Bits: {} Blue Bits: {}",
                    mode.getWidth(), mode.getHeight(), mode.getRefreshRate(), mode.getRedBits(),
                    mode.getGreenBits(), mode.getBlueBits());
        }

        return glfwVidMode;
    }

    private GLFWvidmode getDesktopVideoMode(long monitor) {
        ByteBuffer mode = GLFW.glfwGetVideoMode(monitor);
        GLFWvidmode glfwVidMode = new GLFWvidmode();
        glfwVidMode.setBlueBits(GLFWvidmode.blueBits(mode));
        glfwVidMode.setGreenBits(GLFWvidmode.greenBits(mode));
        glfwVidMode.setHeight(GLFWvidmode.height(mode));
        glfwVidMode.setRedBits(GLFWvidmode.redBits(mode));
        glfwVidMode.setRefreshRate(GLFWvidmode.refreshRate(mode));
        glfwVidMode.setWidth(GLFWvidmode.width(mode));
        return glfwVidMode;
    }

    private GLFWvidmode[] getVideoModes(long monitor) {
        IntBuffer count = BufferUtils.createIntBuffer(1);
        ByteBuffer modes = GLFW.glfwGetVideoModes(monitor, count);
        GLFWvidmode[] videoModes = new GLFWvidmode[count.get(0)];
        for (int i = 0, max = count.get(0); i < max; i++) {

            // Advance position
            modes.position(i * GLFWvidmode.SIZEOF);

            // Create new video mode and extract data
            GLFWvidmode glfwVidMode = new GLFWvidmode();
            glfwVidMode.setBlueBits(GLFWvidmode.blueBits(modes));
            glfwVidMode.setGreenBits(GLFWvidmode.greenBits(modes));
            glfwVidMode.setHeight(GLFWvidmode.height(modes));
            glfwVidMode.setRedBits(GLFWvidmode.redBits(modes));
            glfwVidMode.setRefreshRate(GLFWvidmode.refreshRate(modes));
            glfwVidMode.setWidth(GLFWvidmode.width(modes));

            // Save video mode to array
            videoModes[i] = glfwVidMode;
        }
        return videoModes;
    }

    private void resize(int width, int height) {
        Fw.config.currentHeight = height;
        Fw.config.currentWidth = width;
        wasResized = true;
    }

    public void create() {

        // Update the configuration
        Configuration config = Fw.config;
        config.fullscreenEnabled = fullscreen;
        config.vSyncEnabled = vSync;

        // Setup variables
        long monitor = GLFW.glfwGetPrimaryMonitor();
        GLFWvidmode videoMode = null;

        if (fullscreen) {

            // Attempt to find a matching full screen compatible mode
            videoMode = findVideoMode(monitor, config.fullscreenWidth, config.fullscreenHeight,
                    config.fullscreenRefreshRate, config.framebufferRedBits, config.framebufferGreenBits,
                    config.framebufferBlueBits);

            if (videoMode == null) {
                log.warn("Didn't find a fullscreen display mode for the requested resolution: "
                        + "Width: {}px Height: {}px Refresh Rate: {}hz Red Bits: {} Green Bits: {} Blue Bits: {}",
                        config.fullscreenWidth, config.fullscreenHeight, config.fullscreenRefreshRate,
                        config.framebufferRedBits, config.framebufferGreenBits, config.framebufferBlueBits);
            }
        }

        // Either a full screen mode wasn't requested or we couldn't find one that matched our settings
        if (videoMode == null) {
            GLFWvidmode desktopVideoMode = getDesktopVideoMode(monitor);
            videoMode = new GLFWvidmode();
            videoMode.setBlueBits(desktopVideoMode.getBlueBits());
            videoMode.setGreenBits(desktopVideoMode.getGreenBits());
            videoMode.setHeight(config.desktopHeight);
            videoMode.setRedBits(desktopVideoMode.getRedBits());
            videoMode.setRefreshRate(desktopVideoMode.getRefreshRate());
            videoMode.setWidth(config.desktopWidth);
        }

        // Reset window hints to defaults before setting them
        GLFW.glfwDefaultWindowHints();
        GLFW.glfwWindowHint(GLFW.GLFW_ALPHA_BITS, videoMode.getBlueBits());
        GLFW.glfwWindowHint(GLFW.GLFW_BLUE_BITS, videoMode.getBlueBits());
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, config.requestedOpenglMajorVersion);
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, config.requestedOpenglMinorVersion);
        GLFW.glfwWindowHint(GLFW.GLFW_DEPTH_BITS, config.framebufferDepthBits);
        GLFW.glfwWindowHint(GLFW.GLFW_GREEN_BITS, videoMode.getGreenBits());
        GLFW.glfwWindowHint(GLFW.GLFW_RED_BITS, videoMode.getRedBits());
        GLFW.glfwWindowHint(GLFW.GLFW_REFRESH_RATE, videoMode.getRefreshRate());
        GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE,
                (fullscreen) ? GL11.GL_FALSE : ((config.desktopResizable) ? GL11.GL_TRUE : GL11.GL_FALSE));
        if ((config.requestedOpenglMajorVersion >= 3) && (config.requestedOpenglMinorVersion >= 0)) {
            GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT,
                    (config.forwardCompatible) ? GL11.GL_TRUE : GL11.GL_FALSE);
        }
        if (((config.requestedOpenglMajorVersion == 3) && (config.requestedOpenglMinorVersion >= 2))
                || (config.requestedOpenglMajorVersion >= 4)) {
            GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE,
                    (config.coreProfile) ? GLFW.GLFW_OPENGL_CORE_PROFILE : GLFW.GLFW_OPENGL_COMPAT_PROFILE);
        } else {
            GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_ANY_PROFILE);
        }
        GLFW.glfwWindowHint(GLFW.GLFW_SAMPLES, config.framebufferSamples);
        GLFW.glfwWindowHint(GLFW.GLFW_SRGB_CAPABLE, (config.framebufferSrgb) ? GL11.GL_TRUE : GL11.GL_FALSE);
        GLFW.glfwWindowHint(GLFW.GLFW_STENCIL_BITS, config.framebufferStencilBits);
        GLFW.glfwWindowHint(GLFW.GLFW_STEREO, (config.framebufferStereo) ? GL11.GL_TRUE : GL11.GL_FALSE);
        GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GL11.GL_FALSE);

        // Create new window
        long fullscreenMonitor = (fullscreen) ? monitor : MemoryUtil.NULL;
        long newWindow = GLFW.glfwCreateWindow(videoMode.getWidth(), videoMode.getHeight(), config.windowTitle,
                fullscreenMonitor, window);
        if (newWindow == MemoryUtil.NULL) {
            throw new WindowException("Failed to create the GLFW window");
        }

        log.info(
                "Created window with display mode: "
                        + "Width: {}px Height: {}px Refresh Rate: {}hz Red Bits: {} Green Bits: {} Blue Bits: {}",
                videoMode.getWidth(), videoMode.getHeight(), videoMode.getRefreshRate(), videoMode.getRedBits(),
                videoMode.getGreenBits(), videoMode.getBlueBits());

        // Destroy old window and callback
        if (window != MemoryUtil.NULL) {
            GLFW.glfwDestroyWindow(window);
            cursorPosCallback.release();
            keyCallback.release();
            mouseButtonCallback.release();
            scrollCallback.release();
            windowSizeCallback.release();
            Fw.input.clearNextState();
        }

        // Resize
        resize(videoMode.getWidth(), videoMode.getHeight());

        // Create GL context
        GLFW.glfwMakeContextCurrent(newWindow);
        GLContext.createFromCurrent();

        // Set the window location
        if (!fullscreen) {
            if ((config.desktopLocationX != -1) && (config.desktopLocationY != -1)) {
                GLFW.glfwSetWindowPos(newWindow, config.desktopLocationX, config.desktopLocationY);
            } else {
                // We can't reuse desktopVideoMode from above since after
                // we switch out of full screen it would be the full screen
                // video mode and not the current desktop.
                GLFWvidmode desktopVideoMode = getDesktopVideoMode(monitor);
                int x = (desktopVideoMode.getWidth() - config.desktopWidth) / 2;
                int y = (desktopVideoMode.getHeight() - config.desktopHeight) / 2;
                GLFW.glfwSetWindowPos(newWindow, x, y);
            }
        }

        // Turn on/off vsync
        if (vSync) {
            GLFW.glfwSwapInterval(1);
        } else {
            GLFW.glfwSwapInterval(0);
        }

        // Events
        GLFW.glfwSetCursorPosCallback(newWindow, cursorPosCallback = new GLFWCursorPosCallback() {
            @Override
            public void invoke(long window, double x, double y) {
                Fw.input.handleMouseMoveEvent(x, y);
            }
        });
        GLFW.glfwSetKeyCallback(newWindow, keyCallback = new GLFWKeyCallback() {
            @Override
            public void invoke(long window, int key, int scancode, int action, int mods) {
                Fw.input.addKeyboardEvent(key, mods, scancode, action != GLFW.GLFW_RELEASE);
            }
        });
        GLFW.glfwSetMouseButtonCallback(newWindow, mouseButtonCallback = new GLFWMouseButtonCallback() {
            @Override
            public void invoke(long window, int button, int action, int mods) {
                Fw.input.addMouseEvent(button, mods, action != GLFW.GLFW_RELEASE);
            }
        });
        GLFW.glfwSetScrollCallback(newWindow, scrollCallback = new GLFWScrollCallback() {
            @Override
            public void invoke(long window, double xoffset, double yoffset) {
                Fw.input.handleMouseScrollEvent(xoffset, yoffset);
            }
        });
        GLFW.glfwSetWindowSizeCallback(newWindow, windowSizeCallback = new GLFWWindowSizeCallback() {
            @Override
            public void invoke(long window, int width, int height) {
                resize(width, height);
            }
        });

        // Show the window
        GLFW.glfwShowWindow(newWindow);

        // Save window
        window = newWindow;

        // Hide/unhide the cursor
        Fw.input.updateInputMode();
    }

    public void destroy() {
        if (!initialized) {
            return;
        }
        GLFW.glfwDestroyWindow(window);
        GLFW.glfwTerminate();
        cursorPosCallback.release();
        errorCallback.release();
        keyCallback.release();
        mouseButtonCallback.release();
        scrollCallback.release();
        windowSizeCallback.release();
        window = MemoryUtil.NULL;
        initialized = false;
    }

    public void destroyWindow() {
        if (window == MemoryUtil.NULL) {
            return;
        }
        GLFW.glfwDestroyWindow(window);
        window = MemoryUtil.NULL;
    }

    public int getHeight() {
        GLFW.glfwGetWindowSize(window, widthBuffer, heightBuffer);
        return heightBuffer.get(0);
    }

    public int getWidth() {
        GLFW.glfwGetWindowSize(window, widthBuffer, heightBuffer);
        return widthBuffer.get(0);
    }

    public long getWindow() {
        return window;
    }

    public boolean isCloseRequested() {
        return (GLFW.glfwWindowShouldClose(window) == GL11.GL_TRUE);
    }

    public boolean isFocused() {
        return (GLFW.glfwGetWindowAttrib(window, GLFW.GLFW_FOCUSED) == GL11.GL_TRUE);
    }

    public boolean wasResized() {
        return wasResized;
    }

    public void setWasResized(boolean wasResized) {
        this.wasResized = wasResized;
    }

    public void init() {
        if (initialized) {
            return;
        }
        GLFW.glfwSetErrorCallback(errorCallback = Callbacks.errorCallbackThrow());
        if (GLFW.glfwInit() != GL11.GL_TRUE) {
            GLFW.glfwTerminate();
            throw new WindowException("Unable to initialize GLFW");
        }
        initialized = true;
    }

    public void printDisplayModes() {

        // These modes should always be fullscreen compatible
        long monitor = GLFW.glfwGetPrimaryMonitor();
        GLFWvidmode[] videoModes = getVideoModes(monitor);

        for (GLFWvidmode videoMode : videoModes) {
            System.out.println("Found fullscreen compatible mode: " + "Width: " + videoMode.getWidth()
                    + "px Height: " + videoMode.getHeight() + "px Refresh Rate: " + videoMode.getRefreshRate()
                    + "hz Red Bits: " + videoMode.getRedBits() + " Green Bits: " + videoMode.getGreenBits()
                    + " Blue Bits: " + videoMode.getBlueBits());
        }
    }

    public void processMessages() {
        GLFW.glfwPollEvents();
    }

    public void setTitle(String title) {
        GLFW.glfwSetWindowTitle(window, title);
    }

    public void sync(int fps) {
        if (fps <= 0) {
            return;
        }

        long overSleep = 0;
        long sleepTime = Timer.ONE_NANO_SECOND / fps;
        long yieldTime = Math.min(sleepTime, variableYieldTime + sleepTime % (1000000L));

        try {
            while (true) {
                long t = System.nanoTime() - lastSyncTime;

                if (t < sleepTime - yieldTime) {
                    Thread.sleep(1);
                } else if (t < sleepTime) {
                    Thread.yield();
                } else {
                    overSleep = t - sleepTime;
                    break;
                }
            }
        } catch (InterruptedException e) {
            // Do nothing
        } finally {
            lastSyncTime = System.nanoTime() - Math.min(overSleep, sleepTime);

            if (overSleep > variableYieldTime) {
                variableYieldTime = Math.min(variableYieldTime + 200 * 1000, sleepTime); // Increse 200 microseconds
            } else if (overSleep < variableYieldTime - 200 * 1000) {
                variableYieldTime = Math.max(variableYieldTime - 2 * 1000, 0); // Decrease 2 microseconds
            }
        }
    }

    public void toggleFullscreen() {
        fullscreen = !fullscreen;
        create();
    }

    public void toggleVSync() {
        vSync = !vSync;
        create();
    }

    public void update() {
        GLFW.glfwSwapBuffers(window);
    }

}