1 /*******************************************************************************
2 * Copyright 2011 See AUTHORS file.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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 ******************************************************************************/
17 package com.badlogic.gdx.backends.lwjgl;
19 import java.awt.Canvas;
20 import java.awt.Cursor;
21 import java.awt.Dimension;
22 import java.awt.GraphicsEnvironment;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
28 import org.lwjgl.LWJGLException;
29 import org.lwjgl.opengl.AWTGLCanvas;
30 import org.lwjgl.opengl.PixelFormat;
32 import com.badlogic.gdx.Application;
33 import com.badlogic.gdx.ApplicationListener;
34 import com.badlogic.gdx.Audio;
35 import com.badlogic.gdx.Files;
36 import com.badlogic.gdx.Gdx;
37 import com.badlogic.gdx.Graphics;
38 import com.badlogic.gdx.Input;
39 import com.badlogic.gdx.LifecycleListener;
40 import com.badlogic.gdx.Net;
41 import com.badlogic.gdx.Preferences;
42 import com.badlogic.gdx.backends.openal.OpenALAudio;
43 import com.badlogic.gdx.utils.Array;
44 import com.badlogic.gdx.utils.Clipboard;
45 import com.badlogic.gdx.utils.GdxRuntimeException;
47 /** An OpenGL surface on an AWT Canvas, allowing OpenGL to be embedded in a Swing application. All OpenGL calls are done on the
48 * EDT. This is slightly less efficient then a dedicated thread, but greatly simplifies synchronization. Note that you may need to
49 * call {@link #stop()} or a Swing application may deadlock on System.exit due to how LWJGL and/or Swing deal with shutdown hooks.
50 * @author Nathan Sweet */
51 public class LwjglAWTCanvas implements Application {
52 final LwjglGraphics graphics;
53 final OpenALAudio audio;
54 final LwjglFiles files;
55 final LwjglAWTInput input;
57 final ApplicationListener listener;
58 final AWTGLCanvas canvas;
59 final List<Runnable> runnables = new ArrayList();
60 final List<Runnable> executedRunnables = new ArrayList();
61 final Array<LifecycleListener> lifecycleListeners = new Array<LifecycleListener>();
62 boolean running = true;
65 int logLevel = LOG_INFO;
66 private Cursor cursor;
68 public LwjglAWTCanvas (ApplicationListener listener, boolean useGL2) {
69 this(listener, useGL2, null);
72 public LwjglAWTCanvas (ApplicationListener listener, boolean useGL2, LwjglAWTCanvas sharedContextCanvas) {
73 LwjglNativesLoader.load();
75 AWTGLCanvas sharedDrawable = sharedContextCanvas != null ? sharedContextCanvas.canvas : null;
77 canvas = new AWTGLCanvas(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(), new PixelFormat(),
79 private final Dimension minSize = new Dimension(0, 0);
81 public Dimension getMinimumSize () {
86 public void initGL () {
91 public void paintGL () {
93 LwjglAWTCanvas.this.render();
96 } catch (LWJGLException ex) {
97 throw new GdxRuntimeException(ex);
101 } catch (LWJGLException ex) {
102 throw new GdxRuntimeException(ex);
105 graphics = new LwjglGraphics(canvas, useGL2) {
106 public void setTitle (String title) {
107 super.setTitle(title);
108 LwjglAWTCanvas.this.setTitle(title);
111 public boolean setDisplayMode (int width, int height, boolean fullscreen) {
112 if (!super.setDisplayMode(width, height, fullscreen)) return false;
113 if (!fullscreen) LwjglAWTCanvas.this.setDisplayMode(width, height);
117 public boolean setDisplayMode (DisplayMode displayMode) {
118 if (!super.setDisplayMode(displayMode)) return false;
119 LwjglAWTCanvas.this.setDisplayMode(displayMode.width, displayMode.height);
123 if (!LwjglApplicationConfiguration.disableAudio && Gdx.audio == null) {
124 audio = new OpenALAudio();
129 if (Gdx.files == null) {
130 files = new LwjglFiles();
135 if (Gdx.net == null) {
136 net = new LwjglNet();
141 input = new LwjglAWTInput(canvas);
142 this.listener = listener;
147 protected void setDisplayMode (int width, int height) {
150 protected void setTitle (String title) {
154 public ApplicationListener getApplicationListener () {
158 public Canvas getCanvas () {
163 public Audio getAudio () {
168 public Files getFiles () {
173 public Graphics getGraphics () {
178 public Input getInput () {
183 public Net getNet () {
188 public ApplicationType getType () {
189 return ApplicationType.Desktop;
193 public int getVersion () {
199 if (audio != null) Gdx.audio = audio;
200 if (files != null) Gdx.files = files;
201 if (net != null) Gdx.net = net;
202 Gdx.graphics = graphics;
209 graphics.initiateGLInstances();
211 lastWidth = Math.max(1, graphics.getWidth());
212 lastHeight = Math.max(1, graphics.getHeight());
213 listener.resize(lastWidth, lastHeight);
215 } catch (Exception ex) {
217 throw new GdxRuntimeException(ex);
222 if (!running) return;
225 canvas.setCursor(cursor);
226 graphics.updateTime();
228 int width = Math.max(1, graphics.getWidth());
229 int height = Math.max(1, graphics.getHeight());
230 if (lastWidth != width || lastHeight != height) {
233 Gdx.gl.glViewport(0, 0, lastWidth, lastHeight);
234 resize(width, height);
235 listener.resize(width, height);
238 synchronized (runnables) {
239 executedRunnables.clear();
240 executedRunnables.addAll(runnables);
243 for (int i = 0; i < executedRunnables.size(); i++) {
245 executedRunnables.get(i).run();
246 } catch (Throwable t) {
252 input.processEvents();
261 /** Called after {@link ApplicationListener} create and resize, but before the game loop iteration. */
262 protected void start () {
265 /** Called when the canvas size changes. */
266 protected void resize (int width, int height) {
269 /** Called when the game loop has stopped. */
270 protected void stopped () {
273 public void stop () {
274 if (!running) return;
277 Array<LifecycleListener> listeners = lifecycleListeners;
278 synchronized (listeners) {
279 for (LifecycleListener listener : listeners) {
296 if (files != null) Gdx.files = null;
298 if (net != null) Gdx.net = null;
304 public long getJavaHeap () {
305 return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
309 public long getNativeHeap () {
310 return getJavaHeap();
313 Map<String, Preferences> preferences = new HashMap<String, Preferences>();
316 public Preferences getPreferences (String name) {
317 if (preferences.containsKey(name)) {
318 return preferences.get(name);
320 Preferences prefs = new LwjglPreferences(name);
321 preferences.put(name, prefs);
327 public Clipboard getClipboard () {
328 return new LwjglClipboard();
332 public void postRunnable (Runnable runnable) {
333 synchronized (runnables) {
334 runnables.add(runnable);
339 public void debug (String tag, String message) {
340 if (logLevel >= LOG_DEBUG) {
341 System.out.println(tag + ": " + message);
346 public void debug (String tag, String message, Throwable exception) {
347 if (logLevel >= LOG_DEBUG) {
348 System.out.println(tag + ": " + message);
349 exception.printStackTrace(System.out);
353 public void log (String tag, String message) {
354 if (logLevel >= LOG_INFO) {
355 System.out.println(tag + ": " + message);
360 public void log (String tag, String message, Exception exception) {
361 if (logLevel >= LOG_INFO) {
362 System.out.println(tag + ": " + message);
363 exception.printStackTrace(System.out);
368 public void error (String tag, String message) {
369 if (logLevel >= LOG_ERROR) {
370 System.err.println(tag + ": " + message);
375 public void error (String tag, String message, Throwable exception) {
376 if (logLevel >= LOG_ERROR) {
377 System.err.println(tag + ": " + message);
378 exception.printStackTrace(System.err);
383 public void setLogLevel (int logLevel) {
384 this.logLevel = logLevel;
388 public int getLogLevel() {
393 public void exit () {
394 postRunnable(new Runnable() {
403 /** Make the canvas' context current. It is highly recommended that the context is only made current inside the AWT thread (for
404 * example in an overridden paintGL()). */
405 public void makeCurrent () {
407 canvas.makeCurrent();
409 } catch (LWJGLException ex) {
410 throw new GdxRuntimeException(ex);
414 /** Test whether the canvas' context is current. */
415 public boolean isCurrent () {
417 return canvas.isCurrent();
418 } catch (LWJGLException ex) {
419 throw new GdxRuntimeException(ex);
423 /** @param cursor May be null. */
424 public void setCursor (Cursor cursor) {
425 this.cursor = cursor;
429 public void addLifecycleListener (LifecycleListener listener) {
430 synchronized (lifecycleListeners) {
431 lifecycleListeners.add(listener);
436 public void removeLifecycleListener (LifecycleListener listener) {
437 synchronized (lifecycleListeners) {
438 lifecycleListeners.removeValue(listener, true);