You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
8.5 KiB
273 lines
8.5 KiB
/*
|
|
* Copyright (c) 2009-2012 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.lwjgl;
|
|
|
|
import com.jme3.input.JoyInput;
|
|
import com.jme3.input.KeyInput;
|
|
import com.jme3.input.MouseInput;
|
|
import com.jme3.input.TouchInput;
|
|
import com.jme3.input.lwjgl.JInputJoyInput;
|
|
import com.jme3.input.lwjgl.LwjglKeyInput;
|
|
import com.jme3.input.lwjgl.LwjglMouseInput;
|
|
import com.jme3.system.AppSettings;
|
|
import com.jme3.system.JmeContext.Type;
|
|
import com.jme3.system.JmeSystem;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
import org.lwjgl.LWJGLException;
|
|
import org.lwjgl.Sys;
|
|
import org.lwjgl.opengl.Display;
|
|
import org.lwjgl.opengl.OpenGLException;
|
|
import org.lwjgl.opengl.Util;
|
|
|
|
public abstract class LwjglAbstractDisplay extends LwjglContext implements Runnable {
|
|
|
|
private static final Logger logger = Logger.getLogger(LwjglAbstractDisplay.class.getName());
|
|
|
|
protected AtomicBoolean needClose = new AtomicBoolean(false);
|
|
protected boolean wasActive = false;
|
|
protected int frameRate = 0;
|
|
protected boolean autoFlush = true;
|
|
|
|
/**
|
|
* @return Type.Display or Type.Canvas
|
|
*/
|
|
public abstract Type getType();
|
|
|
|
/**
|
|
* Set the title if its a windowed display
|
|
* @param title
|
|
*/
|
|
public abstract void setTitle(String title);
|
|
|
|
/**
|
|
* Restart if its a windowed or full-screen display.
|
|
*/
|
|
public abstract void restart();
|
|
|
|
/**
|
|
* Apply the settings, changing resolution, etc.
|
|
* @param settings
|
|
*/
|
|
protected abstract void createContext(AppSettings settings) throws LWJGLException;
|
|
|
|
/**
|
|
* Destroy the context.
|
|
*/
|
|
protected abstract void destroyContext();
|
|
|
|
/**
|
|
* Does LWJGL display initialization in the OpenGL thread
|
|
*/
|
|
protected boolean initInThread(){
|
|
try {
|
|
if (!JmeSystem.isLowPermissions()){
|
|
// Enable uncaught exception handler only for current thread
|
|
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
|
public void uncaughtException(Thread thread, Throwable thrown) {
|
|
listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
|
|
if (needClose.get()){
|
|
// listener.handleError() has requested the
|
|
// context to close. Satisfy request.
|
|
deinitInThread();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// For canvas, this will create a pbuffer,
|
|
// allowing us to query information.
|
|
// When the canvas context becomes available, it will
|
|
// be replaced seamlessly.
|
|
createContext(settings);
|
|
printContextInitInfo();
|
|
|
|
created.set(true);
|
|
super.internalCreate();
|
|
} catch (Exception ex){
|
|
try {
|
|
if (Display.isCreated())
|
|
Display.destroy();
|
|
} catch (Exception ex2){
|
|
logger.log(Level.WARNING, null, ex2);
|
|
}
|
|
|
|
listener.handleError("Failed to create display", ex);
|
|
return false; // if we failed to create display, do not continue
|
|
}
|
|
|
|
listener.initialize();
|
|
return true;
|
|
}
|
|
|
|
protected boolean checkGLError(){
|
|
try {
|
|
Util.checkGLError();
|
|
} catch (OpenGLException ex){
|
|
listener.handleError("An OpenGL error has occured!", ex);
|
|
}
|
|
// NOTE: Always return true since this is used in an "assert" statement
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* execute one iteration of the render loop in the OpenGL thread
|
|
*/
|
|
protected void runLoop(){
|
|
if (!created.get())
|
|
throw new IllegalStateException();
|
|
|
|
listener.update();
|
|
|
|
// All this does is call swap buffers
|
|
// If the canvas is not active, there's no need to waste time
|
|
// doing that ..
|
|
if (renderable.get()){
|
|
assert checkGLError();
|
|
|
|
// calls swap buffers, etc.
|
|
try {
|
|
if (autoFlush){
|
|
Display.update(false);
|
|
}else{
|
|
Display.processMessages();
|
|
Thread.sleep(50);
|
|
// add a small wait
|
|
// to reduce CPU usage
|
|
}
|
|
} catch (Throwable ex){
|
|
listener.handleError("Error while swapping buffers", ex);
|
|
}
|
|
}
|
|
|
|
if (frameRate > 0)
|
|
Display.sync(frameRate);
|
|
|
|
if (renderable.get()){
|
|
if (autoFlush){
|
|
// check input after we synchronize with framerate.
|
|
// this reduces input lag.
|
|
Display.processMessages();
|
|
}
|
|
}
|
|
|
|
// Subclasses just call GLObjectManager clean up objects here
|
|
// it is safe .. for now.
|
|
renderer.onFrame();
|
|
}
|
|
|
|
/**
|
|
* De-initialize in the OpenGL thread.
|
|
*/
|
|
protected void deinitInThread(){
|
|
destroyContext();
|
|
|
|
listener.destroy();
|
|
logger.fine("Display destroyed.");
|
|
super.internalDestroy();
|
|
}
|
|
|
|
public void run(){
|
|
if (listener == null) {
|
|
throw new IllegalStateException("SystemListener is not set on context!"
|
|
+ "Must set with JmeContext.setSystemListner().");
|
|
}
|
|
|
|
loadNatives();
|
|
logger.log(Level.FINE, "Using LWJGL {0}", Sys.getVersion());
|
|
if (!initInThread()) {
|
|
logger.log(Level.SEVERE, "Display initialization failed. Cannot continue.");
|
|
return;
|
|
}
|
|
while (true){
|
|
if (renderable.get()){
|
|
if (Display.isCloseRequested())
|
|
listener.requestClose(false);
|
|
|
|
if (wasActive != Display.isActive()) {
|
|
if (!wasActive) {
|
|
listener.gainFocus();
|
|
timer.reset();
|
|
wasActive = true;
|
|
} else {
|
|
listener.loseFocus();
|
|
wasActive = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
runLoop();
|
|
|
|
if (needClose.get())
|
|
break;
|
|
}
|
|
deinitInThread();
|
|
}
|
|
|
|
public JoyInput getJoyInput() {
|
|
if (joyInput == null){
|
|
joyInput = new JInputJoyInput();
|
|
}
|
|
return joyInput;
|
|
}
|
|
|
|
public MouseInput getMouseInput() {
|
|
if (mouseInput == null){
|
|
mouseInput = new LwjglMouseInput(this);
|
|
}
|
|
return mouseInput;
|
|
}
|
|
|
|
public KeyInput getKeyInput() {
|
|
if (keyInput == null){
|
|
keyInput = new LwjglKeyInput(this);
|
|
}
|
|
return keyInput;
|
|
}
|
|
|
|
public TouchInput getTouchInput() {
|
|
return null;
|
|
}
|
|
|
|
public void setAutoFlushFrames(boolean enabled){
|
|
this.autoFlush = enabled;
|
|
}
|
|
|
|
public void destroy(boolean waitFor){
|
|
needClose.set(true);
|
|
if (waitFor)
|
|
waitFor(false);
|
|
}
|
|
|
|
}
|
|
|