git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9021 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
9c6ca69b34
commit
bd828d629c
@ -1,478 +1,482 @@ |
|||||||
/* |
F/* |
||||||
* Copyright (c) 2009-2010 jMonkeyEngine |
* Copyright (c) 2009-2010 jMonkeyEngine |
||||||
* All rights reserved. |
* All rights reserved. |
||||||
* |
* |
||||||
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
||||||
* modification, are permitted provided that the following conditions are |
* modification, are permitted provided that the following conditions are |
||||||
* met: |
* met: |
||||||
* |
* |
||||||
* * Redistributions of source code must retain the above copyright |
* * Redistributions of source code must retain the above copyright |
||||||
* notice, this list of conditions and the following disclaimer. |
* notice, this list of conditions and the following disclaimer. |
||||||
* |
* |
||||||
* * Redistributions in binary form must reproduce the above copyright |
* * Redistributions in binary form must reproduce the above copyright |
||||||
* notice, this list of conditions and the following disclaimer in the |
* notice, this list of conditions and the following disclaimer in the |
||||||
* documentation and/or other materials provided with the distribution. |
* documentation and/or other materials provided with the distribution. |
||||||
* |
* |
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
* may be used to endorse or promote products derived from this software |
* may be used to endorse or promote products derived from this software |
||||||
* without specific prior written permission. |
* without specific prior written permission. |
||||||
* |
* |
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
*/ |
*/ |
||||||
|
|
||||||
package com.jme3.system.lwjgl; |
package com.jme3.system.lwjgl; |
||||||
|
|
||||||
import com.jme3.system.AppSettings; |
import com.jme3.system.AppSettings; |
||||||
import com.jme3.system.JmeCanvasContext; |
import com.jme3.system.JmeCanvasContext; |
||||||
import com.jme3.system.JmeContext.Type; |
import com.jme3.system.JmeContext.Type; |
||||||
import com.jme3.system.JmeSystem; |
import com.jme3.system.JmeSystem; |
||||||
import com.jme3.system.Platform; |
import com.jme3.system.Platform; |
||||||
import java.awt.Canvas; |
import java.awt.Canvas; |
||||||
import java.util.logging.Level; |
import java.util.logging.Level; |
||||||
import java.util.logging.Logger; |
import java.util.logging.Logger; |
||||||
import javax.swing.SwingUtilities; |
import javax.swing.SwingUtilities; |
||||||
import org.lwjgl.LWJGLException; |
import org.lwjgl.LWJGLException; |
||||||
import org.lwjgl.input.Keyboard; |
import org.lwjgl.input.Keyboard; |
||||||
import org.lwjgl.input.Mouse; |
import org.lwjgl.input.Mouse; |
||||||
import org.lwjgl.opengl.Display; |
import org.lwjgl.opengl.Display; |
||||||
import org.lwjgl.opengl.Pbuffer; |
import org.lwjgl.opengl.Pbuffer; |
||||||
import org.lwjgl.opengl.PixelFormat; |
import org.lwjgl.opengl.PixelFormat; |
||||||
|
|
||||||
public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContext { |
public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContext { |
||||||
|
|
||||||
protected static final int TASK_NOTHING = 0, |
protected static final int TASK_NOTHING = 0, |
||||||
TASK_DESTROY_DISPLAY = 1, |
TASK_DESTROY_DISPLAY = 1, |
||||||
TASK_CREATE_DISPLAY = 2, |
TASK_CREATE_DISPLAY = 2, |
||||||
TASK_COMPLETE = 3; |
TASK_COMPLETE = 3; |
||||||
|
|
||||||
// protected static final boolean USE_SHARED_CONTEXT =
|
// protected static final boolean USE_SHARED_CONTEXT =
|
||||||
// Boolean.parseBoolean(System.getProperty("jme3.canvas.sharedctx", "true"));
|
// Boolean.parseBoolean(System.getProperty("jme3.canvas.sharedctx", "true"));
|
||||||
|
|
||||||
protected static final boolean USE_SHARED_CONTEXT = false; |
protected static final boolean USE_SHARED_CONTEXT = false; |
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName()); |
private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName()); |
||||||
private Canvas canvas; |
private Canvas canvas; |
||||||
private int width; |
private int width; |
||||||
private int height; |
private int height; |
||||||
|
|
||||||
private final Object taskLock = new Object(); |
private final Object taskLock = new Object(); |
||||||
private int desiredTask = TASK_NOTHING; |
private int desiredTask = TASK_NOTHING; |
||||||
|
|
||||||
private Thread renderThread; |
private Thread renderThread; |
||||||
private boolean runningFirstTime = true; |
private boolean runningFirstTime = true; |
||||||
private boolean mouseWasGrabbed = false; |
private boolean mouseWasGrabbed = false; |
||||||
|
|
||||||
private boolean mouseWasCreated = false; |
private boolean mouseWasCreated = false; |
||||||
private boolean keyboardWasCreated = false; |
private boolean keyboardWasCreated = false; |
||||||
|
|
||||||
private Pbuffer pbuffer; |
private Pbuffer pbuffer; |
||||||
private PixelFormat pbufferFormat; |
private PixelFormat pbufferFormat; |
||||||
private PixelFormat canvasFormat; |
private PixelFormat canvasFormat; |
||||||
|
|
||||||
private class GLCanvas extends Canvas { |
private class GLCanvas extends Canvas { |
||||||
@Override |
@Override |
||||||
public void addNotify(){ |
public void addNotify(){ |
||||||
super.addNotify(); |
super.addNotify(); |
||||||
|
|
||||||
if (renderThread != null && renderThread.getState() == Thread.State.TERMINATED) |
if (renderThread != null && renderThread.getState() == Thread.State.TERMINATED) |
||||||
return; // already destroyed.
|
return; // already destroyed.
|
||||||
|
|
||||||
if (renderThread == null){ |
if (renderThread == null){ |
||||||
logger.log(Level.INFO, "EDT: Creating OGL thread."); |
logger.log(Level.INFO, "EDT: Creating OGL thread."); |
||||||
|
|
||||||
// Also set some settings on the canvas here.
|
// Also set some settings on the canvas here.
|
||||||
// So we don't do it outside the AWT thread.
|
// So we don't do it outside the AWT thread.
|
||||||
canvas.setFocusable(true); |
canvas.setFocusable(true); |
||||||
canvas.setIgnoreRepaint(true); |
canvas.setIgnoreRepaint(true); |
||||||
|
|
||||||
renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread"); |
renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread"); |
||||||
renderThread.start(); |
renderThread.start(); |
||||||
}else if (needClose.get()){ |
}else if (needClose.get()){ |
||||||
return; |
return; |
||||||
} |
} |
||||||
|
|
||||||
logger.log(Level.INFO, "EDT: Telling OGL to create display .."); |
logger.log(Level.INFO, "EDT: Telling OGL to create display .."); |
||||||
synchronized (taskLock){ |
synchronized (taskLock){ |
||||||
desiredTask = TASK_CREATE_DISPLAY; |
desiredTask = TASK_CREATE_DISPLAY; |
||||||
// while (desiredTask != TASK_COMPLETE){
|
// while (desiredTask != TASK_COMPLETE){
|
||||||
// try {
|
// try {
|
||||||
// taskLock.wait();
|
// taskLock.wait();
|
||||||
// } catch (InterruptedException ex) {
|
// } catch (InterruptedException ex) {
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// desiredTask = TASK_NOTHING;
|
// desiredTask = TASK_NOTHING;
|
||||||
} |
} |
||||||
// logger.log(Level.INFO, "EDT: OGL has created the display");
|
// logger.log(Level.INFO, "EDT: OGL has created the display");
|
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public void removeNotify(){ |
public void removeNotify(){ |
||||||
if (needClose.get()){ |
if (needClose.get()){ |
||||||
logger.log(Level.INFO, "EDT: Application is stopped. Not restoring canvas."); |
logger.log(Level.INFO, "EDT: Application is stopped. Not restoring canvas."); |
||||||
super.removeNotify(); |
super.removeNotify(); |
||||||
return; |
return; |
||||||
} |
} |
||||||
|
|
||||||
// We must tell GL context to shutdown and wait for it to
|
// We must tell GL context to shutdown and wait for it to
|
||||||
// shutdown, otherwise, issues will occur.
|
// shutdown, otherwise, issues will occur.
|
||||||
logger.log(Level.INFO, "EDT: Telling OGL to destroy display .."); |
logger.log(Level.INFO, "EDT: Telling OGL to destroy display .."); |
||||||
synchronized (taskLock){ |
synchronized (taskLock){ |
||||||
desiredTask = TASK_DESTROY_DISPLAY; |
desiredTask = TASK_DESTROY_DISPLAY; |
||||||
while (desiredTask != TASK_COMPLETE){ |
while (desiredTask != TASK_COMPLETE){ |
||||||
try { |
try { |
||||||
taskLock.wait(); |
taskLock.wait(); |
||||||
} catch (InterruptedException ex){ |
} catch (InterruptedException ex){ |
||||||
super.removeNotify(); |
super.removeNotify(); |
||||||
return; |
return; |
||||||
} |
} |
||||||
} |
} |
||||||
desiredTask = TASK_NOTHING; |
desiredTask = TASK_NOTHING; |
||||||
} |
} |
||||||
|
|
||||||
logger.log(Level.INFO, "EDT: Acknowledged receipt of canvas death"); |
logger.log(Level.INFO, "EDT: Acknowledged receipt of canvas death"); |
||||||
// GL context is dead at this point
|
// GL context is dead at this point
|
||||||
|
|
||||||
super.removeNotify(); |
super.removeNotify(); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
public LwjglCanvas(){ |
public LwjglCanvas(){ |
||||||
super(); |
super(); |
||||||
canvas = new GLCanvas(); |
canvas = new GLCanvas(); |
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public Type getType() { |
public Type getType() { |
||||||
return Type.Canvas; |
return Type.Canvas; |
||||||
} |
} |
||||||
|
|
||||||
public void create(boolean waitFor){ |
public void create(boolean waitFor){ |
||||||
if (renderThread == null){ |
if (renderThread == null){ |
||||||
logger.log(Level.INFO, "MAIN: Creating OGL thread."); |
logger.log(Level.INFO, "MAIN: Creating OGL thread."); |
||||||
|
|
||||||
renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread"); |
renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread"); |
||||||
renderThread.start(); |
renderThread.start(); |
||||||
} |
} |
||||||
// do not do anything.
|
// do not do anything.
|
||||||
// superclass's create() will be called at initInThread()
|
// superclass's create() will be called at initInThread()
|
||||||
if (waitFor) |
if (waitFor) |
||||||
waitFor(true); |
waitFor(true); |
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public void setTitle(String title) { |
public void setTitle(String title) { |
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public void restart() { |
public void restart() { |
||||||
frameRate = settings.getFrameRate(); |
frameRate = settings.getFrameRate(); |
||||||
// TODO: Handle other cases, like change of pixel format, etc.
|
// TODO: Handle other cases, like change of pixel format, etc.
|
||||||
} |
} |
||||||
|
|
||||||
public Canvas getCanvas(){ |
public Canvas getCanvas(){ |
||||||
return canvas; |
return canvas; |
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
protected void runLoop(){ |
protected void runLoop(){ |
||||||
if (desiredTask != TASK_NOTHING){ |
if (desiredTask != TASK_NOTHING){ |
||||||
synchronized (taskLock){ |
synchronized (taskLock){ |
||||||
switch (desiredTask){ |
switch (desiredTask){ |
||||||
case TASK_CREATE_DISPLAY: |
case TASK_CREATE_DISPLAY: |
||||||
logger.log(Level.INFO, "OGL: Creating display .."); |
logger.log(Level.INFO, "OGL: Creating display .."); |
||||||
restoreCanvas(); |
restoreCanvas(); |
||||||
listener.gainFocus(); |
listener.gainFocus(); |
||||||
desiredTask = TASK_NOTHING; |
desiredTask = TASK_NOTHING; |
||||||
break; |
break; |
||||||
case TASK_DESTROY_DISPLAY: |
case TASK_DESTROY_DISPLAY: |
||||||
logger.log(Level.INFO, "OGL: Destroying display .."); |
logger.log(Level.INFO, "OGL: Destroying display .."); |
||||||
listener.loseFocus(); |
listener.loseFocus(); |
||||||
pauseCanvas(); |
pauseCanvas(); |
||||||
break; |
break; |
||||||
} |
} |
||||||
desiredTask = TASK_COMPLETE; |
desiredTask = TASK_COMPLETE; |
||||||
taskLock.notifyAll(); |
taskLock.notifyAll(); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
if (renderable.get()){ |
if (renderable.get()){ |
||||||
int newWidth = Math.max(canvas.getWidth(), 1); |
int newWidth = Math.max(canvas.getWidth(), 1); |
||||||
int newHeight = Math.max(canvas.getHeight(), 1); |
int newHeight = Math.max(canvas.getHeight(), 1); |
||||||
if (width != newWidth || height != newHeight){ |
if (width != newWidth || height != newHeight){ |
||||||
width = newWidth; |
width = newWidth; |
||||||
height = newHeight; |
height = newHeight; |
||||||
if (listener != null){ |
if (listener != null){ |
||||||
listener.reshape(width, height); |
listener.reshape(width, height); |
||||||
} |
} |
||||||
} |
} |
||||||
}else{ |
}else{ |
||||||
if (frameRate <= 0){ |
if (frameRate <= 0){ |
||||||
// NOTE: MUST be done otherwise
|
// NOTE: MUST be done otherwise
|
||||||
// Windows OS will freeze
|
// Windows OS will freeze
|
||||||
Display.sync(30); |
Display.sync(30); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
super.runLoop(); |
super.runLoop(); |
||||||
} |
} |
||||||
|
|
||||||
private void pauseCanvas(){ |
private void pauseCanvas(){ |
||||||
if (Mouse.isCreated()){ |
if (Mouse.isCreated()){ |
||||||
if (Mouse.isGrabbed()){ |
if (Mouse.isGrabbed()){ |
||||||
Mouse.setGrabbed(false); |
Mouse.setGrabbed(false); |
||||||
mouseWasGrabbed = true; |
mouseWasGrabbed = true; |
||||||
} |
} |
||||||
mouseWasCreated = true; |
mouseWasCreated = true; |
||||||
Mouse.destroy(); |
Mouse.destroy(); |
||||||
} |
} |
||||||
if (Keyboard.isCreated()){ |
if (Keyboard.isCreated()){ |
||||||
keyboardWasCreated = true; |
keyboardWasCreated = true; |
||||||
Keyboard.destroy(); |
Keyboard.destroy(); |
||||||
} |
} |
||||||
|
|
||||||
renderable.set(false); |
renderable.set(false); |
||||||
destroyContext(); |
destroyContext(); |
||||||
} |
} |
||||||
|
|
||||||
/** |
/** |
||||||
* Called to restore the canvas. |
* Called to restore the canvas. |
||||||
*/ |
*/ |
||||||
private void restoreCanvas(){ |
private void restoreCanvas(){ |
||||||
logger.log(Level.INFO, "OGL: Waiting for canvas to become displayable.."); |
logger.log(Level.INFO, "OGL: Waiting for canvas to become displayable.."); |
||||||
while (!canvas.isDisplayable()){ |
while (!canvas.isDisplayable()){ |
||||||
try { |
try { |
||||||
Thread.sleep(10); |
Thread.sleep(10); |
||||||
} catch (InterruptedException ex) { |
} catch (InterruptedException ex) { |
||||||
logger.log(Level.SEVERE, "OGL: Interrupted! ", ex); |
logger.log(Level.SEVERE, "OGL: Interrupted! ", ex); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
logger.log(Level.INFO, "OGL: Creating display context .."); |
logger.log(Level.INFO, "OGL: Creating display context .."); |
||||||
|
|
||||||
// Set renderable to true, since canvas is now displayable.
|
// Set renderable to true, since canvas is now displayable.
|
||||||
renderable.set(true); |
renderable.set(true); |
||||||
createContext(settings); |
createContext(settings); |
||||||
|
|
||||||
logger.log(Level.INFO, "OGL: Display is active!"); |
logger.log(Level.INFO, "OGL: Display is active!"); |
||||||
|
|
||||||
try { |
try { |
||||||
if (mouseWasCreated){ |
if (mouseWasCreated){ |
||||||
Mouse.create(); |
Mouse.create(); |
||||||
if (mouseWasGrabbed){ |
if (mouseWasGrabbed){ |
||||||
Mouse.setGrabbed(true); |
Mouse.setGrabbed(true); |
||||||
mouseWasGrabbed = false; |
mouseWasGrabbed = false; |
||||||
} |
} |
||||||
} |
} |
||||||
if (keyboardWasCreated){ |
if (keyboardWasCreated){ |
||||||
Keyboard.create(); |
Keyboard.create(); |
||||||
keyboardWasCreated = false; |
keyboardWasCreated = false; |
||||||
} |
} |
||||||
} catch (LWJGLException ex){ |
} catch (LWJGLException ex){ |
||||||
logger.log(Level.SEVERE, "Encountered exception when restoring input", ex); |
logger.log(Level.SEVERE, "Encountered exception when restoring input", ex); |
||||||
} |
} |
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable(){ |
SwingUtilities.invokeLater(new Runnable(){ |
||||||
public void run(){ |
public void run(){ |
||||||
canvas.requestFocus(); |
canvas.requestFocus(); |
||||||
} |
} |
||||||
}); |
}); |
||||||
} |
} |
||||||
|
|
||||||
/** |
/** |
||||||
* It seems it is best to use one pixel format for all shared contexts. |
* It seems it is best to use one pixel format for all shared contexts. |
||||||
* @see <a href="http://developer.apple.com/library/mac/#qa/qa1248/_index.html">http://developer.apple.com/library/mac/#qa/qa1248/_index.html</a>
|
* @see <a href="http://developer.apple.com/library/mac/#qa/qa1248/_index.html">http://developer.apple.com/library/mac/#qa/qa1248/_index.html</a>
|
||||||
*/ |
*/ |
||||||
protected PixelFormat acquirePixelFormat(boolean forPbuffer){ |
protected PixelFormat acquirePixelFormat(boolean forPbuffer){ |
||||||
if (forPbuffer){ |
if (forPbuffer){ |
||||||
// Use 0 samples for pbuffer format, prevents
|
// Use 0 samples for pbuffer format, prevents
|
||||||
// crashes on bad drivers
|
// crashes on bad drivers
|
||||||
if (pbufferFormat == null){ |
if (pbufferFormat == null){ |
||||||
pbufferFormat = new PixelFormat(settings.getBitsPerPixel(), |
pbufferFormat = new PixelFormat(settings.getBitsPerPixel(), |
||||||
0, |
0, |
||||||
settings.getDepthBits(), |
settings.getDepthBits(), |
||||||
settings.getStencilBits(), |
settings.getStencilBits(), |
||||||
0); |
0); |
||||||
} |
} |
||||||
return pbufferFormat; |
return pbufferFormat; |
||||||
}else{ |
}else{ |
||||||
if (canvasFormat == null){ |
if (canvasFormat == null){ |
||||||
canvasFormat = new PixelFormat(settings.getBitsPerPixel(), |
int samples = 0; |
||||||
0, |
if (settings.getSamples() > 1){ |
||||||
settings.getDepthBits(), |
samples = settings.getSamples(); |
||||||
settings.getStencilBits(), |
} |
||||||
settings.getSamples()); |
canvasFormat = new PixelFormat(settings.getBitsPerPixel(), |
||||||
} |
0, |
||||||
return canvasFormat; |
settings.getDepthBits(), |
||||||
} |
settings.getStencilBits(), |
||||||
} |
samples); |
||||||
|
} |
||||||
/** |
return canvasFormat; |
||||||
* Makes sure the pbuffer is available and ready for use |
} |
||||||
*/ |
} |
||||||
protected void makePbufferAvailable() throws LWJGLException{ |
|
||||||
if (pbuffer != null && pbuffer.isBufferLost()){ |
/** |
||||||
logger.log(Level.WARNING, "PBuffer was lost!"); |
* Makes sure the pbuffer is available and ready for use |
||||||
pbuffer.destroy(); |
*/ |
||||||
pbuffer = null; |
protected void makePbufferAvailable() throws LWJGLException{ |
||||||
} |
if (pbuffer != null && pbuffer.isBufferLost()){ |
||||||
|
logger.log(Level.WARNING, "PBuffer was lost!"); |
||||||
if (pbuffer == null) { |
pbuffer.destroy(); |
||||||
pbuffer = new Pbuffer(1, 1, acquirePixelFormat(true), null); |
pbuffer = null; |
||||||
pbuffer.makeCurrent(); |
} |
||||||
logger.log(Level.INFO, "OGL: Pbuffer has been created"); |
|
||||||
|
if (pbuffer == null) { |
||||||
// Any created objects are no longer valid
|
pbuffer = new Pbuffer(1, 1, acquirePixelFormat(true), null); |
||||||
if (!runningFirstTime){ |
pbuffer.makeCurrent(); |
||||||
renderer.resetGLObjects(); |
logger.log(Level.INFO, "OGL: Pbuffer has been created"); |
||||||
} |
|
||||||
} |
// Any created objects are no longer valid
|
||||||
|
if (!runningFirstTime){ |
||||||
pbuffer.makeCurrent(); |
renderer.resetGLObjects(); |
||||||
if (!pbuffer.isCurrent()){ |
} |
||||||
throw new LWJGLException("Pbuffer cannot be made current"); |
} |
||||||
} |
|
||||||
} |
pbuffer.makeCurrent(); |
||||||
|
if (!pbuffer.isCurrent()){ |
||||||
protected void destroyPbuffer(){ |
throw new LWJGLException("Pbuffer cannot be made current"); |
||||||
if (pbuffer != null){ |
} |
||||||
if (!pbuffer.isBufferLost()){ |
} |
||||||
pbuffer.destroy(); |
|
||||||
} |
protected void destroyPbuffer(){ |
||||||
pbuffer = null; |
if (pbuffer != null){ |
||||||
} |
if (!pbuffer.isBufferLost()){ |
||||||
} |
pbuffer.destroy(); |
||||||
|
} |
||||||
/** |
pbuffer = null; |
||||||
* This is called: |
} |
||||||
* 1) When the context thread ends |
} |
||||||
* 2) Any time the canvas becomes non-displayable |
|
||||||
*/ |
/** |
||||||
protected void destroyContext(){ |
* This is called: |
||||||
try { |
* 1) When the context thread ends |
||||||
// invalidate the state so renderer can resume operation
|
* 2) Any time the canvas becomes non-displayable |
||||||
if (!USE_SHARED_CONTEXT){ |
*/ |
||||||
renderer.cleanup(); |
protected void destroyContext(){ |
||||||
} |
try { |
||||||
|
// invalidate the state so renderer can resume operation
|
||||||
if (Display.isCreated()){ |
if (!USE_SHARED_CONTEXT){ |
||||||
/* FIXES: |
renderer.cleanup(); |
||||||
* org.lwjgl.LWJGLException: X Error |
} |
||||||
* BadWindow (invalid Window parameter) request_code: 2 minor_code: 0 |
|
||||||
* |
if (Display.isCreated()){ |
||||||
* Destroying keyboard early prevents the error above, triggered |
/* FIXES: |
||||||
* by destroying keyboard in by Display.destroy() or Display.setParent(null). |
* org.lwjgl.LWJGLException: X Error |
||||||
* Therefore Keyboard.destroy() should precede any of these calls. |
* BadWindow (invalid Window parameter) request_code: 2 minor_code: 0 |
||||||
*/ |
* |
||||||
if (Keyboard.isCreated()){ |
* Destroying keyboard early prevents the error above, triggered |
||||||
// Should only happen if called in
|
* by destroying keyboard in by Display.destroy() or Display.setParent(null). |
||||||
// LwjglAbstractDisplay.deinitInThread().
|
* Therefore Keyboard.destroy() should precede any of these calls. |
||||||
Keyboard.destroy(); |
*/ |
||||||
} |
if (Keyboard.isCreated()){ |
||||||
|
// Should only happen if called in
|
||||||
//try {
|
// LwjglAbstractDisplay.deinitInThread().
|
||||||
// NOTE: On Windows XP, not calling setParent(null)
|
Keyboard.destroy(); |
||||||
// freezes the application.
|
} |
||||||
// On Mac it freezes the application.
|
|
||||||
// On Linux it fixes a crash with X Window System.
|
//try {
|
||||||
if (JmeSystem.getPlatform() == Platform.Windows32 |
// NOTE: On Windows XP, not calling setParent(null)
|
||||||
|| JmeSystem.getPlatform() == Platform.Windows64){ |
// freezes the application.
|
||||||
//Display.setParent(null);
|
// On Mac it freezes the application.
|
||||||
} |
// On Linux it fixes a crash with X Window System.
|
||||||
//} catch (LWJGLException ex) {
|
if (JmeSystem.getPlatform() == Platform.Windows32 |
||||||
// logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex);
|
|| JmeSystem.getPlatform() == Platform.Windows64){ |
||||||
//}
|
//Display.setParent(null);
|
||||||
|
} |
||||||
Display.destroy(); |
//} catch (LWJGLException ex) {
|
||||||
} |
// logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex);
|
||||||
|
//}
|
||||||
// The canvas is no longer visible,
|
|
||||||
// but the context thread is still running.
|
Display.destroy(); |
||||||
if (!needClose.get()){ |
} |
||||||
// MUST make sure there's still a context current here ..
|
|
||||||
// Display is dead, make pbuffer available to the system
|
// The canvas is no longer visible,
|
||||||
makePbufferAvailable(); |
// but the context thread is still running.
|
||||||
|
if (!needClose.get()){ |
||||||
renderer.invalidateState(); |
// MUST make sure there's still a context current here ..
|
||||||
}else{ |
// Display is dead, make pbuffer available to the system
|
||||||
// The context thread is no longer running.
|
makePbufferAvailable(); |
||||||
// Destroy pbuffer.
|
|
||||||
destroyPbuffer(); |
renderer.invalidateState(); |
||||||
} |
}else{ |
||||||
} catch (LWJGLException ex) { |
// The context thread is no longer running.
|
||||||
listener.handleError("Failed make pbuffer available", ex); |
// Destroy pbuffer.
|
||||||
} |
destroyPbuffer(); |
||||||
} |
} |
||||||
|
} catch (LWJGLException ex) { |
||||||
/** |
listener.handleError("Failed make pbuffer available", ex); |
||||||
* This is called: |
} |
||||||
* 1) When the context thread starts |
} |
||||||
* 2) Any time the canvas becomes displayable again. |
|
||||||
*/ |
/** |
||||||
@Override |
* This is called: |
||||||
protected void createContext(AppSettings settings) { |
* 1) When the context thread starts |
||||||
// In case canvas is not visible, we still take framerate
|
* 2) Any time the canvas becomes displayable again. |
||||||
// from settings to prevent "100% CPU usage"
|
*/ |
||||||
frameRate = settings.getFrameRate(); |
@Override |
||||||
|
protected void createContext(AppSettings settings) { |
||||||
try { |
// In case canvas is not visible, we still take framerate
|
||||||
if (renderable.get()){ |
// from settings to prevent "100% CPU usage"
|
||||||
if (!runningFirstTime){ |
frameRate = settings.getFrameRate(); |
||||||
// because the display is a different opengl context
|
|
||||||
// must reset the context state.
|
try { |
||||||
if (!USE_SHARED_CONTEXT){ |
if (renderable.get()){ |
||||||
renderer.cleanup(); |
if (!runningFirstTime){ |
||||||
} |
// because the display is a different opengl context
|
||||||
} |
// must reset the context state.
|
||||||
|
if (!USE_SHARED_CONTEXT){ |
||||||
// if the pbuffer is currently active,
|
renderer.cleanup(); |
||||||
// make sure to deactivate it
|
} |
||||||
destroyPbuffer(); |
} |
||||||
|
|
||||||
if (Keyboard.isCreated()){ |
// if the pbuffer is currently active,
|
||||||
Keyboard.destroy(); |
// make sure to deactivate it
|
||||||
} |
destroyPbuffer(); |
||||||
|
|
||||||
try { |
if (Keyboard.isCreated()){ |
||||||
Thread.sleep(1000); |
Keyboard.destroy(); |
||||||
} catch (InterruptedException ex) { |
} |
||||||
} |
|
||||||
|
try { |
||||||
Display.setVSyncEnabled(settings.isVSync()); |
Thread.sleep(1000); |
||||||
Display.setParent(canvas); |
} catch (InterruptedException ex) { |
||||||
|
} |
||||||
if (USE_SHARED_CONTEXT){ |
|
||||||
Display.create(acquirePixelFormat(false), pbuffer); |
Display.setVSyncEnabled(settings.isVSync()); |
||||||
}else{ |
Display.setParent(canvas); |
||||||
Display.create(acquirePixelFormat(false)); |
|
||||||
} |
if (USE_SHARED_CONTEXT){ |
||||||
|
Display.create(acquirePixelFormat(false), pbuffer); |
||||||
renderer.invalidateState(); |
}else{ |
||||||
}else{ |
Display.create(acquirePixelFormat(false)); |
||||||
// First create the pbuffer, if it is needed.
|
} |
||||||
makePbufferAvailable(); |
|
||||||
} |
renderer.invalidateState(); |
||||||
|
}else{ |
||||||
// At this point, the OpenGL context is active.
|
// First create the pbuffer, if it is needed.
|
||||||
if (runningFirstTime){ |
makePbufferAvailable(); |
||||||
// THIS is the part that creates the renderer.
|
} |
||||||
// It must always be called, now that we have the pbuffer workaround.
|
|
||||||
initContextFirstTime(); |
// At this point, the OpenGL context is active.
|
||||||
runningFirstTime = false; |
if (runningFirstTime){ |
||||||
} |
// THIS is the part that creates the renderer.
|
||||||
} catch (LWJGLException ex) { |
// It must always be called, now that we have the pbuffer workaround.
|
||||||
listener.handleError("Failed to initialize OpenGL context", ex); |
initContextFirstTime(); |
||||||
// TODO: Fix deadlock that happens after the error (throw runtime exception?)
|
runningFirstTime = false; |
||||||
} |
} |
||||||
} |
} catch (LWJGLException ex) { |
||||||
} |
listener.handleError("Failed to initialize OpenGL context", ex); |
||||||
|
// TODO: Fix deadlock that happens after the error (throw runtime exception?)
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
@ -1,194 +1,198 @@ |
|||||||
/* |
/* |
||||||
* Copyright (c) 2009-2010 jMonkeyEngine |
* Copyright (c) 2009-2010 jMonkeyEngine |
||||||
* All rights reserved. |
* All rights reserved. |
||||||
* |
* |
||||||
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
||||||
* modification, are permitted provided that the following conditions are |
* modification, are permitted provided that the following conditions are |
||||||
* met: |
* met: |
||||||
* |
* |
||||||
* * Redistributions of source code must retain the above copyright |
* * Redistributions of source code must retain the above copyright |
||||||
* notice, this list of conditions and the following disclaimer. |
* notice, this list of conditions and the following disclaimer. |
||||||
* |
* |
||||||
* * Redistributions in binary form must reproduce the above copyright |
* * Redistributions in binary form must reproduce the above copyright |
||||||
* notice, this list of conditions and the following disclaimer in the |
* notice, this list of conditions and the following disclaimer in the |
||||||
* documentation and/or other materials provided with the distribution. |
* documentation and/or other materials provided with the distribution. |
||||||
* |
* |
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
* may be used to endorse or promote products derived from this software |
* may be used to endorse or promote products derived from this software |
||||||
* without specific prior written permission. |
* without specific prior written permission. |
||||||
* |
* |
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
*/ |
*/ |
||||||
|
|
||||||
package com.jme3.system.lwjgl; |
package com.jme3.system.lwjgl; |
||||||
|
|
||||||
import com.jme3.input.JoyInput; |
import com.jme3.input.JoyInput; |
||||||
import com.jme3.input.KeyInput; |
import com.jme3.input.KeyInput; |
||||||
import com.jme3.input.MouseInput; |
import com.jme3.input.MouseInput; |
||||||
import com.jme3.input.TouchInput; |
import com.jme3.input.TouchInput; |
||||||
import com.jme3.input.dummy.DummyKeyInput; |
import com.jme3.input.dummy.DummyKeyInput; |
||||||
import com.jme3.input.dummy.DummyMouseInput; |
import com.jme3.input.dummy.DummyMouseInput; |
||||||
import java.util.concurrent.atomic.AtomicBoolean; |
import java.util.concurrent.atomic.AtomicBoolean; |
||||||
import java.util.logging.Level; |
import java.util.logging.Level; |
||||||
import java.util.logging.Logger; |
import java.util.logging.Logger; |
||||||
import org.lwjgl.LWJGLException; |
import org.lwjgl.LWJGLException; |
||||||
import org.lwjgl.Sys; |
import org.lwjgl.Sys; |
||||||
import org.lwjgl.opengl.*; |
import org.lwjgl.opengl.*; |
||||||
|
|
||||||
public class LwjglOffscreenBuffer extends LwjglContext implements Runnable { |
public class LwjglOffscreenBuffer extends LwjglContext implements Runnable { |
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(LwjglOffscreenBuffer.class.getName()); |
private static final Logger logger = Logger.getLogger(LwjglOffscreenBuffer.class.getName()); |
||||||
private Pbuffer pbuffer; |
private Pbuffer pbuffer; |
||||||
protected AtomicBoolean needClose = new AtomicBoolean(false); |
protected AtomicBoolean needClose = new AtomicBoolean(false); |
||||||
private int width; |
private int width; |
||||||
private int height; |
private int height; |
||||||
private PixelFormat pixelFormat; |
private PixelFormat pixelFormat; |
||||||
|
|
||||||
protected void initInThread(){ |
protected void initInThread(){ |
||||||
if ((Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0){ |
if ((Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0){ |
||||||
logger.severe("Offscreen surfaces are not supported."); |
logger.severe("Offscreen surfaces are not supported."); |
||||||
return; |
return; |
||||||
} |
} |
||||||
|
|
||||||
pixelFormat = new PixelFormat(settings.getBitsPerPixel(), |
int samples = 0; |
||||||
0, |
if (settings.getSamples() > 1){ |
||||||
settings.getDepthBits(), |
samples = settings.getSamples(); |
||||||
settings.getStencilBits(), |
} |
||||||
settings.getSamples()); |
pixelFormat = new PixelFormat(settings.getBitsPerPixel(), |
||||||
|
0, |
||||||
width = settings.getWidth(); |
settings.getDepthBits(), |
||||||
height = settings.getHeight(); |
settings.getStencilBits(), |
||||||
try{ |
settings.getSamples()); |
||||||
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { |
|
||||||
public void uncaughtException(Thread thread, Throwable thrown) { |
width = settings.getWidth(); |
||||||
listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown); |
height = settings.getHeight(); |
||||||
} |
try{ |
||||||
}); |
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { |
||||||
|
public void uncaughtException(Thread thread, Throwable thrown) { |
||||||
pbuffer = new Pbuffer(width, height, pixelFormat, null, null, createContextAttribs()); |
listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown); |
||||||
pbuffer.makeCurrent(); |
} |
||||||
|
}); |
||||||
renderable.set(true); |
|
||||||
|
pbuffer = new Pbuffer(width, height, pixelFormat, null, null, createContextAttribs()); |
||||||
logger.info("Offscreen buffer created."); |
pbuffer.makeCurrent(); |
||||||
printContextInitInfo(); |
|
||||||
} catch (LWJGLException ex){ |
renderable.set(true); |
||||||
listener.handleError("Failed to create display", ex); |
|
||||||
} finally { |
logger.info("Offscreen buffer created."); |
||||||
// TODO: It is possible to avoid "Failed to find pixel format"
|
printContextInitInfo(); |
||||||
// error here by creating a default display.
|
} catch (LWJGLException ex){ |
||||||
} |
listener.handleError("Failed to create display", ex); |
||||||
super.internalCreate(); |
} finally { |
||||||
listener.initialize(); |
// TODO: It is possible to avoid "Failed to find pixel format"
|
||||||
} |
// error here by creating a default display.
|
||||||
|
} |
||||||
protected boolean checkGLError(){ |
super.internalCreate(); |
||||||
try { |
listener.initialize(); |
||||||
Util.checkGLError(); |
} |
||||||
} catch (OpenGLException ex){ |
|
||||||
listener.handleError("An OpenGL error has occured!", ex); |
protected boolean checkGLError(){ |
||||||
} |
try { |
||||||
// NOTE: Always return true since this is used in an "assert" statement
|
Util.checkGLError(); |
||||||
return true; |
} catch (OpenGLException ex){ |
||||||
} |
listener.handleError("An OpenGL error has occured!", ex); |
||||||
|
} |
||||||
protected void runLoop(){ |
// NOTE: Always return true since this is used in an "assert" statement
|
||||||
if (!created.get()) |
return true; |
||||||
throw new IllegalStateException(); |
} |
||||||
|
|
||||||
if (pbuffer.isBufferLost()){ |
protected void runLoop(){ |
||||||
pbuffer.destroy(); |
if (!created.get()) |
||||||
try{ |
throw new IllegalStateException(); |
||||||
pbuffer = new Pbuffer(width, height, pixelFormat, null); |
|
||||||
pbuffer.makeCurrent(); |
if (pbuffer.isBufferLost()){ |
||||||
}catch (LWJGLException ex){ |
pbuffer.destroy(); |
||||||
listener.handleError("Failed to restore pbuffer content", ex); |
try{ |
||||||
} |
pbuffer = new Pbuffer(width, height, pixelFormat, null); |
||||||
} |
pbuffer.makeCurrent(); |
||||||
|
}catch (LWJGLException ex){ |
||||||
listener.update(); |
listener.handleError("Failed to restore pbuffer content", ex); |
||||||
assert checkGLError(); |
} |
||||||
|
} |
||||||
renderer.onFrame(); |
|
||||||
|
listener.update(); |
||||||
int frameRate = settings.getFrameRate(); |
assert checkGLError(); |
||||||
if (frameRate >= 1){ |
|
||||||
Display.sync(frameRate); |
renderer.onFrame(); |
||||||
} |
|
||||||
} |
int frameRate = settings.getFrameRate(); |
||||||
|
if (frameRate >= 1){ |
||||||
protected void deinitInThread(){ |
Display.sync(frameRate); |
||||||
renderable.set(false); |
} |
||||||
|
} |
||||||
listener.destroy(); |
|
||||||
renderer.cleanup(); |
protected void deinitInThread(){ |
||||||
pbuffer.destroy(); |
renderable.set(false); |
||||||
logger.info("Offscreen buffer destroyed."); |
|
||||||
} |
listener.destroy(); |
||||||
|
renderer.cleanup(); |
||||||
public void run(){ |
pbuffer.destroy(); |
||||||
logger.log(Level.INFO, "Using LWJGL {0}", Sys.getVersion()); |
logger.info("Offscreen buffer destroyed."); |
||||||
initInThread(); |
} |
||||||
while (!needClose.get()){ |
|
||||||
runLoop(); |
public void run(){ |
||||||
} |
logger.log(Level.INFO, "Using LWJGL {0}", Sys.getVersion()); |
||||||
deinitInThread(); |
initInThread(); |
||||||
} |
while (!needClose.get()){ |
||||||
|
runLoop(); |
||||||
public void destroy(boolean waitFor){ |
} |
||||||
needClose.set(true); |
deinitInThread(); |
||||||
if (waitFor) |
} |
||||||
waitFor(false); |
|
||||||
} |
public void destroy(boolean waitFor){ |
||||||
|
needClose.set(true); |
||||||
public void create(boolean waitFor){ |
if (waitFor) |
||||||
if (created.get()){ |
waitFor(false); |
||||||
logger.warning("create() called when pbuffer is already created!"); |
} |
||||||
return; |
|
||||||
} |
public void create(boolean waitFor){ |
||||||
|
if (created.get()){ |
||||||
new Thread(this, "LWJGL Renderer Thread").start(); |
logger.warning("create() called when pbuffer is already created!"); |
||||||
if (waitFor) |
return; |
||||||
waitFor(true); |
} |
||||||
} |
|
||||||
|
new Thread(this, "LWJGL Renderer Thread").start(); |
||||||
public void restart() { |
if (waitFor) |
||||||
} |
waitFor(true); |
||||||
|
} |
||||||
public void setAutoFlushFrames(boolean enabled){ |
|
||||||
} |
public void restart() { |
||||||
|
} |
||||||
public Type getType() { |
|
||||||
return Type.OffscreenSurface; |
public void setAutoFlushFrames(boolean enabled){ |
||||||
} |
} |
||||||
|
|
||||||
public MouseInput getMouseInput() { |
public Type getType() { |
||||||
return new DummyMouseInput(); |
return Type.OffscreenSurface; |
||||||
} |
} |
||||||
|
|
||||||
public KeyInput getKeyInput() { |
public MouseInput getMouseInput() { |
||||||
return new DummyKeyInput(); |
return new DummyMouseInput(); |
||||||
} |
} |
||||||
|
|
||||||
public JoyInput getJoyInput() { |
public KeyInput getKeyInput() { |
||||||
return null; |
return new DummyKeyInput(); |
||||||
} |
} |
||||||
|
|
||||||
public TouchInput getTouchInput() { |
public JoyInput getJoyInput() { |
||||||
return null; |
return null; |
||||||
} |
} |
||||||
|
|
||||||
public void setTitle(String title) { |
public TouchInput getTouchInput() { |
||||||
} |
return null; |
||||||
|
} |
||||||
} |
|
||||||
|
public void setTitle(String title) { |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
Loading…
Reference in new issue