1 /*******************************************************************************
2 * Copyright 2011 See AUTHORS file.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 ******************************************************************************/
17 package com.badlogic.gdx.backends.gwt;
19 import com.badlogic.gdx.Application;
20 import com.badlogic.gdx.ApplicationListener;
21 import com.badlogic.gdx.Audio;
22 import com.badlogic.gdx.Files;
23 import com.badlogic.gdx.Gdx;
24 import com.badlogic.gdx.Graphics;
25 import com.badlogic.gdx.Input;
26 import com.badlogic.gdx.LifecycleListener;
27 import com.badlogic.gdx.Net;
28 import com.badlogic.gdx.Preferences;
29 import com.badlogic.gdx.backends.gwt.preloader.Preloader;
30 import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderCallback;
31 import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderState;
32 import com.badlogic.gdx.backends.gwt.soundmanager2.SoundManager;
33 import com.badlogic.gdx.utils.Array;
34 import com.badlogic.gdx.utils.Clipboard;
35 import com.badlogic.gdx.utils.ObjectMap;
36 import com.badlogic.gdx.utils.TimeUtils;
37 import com.google.gwt.core.client.EntryPoint;
38 import com.google.gwt.core.client.GWT;
39 import com.google.gwt.core.client.JavaScriptObject;
40 import com.google.gwt.dom.client.Document;
41 import com.google.gwt.dom.client.Element;
42 import com.google.gwt.dom.client.Style;
43 import com.google.gwt.dom.client.Style.Unit;
44 import com.google.gwt.user.client.Timer;
45 import com.google.gwt.user.client.ui.FlowPanel;
46 import com.google.gwt.user.client.ui.HTMLPanel;
47 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
48 import com.google.gwt.user.client.ui.HasVerticalAlignment;
49 import com.google.gwt.user.client.ui.Image;
50 import com.google.gwt.user.client.ui.InlineHTML;
51 import com.google.gwt.user.client.ui.Label;
52 import com.google.gwt.user.client.ui.Panel;
53 import com.google.gwt.user.client.ui.RootPanel;
54 import com.google.gwt.user.client.ui.SimplePanel;
55 import com.google.gwt.user.client.ui.TextArea;
56 import com.google.gwt.user.client.ui.VerticalPanel;
58 /** Implementation of an {@link Application} based on GWT. Clients have to override {@link #getConfig()} and
59 * {@link #getApplicationListener()}. Clients can override the default loading screen via
60 * {@link #getPreloaderCallback()} and implement any loading screen drawing via GWT widgets.
62 public abstract class GwtApplication implements EntryPoint, Application {
63 private ApplicationListener listener;
64 private GwtApplicationConfiguration config;
65 private GwtGraphics graphics;
66 private GwtInput input;
68 private Panel root = null;
69 private TextArea log = null;
70 private int logLevel = LOG_ERROR;
71 private Array<Runnable> runnables = new Array<Runnable>();
72 private Array<Runnable> runnablesHelper = new Array<Runnable>();
73 private Array<LifecycleListener> lifecycleListeners = new Array<LifecycleListener>();
74 private int lastWidth;
75 private int lastHeight;
77 private static AgentInfo agentInfo;
78 private ObjectMap<String, Preferences> prefs = new ObjectMap<String, Preferences>();
80 /** @return the configuration for the {@link GwtApplication}. */
81 public abstract GwtApplicationConfiguration getConfig ();
84 public String getPreloaderBaseURL()
86 return GWT.getHostPageBaseURL() + "assets/";
90 public void onModuleLoad () {
91 GwtApplication.agentInfo = computeAgentInfo();
92 this.listener = getApplicationListener();
93 this.config = getConfig();
94 this.log = config.log;
96 if (config.rootPanel != null) {
97 this.root = config.rootPanel;
99 Element element = Document.get().getElementById("embed-" + GWT.getModuleName());
100 if (element == null) {
101 VerticalPanel panel = new VerticalPanel();
102 panel.setWidth("" + config.width + "px");
103 panel.setHeight("" + config.height + "px");
104 panel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
105 panel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
106 RootPanel.get().add(panel);
107 RootPanel.get().setWidth("" + config.width + "px");
108 RootPanel.get().setHeight("" + config.height + "px");
111 VerticalPanel panel = new VerticalPanel();
112 panel.setWidth("" + config.width + "px");
113 panel.setHeight("" + config.height + "px");
114 panel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
115 panel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
116 element.appendChild(panel.getElement());
121 // initialize SoundManager2
122 SoundManager.init(GWT.getModuleBaseURL(), 9, true, new SoundManager.SoundManagerCallback(){
125 public void onready () {
126 final PreloaderCallback callback = getPreloaderCallback();
127 preloader = createPreloader();
128 preloader.preload("assets.txt", new PreloaderCallback() {
130 public void error (String file) {
131 callback.error(file);
135 public void update (PreloaderState state) {
136 callback.update(state);
137 if (state.hasEnded()) {
138 getRootPanel().clear();
146 public void ontimeout (String status, String errorType) {
147 error("SoundManager", status + " " + errorType);
156 graphics = new GwtGraphics(root, config);
157 } catch (Throwable e) {
159 root.add(new Label("Sorry, your browser doesn't seem to support WebGL"));
162 lastWidth = graphics.getWidth();
163 lastHeight = graphics.getHeight();
165 Gdx.audio = new GwtAudio();
166 Gdx.graphics = graphics;
167 Gdx.gl20 = graphics.getGL20();
168 Gdx.gl = graphics.getGLCommon();
169 Gdx.files = new GwtFiles(preloader);
170 this.input = new GwtInput(graphics.canvas);
171 Gdx.input = this.input;
172 this.net = new GwtNet();
175 // tell listener about app creation
178 listener.resize(graphics.getWidth(), graphics.getHeight());
179 } catch (Throwable t) {
180 error("GwtApplication", "exception: " + t.getMessage(), t);
182 throw new RuntimeException(t);
185 // setup rendering timer
191 } catch (Throwable t) {
192 error("GwtApplication", "exception: " + t.getMessage(), t);
193 throw new RuntimeException(t);
196 }.scheduleRepeating((int)((1f / config.fps) * 1000));
201 if (Gdx.graphics.getWidth() != lastWidth || Gdx.graphics.getHeight() != lastHeight) {
202 GwtApplication.this.listener.resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
203 lastWidth = graphics.getWidth();
204 lastHeight = graphics.getHeight();
205 Gdx.gl.glViewport(0, 0, lastWidth, lastHeight);
207 runnablesHelper.addAll(runnables);
209 for (int i = 0; i < runnablesHelper.size; i++) {
210 runnablesHelper.get(i).run();
212 runnablesHelper.clear();
214 input.justTouched = false;
217 public Panel getRootPanel () {
221 long loadStart = TimeUtils.nanoTime();
223 public Preloader createPreloader() {
224 return new Preloader(getPreloaderBaseURL());
227 public PreloaderCallback getPreloaderCallback () {
228 final Panel preloaderPanel = new VerticalPanel();
229 preloaderPanel.setStyleName("gdx-preloader");
230 final Image logo = new Image(GWT.getModuleBaseURL() + "logo.png");
231 logo.setStyleName("logo");
232 preloaderPanel.add(logo);
233 final Panel meterPanel = new SimplePanel();
234 meterPanel.setStyleName("gdx-meter");
235 meterPanel.addStyleName("red");
236 final InlineHTML meter = new InlineHTML();
237 final Style meterStyle = meter.getElement().getStyle();
238 meterStyle.setWidth(0, Unit.PCT);
239 meterPanel.add(meter);
240 preloaderPanel.add(meterPanel);
241 getRootPanel().add(preloaderPanel);
242 return new PreloaderCallback() {
245 public void error (String file) {
246 System.out.println("error: " + file);
250 public void update (PreloaderState state) {
251 meterStyle.setWidth(100f * state.getProgress(), Unit.PCT);
258 public Graphics getGraphics () {
263 public Audio getAudio () {
268 public Input getInput () {
273 public Files getFiles () {
278 public Net getNet() {
282 private void checkLogLabel () {
284 log = new TextArea();
285 log.setSize(graphics.getWidth() + "px", "200px");
286 log.setReadOnly(true);
292 public void log (String tag, String message) {
293 if (logLevel >= LOG_INFO) {
295 log.setText(log.getText() + "\n" + tag + ": " + message);
296 log.setCursorPos(log.getText().length() - 1);
297 System.out.println(tag + ": " + message);
302 public void log (String tag, String message, Exception exception) {
303 if (logLevel >= LOG_INFO) {
305 log.setText(log.getText() + "\n" + tag + ": " + message + "\n" + exception.getMessage() + "\n");
306 log.setCursorPos(log.getText().length() - 1);
307 System.out.println(tag + ": " + message + "\n" + exception.getMessage());
308 System.out.println(getStackTrace(exception));
313 public void error (String tag, String message) {
314 if (logLevel >= LOG_ERROR) {
316 log.setText(log.getText() + "\n" + tag + ": " + message);
317 log.setCursorPos(log.getText().length() - 1);
318 System.err.println(tag + ": " + message);
323 public void error (String tag, String message, Throwable exception) {
324 if (logLevel >= LOG_ERROR) {
326 log.setText(log.getText() + "\n" + tag + ": " + message + "\n" + exception.getMessage());
327 log.setCursorPos(log.getText().length() - 1);
328 System.err.println(tag + ": " + message + "\n" + exception.getMessage() + "\n");
329 System.out.println(getStackTrace(exception));
334 public void debug (String tag, String message) {
335 if (logLevel >= LOG_DEBUG) {
337 log.setText(log.getText() + "\n" + tag + ": " + message + "\n");
338 log.setCursorPos(log.getText().length() - 1);
339 System.out.println(tag + ": " + message + "\n");
344 public void debug (String tag, String message, Throwable exception) {
345 if (logLevel >= LOG_DEBUG) {
347 log.setText(log.getText() + "\n" + tag + ": " + message + "\n" + exception.getMessage() + "\n");
348 log.setCursorPos(log.getText().length() - 1);
349 System.out.println(tag + ": " + message + "\n" + exception.getMessage());
350 System.out.println(getStackTrace(exception));
354 private String getStackTrace (Throwable e) {
355 StringBuffer buffer = new StringBuffer();
356 for (StackTraceElement trace : e.getStackTrace()) {
357 buffer.append(trace.toString() + "\n");
359 return buffer.toString();
363 public void setLogLevel (int logLevel) {
364 this.logLevel = logLevel;
368 public int getLogLevel() {
373 public ApplicationType getType () {
374 return ApplicationType.WebGL;
378 public int getVersion () {
383 public long getJavaHeap () {
388 public long getNativeHeap () {
393 public Preferences getPreferences (String name) {
394 Preferences pref = prefs.get(name);
396 pref = new GwtPreferences(name);
397 prefs.put(name, pref);
403 public Clipboard getClipboard() {
404 return new Clipboard() {
406 public String getContents () {
411 public void setContents (String content) {
417 public void postRunnable (Runnable runnable) {
418 runnables.add(runnable);
422 public void exit () {
425 /** Contains precomputed information on the user-agent. Useful for dealing with browser and OS behavioral differences. Kindly
426 * borrowed from PlayN */
427 public static AgentInfo agentInfo () {
431 /** kindly borrowed from PlayN **/
432 private static native AgentInfo computeAgentInfo () /*-{
433 var userAgent = navigator.userAgent.toLowerCase();
435 // browser type flags
436 isFirefox : userAgent.indexOf("firefox") != -1,
437 isChrome : userAgent.indexOf("chrome") != -1,
438 isSafari : userAgent.indexOf("safari") != -1,
439 isOpera : userAgent.indexOf("opera") != -1,
440 isIE : userAgent.indexOf("msie") != -1,
442 isMacOS : userAgent.indexOf("mac") != -1,
443 isLinux : userAgent.indexOf("linux") != -1,
444 isWindows : userAgent.indexOf("win") != -1
448 /** Returned by {@link #agentInfo}. Kindly borrowed from PlayN. */
449 public static class AgentInfo extends JavaScriptObject {
450 public final native boolean isFirefox () /*-{
451 return this.isFirefox;
454 public final native boolean isChrome () /*-{
455 return this.isChrome;
458 public final native boolean isSafari () /*-{
459 return this.isSafari;
462 public final native boolean isOpera () /*-{
466 public final native boolean isIE () /*-{
470 public final native boolean isMacOS () /*-{
474 public final native boolean isLinux () /*-{
478 public final native boolean isWindows () /*-{
479 return this.isWindows;
482 protected AgentInfo () {
486 public String getBaseUrl () {
487 return preloader.baseUrl;
490 public Preloader getPreloader () {
495 public void addLifecycleListener (LifecycleListener listener) {
496 synchronized(lifecycleListeners) {
497 lifecycleListeners.add(listener);
502 public void removeLifecycleListener (LifecycleListener listener) {
503 synchronized(lifecycleListeners) {
504 lifecycleListeners.removeValue(listener, true);
508 native static public void consoleLog(String message) /*-{
509 console.log( "GWT: " + message );