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

com.badlogic.gdx.backends.lwjgl.LwjglAWTCanvas 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.awt.Canvas;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.PaintEvent;
import java.util.HashMap;
import java.util.Map;

import javax.swing.SwingUtilities;

import com.badlogic.gdx.ApplicationLogger;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.AWTGLCanvas;
import org.lwjgl.opengl.Display;
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.lwjgl.audio.OpenALLwjglAudio;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Clipboard;

/** An OpenGL surface on an AWT Canvas, allowing OpenGL to be embedded in a Swing application. This uses {@link AWTGLCanvas},
 * which allows multiple LwjglAWTCanvas to be used in a single 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 LwjglAWTCanvas implements Application {
	static int instanceCount;

	LwjglGraphics graphics;
	OpenALLwjglAudio audio;
	LwjglFiles files;
	LwjglAWTInput input;
	LwjglNet net;
	final ApplicationListener listener;
	AWTGLCanvas canvas;
	final Array runnables = new Array();
	final Array executedRunnables = new Array();
	final Array lifecycleListeners = new Array();
	boolean running = true;
	int lastWidth;
	int lastHeight;
	int logLevel = LOG_INFO;
	ApplicationLogger applicationLogger;
	final String logTag = "LwjglAWTCanvas";
	Cursor cursor;

	public LwjglAWTCanvas (ApplicationListener listener) {
		this(listener, null, null);
	}

	public LwjglAWTCanvas (ApplicationListener listener, LwjglAWTCanvas sharedContextCanvas) {
		this(listener, null, sharedContextCanvas);
	}

	public LwjglAWTCanvas (ApplicationListener listener, LwjglApplicationConfiguration config) {
		this(listener, config, null);
	}

	public LwjglAWTCanvas (ApplicationListener listener, LwjglApplicationConfiguration config,
		LwjglAWTCanvas sharedContextCanvas) {
		this.listener = listener;
		if (config == null) config = new LwjglApplicationConfiguration();

		LwjglNativesLoader.load();
		setApplicationLogger(new LwjglApplicationLogger());
		instanceCount++;

		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);
				private final NonSystemPaint nonSystemPaint = new NonSystemPaint(this);

				@Override
				public Dimension getMinimumSize () {
					return minSize;
				}

				@Override
				public void initGL () {
					create();
				}

				@Override
				public void paintGL () {
					try {
						boolean systemPaint = !(EventQueue.getCurrentEvent() instanceof NonSystemPaint);
						render(systemPaint);
						Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(nonSystemPaint);
					} catch (Throwable ex) {
						exception(ex);
					}
				}
			};
		} catch (Throwable ex) {
			exception(ex);
			return;
		}

		canvas.setBackground(new Color(config.initialBackgroundColor.r, config.initialBackgroundColor.g,
			config.initialBackgroundColor.b, config.initialBackgroundColor.a));

		graphics = new LwjglGraphics(canvas, config) {
			@Override
			public void setTitle (String title) {
				super.setTitle(title);
				LwjglAWTCanvas.this.setTitle(title);
			}

			@Override
			public boolean setWindowedMode (int width, int height) {
				if (!super.setWindowedMode(width, height)) return false;
				LwjglAWTCanvas.this.setDisplayMode(width, height);
				return true;
			}

			@Override
			public boolean setFullscreenMode (DisplayMode displayMode) {
				if (!super.setFullscreenMode(displayMode)) return false;
				LwjglAWTCanvas.this.setDisplayMode(displayMode.width, displayMode.height);
				return true;
			}

			public boolean shouldRender () {
				synchronized (this) {
					boolean rq = requestRendering;
					requestRendering = false;
					return rq || isContinuous;
				}
			}
		};

		if (!LwjglApplicationConfiguration.disableAudio && Gdx.audio == null) audio = new OpenALLwjglAudio();
		if (Gdx.files == null) files = new LwjglFiles();
		if (Gdx.net == null) net = new LwjglNet(config);
		input = new LwjglAWTInput(this);
		setGlobals();
	}

	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 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.initiateGL();
			canvas.setVSyncEnabled(graphics.config.vSyncEnabled);
			listener.create();
			lastWidth = Math.max(1, graphics.getWidth());
			lastHeight = Math.max(1, graphics.getHeight());
			listener.resize(lastWidth, lastHeight);
			start();
		} catch (Throwable ex) {
			stopped();
			exception(ex);
		}
	}

	void render (boolean shouldRender) throws LWJGLException {
		if (!running) return;

		setGlobals();
		canvas.setCursor(cursor);

		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;

		shouldRender |= graphics.shouldRender();
		input.processEvents();
		if (audio != null) audio.update();

		if (shouldRender) {
			graphics.updateTime();
			graphics.frameId++;
			listener.render();
			canvas.swapBuffers();
		}

		Display.sync(getFrameRate() * instanceCount);
	}

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

	/** Returns true when the frame containing the canvas is the foreground window. */
	public boolean isActive () {
		Component root = SwingUtilities.getRoot(canvas);
		return root instanceof Frame ? ((Frame)root).isActive() : true;
	}

	/** 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 listeners = lifecycleListeners;

		// To allow destroying of OpenGL textures during disposal.
		if (canvas.isDisplayable()) {
			makeCurrent();
		} else {
			error(logTag, "OpenGL context destroyed before application listener has had a chance to dispose of textures.");
		}

		synchronized (listeners) {
			for (LifecycleListener listener : listeners) {
				listener.pause();
				listener.dispose();
			}
		}
		listener.pause();
		listener.dispose();

		Gdx.app = null;

		Gdx.graphics = null;

		if (audio != null) {
			audio.dispose();
			Gdx.audio = null;
		}

		if (files != null) Gdx.files = null;

		if (net != null) Gdx.net = null;

		instanceCount--;

		stopped();
	}

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

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

	Map preferences = new HashMap();

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

	@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 () {
				stop();
				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 (Throwable ex) {
			exception(ex);
		}
	}

	/** Test whether the canvas' context is current. */
	public boolean isCurrent () {
		try {
			return canvas.isCurrent();
		} catch (Throwable ex) {
			exception(ex);
			return false;
		}
	}

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

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

	static public class NonSystemPaint extends PaintEvent {
		public NonSystemPaint (AWTGLCanvas canvas) {
			super(canvas, UPDATE, new Rectangle(0, 0, 99999, 99999));
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy