/** * Copyright 2012 Jason Sorensen (sorensenj@smert.net) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package net.smert.frameworkgl; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Jason Sorensen <sorensenj@smert.net> */ public class Application { private final static Logger log = LoggerFactory.getLogger(Application.class); private volatile boolean isRunning; private final Configuration config; private final Logging logging; private Screen screen; private Thread mainLoopThread; private ThrowableHandler throwableHandler; public Application(Configuration config, Logging logging, ThrowableHandler throwableHandler) { this.config = config; this.logging = logging; this.throwableHandler = throwableHandler; } private void startMainLoopThread() { mainLoopThread = new Thread("LWJGL Application - Main Loop") { @Override public void run() { boolean exceptionCaught = false; try { Fw.window.init(); Fw.window.create(); Fw.graphics.init(); Fw.input.init(); Application.this.mainLoop(); } catch (Throwable t) { exceptionCaught = true; log.error("Main Loop Thread Exception", t); Application.this.handleThrowable(t); } finally { Fw.audio.destroy(); Fw.net.destroy(); Fw.graphics.destroy(); // Shutdown in reverse order Fw.window.destroy(); if (exceptionCaught) { System.exit(-1); } } } }; mainLoopThread.start(); log.info("Thread Started"); } void handleThrowable(Throwable t) { throwableHandler.process(t); } void mainLoop() { // Initialize screen screen.init(); // Reset timer so delta is correct Fw.timer.reset(); // Set the window previously active boolean wasActive = true; while (isRunning()) { // Process operating system events Fw.input.clearEvents(); Fw.window.processMessages(); config.inFocus = Fw.window.isFocused(); // Lost focus if (wasActive && !config.inFocus && config.pauseNotInFocus) { wasActive = false; screen.pause(); } // Regained focus if (!wasActive && config.inFocus && config.pauseNotInFocus) { wasActive = true; screen.resume(); } // Was the window closed? if (Fw.window.isCloseRequested()) { stopRunning(); break; } // Was the window resized? if (Fw.window.wasResized()) { Fw.window.setWasResized(false); screen.resize(config.currentWidth, config.currentHeight); } // Update and render Fw.input.update(); Fw.timer.update(); screen.render(); Fw.window.update(); // Limit frame rate int frameRateLimit; if (config.inFocus) { frameRateLimit = config.foregroundFrameRate; } else { frameRateLimit = config.backgroundFrameRate; } Fw.window.sync(frameRateLimit); } // Shutdown screen.pause(); screen.destroy(); } public boolean isRunning() { return isRunning; } public void run(Screen screen) throws IOException { this.screen = screen; logging.reset(); // Configure logging startRunning(); startMainLoopThread(); } public void setThrowableHandler(ThrowableHandler throwableHandler) { this.throwableHandler = throwableHandler; } /** * Switches to a new screen. This method calls pause() and destroy() on the existing screen. With this in mind you * should only call this method from inside the current screen's render() method. The new screen's init() and * resize() methods will be called. * * @param screen The new screen which will be switched to. */ public void switchScreen(Screen screen) { if (this.screen != null) { this.screen.pause(); this.screen.destroy(); } screen.init(); screen.resize(config.currentWidth, config.currentHeight); this.screen = screen; } public void startRunning() { isRunning = true; } public void stopRunning() { isRunning = false; } }