All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.badlogic.gdx.backends.lwjgl.LwjglCanvas Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * 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.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

import com.badlogic.gdx.backends.lwjgl.audio.LwjglAudio;
import com.badlogic.gdx.utils.*;
import org.lwjgl.opengl.AWTGLCanvas;
import org.lwjgl.opengl.Display;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.ApplicationLogger;
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.OpenALLwjglAudio;

import java.awt.Canvas;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.geom.AffineTransform;

/** 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 LwjglApplicationBase {
	static boolean isWindows = System.getProperty("os.name").contains("Windows");

	LwjglGraphics graphics;
	LwjglAudio audio;
	Files files;
	LwjglInput input;
	LwjglNet net;
	ApplicationListener listener;
	Canvas canvas;
	final Array runnables = new Array();
	final Array executedRunnables = new Array();
	final Array lifecycleListeners = new Array();
	boolean running = true;
	int logLevel = LOG_INFO;
	ApplicationLogger applicationLogger;
	Cursor cursor;
	float scaleX, scaleY;
	boolean postedRunnableStacktraces;
	final Map preferences = new HashMap();

	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();
		setApplicationLogger(new LwjglApplicationLogger());
		canvas = new Canvas() {
			private final Dimension minSize = new Dimension(1, 1);

			public final void addNotify () {
				super.addNotify();

				AffineTransform transform = getGraphicsConfiguration().getDefaultTransform();
				scaleX = (float)transform.getScaleX();
				scaleY = (float)transform.getScaleY();

				if (SharedLibraryLoader.os == Os.MacOsX) {
					EventQueue.invokeLater(new Runnable() {
						public void run () {
							create();
						}
					});
				} else
					create();
			}

			public final void removeNotify () {
				stop();
				super.removeNotify();
			}

			public Dimension getMinimumSize () {
				return minSize;
			}

			public int getWidth () {
				return Math.round(super.getWidth() * scaleX);
			}

			public int getHeight () {
				return Math.round(super.getHeight() * scaleY);
			}
		};
		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 = createAudio(config);
		files = createFiles();
		input = createInput(config);
		net = new LwjglNet(config);
		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) {
			try {
				Display.destroy();
				if (audio != null) audio.dispose();
			} catch (Throwable ignored) {
			}
			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();
					applyCursor(cursor);

					boolean shouldRender = checkResize();

					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 (checkResize()) shouldRender = true;

					if (shouldRender) {
						graphics.updateTime();
						graphics.frameId++;
						listener.render();
						Display.update(false);
					}

					Display.sync(getFrameRate());
				} catch (Throwable ex) {
					exception(ex);
				}
				EventQueue.invokeLater(this);
			}

			private boolean checkResize () {
				Container parent = canvas.getParent();
				if (parent != null && (canvas.getWidth() != parent.getWidth() || canvas.getHeight() != parent.getHeight()))
					canvas.setSize(parent.getWidth(), parent.getHeight());

				int width = Math.max(1, graphics.getWidth());
				int height = Math.max(1, graphics.getHeight());
				if (lastWidth == width && lastHeight == height) return false;
				lastWidth = width;
				lastHeight = height;

				Display.setLocation(0, 0);
				Gdx.gl.glViewport(0, 0, width, height);
				resize(width, height);
				listener.resize(width, height);
				return true;
			}
		});
	}

	protected void applyCursor (Cursor cursor) {
		if (cursor != null || !isWindows) {
			try {
				canvas.setCursor(cursor);
			} catch (Throwable ignored) { // Seems to fail on Linux sometimes.
			}
		}
	}

	public boolean executeRunnables () {
		synchronized (runnables) {
			for (int i = runnables.size - 1; i >= 0; i--)
				executedRunnables.add(runnables.get(i));
			runnables.clear();
		}
		if (executedRunnables.size == 0) return false;
		do {
			Runnable runnable = (Runnable)executedRunnables.pop();
			Throwable caller = (Throwable)executedRunnables.pop();
			try {
				runnable.run();
			} catch (Throwable ex) {
				postedException(ex, caller);
			}
		} while (executedRunnables.size > 0);
		return true;
	}

	protected void postedException (Throwable ex, @Null Throwable caller) {
		if (caller == null) throw new RuntimeException(ex);
		StringWriter buffer = new StringWriter(1024);
		caller.printStackTrace(new PrintWriter(buffer));
		throw new RuntimeException("Posted: " + buffer, ex);
	}

	protected void exception (Throwable ex) {
		ex.printStackTrace();
		stop();
	}

	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;
	}

	/** 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 () {
	}

	/** Called after dispose is complete. */
	protected void disposed () {
	}

	public void stop () {
		EventQueue.invokeLater(new Runnable() {
			public void run () {
				if (!running) return;
				running = false;
				Array listeners = lifecycleListeners;
				synchronized (listeners) {
					for (LifecycleListener listener : listeners) {
						listener.pause();
						listener.dispose();
					}
				}
				listener.pause();
				listener.dispose();
				try {
					Display.destroy();
					if (audio != null) audio.dispose();
				} catch (Throwable ignored) {
				}
				disposed();
			}
		});
	}

	@Override
	public long getJavaHeap () {
		return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
	}

	@Override
	public long getNativeHeap () {
		return getJavaHeap();
	}

	@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);
			runnables.add(postedRunnableStacktraces ? new Throwable() : null);
			graphics.requestRendering();
		}
	}

	@Override
	public void debug (String tag, String message) {
		if (logLevel >= LOG_DEBUG) getApplicationLogger().debug(tag, message);
	}

	@Override
	public void debug (String tag, String message, Throwable exception) {
		if (logLevel >= LOG_DEBUG) getApplicationLogger().debug(tag, message, exception);
	}

	@Override
	public void log (String tag, String message) {
		if (logLevel >= LOG_INFO) getApplicationLogger().log(tag, message);
	}

	@Override
	public void log (String tag, String message, Throwable exception) {
		if (logLevel >= LOG_INFO) getApplicationLogger().log(tag, message, exception);
	}

	@Override
	public void error (String tag, String message) {
		if (logLevel >= LOG_ERROR) getApplicationLogger().error(tag, message);
	}

	@Override
	public void error (String tag, String message, Throwable exception) {
		if (logLevel >= LOG_ERROR) getApplicationLogger().error(tag, message, exception);
	}

	@Override
	public void setLogLevel (int logLevel) {
		this.logLevel = logLevel;
	}

	@Override
	public int getLogLevel () {
		return logLevel;
	}

	@Override
	public void setApplicationLogger (ApplicationLogger applicationLogger) {
		this.applicationLogger = applicationLogger;
	}

	@Override
	public ApplicationLogger getApplicationLogger () {
		return applicationLogger;
	}

	@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);
		}
	}

	/** When true, {@link #postRunnable(Runnable)} keeps the stacktrace (which is an allocation) so it can be included if the
	 * runnable later throws an exception. Default is false. */
	public void setPostedRunnableStacktraces (boolean postedRunnableStacktraces) {
		this.postedRunnableStacktraces = postedRunnableStacktraces;
	}

	protected Files createFiles () {
		return new LwjglFiles();
	}

	@Override
	public LwjglAudio createAudio (LwjglApplicationConfiguration config) {
		return new OpenALLwjglAudio();
	}

	@Override
	public LwjglInput createInput (LwjglApplicationConfiguration config) {
		return new DefaultLwjglInput();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy