/*******************************************************************************
* Copyright 2011 See libgdx 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.gwt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.gwt.preloader.Preloader;
import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderCallback;
import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderState;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Clipboard;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.TimeUtils;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Panel;
/**
* Implementation of an {@link Application} based on GWT. Clients have to
* override {@link #getConfig()} and {@link #getApplicationListener()}. Clients
* can override the default loading screen via {@link #getPreloaderCallback()}
* and implement any loading screen drawing via GWT widgets.
*
* @author mzechner
*/
public abstract class GwtApplication implements EntryPoint, Application {
private final static Logger log = LoggerFactory.getLogger(GwtApplication.class);
private ApplicationListener listener;
private GwtApplicationConfiguration config;
private GwtGraphics graphics;
private GwtInput input;
private GwtNet net;
private int logLevel = LOG_ERROR;
private Array<Runnable> runnables = new Array<Runnable>();
private Array<Runnable> runnablesHelper = new Array<Runnable>();
private Array<LifecycleListener> lifecycleListeners = new Array<LifecycleListener>();
private int lastWidth;
private int lastHeight;
Preloader preloader;
private static AgentInfo agentInfo;
private ObjectMap<String, Preferences> prefs = new ObjectMap<String, Preferences>();
/** @return the configuration for the {@link GwtApplication}. */
public abstract GwtApplicationConfiguration getConfig();
public String getPreloaderBaseURL() {
return GWT.getHostPageBaseURL() + "assets/";
}
@Override
public void onModuleLoad() {
GwtApplication.agentInfo = computeAgentInfo();
this.listener = getApplicationListener();
this.config = getConfig();
final PreloaderCallback callback = getPreloaderCallback();
preloader = createPreloader();
preloader.preload("assets.txt", new PreloaderCallback() {
@Override
public void error(String file) {
callback.error(file);
}
@Override
public void update(PreloaderState state) {
callback.update(state);
if (state.hasEnded()) {
//getRootPanel().clear();
setupLoop();
}
}
});
}
void setupLoop() {
Gdx.app = this;
// setup modules
try {
graphics = new GwtGraphics(null, config);
} catch (Throwable e) {
error("GwtApplication", "exception: " + e.getMessage(), e);
//root.clear();
//root.add(new Label("Sorry, your browser doesn't seem to support WebGL"));
return;
}
lastWidth = graphics.getWidth();
lastHeight = graphics.getHeight();
Gdx.app = this;
Gdx.graphics = graphics;
Gdx.gl20 = graphics.getGL20();
Gdx.gl = Gdx.gl20;
Gdx.files = new GwtFiles(preloader);
this.input = new GwtInput(graphics.canvas);
Gdx.input = this.input;
this.net = new GwtNet();
Gdx.net = this.net;
// tell listener about app creation
try {
listener.create();
listener.resize(graphics.getWidth(), graphics.getHeight());
} catch (Throwable t) {
error("GwtApplication", "exception: " + t.getMessage(), t);
t.printStackTrace();
throw new RuntimeException(t);
}
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
final int frameTime = (int) ((1f / config.fps) * 1000);
// setup rendering timer
new Timer() {
@Override
public void run() {
try {
mainLoop(this, frameTime);
} catch (Throwable t) {
error("GwtApplication", "exception: " + t.getMessage(), t);
throw new RuntimeException(t);
}
}
}.schedule(1);
}
void mainLoop(Timer timer, int frameTime) {
graphics.update();
if (Gdx.graphics.getWidth() != lastWidth || Gdx.graphics.getHeight() != lastHeight) {
GwtApplication.this.listener.resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
lastWidth = graphics.getWidth();
lastHeight = graphics.getHeight();
Gdx.gl.glViewport(0, 0, lastWidth, lastHeight);
}
runnablesHelper.addAll(runnables);
runnables.clear();
for (int i = 0; i < runnablesHelper.size; i++) {
runnablesHelper.get(i).run();
}
runnablesHelper.clear();
listener.render();
input.justTouched = false;
long now = System.currentTimeMillis();
int diff = (int) (now - graphics.lastTimeStamp);
diff = frameTime - diff;
timer.schedule(diff > 5 ? diff : 5);
}
public Panel getRootPanel() {
throw new GdxRuntimeException("no panel!");
}
long loadStart = TimeUtils.nanoTime();
public Preloader createPreloader() {
return new Preloader(getPreloaderBaseURL());
}
public PreloaderCallback getPreloaderCallback() {
return null;
}
@Override
public Graphics getGraphics() {
return graphics;
}
@Override
public Audio getAudio() {
return Gdx.audio;
}
@Override
public Input getInput() {
return Gdx.input;
}
@Override
public Files getFiles() {
return Gdx.files;
}
@Override
public Net getNet() {
return Gdx.net;
}
@Override
public void log(String tag, String message) {
log.info("{} : {}", tag, message);
}
@Override
public void log(String tag, String message, Throwable exception) {
log.info("{} : {}\n{}", tag, exception, getStackTrace(exception));
}
@Override
public void error(String tag, String message) {
log.error("{} : {}", tag, message);
}
@Override
public void error(String tag, String message, Throwable exception) {
log.error("{} : {}\n{}", tag, message, getStackTrace(exception));
}
@Override
public void debug(String tag, String message) {
log.debug("{} : {}", tag, message);
}
@Override
public void debug(String tag, String message, Throwable exception) {
log.debug("{} : {}\n{}", tag, message, getStackTrace(exception));
}
private String getStackTrace(Throwable e) {
StringBuffer buffer = new StringBuffer();
for (StackTraceElement trace : e.getStackTrace()) {
buffer.append(trace.toString() + "\n");
}
return buffer.toString();
}
@Override
public void setLogLevel(int logLevel) {
}
@Override
public int getLogLevel() {
return LOG_DEBUG;
}
@Override
public ApplicationType getType() {
return ApplicationType.WebGL;
}
@Override
public int getVersion() {
return 0;
}
@Override
public long getJavaHeap() {
return 0;
}
@Override
public long getNativeHeap() {
return 0;
}
@Override
public Preferences getPreferences(String name) {
Preferences pref = prefs.get(name);
if (pref == null) {
pref = new GwtPreferences(name);
prefs.put(name, pref);
}
return pref;
}
@Override
public Clipboard getClipboard() {
return new Clipboard() {
@Override
public String getContents() {
return null;
}
@Override
public void setContents(String content) {
}
};
}
@Override
public void postRunnable(Runnable runnable) {
runnables.add(runnable);
}
@Override
public void exit() {
}
/**
* Contains precomputed information on the user-agent. Useful for dealing
* with browser and OS behavioral differences. Kindly
* borrowed from PlayN
*/
public static AgentInfo agentInfo() {
return agentInfo;
}
/** kindly borrowed from PlayN **/
private static native AgentInfo computeAgentInfo() /*-{
var userAgent = navigator.userAgent.toLowerCase();
return {
// browser type flags
isFirefox : userAgent.indexOf("firefox") != -1,
isChrome : userAgent.indexOf("chrome") != -1,
isSafari : userAgent.indexOf("safari") != -1,
isOpera : userAgent.indexOf("opera") != -1,
isIE : userAgent.indexOf("msie") != -1,
// OS type flags
isMacOS : userAgent.indexOf("mac") != -1,
isLinux : userAgent.indexOf("linux") != -1,
isWindows : userAgent.indexOf("win") != -1
};
}-*/;
/** Returned by {@link #agentInfo}. Kindly borrowed from PlayN. */
public static class AgentInfo extends JavaScriptObject {
public final native boolean isFirefox() /*-{
return this.isFirefox;
}-*/;
public final native boolean isChrome() /*-{
return this.isChrome;
}-*/;
public final native boolean isSafari() /*-{
return this.isSafari;
}-*/;
public final native boolean isOpera() /*-{
return this.isOpera;
}-*/;
public final native boolean isIE() /*-{
return this.isIE;
}-*/;
public final native boolean isMacOS() /*-{
return this.isMacOS;
}-*/;
public final native boolean isLinux() /*-{
return this.isLinux;
}-*/;
public final native boolean isWindows() /*-{
return this.isWindows;
}-*/;
protected AgentInfo() {
}
}
public String getBaseUrl() {
return preloader.baseUrl;
}
public Preloader getPreloader() {
return preloader;
}
@Override
public void addLifecycleListener(LifecycleListener listener) {
synchronized (lifecycleListeners) {
lifecycleListeners.add(listener);
}
}
@Override
public void removeLifecycleListener(LifecycleListener listener) {
synchronized (lifecycleListeners) {
lifecycleListeners.removeValue(listener, true);
}
}
}