1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.jme3.system; 34 35 import com.jme3.input.JoyInput; 36 import com.jme3.input.KeyInput; 37 import com.jme3.input.MouseInput; 38 import com.jme3.input.TouchInput; 39 import com.jme3.input.dummy.DummyKeyInput; 40 import com.jme3.input.dummy.DummyMouseInput; 41 import com.jme3.renderer.Renderer; 42 import java.util.concurrent.atomic.AtomicBoolean; 43 import java.util.logging.Level; 44 import java.util.logging.Logger; 45 46 public class NullContext implements JmeContext, Runnable { 47 48 protected static final Logger logger = Logger.getLogger(NullContext.class.getName()); 49 50 protected AtomicBoolean created = new AtomicBoolean(false); 51 protected AtomicBoolean needClose = new AtomicBoolean(false); 52 protected final Object createdLock = new Object(); 53 54 protected int frameRate; 55 protected AppSettings settings = new AppSettings(true); 56 protected Timer timer; 57 protected SystemListener listener; 58 protected NullRenderer renderer; 59 60 public Type getType() { 61 return Type.Headless; 62 } 63 64 public void setSystemListener(SystemListener listener){ 65 this.listener = listener; 66 } 67 68 protected void initInThread(){ 69 logger.info("NullContext created."); 70 logger.log(Level.FINE, "Running on thread: {0}", Thread.currentThread().getName()); 71 72 Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 73 public void uncaughtException(Thread thread, Throwable thrown) { 74 listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown); 75 } 76 }); 77 78 timer = new NanoTimer(); 79 renderer = new NullRenderer(); 80 synchronized (createdLock){ 81 created.set(true); 82 createdLock.notifyAll(); 83 } 84 85 listener.initialize(); 86 } 87 88 protected void deinitInThread(){ 89 listener.destroy(); 90 timer = null; 91 synchronized (createdLock){ 92 created.set(false); 93 createdLock.notifyAll(); 94 } 95 } 96 97 private static long timeThen; 98 private static long timeLate; 99 100 public void sync(int fps) { 101 long timeNow; 102 long gapTo; 103 long savedTimeLate; 104 105 gapTo = timer.getResolution() / fps + timeThen; 106 timeNow = timer.getTime(); 107 savedTimeLate = timeLate; 108 109 try { 110 while (gapTo > timeNow + savedTimeLate) { 111 Thread.sleep(1); 112 timeNow = timer.getTime(); 113 } 114 } catch (InterruptedException e) { 115 Thread.currentThread().interrupt(); 116 } 117 118 if (gapTo < timeNow) { 119 timeLate = timeNow - gapTo; 120 } else { 121 timeLate = 0; 122 } 123 124 timeThen = timeNow; 125 } 126 127 public void run(){ 128 initInThread(); 129 130 while (!needClose.get()){ 131 listener.update(); 132 133 if (frameRate > 0) 134 sync(frameRate); 135 } 136 137 deinitInThread(); 138 139 logger.info("NullContext destroyed."); 140 } 141 142 public void destroy(boolean waitFor){ 143 needClose.set(true); 144 if (waitFor) 145 waitFor(false); 146 } 147 148 public void create(boolean waitFor){ 149 if (created.get()){ 150 logger.warning("create() called when NullContext is already created!"); 151 return; 152 } 153 154 new Thread(this, "Headless Application Thread").start(); 155 if (waitFor) 156 waitFor(true); 157 } 158 159 public void restart() { 160 } 161 162 public void setAutoFlushFrames(boolean enabled){ 163 } 164 165 public MouseInput getMouseInput() { 166 return new DummyMouseInput(); 167 } 168 169 public KeyInput getKeyInput() { 170 return new DummyKeyInput(); 171 } 172 173 public JoyInput getJoyInput() { 174 return null; 175 } 176 177 public TouchInput getTouchInput() { 178 return null; 179 } 180 181 public void setTitle(String title) { 182 } 183 184 public void create(){ 185 create(false); 186 } 187 188 public void destroy(){ 189 destroy(false); 190 } 191 192 protected void waitFor(boolean createdVal){ 193 synchronized (createdLock){ 194 while (created.get() != createdVal){ 195 try { 196 createdLock.wait(); 197 } catch (InterruptedException ex) { 198 } 199 } 200 } 201 } 202 203 public boolean isCreated(){ 204 return created.get(); 205 } 206 207 public void setSettings(AppSettings settings) { 208 this.settings.copyFrom(settings); 209 frameRate = settings.getFrameRate(); 210 if (frameRate <= 0) 211 frameRate = 60; // use default update rate. 212 } 213 214 public AppSettings getSettings(){ 215 return settings; 216 } 217 218 public Renderer getRenderer() { 219 return renderer; 220 } 221 222 public Timer getTimer() { 223 return timer; 224 } 225 226 public boolean isRenderable() { 227 return true; // Doesn't really matter if true or false. Either way 228 // RenderManager won't render anything. 229 } 230 } 231