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 33package com.jme3.system.lwjgl; 34 35import com.jme3.input.JoyInput; 36import com.jme3.input.KeyInput; 37import com.jme3.input.MouseInput; 38import com.jme3.input.TouchInput; 39import com.jme3.input.lwjgl.JInputJoyInput; 40import com.jme3.input.lwjgl.LwjglKeyInput; 41import com.jme3.input.lwjgl.LwjglMouseInput; 42import com.jme3.system.AppSettings; 43import com.jme3.system.JmeContext.Type; 44import com.jme3.system.JmeSystem; 45import java.util.concurrent.atomic.AtomicBoolean; 46import java.util.logging.Level; 47import java.util.logging.Logger; 48import org.lwjgl.LWJGLException; 49import org.lwjgl.Sys; 50import org.lwjgl.opengl.Display; 51import org.lwjgl.opengl.OpenGLException; 52import org.lwjgl.opengl.Util; 53 54public abstract class LwjglAbstractDisplay extends LwjglContext implements Runnable { 55 56 private static final Logger logger = Logger.getLogger(LwjglAbstractDisplay.class.getName()); 57 58 protected AtomicBoolean needClose = new AtomicBoolean(false); 59 protected boolean wasActive = false; 60 protected int frameRate = 0; 61 protected boolean autoFlush = true; 62 63 /** 64 * @return Type.Display or Type.Canvas 65 */ 66 public abstract Type getType(); 67 68 /** 69 * Set the title if its a windowed display 70 * @param title 71 */ 72 public abstract void setTitle(String title); 73 74 /** 75 * Restart if its a windowed or full-screen display. 76 */ 77 public abstract void restart(); 78 79 /** 80 * Apply the settings, changing resolution, etc. 81 * @param settings 82 */ 83 protected abstract void createContext(AppSettings settings) throws LWJGLException; 84 85 /** 86 * Destroy the context. 87 */ 88 protected abstract void destroyContext(); 89 90 /** 91 * Does LWJGL display initialization in the OpenGL thread 92 */ 93 protected void initInThread(){ 94 try{ 95 if (!JmeSystem.isLowPermissions()){ 96 // Enable uncaught exception handler only for current thread 97 Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 98 public void uncaughtException(Thread thread, Throwable thrown) { 99 listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown); 100 if (needClose.get()){ 101 // listener.handleError() has requested the 102 // context to close. Satisfy request. 103 deinitInThread(); 104 } 105 } 106 }); 107 } 108 109 // For canvas, this will create a pbuffer, 110 // allowing us to query information. 111 // When the canvas context becomes available, it will 112 // be replaced seamlessly. 113 createContext(settings); 114 printContextInitInfo(); 115 116 created.set(true); 117 } catch (Exception ex){ 118 try { 119 if (Display.isCreated()) 120 Display.destroy(); 121 } catch (Exception ex2){ 122 logger.log(Level.WARNING, null, ex2); 123 } 124 125 listener.handleError("Failed to create display", ex); 126 return; // if we failed to create display, do not continue 127 } 128 super.internalCreate(); 129 listener.initialize(); 130 } 131 132 protected boolean checkGLError(){ 133 try { 134 Util.checkGLError(); 135 } catch (OpenGLException ex){ 136 listener.handleError("An OpenGL error has occured!", ex); 137 } 138 // NOTE: Always return true since this is used in an "assert" statement 139 return true; 140 } 141 142 /** 143 * execute one iteration of the render loop in the OpenGL thread 144 */ 145 protected void runLoop(){ 146 if (!created.get()) 147 throw new IllegalStateException(); 148 149 listener.update(); 150 151 // All this does is call swap buffers 152 // If the canvas is not active, there's no need to waste time 153 // doing that .. 154 if (renderable.get()){ 155 assert checkGLError(); 156 157 // calls swap buffers, etc. 158 try { 159 if (autoFlush){ 160 Display.update(false); 161 }else{ 162 Display.processMessages(); 163 Thread.sleep(50); 164 // add a small wait 165 // to reduce CPU usage 166 } 167 } catch (Throwable ex){ 168 listener.handleError("Error while swapping buffers", ex); 169 } 170 } 171 172 if (frameRate > 0) 173 Display.sync(frameRate); 174 175 if (renderable.get()){ 176 if (autoFlush){ 177 // check input after we synchronize with framerate. 178 // this reduces input lag. 179 Display.processMessages(); 180 } 181 } 182 183 // Subclasses just call GLObjectManager clean up objects here 184 // it is safe .. for now. 185 renderer.onFrame(); 186 } 187 188 /** 189 * De-initialize in the OpenGL thread. 190 */ 191 protected void deinitInThread(){ 192 destroyContext(); 193 194 listener.destroy(); 195 logger.info("Display destroyed."); 196 super.internalDestroy(); 197 } 198 199 public void run(){ 200 if (listener == null) 201 throw new IllegalStateException("SystemListener is not set on context!" 202 + "Must set with JmeContext.setSystemListner()."); 203 204 logger.log(Level.INFO, "Using LWJGL {0}", Sys.getVersion()); 205 initInThread(); 206 while (true){ 207 if (renderable.get()){ 208 if (Display.isCloseRequested()) 209 listener.requestClose(false); 210 211 if (wasActive != Display.isActive()) { 212 if (!wasActive) { 213 listener.gainFocus(); 214 timer.reset(); 215 wasActive = true; 216 } else { 217 listener.loseFocus(); 218 wasActive = false; 219 } 220 } 221 } 222 223 runLoop(); 224 225 if (needClose.get()) 226 break; 227 } 228 deinitInThread(); 229 } 230 231 public JoyInput getJoyInput() { 232 if (joyInput == null){ 233 joyInput = new JInputJoyInput(); 234 } 235 return joyInput; 236 } 237 238 public MouseInput getMouseInput() { 239 if (mouseInput == null){ 240 mouseInput = new LwjglMouseInput(this); 241 } 242 return mouseInput; 243 } 244 245 public KeyInput getKeyInput() { 246 if (keyInput == null){ 247 keyInput = new LwjglKeyInput(this); 248 } 249 return keyInput; 250 } 251 252 public TouchInput getTouchInput() { 253 return null; 254 } 255 256 public void setAutoFlushFrames(boolean enabled){ 257 this.autoFlush = enabled; 258 } 259 260 public void destroy(boolean waitFor){ 261 needClose.set(true); 262 if (waitFor) 263 waitFor(false); 264 } 265 266} 267