/* * Copyright (c) 2002-2004 Light Weight Java Game Library Project * 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 'Light Weight Java Game Library' 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 org.lwjgl.opengl; /** * This is the abstract class for a Window in LWJGL. LWJGL windows have some * peculiar characteristics: * * - width and height are always fixed and cannot be changed * - the position of the window may or may not be programmable but once specified * cannot be changed programmatically * - the window may be closeable by the user or operating system, and may be minimized * by the user or operating system * - only one window may ever be open at once * - the operating system may or may not be able to do fullscreen or windowed windows. * * @author foo */ import org.lwjgl.Display; import org.lwjgl.Sys; import org.lwjgl.input.Controller; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.LWJGLException; public final class Window { static { Sys.initialize(); } /** X coordinate of the window */ private static int x; /** * Y coordinate of the window. Y in window coordinates is from the top of the display down, * unlike GL, where it is typically at the bottom of the display. */ private static int y; /** Width of the window */ private static int width; /** Height of the window */ private static int height; /** Title of the window */ private static String title; /** Fullscreen */ private static boolean fullscreen; /** Undecorated */ private static boolean undecorated; /** Tracks VBO state for the window context */ private static VBOTracker vbo_tracker; /** A unique context object, so we can track different contexts between creates() and destroys() */ private static Window context; /** Whether we created the Mouse */ private static boolean createdMouse; /** Whether we created the Keyboard */ private static boolean createdKeyboard; /** Whether we created the Controller */ private static boolean createdController; /** * Only constructed by ourselves */ private Window() { } /** * @return the X coordinate of the window (always 0 for fullscreen) */ public static int getX() { if (!isCreated()) throw new IllegalStateException("Cannot get X on uncreated window"); return x; } /** * @return the Y coordinate of the window (always 0 for fullscreen) */ public static int getY() { if (!isCreated()) throw new IllegalStateException("Cannot get Y on uncreated window"); return y; } /** * @return the width of the window */ public static int getWidth() { if (!isCreated()) throw new IllegalStateException("Cannot get width on uncreated window"); return width; } /** * @return the height of the window */ public static int getHeight() { if (!isCreated()) throw new IllegalStateException("Cannot get height on uncreated window"); return height; } /** * @return the title of the window */ public static String getTitle() { if (!isCreated()) throw new IllegalStateException("Cannot get title on uncreated window"); return title; } /** * @return whether this window is in fullscreen mode */ public static boolean isFullscreen() { if (!isCreated()) throw new IllegalStateException("Cannot determine fullscreen state of uncreated window"); return fullscreen; } /** * Set the title of the window. This may be ignored by the underlying OS. * @param newTitle The new window title */ public static void setTitle(String newTitle) { if (!isCreated()) throw new IllegalStateException("Cannot set title on uncreated window"); title = newTitle; nSetTitle(title); } /** * Native implementation of setTitle(). This will read the window's title member * and stash it in the native title of the window. */ private static native void nSetTitle(String title); /** * @return true if the user or operating system has asked the window to close */ public static boolean isCloseRequested() { if (!isCreated()) throw new IllegalStateException("Cannot determine close requested state of uncreated window"); return nIsCloseRequested(); } private static native boolean nIsCloseRequested(); /** * @return true if the window is minimized or otherwise not visible */ public static boolean isMinimized() { if (!isCreated()) throw new IllegalStateException("Cannot determine minimized state of uncreated window"); return nIsMinimized(); } private static native boolean nIsMinimized(); /** * @return true if window is focused */ public static boolean isFocused() { if (!isCreated()) throw new IllegalStateException("Cannot determine focused state of uncreated window"); return nIsFocused(); } private static native boolean nIsFocused(); /** * Minimize the game and allow the operating system's default display to become * visible. It is the responsibility of LWJGL's native code to restore the display * to its normal display settings. * * If the display is already minimized then this is a no-op. */ public static native void minimize(); /** * Restore the game and hide the operating system away. It is the responsibility of * LWJGL's native code to restore the display to its game display settings. * * If the display is not minimized then this is a no-op/ */ public static native void restore(); /** * Determine if the window's contents have been damaged by external events. * If you are writing a straightforward game rendering loop and simply paint * every frame regardless, you can ignore this flag altogether. If you are * trying to be kind to other processes you can check this flag and only * redraw when it returns true. The flag is cleared when you call paint(). * * @return true if the window has been damaged by external changes * and needs to repaint itself */ public static boolean isDirty() { if (!isCreated()) throw new IllegalStateException("Cannot determine dirty state of uncreated window"); return nIsDirty(); } private static native boolean nIsDirty(); /** * Update the window. This processes operating system events, and if the window is visible * clears the dirty flag and swaps the buffers. * @throws OpenGLException if an OpenGL error has occured since the last call to GL11.glGetError() */ public static void update() { if (!isCreated()) throw new IllegalStateException("Cannot update uncreated window"); nUpdate(); if ((isDirty() && !isMinimized()) || (isFocused() && !isMinimized())) { Util.checkGLError(); swapBuffers(); } // Poll the input devices while we're here if (Mouse.isCreated()) { Mouse.poll(); if (Mouse.isBuffered()) { Mouse.read(); } Mouse.updateCursor(); } if (Keyboard.isCreated()) { Keyboard.poll(); if (Keyboard.isBuffered()) { Keyboard.read(); } } if (Controller.isCreated()) { Controller.poll(); if (Controller.isBuffered()) { Controller.read(); } } } /** * Swap double buffers. */ private static native void swapBuffers(); /** * Make the Window the current rendering context for GL calls. */ public static synchronized void makeCurrent() { if (!isCreated()) throw new IllegalStateException("No window created to make current"); nMakeCurrent(); GLContext.useContext(context); } /** * Make the window the current rendering context for GL calls. */ private static native void nMakeCurrent(); /** * Create a fullscreen window that matches the current display depth. Default common values are chosen * for common OpenGL rendering operations: you will get at least a 16-bit depth buffer, an 8 bit stencil * buffer, probably no alpha buffer, and probably no multisampling. *

The window created will be set up in orthographic 2D projection, with 1:1 pixel ratio with GL coordinates. * @param title * @throws LWJGLException */ public static void create(String title) throws LWJGLException { create(title, Display.getDepth(), 0, 16, 8, 0); } /** * Create a fullscreen window. If the underlying OS does not * support fullscreen mode, then a window will be created instead. If this * fails too then an LWJGLException will be thrown. *

The window created will be set up in orthographic 2D projection, with 1:1 pixel ratio with GL coordinates. * @param title The title of the window * @param bpp Minimum bits per pixel * @param alpha Minimum bits per pixel in alpha buffer * @param depth Minimum bits per pixel in depth buffer * @param stencil Minimum bits per pixel in stencil buffer * @throws LWJGLException if the window could not be created for any reason; typically because * the minimum requirements could not be met satisfactorily */ public static void create(String title, int bpp, int alpha, int depth, int stencil) throws LWJGLException { create(title, bpp, alpha, depth, stencil, 0); } /** * Create a fullscreen window. If the underlying OS does not * support fullscreen mode, then a window will be created instead. If this * fails too then an LWJGLException will be thrown. *

The window created will be set up in orthographic 2D projection, with 1:1 pixel ratio with GL coordinates. * @param title The title of the window * @param bpp Minimum bits per pixel * @param alpha Minimum bits per pixel in alpha buffer * @param depth Minimum bits per pixel in depth buffer * @param stencil Minimum bits per pixel in stencil buffer * @param samples Minimum samples in multisample buffer (corresponds to GL_SAMPLES_ARB in GL_ARB_multisample spec). Pass 0 to disable multisampling. This parameter is ignored if GL_ARB_multisample is not supported. * @throws LWJGLException if the window could not be created for any reason; typically because * the minimum requirements could not be met satisfactorily */ public static void create(String title, int bpp, int alpha, int depth, int stencil, int samples) throws LWJGLException { if (isCreated()) throw new IllegalStateException("Only one LWJGL window may be instantiated at any one time."); Window.fullscreen = true; Window.undecorated = true; Window.x = 0; Window.y = 0; Window.width = Display.getWidth(); Window.height = Display.getHeight(); Window.title = title; createWindow(bpp, alpha, depth, stencil, samples); } /** * Create a window. If the underlying OS does not have "floating" windows, then a fullscreen * display will be created instead. If this fails too then an LWJGLException will be thrown. * If the window is created fullscreen, then its size may not match the specified size * here. *

The window created will be set up in orthographic 2D projection, with 1:1 pixel ratio with GL coordinates. * @param title The title of the window * @param x The position of the window on the x axis. May be ignored. * @param y The position of the window on the y axis. May be ignored. * @param width The width of the window's client area * @param height The height of the window's client area * @param bpp Minimum bits per pixel * @param alpha Minimum bits per pixel in alpha buffer * @param depth Minimum bits per pixel in depth buffer * @throws LWJGLException if the window could not be created for any reason; typically because * the minimum requirements could not be met satisfactorily */ public static void create(String title, int x, int y, int width, int height, int bpp, int alpha, int depth, int stencil) throws LWJGLException { create(title, x, y, width, height, false, bpp, alpha, depth, stencil); } /** * Create a window. If the underlying OS does not have "floating" windows, then a fullscreen * display will be created instead. If this fails too then an LWJGLException will be thrown. * If the window is created fullscreen, then its size may not match the specified size * here. *

The window created will be set up in orthographic 2D projection, with 1:1 pixel ratio with GL coordinates. * @param title The title of the window * @param x The position of the window on the x axis. May be ignored. * @param y The position of the window on the y axis. May be ignored. * @param width The width of the window's client area * @param height The height of the window's client area * @param undecorated A hint to specify that the window should not have OS decorations such as drag bar and close box * @param bpp Minimum bits per pixel * @param alpha Minimum bits per pixel in alpha buffer * @param depth Minimum bits per pixel in depth buffer * @throws LWJGLException if the window could not be created for any reason; typically because * the minimum requirements could not be met satisfactorily */ public static void create(String title, int x, int y, int width, int height, boolean undecorated, int bpp, int alpha, int depth, int stencil) throws LWJGLException { create(title, x, y, width, height, undecorated, bpp, alpha, depth, stencil, 0); } /** * Create a window. If the underlying OS does not have "floating" windows, then a fullscreen * display will be created instead. If this fails too then an LWJGLException will be thrown. * If the window is created fullscreen, then its size may not match the specified size * here. *

The window created will be set up in orthographic 2D projection, with 1:1 pixel ratio with GL coordinates. * @param title The title of the window * @param x The position of the window on the x axis. May be ignored. * @param y The position of the window on the y axis. May be ignored. * @param width The width of the window's client area * @param height The height of the window's client area * @param bpp Minimum bits per pixel * @param alpha Minimum bits per pixel in alpha buffer * @param depth Minimum bits per pixel in depth buffer * @param stencil Minimum bits per pixel in stencil buffer * @param samples Minimum samples in multisample buffer (corresponds to GL_SAMPLES_ARB in GL_ARB_multisample spec). Pass 0 to disable multisampling. This parameter is ignored if GL_ARB_multisample is not supported. * @throws LWJGLException if the window could not be created for any reason; typically because * the minimum requirements could not be met satisfactorily */ public static void create(String title, int x, int y, int width, int height, int bpp, int alpha, int depth, int stencil, int samples) throws LWJGLException { create(title, x, y, width, height, false, bpp, alpha, depth, stencil, samples); } /** * Create a window. If the underlying OS does not have "floating" windows, then a fullscreen * display will be created instead. If this fails too then an LWJGLException will be thrown. * If the window is created fullscreen, then its size may not match the specified size * here. *

The window created will be set up in orthographic 2D projection, with 1:1 pixel ratio with GL coordinates. * @param title The title of the window * @param x The position of the window on the x axis. May be ignored. * @param y The position of the window on the y axis. May be ignored. * @param width The width of the window's client area * @param height The height of the window's client area * @param undecorated A hint to specify that the window should not have OS decorations such as drag bar and close box * @param bpp Minimum bits per pixel * @param alpha Minimum bits per pixel in alpha buffer * @param depth Minimum bits per pixel in depth buffer * @param stencil Minimum bits per pixel in stencil buffer * @param samples Minimum samples in multisample buffer (corresponds to GL_SAMPLES_ARB in GL_ARB_multisample spec). Pass 0 to disable multisampling. This parameter is ignored if GL_ARB_multisample is not supported. * @throws LWJGLException if the window could not be created for any reason; typically because * the minimum requirements could not be met satisfactorily */ public static void create(String title, int x, int y, int width, int height, boolean undecorated, int bpp, int alpha, int depth, int stencil, int samples) throws LWJGLException { if (isCreated()) throw new IllegalStateException("Only one LWJGL window may be instantiated at any one time."); Window.fullscreen = false; Window.undecorated = undecorated; Window.x = x; Window.y = y; Window.width = width; Window.height = height; Window.title = title; createWindow(bpp, alpha, depth, stencil, samples); } /** * Create the native window peer. * @throws LWJGLException */ private static native void nCreate( String title, int x, int y, int width, int height, boolean fullscreen, boolean undecorated, int bpp, int alpha, int depth, int stencil, int samples) throws LWJGLException; private static void createWindow(int bpp, int alpha, int depth, int stencil, int samples) throws LWJGLException { nCreate(title, x, y, width, height, fullscreen, undecorated, bpp, alpha, depth, stencil, samples); context = new Window(); makeCurrent(); // Put the window into orthographic projection mode with 1:1 pixel ratio. // We haven't used GLU here to do this to avoid an unnecessary dependency. GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glLoadIdentity(); GL11.glOrtho(0.0, (double) width, 0.0, (double) height, -1.0, 1.0); GL11.glMatrixMode(GL11.GL_MODELVIEW); GL11.glLoadIdentity(); GL11.glViewport(0, 0, width, height); // Automatically create mouse, keyboard and controller if (!Boolean.getBoolean("org.lwjgl.opengl.Window.noinput")) { if (!Mouse.isCreated() && !Boolean.getBoolean("org.lwjgl.opengl.Window.nomouse")) { try { Mouse.create(); createdMouse = true; Mouse.enableBuffer(); } catch (LWJGLException e) { if (Sys.DEBUG) { e.printStackTrace(System.err); } else { Sys.log("Failed to create Mouse: "+e); } } } if (!Keyboard.isCreated() && !Boolean.getBoolean("org.lwjgl.opengl.Window.nokeyboard")) { try { Keyboard.create(); createdKeyboard = true; Keyboard.enableBuffer(); Keyboard.enableTranslation(); } catch (LWJGLException e) { if (Sys.DEBUG) { e.printStackTrace(System.err); } else { Sys.log("Failed to create Keyboard: "+e); } } } if (!Controller.isCreated() && !Boolean.getBoolean("org.lwjgl.opengl.Window.nocontroller")) { try { Controller.create(); createdController = true; } catch (LWJGLException e) { if (Sys.DEBUG) { e.printStackTrace(System.err); } else { Sys.log("Failed to create Controller: "+e); } } } } } /** * Destroy the Window. After this call, there will be no current GL rendering context, * regardless of whether the Window was the current rendering context. */ public static synchronized void destroy() { if (context == null) { return; } // Automatically destroy keyboard, mouse, and controller if (createdMouse && Mouse.isCreated()) { Mouse.destroy(); createdMouse = false; } if (createdKeyboard && Keyboard.isCreated()) { Keyboard.destroy(); createdKeyboard = false; } if (createdController && Controller.isCreated()) { Controller.destroy(); createdController = false; } makeCurrent(); nDestroy(); context = null; } /** * @return the unique Window context (or null, if the Window has not been created) */ public static Object getContext() { return context; } /** * Destroy the native window peer. */ private static native void nDestroy(); /** * @return true if the window's native peer has been created */ public static boolean isCreated() { return context != null; } /** * Updates the windows internal state. This must be called at least once per video frame * to handle window close requests, moves, paints, etc. */ private static native void nUpdate(); /** * Determines to the best of the platform's ability whether monitor vysnc is enabled on * this window. The failsafe assumption is that when vsync cannot be determined, this * method returns false, and you should rely on using a hires timer to throttle your * framerate rather than relying on monitor sync (even if monitor sync is actually working). * Therefore you can guarantee that if we return true from this method that we're pretty * certain vsync is enabled. * @return boolean */ public static boolean isVSyncEnabled() { if (!isCreated()) throw new IllegalStateException("Cannot determine vsync state of uncreated window"); return nIsVSyncEnabled(); } private static native boolean nIsVSyncEnabled(); /** * Enable or disable vertical monitor synchronization. This call is a best-attempt at changing * the vertical refresh synchronization of the monitor, and is not guaranteed to be successful. * To check whether the call might have been successful, call isVSyncEnabled(). * @param sync true to synchronize; false to ignore synchronization */ public static void setVSyncEnabled(boolean sync) { if (!isCreated()) throw new IllegalStateException("Cannot set vsync state of uncreated window"); nSetVSyncEnabled(sync); } private static native void nSetVSyncEnabled(boolean sync); /** * Set the window's location. This is a no-op on fullscreen windows. * The window is clamped to remain entirely on the screen. If you attempt * to position the window such that it would extend off the screen, the window * is simply placed as close to the edge as possible. * @param x, y The new window location */ public static void setLocation(int x, int y) { if (!isCreated()) throw new IllegalStateException("Cannot move uncreated window"); if (fullscreen) { return; } Window.x = Math.max(0, Math.min(Display.getWidth() - Window.width, x)); Window.y = Math.max(0, Math.min(Display.getHeight() - Window.height, y)); nReshape(Window.x, Window.y, Window.width, Window.height); } /** * Set the window's size. This is a no-op on fullscreen windows. * The window is clamped to remain entirely on the screen. If you attempt * to position the window such that it would extend off the screen, the window * is simply placed as close to the edge as possible. * @param width, height The new window dimensions */ public static void setSize(int width, int height) { if (!isCreated()) throw new IllegalStateException("Cannot resize uncreated window"); if (fullscreen) { return; } Window.width = Math.max(0, Math.min(Display.getWidth() - Window.x, width)); Window.height = Math.max(0, Math.min(Display.getHeight() - Window.y, height)); nReshape(Window.x, Window.y, Window.width, Window.height); } /** * Set the window's bounds. This is a no-op on fullscreen windows. * The window is clamped to remain entirely on the screen. * @param x, y The new window location * @param width, height The new window dimensions */ public static void setBounds(int x, int y, int width, int height) { if (!isCreated()) throw new IllegalStateException("Cannot reshape uncreated window"); if (fullscreen) { return; } width = Math.max(0, Math.min(Display.getWidth(), width)); height = Math.max(0, Math.min(Display.getHeight(), height)); Window.x = Math.max(0, Math.min(Display.getWidth() - width, x)); Window.y = Math.max(0, Math.min(Display.getHeight() - height, y)); Window.width = Math.max(0, Math.min(Display.getWidth() - Window.x, width)); Window.height = Math.max(0, Math.min(Display.getHeight() - Window.y, height)); nReshape(Window.x, Window.y, Window.width, Window.height); } /** * Native method to reshape the window * @param x, y The new window location * @param width, height The new window dimensions */ private static native void nReshape(int x, int y, int width, int height); }