Home | History | Annotate | Download | only in jglfw
      1 /*******************************************************************************
      2  * Copyright 2011 See AUTHORS file.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *   http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  ******************************************************************************/
     16 
     17 package com.badlogic.gdx.backends.jglfw;
     18 
     19 import static com.badlogic.jglfw.Glfw.*;
     20 
     21 import com.badlogic.gdx.Application;
     22 import com.badlogic.gdx.ApplicationListener;
     23 import com.badlogic.gdx.Gdx;
     24 import com.badlogic.gdx.Graphics;
     25 import com.badlogic.gdx.graphics.Color;
     26 import com.badlogic.gdx.graphics.Cursor;
     27 import com.badlogic.gdx.graphics.GL20;
     28 import com.badlogic.gdx.graphics.GL30;
     29 import com.badlogic.gdx.graphics.Pixmap;
     30 import com.badlogic.gdx.graphics.Cursor.SystemCursor;
     31 import com.badlogic.gdx.graphics.glutils.GLVersion;
     32 import com.badlogic.gdx.utils.Array;
     33 import com.badlogic.gdx.utils.GdxRuntimeException;
     34 import com.badlogic.jglfw.Glfw;
     35 import com.badlogic.jglfw.GlfwVideoMode;
     36 import com.badlogic.jglfw.gl.GL;
     37 
     38 import java.awt.Toolkit;
     39 
     40 /** An implementation of the {@link Graphics} interface based on GLFW.
     41  * @author Nathan Sweet */
     42 public class JglfwGraphics implements Graphics {
     43 	static final boolean isMac = System.getProperty("os.name").contains("OS X");
     44 	static final boolean isWindows = System.getProperty("os.name").contains("Windows");
     45 	static final boolean isLinux = System.getProperty("os.name").contains("Linux");
     46 
     47 	static GLVersion glVersion;
     48 
     49 	long window;
     50 	private boolean fullscreen;
     51 	private long fullscreenMonitor;
     52 	private String title;
     53 	private boolean resizable, undecorated;
     54 	private BufferFormat bufferFormat;
     55 	private boolean vSync;
     56 	private int x, y, width, height;
     57 	private boolean visible;
     58 	private Color initialBackgroundColor;
     59 	private volatile boolean isContinuous = true, renderRequested;
     60 	volatile boolean foreground, minimized;
     61 
     62 	private long frameId = -1;
     63 	private float deltaTime;
     64 	private long frameStart, lastTime = -1;
     65 	private int frames, fps;
     66 
     67 	private JglfwGL20 gl20;
     68 
     69 	public JglfwGraphics (JglfwApplicationConfiguration config) {
     70 		// Store values from config.
     71 		bufferFormat = new BufferFormat(config.r, config.g, config.b, config.a, config.depth, config.stencil, config.samples, false);
     72 		title = config.title;
     73 		resizable = config.resizable;
     74 		undecorated = config.undecorated;
     75 		x = config.x;
     76 		y = config.y;
     77 		vSync = config.vSync;
     78 		initialBackgroundColor = config.initialBackgroundColor;
     79 		if (config.fullscreenMonitorIndex != -1) { // Use monitor specified in config if it is valid.
     80 			long[] monitors = glfwGetMonitors();
     81 			if (config.fullscreenMonitorIndex < monitors.length) fullscreenMonitor = monitors[config.fullscreenMonitorIndex];
     82 		}
     83 
     84 		// Create window.
     85 		if (!createWindow(config.width, config.height, config.fullscreen)) {
     86 			throw new GdxRuntimeException("Unable to create window: " + config.width + "x" + config.height + ", fullscreen: "
     87 				+ config.fullscreen);
     88 		}
     89 
     90 		// Create GL.
     91 		String versionString = GL.glGetString(GL20.GL_VERSION);
     92 		String vendorString = GL.glGetString(GL20.GL_VENDOR);
     93 		String rendererString = GL.glGetString(GL20.GL_RENDERER);
     94 		glVersion = new GLVersion(Application.ApplicationType.Desktop, versionString, vendorString, rendererString);
     95 
     96 
     97 		if (glVersion.getMajorVersion() <= 1)
     98 			throw new GdxRuntimeException("OpenGL 2.0 or higher with the FBO extension is required. OpenGL version: " + glVersion.getMajorVersion() + ":" + glVersion.getMinorVersion());
     99 		if (glVersion.getMajorVersion() == 2) {
    100 			if (!supportsExtension("GL_EXT_framebuffer_object") && !supportsExtension("GL_ARB_framebuffer_object")) {
    101 				throw new GdxRuntimeException("OpenGL 2.0 or higher with the FBO extension is required. OpenGL version: " + glVersion.getMajorVersion() + ":" + glVersion.getMinorVersion()
    102 					+ ", FBO extension: false");
    103 			}
    104 		}
    105 
    106 		gl20 = new JglfwGL20();
    107 		Gdx.gl = gl20;
    108 		Gdx.gl20 = gl20;
    109 
    110 		if (!config.hidden) show();
    111 	}
    112 
    113 	private boolean createWindow (int width, int height, boolean fullscreen) {
    114 		if (fullscreen && fullscreenMonitor == 0) fullscreenMonitor = getWindowMonitor();
    115 
    116 		glfwWindowHint(GLFW_VISIBLE, 0);
    117 		glfwWindowHint(GLFW_RESIZABLE, resizable ? 1 : 0);
    118 		glfwWindowHint(GLFW_UNDECORATED, undecorated ? 1 : 0);
    119 		glfwWindowHint(GLFW_RED_BITS, bufferFormat.r);
    120 		glfwWindowHint(GLFW_GREEN_BITS, bufferFormat.g);
    121 		glfwWindowHint(GLFW_BLUE_BITS, bufferFormat.b);
    122 		glfwWindowHint(GLFW_ALPHA_BITS, bufferFormat.a);
    123 		glfwWindowHint(GLFW_DEPTH_BITS, bufferFormat.depth);
    124 		glfwWindowHint(GLFW_STENCIL_BITS, bufferFormat.stencil);
    125 		glfwWindowHint(GLFW_SAMPLES, bufferFormat.samples);
    126 
    127 		boolean mouseCaptured = window != 0 && glfwGetInputMode(window, GLFW_CURSOR_MODE) == GLFW_CURSOR_CAPTURED;
    128 
    129 		long oldWindow = window;
    130 		long newWindow = glfwCreateWindow(width, height, title, fullscreen ? fullscreenMonitor : 0, oldWindow);
    131 		if (newWindow == 0) return false;
    132 		if (oldWindow != 0) glfwDestroyWindow(oldWindow);
    133 		window = newWindow;
    134 		this.width = Math.max(1, width);
    135 		this.height = Math.max(1, height);
    136 
    137 		this.fullscreen = fullscreen;
    138 		if (!fullscreen) {
    139 			if (x == -1 || y == -1) {
    140 				DisplayMode mode = getDisplayMode();
    141 				x = (mode.width - width) / 2;
    142 				y = (mode.height - height) / 2;
    143 			}
    144 			glfwSetWindowPos(window, x, y);
    145 		}
    146 
    147 		if (!mouseCaptured) glfwSetInputMode(window, GLFW_CURSOR_MODE, GLFW_CURSOR_NORMAL); // Prevent fullscreen from taking mouse.
    148 
    149 		glfwMakeContextCurrent(newWindow);
    150 		setVSync(vSync);
    151 		if (visible) glfwShowWindow(window);
    152 
    153 		return true;
    154 	}
    155 
    156 	void frameStart (long time) {
    157 		if (lastTime == -1) lastTime = time;
    158 		deltaTime = (time - lastTime) / 1000000000.0f;
    159 		lastTime = time;
    160 
    161 		if (time - frameStart >= 1000000000) {
    162 			fps = frames;
    163 			frames = 0;
    164 			frameStart = time;
    165 		}
    166 		frames++;
    167 		frameId++;
    168 	}
    169 
    170 	void sizeChanged (int width, int height) {
    171 		if (isMac) {
    172 			glfwShowWindow(window); // This is required to refresh the NSOpenGLContext on OSX!
    173 		}
    174 		width = Math.max(1, width);
    175 		height = Math.max(1, height);
    176 		this.width = width;
    177 		this.height = height;
    178 		Gdx.gl.glViewport(0, 0, width, height);
    179 		ApplicationListener listener = Gdx.app.getApplicationListener();
    180 		if (listener != null) listener.resize(width, height);
    181 		requestRendering();
    182 	}
    183 
    184 	void positionChanged (int x, int y) {
    185 		this.x = x;
    186 		this.y = y;
    187 	}
    188 
    189 	public boolean isGL20Available () {
    190 		return gl20 != null;
    191 	}
    192 
    193 	public GL20 getGL20 () {
    194 		return gl20;
    195 	}
    196 
    197 	public int getWidth () {
    198 		return width;
    199 	}
    200 
    201 	public int getHeight () {
    202 		return height;
    203 	}
    204 
    205 	@Override
    206 	public int getBackBufferWidth () {
    207 		return width;
    208 	}
    209 
    210 	@Override
    211 	public int getBackBufferHeight () {
    212 		return height;
    213 	}
    214 
    215 	public long getFrameId () {
    216 		return frameId;
    217 	}
    218 
    219 	public float getDeltaTime () {
    220 		return deltaTime;
    221 	}
    222 
    223 	public float getRawDeltaTime () {
    224 		return deltaTime;
    225 	}
    226 
    227 	public int getFramesPerSecond () {
    228 		return fps;
    229 	}
    230 
    231 	public GraphicsType getType () {
    232 		return GraphicsType.JGLFW;
    233 	}
    234 
    235 	public GLVersion getGLVersion () {
    236 		return glVersion;
    237 	}
    238 
    239 	public float getPpiX () {
    240 		// return getWidth() / (glfwGetMonitorPhysicalWidth(getWindowMonitor()) * 0.03937f); // mm to inches
    241 		return Toolkit.getDefaultToolkit().getScreenResolution();
    242 	}
    243 
    244 	public float getPpiY () {
    245 		// return getHeight() / (glfwGetMonitorPhysicalHeight(getWindowMonitor()) * 0.03937f); // mm to inches
    246 		return Toolkit.getDefaultToolkit().getScreenResolution();
    247 	}
    248 
    249 	public float getPpcX () {
    250 		// return getWidth() / (glfwGetMonitorPhysicalWidth(getWindowMonitor()) / 10); // mm to cm
    251 		return Toolkit.getDefaultToolkit().getScreenResolution() / 2.54f;
    252 	}
    253 
    254 	public float getPpcY () {
    255 		// return getHeight() / (glfwGetMonitorPhysicalHeight(getWindowMonitor()) / 10); // mm to cm
    256 		return Toolkit.getDefaultToolkit().getScreenResolution() / 2.54f;
    257 	}
    258 
    259 	public float getDensity () {
    260 		// long monitor = getWindowMonitor();
    261 		// float mmWidth = glfwGetMonitorPhysicalWidth(monitor);
    262 		// float mmHeight = glfwGetMonitorPhysicalHeight(monitor);
    263 		// float inches = (float)Math.sqrt(mmWidth * mmWidth + mmHeight * mmHeight) * 0.03937f; // mm to inches
    264 		// float pixelWidth = getWidth();
    265 		// float pixelHeight = getHeight();
    266 		// float pixels = (float)Math.sqrt(pixelWidth * pixelWidth + pixelHeight * pixelHeight);
    267 		// float diagonalPpi = pixels / inches;
    268 		// return diagonalPpi / 160f;
    269 		return Toolkit.getDefaultToolkit().getScreenResolution() / 160f;
    270 	}
    271 
    272 	public boolean supportsDisplayModeChange () {
    273 		return true;
    274 	}
    275 
    276 	@Override
    277 	public Monitor getPrimaryMonitor () {
    278 		return new JglfwMonitor(0, 0, "Primary Monitor");
    279 	}
    280 
    281 	@Override
    282 	public Monitor getMonitor () {
    283 		return getPrimaryMonitor();
    284 	}
    285 
    286 	@Override
    287 	public Monitor[] getMonitors () {
    288 		return new Monitor[] { getPrimaryMonitor() };
    289 	}
    290 
    291 	@Override
    292 	public DisplayMode[] getDisplayModes (Monitor monitor) {
    293 		return getDisplayModes();
    294 	}
    295 
    296 	@Override
    297 	public DisplayMode getDisplayMode (Monitor monitor) {
    298 		return getDisplayMode();
    299 	}
    300 
    301 	private long getWindowMonitor () {
    302 		if (window != 0) {
    303 			long monitor = glfwGetWindowMonitor(window);
    304 			if (monitor != 0) return monitor;
    305 		}
    306 		return glfwGetPrimaryMonitor();
    307 	}
    308 
    309 	public DisplayMode[] getDisplayModes () {
    310 		Array<DisplayMode> modes = new Array();
    311 		for (GlfwVideoMode mode : glfwGetVideoModes(getWindowMonitor()))
    312 			modes.add(new JglfwDisplayMode(mode.width, mode.height, 0, mode.redBits + mode.greenBits + mode.blueBits));
    313 		return modes.toArray(DisplayMode.class);
    314 	}
    315 
    316 	public DisplayMode getDisplayMode () {
    317 		GlfwVideoMode mode = glfwGetVideoMode(getWindowMonitor());
    318 		return new JglfwDisplayMode(mode.width, mode.height, 0, mode.redBits + mode.greenBits + mode.blueBits);
    319 	}
    320 
    321 	public boolean setFullscreenMode (DisplayMode displayMode) {
    322 		bufferFormat = new BufferFormat( //
    323 			displayMode.bitsPerPixel == 16 ? 5 : 8, //
    324 			displayMode.bitsPerPixel == 16 ? 6 : 8, //
    325 			displayMode.bitsPerPixel == 16 ? 6 : 8, //
    326 			bufferFormat.a, bufferFormat.depth, bufferFormat.stencil, bufferFormat.samples, false);
    327 		boolean success = createWindow(displayMode.width, displayMode.height, fullscreen);
    328 		if (success && fullscreen) sizeChanged(displayMode.width, displayMode.height);
    329 		return success;
    330 	}
    331 
    332 	public boolean setWindowedMode (int width, int height) {
    333 		boolean fullscreen = false;
    334 		if (fullscreen || this.fullscreen) {
    335 			boolean success = createWindow(width, height, fullscreen);
    336 			if (success && fullscreen) sizeChanged(width, height);
    337 			return success;
    338 		}
    339 
    340 		glfwSetWindowSize(window, width, height);
    341 		return true;
    342 	}
    343 
    344 	public void setTitle (String title) {
    345 		if (title == null) title = "";
    346 		glfwSetWindowTitle(window, title);
    347 		this.title = title;
    348 	}
    349 
    350 	/**
    351 	 * Note: GLFW requires that the window be recreated for this change to take effect.
    352 	 */
    353 	@Override
    354 	public void setUndecorated (boolean undecorated) {
    355 		this.undecorated = undecorated;
    356 	}
    357 
    358 	/**
    359 	 * Note: GLFW requires that the window be recreated for this change to take effect.
    360 	 */
    361 	@Override
    362 	public void setResizable (boolean resizable) {
    363 		this.resizable = resizable;
    364 	}
    365 
    366 	public void setVSync (boolean vsync) {
    367 		this.vSync = vsync;
    368 		glfwSwapInterval(vsync ? 1 : 0);
    369 	}
    370 
    371 	public BufferFormat getBufferFormat () {
    372 		return bufferFormat;
    373 	}
    374 
    375 	public boolean supportsExtension (String extension) {
    376 		return glfwExtensionSupported(extension);
    377 	}
    378 
    379 	public void setContinuousRendering (boolean isContinuous) {
    380 		this.isContinuous = isContinuous;
    381 	}
    382 
    383 	public boolean isContinuousRendering () {
    384 		return isContinuous;
    385 	}
    386 
    387 	public void requestRendering () {
    388 		renderRequested = true;
    389 	}
    390 
    391 	public boolean isFullscreen () {
    392 		return fullscreen;
    393 	}
    394 
    395 	/** Returns the JGLFW window handle. Note this should not be stored externally as it may change if the window is recreated to
    396 	 * enter/exit fullscreen. */
    397 	public long getWindow () {
    398 		return window;
    399 	}
    400 
    401 	public int getX () {
    402 		return x;
    403 	}
    404 
    405 	public int getY () {
    406 		return y;
    407 	}
    408 
    409 	public void setPosition (int x, int y) {
    410 		glfwSetWindowPos(window, x, y);
    411 	}
    412 
    413 	public void hide () {
    414 		visible = false;
    415 		glfwHideWindow(window);
    416 	}
    417 
    418 	public void show () {
    419 		visible = true;
    420 		glfwShowWindow(window);
    421 
    422 		Gdx.gl.glClearColor(initialBackgroundColor.r, initialBackgroundColor.g, initialBackgroundColor.b, initialBackgroundColor.a);
    423 		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    424 		glfwSwapBuffers(window);
    425 	}
    426 
    427 	public boolean isHidden () {
    428 		return !visible;
    429 	}
    430 
    431 	public boolean isMinimized () {
    432 		return minimized;
    433 	}
    434 
    435 	public boolean isForeground () {
    436 		return foreground;
    437 	}
    438 
    439 	public void minimize () {
    440 		glfwIconifyWindow(window);
    441 	}
    442 
    443 	public void restore () {
    444 		glfwRestoreWindow(window);
    445 	}
    446 
    447 	boolean shouldRender () {
    448 		try {
    449 			return renderRequested || isContinuous;
    450 		} finally {
    451 			renderRequested = false;
    452 		}
    453 	}
    454 
    455 	@Override
    456 	public boolean isGL30Available () {
    457 		return false;
    458 	}
    459 
    460 	@Override
    461 	public GL30 getGL30 () {
    462 		return null;
    463 	}
    464 
    465 	@Override
    466 	public Cursor newCursor (Pixmap pixmap, int xHotspot, int yHotspot) {
    467 		return null;
    468 	}
    469 
    470 	@Override
    471 	public void setCursor (Cursor cursor) {
    472 	}
    473 
    474 	@Override
    475 	public void setSystemCursor (SystemCursor systemCursor) {
    476 	}
    477 
    478 	static class JglfwDisplayMode extends DisplayMode {
    479 		protected JglfwDisplayMode (int width, int height, int refreshRate, int bitsPerPixel) {
    480 			super(width, height, refreshRate, bitsPerPixel);
    481 		}
    482 	}
    483 
    484 	static class JglfwMonitor extends Monitor {
    485 		public JglfwMonitor (int virtualX, int virtualY, String name) {
    486 			super(virtualX, virtualY, name);
    487 		}
    488 	}
    489 }
    490