/* * Copyright (c) 2002-2004 LWJGL 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 'LWJGL' 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: * * - 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; /** 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 visible, false if not */ public static boolean isVisible() { if (!isCreated()) throw new IllegalStateException("Cannot determine minimized state of uncreated window"); return nIsVisible(); } private static native boolean nIsVisible(); /** * @return true if window is active, that is, the foreground display of the operating system. */ public static boolean isActive() { if (!isCreated()) throw new IllegalStateException("Cannot determine focused state of uncreated window"); return nIsActive(); } private static native boolean nIsActive(); /** * 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 update() or isDirty() is called. * * @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"); // We paint only when the window is visible or dirty if (isVisible() || isDirty()) { Util.checkGLError(); swapBuffers(); } nUpdate(); // Poll the input devices while we're here if (Mouse.isCreated()) { Mouse.poll(); Mouse.updateCursor(); } if (Keyboard.isCreated()) { Keyboard.poll(); } if (Controller.isCreated()) { Controller.poll(); } } /** * 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.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, 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 { if (isCreated()) throw new IllegalStateException("Only one LWJGL window may be instantiated at any one time."); Window.fullscreen = false; 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, 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, 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(); /** * 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. * @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); }