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 17package com.badlogic.gdx.backends.lwjgl; 18 19import java.awt.Canvas; 20import java.io.File; 21 22import org.lwjgl.LWJGLException; 23import org.lwjgl.opengl.Display; 24 25import com.badlogic.gdx.Application; 26import com.badlogic.gdx.ApplicationListener; 27import com.badlogic.gdx.Audio; 28import com.badlogic.gdx.Files; 29import com.badlogic.gdx.Gdx; 30import com.badlogic.gdx.Input; 31import com.badlogic.gdx.LifecycleListener; 32import com.badlogic.gdx.Net; 33import com.badlogic.gdx.Preferences; 34import com.badlogic.gdx.backends.lwjgl.audio.OpenALAudio; 35import com.badlogic.gdx.utils.Array; 36import com.badlogic.gdx.utils.Clipboard; 37import com.badlogic.gdx.utils.GdxRuntimeException; 38import com.badlogic.gdx.utils.ObjectMap; 39import com.badlogic.gdx.utils.SnapshotArray; 40 41/** An OpenGL surface fullscreen or in a lightweight window. */ 42public class LwjglApplication implements Application { 43 protected final LwjglGraphics graphics; 44 protected OpenALAudio audio; 45 protected final LwjglFiles files; 46 protected final LwjglInput input; 47 protected final LwjglNet net; 48 protected final ApplicationListener listener; 49 protected Thread mainLoopThread; 50 protected boolean running = true; 51 protected final Array<Runnable> runnables = new Array<Runnable>(); 52 protected final Array<Runnable> executedRunnables = new Array<Runnable>(); 53 protected final SnapshotArray<LifecycleListener> lifecycleListeners = new SnapshotArray<LifecycleListener>(LifecycleListener.class); 54 protected int logLevel = LOG_INFO; 55 protected String preferencesdir; 56 protected Files.FileType preferencesFileType; 57 58 public LwjglApplication (ApplicationListener listener, String title, int width, int height) { 59 this(listener, createConfig(title, width, height)); 60 } 61 62 public LwjglApplication (ApplicationListener listener) { 63 this(listener, null, 640, 480); 64 } 65 66 public LwjglApplication (ApplicationListener listener, LwjglApplicationConfiguration config) { 67 this(listener, config, new LwjglGraphics(config)); 68 } 69 70 public LwjglApplication (ApplicationListener listener, Canvas canvas) { 71 this(listener, new LwjglApplicationConfiguration(), new LwjglGraphics(canvas)); 72 } 73 74 public LwjglApplication (ApplicationListener listener, LwjglApplicationConfiguration config, Canvas canvas) { 75 this(listener, config, new LwjglGraphics(canvas, config)); 76 } 77 78 public LwjglApplication (ApplicationListener listener, LwjglApplicationConfiguration config, LwjglGraphics graphics) { 79 LwjglNativesLoader.load(); 80 81 if (config.title == null) config.title = listener.getClass().getSimpleName(); 82 83 this.graphics = graphics; 84 if (!LwjglApplicationConfiguration.disableAudio) { 85 try { 86 audio = new OpenALAudio(config.audioDeviceSimultaneousSources, config.audioDeviceBufferCount, 87 config.audioDeviceBufferSize); 88 } catch (Throwable t) { 89 log("LwjglApplication", "Couldn't initialize audio, disabling audio", t); 90 LwjglApplicationConfiguration.disableAudio = true; 91 } 92 } 93 files = new LwjglFiles(); 94 input = new LwjglInput(); 95 net = new LwjglNet(); 96 this.listener = listener; 97 this.preferencesdir = config.preferencesDirectory; 98 this.preferencesFileType = config.preferencesFileType; 99 100 Gdx.app = this; 101 Gdx.graphics = graphics; 102 Gdx.audio = audio; 103 Gdx.files = files; 104 Gdx.input = input; 105 Gdx.net = net; 106 initialize(); 107 } 108 109 private static LwjglApplicationConfiguration createConfig (String title, int width, int height) { 110 LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); 111 config.title = title; 112 config.width = width; 113 config.height = height; 114 config.vSyncEnabled = true; 115 return config; 116 } 117 118 private void initialize () { 119 mainLoopThread = new Thread("LWJGL Application") { 120 @Override 121 public void run () { 122 graphics.setVSync(graphics.config.vSyncEnabled); 123 try { 124 LwjglApplication.this.mainLoop(); 125 } catch (Throwable t) { 126 if (audio != null) audio.dispose(); 127 Gdx.input.setCursorCatched(false); 128 if (t instanceof RuntimeException) 129 throw (RuntimeException)t; 130 else 131 throw new GdxRuntimeException(t); 132 } 133 } 134 }; 135 mainLoopThread.start(); 136 } 137 138 void mainLoop () { 139 SnapshotArray<LifecycleListener> lifecycleListeners = this.lifecycleListeners; 140 141 try { 142 graphics.setupDisplay(); 143 } catch (LWJGLException e) { 144 throw new GdxRuntimeException(e); 145 } 146 147 listener.create(); 148 graphics.resize = true; 149 150 int lastWidth = graphics.getWidth(); 151 int lastHeight = graphics.getHeight(); 152 153 graphics.lastTime = System.nanoTime(); 154 boolean wasActive = true; 155 while (running) { 156 Display.processMessages(); 157 if (Display.isCloseRequested()) exit(); 158 159 boolean isActive = Display.isActive(); 160 if (wasActive && !isActive) { // if it's just recently minimized from active state 161 wasActive = false; 162 synchronized (lifecycleListeners) { 163 LifecycleListener[] listeners = lifecycleListeners.begin(); 164 for (int i = 0, n = lifecycleListeners.size; i < n; ++i) 165 listeners[i].pause(); 166 lifecycleListeners.end(); 167 } 168 listener.pause(); 169 } 170 if (!wasActive && isActive) { // if it's just recently focused from minimized state 171 wasActive = true; 172 synchronized (lifecycleListeners) { 173 LifecycleListener[] listeners = lifecycleListeners.begin(); 174 for (int i = 0, n = lifecycleListeners.size; i < n; ++i) 175 listeners[i].resume(); 176 lifecycleListeners.end(); 177 } 178 listener.resume(); 179 } 180 181 boolean shouldRender = false; 182 183 if (graphics.canvas != null) { 184 int width = graphics.canvas.getWidth(); 185 int height = graphics.canvas.getHeight(); 186 if (lastWidth != width || lastHeight != height) { 187 lastWidth = width; 188 lastHeight = height; 189 Gdx.gl.glViewport(0, 0, lastWidth, lastHeight); 190 listener.resize(lastWidth, lastHeight); 191 shouldRender = true; 192 } 193 } else { 194 graphics.config.x = Display.getX(); 195 graphics.config.y = Display.getY(); 196 if (graphics.resize || Display.wasResized() 197 || (int)(Display.getWidth() * Display.getPixelScaleFactor()) != graphics.config.width 198 || (int)(Display.getHeight() * Display.getPixelScaleFactor()) != graphics.config.height) { 199 graphics.resize = false; 200 graphics.config.width = (int)(Display.getWidth() * Display.getPixelScaleFactor()); 201 graphics.config.height = (int)(Display.getHeight() * Display.getPixelScaleFactor()); 202 Gdx.gl.glViewport(0, 0, graphics.config.width, graphics.config.height); 203 if (listener != null) listener.resize(graphics.config.width, graphics.config.height); 204 graphics.requestRendering(); 205 } 206 } 207 208 if (executeRunnables()) shouldRender = true; 209 210 // If one of the runnables set running to false, for example after an exit(). 211 if (!running) break; 212 213 input.update(); 214 shouldRender |= graphics.shouldRender(); 215 input.processEvents(); 216 if (audio != null) audio.update(); 217 218 if (!isActive && graphics.config.backgroundFPS == -1) shouldRender = false; 219 int frameRate = isActive ? graphics.config.foregroundFPS : graphics.config.backgroundFPS; 220 if (shouldRender) { 221 graphics.updateTime(); 222 graphics.frameId++; 223 listener.render(); 224 Display.update(false); 225 } else { 226 // Sleeps to avoid wasting CPU in an empty loop. 227 if (frameRate == -1) frameRate = 10; 228 if (frameRate == 0) frameRate = graphics.config.backgroundFPS; 229 if (frameRate == 0) frameRate = 30; 230 } 231 if (frameRate > 0) Display.sync(frameRate); 232 } 233 234 synchronized (lifecycleListeners) { 235 LifecycleListener[] listeners = lifecycleListeners.begin(); 236 for (int i = 0, n = lifecycleListeners.size; i < n; ++i) { 237 listeners[i].pause(); 238 listeners[i].dispose(); 239 } 240 lifecycleListeners.end(); 241 } 242 listener.pause(); 243 listener.dispose(); 244 Display.destroy(); 245 if (audio != null) audio.dispose(); 246 if (graphics.config.forceExit) System.exit(-1); 247 } 248 249 public boolean executeRunnables () { 250 synchronized (runnables) { 251 for (int i = runnables.size - 1; i >= 0; i--) 252 executedRunnables.add(runnables.get(i)); 253 runnables.clear(); 254 } 255 if (executedRunnables.size == 0) return false; 256 do 257 executedRunnables.pop().run(); 258 while (executedRunnables.size > 0); 259 return true; 260 } 261 262 @Override 263 public ApplicationListener getApplicationListener () { 264 return listener; 265 } 266 267 @Override 268 public Audio getAudio () { 269 return audio; 270 } 271 272 @Override 273 public Files getFiles () { 274 return files; 275 } 276 277 @Override 278 public LwjglGraphics getGraphics () { 279 return graphics; 280 } 281 282 @Override 283 public Input getInput () { 284 return input; 285 } 286 287 @Override 288 public Net getNet () { 289 return net; 290 } 291 292 @Override 293 public ApplicationType getType () { 294 return ApplicationType.Desktop; 295 } 296 297 @Override 298 public int getVersion () { 299 return 0; 300 } 301 302 public void stop () { 303 running = false; 304 try { 305 mainLoopThread.join(); 306 } catch (Exception ex) { 307 } 308 } 309 310 @Override 311 public long getJavaHeap () { 312 return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); 313 } 314 315 @Override 316 public long getNativeHeap () { 317 return getJavaHeap(); 318 } 319 320 ObjectMap<String, Preferences> preferences = new ObjectMap<String, Preferences>(); 321 322 @Override 323 public Preferences getPreferences (String name) { 324 if (preferences.containsKey(name)) { 325 return preferences.get(name); 326 } else { 327 Preferences prefs = new LwjglPreferences(new LwjglFileHandle(new File(preferencesdir, name), preferencesFileType)); 328 preferences.put(name, prefs); 329 return prefs; 330 } 331 } 332 333 @Override 334 public Clipboard getClipboard () { 335 return new LwjglClipboard(); 336 } 337 338 @Override 339 public void postRunnable (Runnable runnable) { 340 synchronized (runnables) { 341 runnables.add(runnable); 342 Gdx.graphics.requestRendering(); 343 } 344 } 345 346 @Override 347 public void debug (String tag, String message) { 348 if (logLevel >= LOG_DEBUG) { 349 System.out.println(tag + ": " + message); 350 } 351 } 352 353 @Override 354 public void debug (String tag, String message, Throwable exception) { 355 if (logLevel >= LOG_DEBUG) { 356 System.out.println(tag + ": " + message); 357 exception.printStackTrace(System.out); 358 } 359 } 360 361 @Override 362 public void log (String tag, String message) { 363 if (logLevel >= LOG_INFO) { 364 System.out.println(tag + ": " + message); 365 } 366 } 367 368 @Override 369 public void log (String tag, String message, Throwable exception) { 370 if (logLevel >= LOG_INFO) { 371 System.out.println(tag + ": " + message); 372 exception.printStackTrace(System.out); 373 } 374 } 375 376 @Override 377 public void error (String tag, String message) { 378 if (logLevel >= LOG_ERROR) { 379 System.err.println(tag + ": " + message); 380 } 381 } 382 383 @Override 384 public void error (String tag, String message, Throwable exception) { 385 if (logLevel >= LOG_ERROR) { 386 System.err.println(tag + ": " + message); 387 exception.printStackTrace(System.err); 388 } 389 } 390 391 @Override 392 public void setLogLevel (int logLevel) { 393 this.logLevel = logLevel; 394 } 395 396 @Override 397 public int getLogLevel () { 398 return logLevel; 399 } 400 401 @Override 402 public void exit () { 403 postRunnable(new Runnable() { 404 @Override 405 public void run () { 406 running = false; 407 } 408 }); 409 } 410 411 @Override 412 public void addLifecycleListener (LifecycleListener listener) { 413 synchronized (lifecycleListeners) { 414 lifecycleListeners.add(listener); 415 } 416 } 417 418 @Override 419 public void removeLifecycleListener (LifecycleListener listener) { 420 synchronized (lifecycleListeners) { 421 lifecycleListeners.removeValue(listener, true); 422 } 423 } 424} 425