/******************************************************************************* * Copyright 2011 See AUTHORS file. * * 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 com.badlogic.gdx.backends.lwjgl; import java.awt.Canvas; import java.awt.Cursor; import java.awt.Dimension; import java.awt.GraphicsEnvironment; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.lwjgl.LWJGLException; import org.lwjgl.opengl.AWTGLCanvas; import org.lwjgl.opengl.PixelFormat; import com.badlogic.gdx.Application; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Audio; import com.badlogic.gdx.Files; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Graphics; import com.badlogic.gdx.Input; import com.badlogic.gdx.LifecycleListener; import com.badlogic.gdx.Net; import com.badlogic.gdx.Preferences; import com.badlogic.gdx.backends.openal.OpenALAudio; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Clipboard; import com.badlogic.gdx.utils.GdxRuntimeException; /** * An OpenGL surface on an AWT Canvas, allowing OpenGL to be embedded in a Swing application. All OpenGL calls are done * on the EDT. This is slightly less efficient then a dedicated thread, but greatly simplifies synchronization. Note * that you may need to call {@link #stop()} or a Swing application may deadlock on System.exit due to how LWJGL and/or * Swing deal with shutdown hooks. * * @author Nathan Sweet */ public class LwjglAWTCanvas implements Application { final LwjglGraphics graphics; final OpenALAudio audio; final LwjglFiles files; final LwjglAWTInput input; final LwjglNet net; final ApplicationListener listener; final AWTGLCanvas canvas; final List<Runnable> runnables = new ArrayList(); final List<Runnable> executedRunnables = new ArrayList(); final Array<LifecycleListener> lifecycleListeners = new Array<LifecycleListener>(); boolean running = true; int lastWidth; int lastHeight; int logLevel = LOG_INFO; private Cursor cursor; public LwjglAWTCanvas(ApplicationListener listener, boolean useGL2) { this(listener, useGL2, null); } public LwjglAWTCanvas(ApplicationListener listener, boolean useGL2, LwjglAWTCanvas sharedContextCanvas) { LwjglNativesLoader.load(); AWTGLCanvas sharedDrawable = sharedContextCanvas != null ? sharedContextCanvas.canvas : null; try { canvas = new AWTGLCanvas(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(), new PixelFormat(), sharedDrawable) { private final Dimension minSize = new Dimension(0, 0); public Dimension getMinimumSize() { return minSize; } @Override public void initGL() { create(); } @Override public void paintGL() { try { LwjglAWTCanvas.this.render(); swapBuffers(); repaint(); } catch (LWJGLException ex) { throw new GdxRuntimeException(ex); } } }; } catch (LWJGLException ex) { throw new GdxRuntimeException(ex); } graphics = new LwjglGraphics(canvas, useGL2) { public void setTitle(String title) { super.setTitle(title); LwjglAWTCanvas.this.setTitle(title); } public boolean setDisplayMode(int width, int height, boolean fullscreen) { if (!super.setDisplayMode(width, height, fullscreen)) return false; if (!fullscreen) LwjglAWTCanvas.this.setDisplayMode(width, height); return true; } public boolean setDisplayMode(DisplayMode displayMode) { if (!super.setDisplayMode(displayMode)) return false; LwjglAWTCanvas.this.setDisplayMode(displayMode.width, displayMode.height); return true; } }; if (!LwjglApplicationConfiguration.disableAudio && Gdx.audio == null) { audio = new OpenALAudio(); Gdx.audio = audio; } else { audio = null; } if (Gdx.files == null) { files = new LwjglFiles(); Gdx.files = files; } else { files = null; } if (Gdx.net == null) { net = new LwjglNet(); Gdx.net = net; } else { net = null; } input = new LwjglAWTInput(canvas); this.listener = listener; setGlobals(); } protected void setDisplayMode(int width, int height) { } protected void setTitle(String title) { } public Canvas getCanvas() { return canvas; } @Override public Audio getAudio() { return Gdx.audio; } @Override public Files getFiles() { return files; } @Override public Graphics getGraphics() { return graphics; } @Override public Input getInput() { return input; } @Override public Net getNet() { return net; } @Override public ApplicationType getType() { return ApplicationType.Desktop; } @Override public int getVersion() { return 0; } void setGlobals() { Gdx.app = this; if (audio != null) Gdx.audio = audio; if (files != null) Gdx.files = files; if (net != null) Gdx.net = net; Gdx.graphics = graphics; Gdx.input = input; } void create() { try { setGlobals(); graphics.initiateGLInstances(); listener.create(); lastWidth = Math.max(1, graphics.getWidth()); lastHeight = Math.max(1, graphics.getHeight()); listener.resize(lastWidth, lastHeight); start(); } catch (Exception ex) { stopped(); throw new GdxRuntimeException(ex); } } void render() { setGlobals(); canvas.setCursor(cursor); graphics.updateTime(); int width = Math.max(1, graphics.getWidth()); int height = Math.max(1, graphics.getHeight()); if (lastWidth != width || lastHeight != height) { lastWidth = width; lastHeight = height; Gdx.gl.glViewport(0, 0, lastWidth, lastHeight); resize(width, height); listener.resize(width, height); } synchronized (runnables) { executedRunnables.clear(); executedRunnables.addAll(runnables); runnables.clear(); for (int i = 0; i < executedRunnables.size(); i++) { try { executedRunnables.get(i).run(); } catch (Throwable t) { t.printStackTrace(); } } } input.processEvents(); if (running) { listener.render(); if (audio != null) { audio.update(); } } } /** Called after {@link ApplicationListener} create and resize, but before the game loop iteration. */ protected void start() { } /** Called when the canvas size changes. */ protected void resize(int width, int height) { } /** Called when the game loop has stopped. */ protected void stopped() { } public void stop() { if (!running) return; running = false; setGlobals(); Array<LifecycleListener> listeners = lifecycleListeners; synchronized (listeners) { for (LifecycleListener listener : listeners) { listener.pause(); listener.dispose(); } } listener.pause(); listener.dispose(); } @Override public long getJavaHeap() { return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); } @Override public long getNativeHeap() { return getJavaHeap(); } Map<String, Preferences> preferences = new HashMap<String, Preferences>(); @Override public Preferences getPreferences(String name) { if (preferences.containsKey(name)) { return preferences.get(name); } else { Preferences prefs = new LwjglPreferences(name); preferences.put(name, prefs); return prefs; } } @Override public Clipboard getClipboard() { return new LwjglClipboard(); } @Override public void postRunnable(Runnable runnable) { synchronized (runnables) { runnables.add(runnable); } } @Override public void debug(String tag, String message) { if (logLevel >= LOG_DEBUG) { System.out.println(tag + ": " + message); } } @Override public void debug(String tag, String message, Throwable exception) { if (logLevel >= LOG_DEBUG) { System.out.println(tag + ": " + message); exception.printStackTrace(System.out); } } public void log(String tag, String message) { if (logLevel >= LOG_INFO) { System.out.println(tag + ": " + message); } } @Override public void log(String tag, String message, Exception exception) { if (logLevel >= LOG_INFO) { System.out.println(tag + ": " + message); exception.printStackTrace(System.out); } } @Override public void error(String tag, String message) { if (logLevel >= LOG_ERROR) { System.err.println(tag + ": " + message); } } @Override public void error(String tag, String message, Throwable exception) { if (logLevel >= LOG_ERROR) { System.err.println(tag + ": " + message); exception.printStackTrace(System.err); } } @Override public void setLogLevel(int logLevel) { this.logLevel = logLevel; } @Override public void exit() { postRunnable(new Runnable() { @Override public void run() { setGlobals(); Array<LifecycleListener> listeners = lifecycleListeners; synchronized (listeners) { for (LifecycleListener listener : listeners) { listener.pause(); listener.dispose(); } } LwjglAWTCanvas.this.listener.pause(); LwjglAWTCanvas.this.listener.dispose(); System.exit(-1); } }); } /** * Make the canvas' context current. It is highly recommended that the context is only made current inside the AWT * thread (for example in an overridden paintGL()). */ public void makeCurrent() { try { canvas.makeCurrent(); setGlobals(); } catch (LWJGLException ex) { throw new GdxRuntimeException(ex); } } /** * @param cursor * May be null. */ public void setCursor(Cursor cursor) { this.cursor = cursor; } @Override public void addLifecycleListener(LifecycleListener listener) { synchronized (lifecycleListeners) { lifecycleListeners.add(listener); } } @Override public void removeLifecycleListener(LifecycleListener listener) { synchronized (lifecycleListeners) { lifecycleListeners.removeValue(listener, true); } } @Override public String getUniqueId() { return "abcd1234desktop"; } @Override public boolean isMadeInChina() { return false; } @Override public boolean hasSDCard() { return true; } @Override public void popOriginalDialog(int dialog_id, Object extra) { } @Override public void popToast(int toast_id, Object content) { } }