• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.lwjgl;
18 
19 import java.awt.Canvas;
20 import java.io.File;
21 
22 import org.lwjgl.LWJGLException;
23 import org.lwjgl.opengl.Display;
24 
25 import com.badlogic.gdx.Application;
26 import com.badlogic.gdx.ApplicationListener;
27 import com.badlogic.gdx.Audio;
28 import com.badlogic.gdx.Files;
29 import com.badlogic.gdx.Gdx;
30 import com.badlogic.gdx.Input;
31 import com.badlogic.gdx.LifecycleListener;
32 import com.badlogic.gdx.Net;
33 import com.badlogic.gdx.Preferences;
34 import com.badlogic.gdx.backends.lwjgl.audio.OpenALAudio;
35 import com.badlogic.gdx.utils.Array;
36 import com.badlogic.gdx.utils.Clipboard;
37 import com.badlogic.gdx.utils.GdxRuntimeException;
38 import com.badlogic.gdx.utils.ObjectMap;
39 import com.badlogic.gdx.utils.SnapshotArray;
40 
41 /** An OpenGL surface fullscreen or in a lightweight window. */
42 public 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 
LwjglApplication(ApplicationListener listener, String title, int width, int height)58 	public LwjglApplication (ApplicationListener listener, String title, int width, int height) {
59 		this(listener, createConfig(title, width, height));
60 	}
61 
LwjglApplication(ApplicationListener listener)62 	public LwjglApplication (ApplicationListener listener) {
63 		this(listener, null, 640, 480);
64 	}
65 
LwjglApplication(ApplicationListener listener, LwjglApplicationConfiguration config)66 	public LwjglApplication (ApplicationListener listener, LwjglApplicationConfiguration config) {
67 		this(listener, config, new LwjglGraphics(config));
68 	}
69 
LwjglApplication(ApplicationListener listener, Canvas canvas)70 	public LwjglApplication (ApplicationListener listener, Canvas canvas) {
71 		this(listener, new LwjglApplicationConfiguration(), new LwjglGraphics(canvas));
72 	}
73 
LwjglApplication(ApplicationListener listener, LwjglApplicationConfiguration config, Canvas canvas)74 	public LwjglApplication (ApplicationListener listener, LwjglApplicationConfiguration config, Canvas canvas) {
75 		this(listener, config, new LwjglGraphics(canvas, config));
76 	}
77 
LwjglApplication(ApplicationListener listener, LwjglApplicationConfiguration config, LwjglGraphics graphics)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 
createConfig(String title, int width, int height)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 
initialize()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 
mainLoop()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 
executeRunnables()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
getApplicationListener()263 	public ApplicationListener getApplicationListener () {
264 		return listener;
265 	}
266 
267 	@Override
getAudio()268 	public Audio getAudio () {
269 		return audio;
270 	}
271 
272 	@Override
getFiles()273 	public Files getFiles () {
274 		return files;
275 	}
276 
277 	@Override
getGraphics()278 	public LwjglGraphics getGraphics () {
279 		return graphics;
280 	}
281 
282 	@Override
getInput()283 	public Input getInput () {
284 		return input;
285 	}
286 
287 	@Override
getNet()288 	public Net getNet () {
289 		return net;
290 	}
291 
292 	@Override
getType()293 	public ApplicationType getType () {
294 		return ApplicationType.Desktop;
295 	}
296 
297 	@Override
getVersion()298 	public int getVersion () {
299 		return 0;
300 	}
301 
stop()302 	public void stop () {
303 		running = false;
304 		try {
305 			mainLoopThread.join();
306 		} catch (Exception ex) {
307 		}
308 	}
309 
310 	@Override
getJavaHeap()311 	public long getJavaHeap () {
312 		return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
313 	}
314 
315 	@Override
getNativeHeap()316 	public long getNativeHeap () {
317 		return getJavaHeap();
318 	}
319 
320 	ObjectMap<String, Preferences> preferences = new ObjectMap<String, Preferences>();
321 
322 	@Override
getPreferences(String name)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
getClipboard()334 	public Clipboard getClipboard () {
335 		return new LwjglClipboard();
336 	}
337 
338 	@Override
postRunnable(Runnable runnable)339 	public void postRunnable (Runnable runnable) {
340 		synchronized (runnables) {
341 			runnables.add(runnable);
342 			Gdx.graphics.requestRendering();
343 		}
344 	}
345 
346 	@Override
debug(String tag, String message)347 	public void debug (String tag, String message) {
348 		if (logLevel >= LOG_DEBUG) {
349 			System.out.println(tag + ": " + message);
350 		}
351 	}
352 
353 	@Override
debug(String tag, String message, Throwable exception)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
log(String tag, String message)362 	public void log (String tag, String message) {
363 		if (logLevel >= LOG_INFO) {
364 			System.out.println(tag + ": " + message);
365 		}
366 	}
367 
368 	@Override
log(String tag, String message, Throwable exception)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
error(String tag, String message)377 	public void error (String tag, String message) {
378 		if (logLevel >= LOG_ERROR) {
379 			System.err.println(tag + ": " + message);
380 		}
381 	}
382 
383 	@Override
error(String tag, String message, Throwable exception)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
setLogLevel(int logLevel)392 	public void setLogLevel (int logLevel) {
393 		this.logLevel = logLevel;
394 	}
395 
396 	@Override
getLogLevel()397 	public int getLogLevel () {
398 		return logLevel;
399 	}
400 
401 	@Override
exit()402 	public void exit () {
403 		postRunnable(new Runnable() {
404 			@Override
405 			public void run () {
406 				running = false;
407 			}
408 		});
409 	}
410 
411 	@Override
addLifecycleListener(LifecycleListener listener)412 	public void addLifecycleListener (LifecycleListener listener) {
413 		synchronized (lifecycleListeners) {
414 			lifecycleListeners.add(listener);
415 		}
416 	}
417 
418 	@Override
removeLifecycleListener(LifecycleListener listener)419 	public void removeLifecycleListener (LifecycleListener listener) {
420 		synchronized (lifecycleListeners) {
421 			lifecycleListeners.removeValue(listener, true);
422 		}
423 	}
424 }
425