| /* |
| * Copyright (c) 2009-2010 jMonkeyEngine |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| package com.jme3.system; |
| |
| import com.jme3.input.JoyInput; |
| import com.jme3.input.KeyInput; |
| import com.jme3.input.MouseInput; |
| import com.jme3.input.TouchInput; |
| import com.jme3.input.dummy.DummyKeyInput; |
| import com.jme3.input.dummy.DummyMouseInput; |
| import com.jme3.renderer.Renderer; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| public class NullContext implements JmeContext, Runnable { |
| |
| protected static final Logger logger = Logger.getLogger(NullContext.class.getName()); |
| |
| protected AtomicBoolean created = new AtomicBoolean(false); |
| protected AtomicBoolean needClose = new AtomicBoolean(false); |
| protected final Object createdLock = new Object(); |
| |
| protected int frameRate; |
| protected AppSettings settings = new AppSettings(true); |
| protected Timer timer; |
| protected SystemListener listener; |
| protected NullRenderer renderer; |
| |
| public Type getType() { |
| return Type.Headless; |
| } |
| |
| public void setSystemListener(SystemListener listener){ |
| this.listener = listener; |
| } |
| |
| protected void initInThread(){ |
| logger.info("NullContext created."); |
| logger.log(Level.FINE, "Running on thread: {0}", Thread.currentThread().getName()); |
| |
| Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { |
| public void uncaughtException(Thread thread, Throwable thrown) { |
| listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown); |
| } |
| }); |
| |
| timer = new NanoTimer(); |
| renderer = new NullRenderer(); |
| synchronized (createdLock){ |
| created.set(true); |
| createdLock.notifyAll(); |
| } |
| |
| listener.initialize(); |
| } |
| |
| protected void deinitInThread(){ |
| listener.destroy(); |
| timer = null; |
| synchronized (createdLock){ |
| created.set(false); |
| createdLock.notifyAll(); |
| } |
| } |
| |
| private static long timeThen; |
| private static long timeLate; |
| |
| public void sync(int fps) { |
| long timeNow; |
| long gapTo; |
| long savedTimeLate; |
| |
| gapTo = timer.getResolution() / fps + timeThen; |
| timeNow = timer.getTime(); |
| savedTimeLate = timeLate; |
| |
| try { |
| while (gapTo > timeNow + savedTimeLate) { |
| Thread.sleep(1); |
| timeNow = timer.getTime(); |
| } |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| } |
| |
| if (gapTo < timeNow) { |
| timeLate = timeNow - gapTo; |
| } else { |
| timeLate = 0; |
| } |
| |
| timeThen = timeNow; |
| } |
| |
| public void run(){ |
| initInThread(); |
| |
| while (!needClose.get()){ |
| listener.update(); |
| |
| if (frameRate > 0) |
| sync(frameRate); |
| } |
| |
| deinitInThread(); |
| |
| logger.info("NullContext destroyed."); |
| } |
| |
| public void destroy(boolean waitFor){ |
| needClose.set(true); |
| if (waitFor) |
| waitFor(false); |
| } |
| |
| public void create(boolean waitFor){ |
| if (created.get()){ |
| logger.warning("create() called when NullContext is already created!"); |
| return; |
| } |
| |
| new Thread(this, "Headless Application Thread").start(); |
| if (waitFor) |
| waitFor(true); |
| } |
| |
| public void restart() { |
| } |
| |
| public void setAutoFlushFrames(boolean enabled){ |
| } |
| |
| public MouseInput getMouseInput() { |
| return new DummyMouseInput(); |
| } |
| |
| public KeyInput getKeyInput() { |
| return new DummyKeyInput(); |
| } |
| |
| public JoyInput getJoyInput() { |
| return null; |
| } |
| |
| public TouchInput getTouchInput() { |
| return null; |
| } |
| |
| public void setTitle(String title) { |
| } |
| |
| public void create(){ |
| create(false); |
| } |
| |
| public void destroy(){ |
| destroy(false); |
| } |
| |
| protected void waitFor(boolean createdVal){ |
| synchronized (createdLock){ |
| while (created.get() != createdVal){ |
| try { |
| createdLock.wait(); |
| } catch (InterruptedException ex) { |
| } |
| } |
| } |
| } |
| |
| public boolean isCreated(){ |
| return created.get(); |
| } |
| |
| public void setSettings(AppSettings settings) { |
| this.settings.copyFrom(settings); |
| frameRate = settings.getFrameRate(); |
| if (frameRate <= 0) |
| frameRate = 60; // use default update rate. |
| } |
| |
| public AppSettings getSettings(){ |
| return settings; |
| } |
| |
| public Renderer getRenderer() { |
| return renderer; |
| } |
| |
| public Timer getTimer() { |
| return timer; |
| } |
| |
| public boolean isRenderable() { |
| return true; // Doesn't really matter if true or false. Either way |
| // RenderManager won't render anything. |
| } |
| } |