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