blob: e2d396b82f1ff595630ed23f079e18ba3452faae [file] [log] [blame]
/*******************************************************************************
* 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.EventQueue;
import java.util.HashMap;
import java.util.Map;
import org.lwjgl.opengl.AWTGLCanvas;
import org.lwjgl.opengl.Display;
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.lwjgl.audio.OpenALAudio;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Clipboard;
import com.badlogic.gdx.utils.SharedLibraryLoader;
/** An OpenGL surface on an AWT Canvas, allowing OpenGL to be embedded in a Swing application. This uses
* {@link Display#setParent(Canvas)}, which is preferred over {@link AWTGLCanvas} but is limited to a single LwjglCanvas in an
* application. All OpenGL calls are done on the EDT. 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 LwjglCanvas implements Application {
static boolean isWindows = System.getProperty("os.name").contains("Windows");
LwjglGraphics graphics;
OpenALAudio audio;
LwjglFiles files;
LwjglInput input;
LwjglNet net;
ApplicationListener listener;
Canvas canvas;
final Array<Runnable> runnables = new Array();
final Array<Runnable> executedRunnables = new Array();
final Array<LifecycleListener> lifecycleListeners = new Array<LifecycleListener>();
boolean running = true;
int logLevel = LOG_INFO;
Cursor cursor;
public LwjglCanvas (ApplicationListener listener) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
initialize(listener, config);
}
public LwjglCanvas (ApplicationListener listener, LwjglApplicationConfiguration config) {
initialize(listener, config);
}
private void initialize (ApplicationListener listener, LwjglApplicationConfiguration config) {
LwjglNativesLoader.load();
canvas = new Canvas() {
private final Dimension minSize = new Dimension(1, 1);
public final void addNotify () {
super.addNotify();
if (SharedLibraryLoader.isMac) {
EventQueue.invokeLater(new Runnable() {
public void run () {
create();
}
});
} else
create();
}
public final void removeNotify () {
stop();
super.removeNotify();
}
public Dimension getMinimumSize () {
return minSize;
}
};
canvas.setSize(1, 1);
canvas.setIgnoreRepaint(true);
graphics = new LwjglGraphics(canvas, config) {
public void setTitle (String title) {
super.setTitle(title);
LwjglCanvas.this.setTitle(title);
}
public boolean setWindowedMode (int width, int height, boolean fullscreen) {
if (!super.setWindowedMode(width, height)) return false;
if (!fullscreen) LwjglCanvas.this.setDisplayMode(width, height);
return true;
}
public boolean setFullscreenMode (DisplayMode displayMode) {
if (!super.setFullscreenMode(displayMode)) return false;
LwjglCanvas.this.setDisplayMode(displayMode.width, displayMode.height);
return true;
}
};
graphics.setVSync(config.vSyncEnabled);
if (!LwjglApplicationConfiguration.disableAudio) audio = new OpenALAudio();
files = new LwjglFiles();
input = new LwjglInput();
net = new LwjglNet();
this.listener = listener;
Gdx.app = this;
Gdx.graphics = graphics;
Gdx.audio = audio;
Gdx.files = files;
Gdx.input = input;
Gdx.net = net;
}
protected void setDisplayMode (int width, int height) {
}
protected void setTitle (String title) {
}
@Override
public ApplicationListener getApplicationListener () {
return listener;
}
public Canvas getCanvas () {
return canvas;
}
@Override
public Audio getAudio () {
return 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 create () {
try {
graphics.setupDisplay();
listener.create();
listener.resize(Math.max(1, graphics.getWidth()), Math.max(1, graphics.getHeight()));
start();
} catch (Exception ex) {
stopped();
exception(ex);
return;
}
EventQueue.invokeLater(new Runnable() {
int lastWidth = Math.max(1, graphics.getWidth());
int lastHeight = Math.max(1, graphics.getHeight());
public void run () {
if (!running || Display.isCloseRequested()) {
running = false;
stopped();
return;
}
try {
Display.processMessages();
if (cursor != null || !isWindows) canvas.setCursor(cursor);
boolean shouldRender = false;
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);
shouldRender = true;
}
if (executeRunnables()) shouldRender = true;
// If one of the runnables set running to false, for example after an exit().
if (!running) return;
input.update();
shouldRender |= graphics.shouldRender();
input.processEvents();
if (audio != null) audio.update();
if (shouldRender) {
graphics.updateTime();
graphics.frameId++;
listener.render();
Display.update(false);
}
Display.sync(getFrameRate());
} catch (Throwable ex) {
exception(ex);
}
EventQueue.invokeLater(this);
}
});
}
public boolean executeRunnables () {
synchronized (runnables) {
for (int i = runnables.size - 1; i >= 0; i--)
executedRunnables.addAll(runnables.get(i));
runnables.clear();
}
if (executedRunnables.size == 0) return false;
do
executedRunnables.pop().run();
while (executedRunnables.size > 0);
return true;
}
protected int getFrameRate () {
int frameRate = Display.isActive() ? graphics.config.foregroundFPS : graphics.config.backgroundFPS;
if (frameRate == -1) frameRate = 10;
if (frameRate == 0) frameRate = graphics.config.backgroundFPS;
if (frameRate == 0) frameRate = 30;
return frameRate;
}
protected void exception (Throwable ex) {
ex.printStackTrace();
stop();
}
/** 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 () {
EventQueue.invokeLater(new Runnable() {
public void run () {
if (!running) return;
running = false;
try {
Display.destroy();
if (audio != null) audio.dispose();
} catch (Throwable ignored) {
}
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, ".prefs/");
preferences.put(name, prefs);
return prefs;
}
}
@Override
public Clipboard getClipboard () {
return new LwjglClipboard();
}
@Override
public void postRunnable (Runnable runnable) {
synchronized (runnables) {
runnables.add(runnable);
Gdx.graphics.requestRendering();
}
}
@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, Throwable 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 int getLogLevel () {
return logLevel;
}
@Override
public void exit () {
postRunnable(new Runnable() {
@Override
public void run () {
LwjglCanvas.this.listener.pause();
LwjglCanvas.this.listener.dispose();
if (audio != null) audio.dispose();
System.exit(-1);
}
});
}
/** @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);
}
}
}