From 54bbd1a726e73841f0bd4cc79e5b12e83a88ba18 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Fri, 28 Jun 2013 19:06:14 +0200 Subject: Add Applet Feature incl. required fixes. - Jake2Applet - Reenable JavaScript Bridge to resize Applet - Pass through 4:3 aspect custom video mode (Jake2 args) - Pass through applet parameter 'jake_args' (Jake2 args) - OSX Hack: Re-create Game at init, otherwise flickering appears (??) - Fix VID.init: Set vid_ref.modifier = true, otherwise not guaranteed VID creation - BeginFrame, R_BeginFrame, beginFrame, activateGLContext: Return 'true', if ctx is available, otherwise false to skip frame. - NEWTWin: - Applet mode (in Applet container): Always release GL ctx - Use GLAnimatorControl impl, to state whether we are animating or not and to tell us, whether we have to release the GL ctx. - Add HOME -> Reparent feature for Applets - Workaround for NEWT/Windows Bug 798 - NEWT Key Handling: - Ignore auto-repeat - Workaround for NEWT/Windows Bug 798 - Add HTML page - normal - debug mode --- src/jake2/render/opengl/GLDriver.java | 3 +- src/jake2/render/opengl/JoglDummyDriver.java | 11 +- src/jake2/render/opengl/JoglES1Driver.java | 11 +- src/jake2/render/opengl/JoglES2Driver.java | 10 +- src/jake2/render/opengl/JoglGL2Driver.java | 12 +- src/jake2/render/opengl/NEWTWin.java | 368 +++++++++++++++++++++++---- 6 files changed, 338 insertions(+), 77 deletions(-) (limited to 'src/jake2/render/opengl') diff --git a/src/jake2/render/opengl/GLDriver.java b/src/jake2/render/opengl/GLDriver.java index 7f86430..9d82593 100644 --- a/src/jake2/render/opengl/GLDriver.java +++ b/src/jake2/render/opengl/GLDriver.java @@ -16,7 +16,8 @@ public interface GLDriver { void shutdown(); - void beginFrame(float camera_separation); + /** @return true if successful, otherwise false. */ + boolean beginFrame(float camera_separation); /** Performs swapBuffers(), ticks the fps counter and performs QUIT if requested. */ void endFrame(); diff --git a/src/jake2/render/opengl/JoglDummyDriver.java b/src/jake2/render/opengl/JoglDummyDriver.java index e962c37..4ba98a0 100644 --- a/src/jake2/render/opengl/JoglDummyDriver.java +++ b/src/jake2/render/opengl/JoglDummyDriver.java @@ -97,8 +97,9 @@ public abstract class JoglDummyDriver extends DummyGL implements GLDriver { return true; } - public void beginFrame(float camera_separation) { - activateGLContext(); + @Override + public boolean beginFrame(float camera_separation) { + return activateGLContext(false); } public void endFrame() { @@ -126,12 +127,12 @@ public abstract class JoglDummyDriver extends DummyGL implements GLDriver { callback.execute(); } - protected final void activateGLContext() { - newtWin.activateGLContext(); + protected final boolean activateGLContext(boolean force) { + return newtWin.activateGLContext(false); } protected final void deactivateGLContext() { - newtWin.activateGLContext(); + newtWin.deactivateGLContext(); } // -------------------------------------------------------------------------- diff --git a/src/jake2/render/opengl/JoglES1Driver.java b/src/jake2/render/opengl/JoglES1Driver.java index 6f138cd..d798c72 100644 --- a/src/jake2/render/opengl/JoglES1Driver.java +++ b/src/jake2/render/opengl/JoglES1Driver.java @@ -108,8 +108,9 @@ public abstract class JoglES1Driver extends JoglGL2ES1 implements GLDriver { return true; } - public void beginFrame(float camera_separation) { - newtWin.activateGLContext(); + @Override + public boolean beginFrame(float camera_separation) { + return activateGLContext(false); } public void endFrame() { @@ -136,12 +137,12 @@ public abstract class JoglES1Driver extends JoglGL2ES1 implements GLDriver { callback.execute(); } - protected final void activateGLContext() { - newtWin.activateGLContext(); + protected final boolean activateGLContext(boolean force) { + return newtWin.activateGLContext(force); } protected final void deactivateGLContext() { - newtWin.activateGLContext(); + newtWin.deactivateGLContext(); } // -------------------------------------------------------------------------- diff --git a/src/jake2/render/opengl/JoglES2Driver.java b/src/jake2/render/opengl/JoglES2Driver.java index 08c95b9..f18a9cb 100644 --- a/src/jake2/render/opengl/JoglES2Driver.java +++ b/src/jake2/render/opengl/JoglES2Driver.java @@ -113,8 +113,8 @@ public abstract class JoglES2Driver extends JoglGL2ES1 implements GLDriver { return true; } - public void beginFrame(float camera_separation) { - activateGLContext(); + public boolean beginFrame(float camera_separation) { + return activateGLContext(false); } public void endFrame() { @@ -141,12 +141,12 @@ public abstract class JoglES2Driver extends JoglGL2ES1 implements GLDriver { callback.execute(); } - protected final void activateGLContext() { - newtWin.activateGLContext(); + protected final boolean activateGLContext(boolean force) { + return newtWin.activateGLContext(force); } protected final void deactivateGLContext() { - newtWin.activateGLContext(); + newtWin.deactivateGLContext(); } // -------------------------------------------------------------------------- diff --git a/src/jake2/render/opengl/JoglGL2Driver.java b/src/jake2/render/opengl/JoglGL2Driver.java index 0c95680..32195cb 100644 --- a/src/jake2/render/opengl/JoglGL2Driver.java +++ b/src/jake2/render/opengl/JoglGL2Driver.java @@ -27,6 +27,7 @@ package jake2.render.opengl; import java.util.List; +import jake2.client.VID; import jake2.game.cvar_t; import jake2.qcommon.Cvar; import jake2.qcommon.xcommand_t; @@ -109,8 +110,9 @@ public abstract class JoglGL2Driver extends JoglGL2ES1 implements GLDriver { return true; } - public void beginFrame(float camera_separation) { - activateGLContext(); + @Override + public boolean beginFrame(float camera_separation) { + return activateGLContext(false); } public void endFrame() { @@ -137,12 +139,12 @@ public abstract class JoglGL2Driver extends JoglGL2ES1 implements GLDriver { callback.execute(); } - protected final void activateGLContext() { - newtWin.activateGLContext(); + protected final boolean activateGLContext(boolean force) { + return newtWin.activateGLContext(force); } protected final void deactivateGLContext() { - newtWin.activateGLContext(); + newtWin.deactivateGLContext(); } // -------------------------------------------------------------------------- diff --git a/src/jake2/render/opengl/NEWTWin.java b/src/jake2/render/opengl/NEWTWin.java index ff7b949..df47bb1 100644 --- a/src/jake2/render/opengl/NEWTWin.java +++ b/src/jake2/render/opengl/NEWTWin.java @@ -15,6 +15,7 @@ import jake2.qcommon.Cvar; import jake2.render.Base; import jake2.sys.NEWTKBD; +import java.io.PrintStream; import java.util.List; import javax.media.nativewindow.CapabilitiesChooser; @@ -22,26 +23,34 @@ import javax.media.nativewindow.WindowClosingProtocol.WindowClosingMode; import javax.media.nativewindow.util.Dimension; import javax.media.nativewindow.util.DimensionImmutable; import javax.media.nativewindow.util.SurfaceSize; -import javax.media.opengl.GLCapabilities; -import javax.media.opengl.GLContext; -import javax.media.opengl.GLProfile; +import javax.media.opengl.*; import jogamp.opengl.FPSCounterImpl; import com.jogamp.common.os.Platform; import com.jogamp.newt.*; -import com.jogamp.newt.event.WindowAdapter; -import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.awt.NewtCanvasAWT; +import com.jogamp.newt.event.*; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.newt.util.MonitorModeUtil; import com.jogamp.opengl.GenericGLCapabilitiesChooser; public class NEWTWin { + final static boolean DEBUG = false; + /** Required due to AWT lock of surface, if Applet! */ + final static boolean FORCE_RELEASE_CTX_VAL = true; + MonitorMode oldDisplayMode = null; volatile Screen screen = null; volatile GLWindow window = null; + volatile GameAnimatorControl animCtrl = null; + /** Encapsulateed AWT dependency */ + volatile Object newtCanvasObject = null; + boolean forceReleaseCtx = false; volatile boolean shouldQuit = false; - final FPSCounterImpl fpsCounter = new FPSCounterImpl(); + volatile boolean shouldPause = false; + volatile boolean shouldReparent = false; + volatile boolean isAnimating = false; public List getModeList() { if( null != window ) { @@ -79,12 +88,10 @@ public class NEWTWin { * @param dim * @param mode * @param fullscreen - * @param driverName TODO + * @param driverName * @return enum Base.rserr_t */ public int setMode(GLProfile glp, Dimension dim, int mode, boolean fullscreen, String driverName) { - final boolean isARM = Platform.CPUFamily.ARM == Platform.getCPUFamily(); - final Dimension newDim = new Dimension(); VID.Printf(Defines.PRINT_ALL, "Initializing OpenGL display for profile "+glp+"\n"); @@ -120,7 +127,9 @@ public class NEWTWin { chooser = new GenericGLCapabilitiesChooser(); // don't trust native GL-TK chooser } } + window = GLWindow.create(screen, caps); + window.setAutoSwapBufferMode(false); window.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); // we do handle QUIT on our own, no GLWindow.display() called. window.setCapabilitiesChooser(chooser); window.addWindowListener(new WindowAdapter() { @@ -133,6 +142,9 @@ public class NEWTWin { } }); window.setTitle("Jake2 ("+driverName+"-newt-"+glp.getName().toLowerCase()+")"); + + animCtrl = new GameAnimatorControl(); + window.setAnimator(animCtrl); final MonitorDevice mainMonitor = window.getMainMonitor(); @@ -146,88 +158,170 @@ public class NEWTWin { window.addWindowListener(NEWTKBD.listener); window.addKeyListener(NEWTKBD.listener); window.addMouseListener(NEWTKBD.listener); - - if (fullscreen) { - MonitorMode mm = findDisplayMode(newDim); - final DimensionImmutable smDim = mm.getSurfaceSize().getResolution(); - newDim.setWidth( smDim.getWidth() ); - newDim.setHeight( smDim.getHeight() ); - mainMonitor.setCurrentMode(mm); - window.setFullscreen(true); - } else { - window.setSize(newDim.getWidth(), newDim.getHeight()); - if (Globals.appletMode) { - // Notify the size listener about the change - final SizeChangeListener listener = Globals.sizeChangeListener; - if (listener != null) { - listener.sizeChanged(newDim.getWidth(), newDim.getHeight()); - } + window.setSize(newDim.getWidth(), newDim.getHeight()); + + isAnimating = true; // no display() invocation on other thread! + + if( !fullscreen && Globals.appletMode ) { + forceReleaseCtx = FORCE_RELEASE_CTX_VAL; + + // Notify the size listener about the change + final SizeChangeListener listener = Globals.sizeChangeListener; + if (listener != null) { + listener.sizeChanged(newDim.getWidth(), newDim.getHeight()); } - } - window.setVisible(true); - - if (!Globals.appletMode) { - while ( !window.isNativeValid()|| !window.isRealized() ) { + window.addKeyListener( new ReparentKeyListener() ); + + final NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(window); + final java.applet.Applet applet = (java.applet.Applet) Globals.applet; + final Runnable appletAddAction = new Runnable() { + public void run() { + applet.add(newtCanvasAWT, java.awt.BorderLayout.CENTER); + applet.validate(); + newtCanvasAWT.setFocusable(true); + newtCanvasAWT.requestFocus(); + if( Platform.OSType.MACOS == Platform.getOSType() && newtCanvasAWT.isOffscreenLayerSurfaceEnabled() ) { + System.err.println("XXX Relayout"); + // force relayout + final int cW = newtCanvasAWT.getWidth(); + final int cH = newtCanvasAWT.getHeight(); + newtCanvasAWT.setSize(cW+1, cH+1); + newtCanvasAWT.setSize(cW, cH); + } + } }; + if( java.awt.EventQueue.isDispatchThread() ) { + System.err.println("XXX Adding on AWT EDT - same thread"); + appletAddAction.run(); + } else { + System.err.println("XXX Adding on AWT EDT - off thread"); + try { + java.awt.EventQueue.invokeAndWait(appletAddAction); + } catch (Exception e) { + throw new RuntimeException("NEWT Exception during NewtCanvasAWT on AWT-EDT", e); + } + } + newtCanvasObject = newtCanvasAWT; + int w=0; + while ( w<10 && !window.isNativeValid()|| !window.isRealized() ) { + w++; try { Thread.sleep(100); } catch (InterruptedException e) {} } + System.err.println("XXX waited = "+w+" * 100 ms"); + } else { + forceReleaseCtx = false; + newtCanvasObject = null; + + if (fullscreen) { + MonitorMode mm = findDisplayMode(newDim); + final DimensionImmutable smDim = mm.getSurfaceSize().getResolution(); + newDim.setWidth( smDim.getWidth() ); + newDim.setHeight( smDim.getHeight() ); + mainMonitor.setCurrentMode(mm); + VID.Printf(Defines.PRINT_ALL, "...MonitorMode "+mm+'\n'); + window.setFullscreen(true); + } + + window.setVisible(true); + window.requestFocus(); + } + if( !window.isNativeValid()|| !window.isRealized() ) { + throw new RuntimeException("NEWT window didn't not realize: "+window); + } + window.display(); // force GL creation + final GLContext ctx = window.getContext(); + if( !ctx.isCreated() ) { + System.err.println("Warning: GL context not created: "+ctx); + } + if( ctx.isCurrent() ) { + throw new RuntimeException("NEWT GL context still current: "+ctx); } - window.requestFocus(); - window.display(); // force GL resource validation - window.setAutoSwapBufferMode(false); VID.Printf(Defines.PRINT_ALL, "...reques GLCaps "+window.getRequestedCapabilities()+'\n'); VID.Printf(Defines.PRINT_ALL, "...chosen GLCaps "+window.getChosenGLCapabilities()+'\n'); VID.Printf(Defines.PRINT_ALL, "...size "+window.getWidth()+" x "+window.getHeight()+'\n'); - fpsCounter.setUpdateFPSFrames(isARM ? 60 : 4*60, System.err); // propagateNewSize("init"); - activateGLContext(); + activateGLContext(true); return Base.rserr_ok; } private void propagateNewSize() { - final int width = window.getWidth(); - final int height = window.getHeight(); - final int _width; - final int mask = ~0x03; - if ((width & 0x03) != 0) { - _width = ( width & mask ) + 4; - } else { - _width = width; + if( null != window ) { + final int width = window.getWidth(); + final int height = window.getHeight(); + final int _width; + final int mask = ~0x03; + if ((width & 0x03) != 0) { + _width = ( width & mask ) + 4; + } else { + _width = width; + } + VID.Printf(Defines.PRINT_ALL, "Resize: " + width + " x " + height + ", masked " + _width + "x" + height + "\n"); + + Base.setVid(_width, height); + // let the sound and input subsystems know about the new window + VID.NewWindow(_width, height); } - VID.Printf(Defines.PRINT_ALL, "Resize: " + width + " x " + height + ", masked " + _width + "x" + height + "\n"); - - Base.setVid(_width, height); - // let the sound and input subsystems know about the new window - VID.NewWindow(_width, height); } - protected final void activateGLContext() { - final GLContext ctx = window.getContext(); - if ( null != ctx && GLContext.getCurrent() != ctx ) { - ctx.makeCurrent(); + protected final boolean activateGLContext(boolean force) { + boolean ctxCurrent = false; + if( force || !shouldPause ) { + final GLContext ctx = window.getContext(); + if ( null != ctx && GLContext.getCurrent() != ctx ) { + if( DEBUG ) { + System.err.println("GLCtx Current pause "+shouldPause+": "+Thread.currentThread().getName()); + } + ctxCurrent = GLContext.CONTEXT_NOT_CURRENT < ctx.makeCurrent(); + } else { + ctxCurrent = true; + } + isAnimating = ctxCurrent; } + return ctxCurrent; } protected final void deactivateGLContext() { final GLContext ctx = window.getContext(); if ( null != ctx && GLContext.getCurrent() == ctx) { + if( DEBUG ) { + System.err.println("GLCtx Release pause "+shouldPause+": "+Thread.currentThread().getName()); + } ctx.release(); } } - /** Performs {@link GLWindow#swapBuffers()}, ticks the fps counter and performs QUIT if requested. */ + /** + * Performs {@link GLWindow#swapBuffers()}, ticks the fps counter and performs QUIT if requested. + */ public final void endFrame() { window.swapBuffers(); - fpsCounter.tickFPS(); + animCtrl.fpsCounter.tickFPS(); if( shouldQuit ) { deactivateGLContext(); Cbuf.ExecuteText(Defines.EXEC_APPEND, "quit"); + } else if( shouldReparent ) { + shouldReparent = false; + deactivateGLContext(); + if( null != newtCanvasObject && null != window ) { + isAnimating = false; // don't let GLDrawableHelper.invoke(..) defer the GLRunnable (preserving GLState that is on OSX/CALayer) + final NewtCanvasAWT newtCanvasAWT = (NewtCanvasAWT) newtCanvasObject; + if(null == window.getParent()) { + forceReleaseCtx = FORCE_RELEASE_CTX_VAL; // Applet + window.reparentWindow( newtCanvasAWT.getNativeWindow() ); + } else { + window.reparentWindow(null); + forceReleaseCtx = false; + } + } + } else if( forceReleaseCtx || shouldPause ) { + deactivateGLContext(); } } + /** Performs QUIT if requested. */ public final void checkQuit() { if( shouldQuit ) { @@ -245,12 +339,174 @@ public class NEWTWin { deactivateGLContext(); final GLWindow _window = window; window = null; - _window.destroy(); // same thing + _window.destroy(); + if( null != Globals.applet && null != newtCanvasObject ) { + final java.applet.Applet applet = (java.applet.Applet) Globals.applet; + final NewtCanvasAWT newtCanvasAWT = (NewtCanvasAWT) newtCanvasObject; + final Runnable appletRemoveAction = new Runnable() { + public void run() { + applet.remove(newtCanvasAWT); + applet.validate(); + } }; + if( java.awt.EventQueue.isDispatchThread() ) { + appletRemoveAction.run(); + } else { + try { + java.awt.EventQueue.invokeAndWait(appletRemoveAction); + } catch (Throwable e) { + System.err.println("Catched "+e.getClass().getName()+": "+e.getMessage()); + e.printStackTrace(); + } + } + newtCanvasAWT.setNEWTChild(null); + newtCanvasObject = null; + } } if( withScreen && null != screen ) { - screen.destroy(); + try { + screen.destroy(); + } catch (Throwable e) { + System.err.println("Catched "+e.getClass().getName()+": "+e.getMessage()); + e.printStackTrace(); + } screen = null; } } + class GameAnimatorControl implements GLAnimatorControl { + final FPSCounterImpl fpsCounter; + final Thread thread; + + GameAnimatorControl() { + final boolean isARM = Platform.CPUFamily.ARM == Platform.getCPUFamily(); + fpsCounter = new FPSCounterImpl(); + fpsCounter.setUpdateFPSFrames(isARM ? 60 : 4*60, System.err); + thread = Thread.currentThread(); + } + + @Override + public final boolean start() { + return false; + } + + @Override + public final boolean stop() { + shouldQuit = true; + return true; + } + + @Override + public final boolean pause() { + if( DEBUG ) { + System.err.println("GLCtx Pause Anim: "+Thread.currentThread().getName()); + Thread.dumpStack(); + } + shouldPause = true; + return true; + } + + @Override + public final boolean resume() { + shouldPause = false; + return true; + } + + @Override + public final boolean isStarted() { + return null != window; + } + + @Override + public final boolean isAnimating() { + return isAnimating; // null != window && !shouldPause; + } + + @Override + public final boolean isPaused() { + return null == window || shouldPause; + } + + @Override + public final Thread getThread() { + return thread; + } + + @Override + public final void add(GLAutoDrawable drawable) {} + + @Override + public final void remove(GLAutoDrawable drawable) {} + + @Override + public final void setUpdateFPSFrames(int frames, PrintStream out) { + fpsCounter.setUpdateFPSFrames(frames, out); + } + + @Override + public final void resetFPSCounter() { + fpsCounter.resetFPSCounter(); + } + + @Override + public final int getUpdateFPSFrames() { + return fpsCounter.getUpdateFPSFrames(); + } + + @Override + public final long getFPSStartTime() { + return fpsCounter.getFPSStartTime(); + } + + @Override + public final long getLastFPSUpdateTime() { + return fpsCounter.getLastFPSUpdateTime(); + } + + @Override + public final long getLastFPSPeriod() { + return fpsCounter.getLastFPSPeriod(); + } + + @Override + public final float getLastFPS() { + return fpsCounter.getLastFPS(); + } + + @Override + public final int getTotalFPSFrames() { + return fpsCounter.getTotalFPSFrames(); + } + + @Override + public final long getTotalFPSDuration() { + return fpsCounter.getTotalFPSDuration(); + } + + @Override + public final float getTotalFPS() { + return fpsCounter.getTotalFPS(); + } + } + + class ReparentKeyListener implements KeyListener { + @Override + public void keyPressed(KeyEvent e) { + System.err.println(e); + if( !e.isAutoRepeat() ) { + int keyCode = e.getKeyCode(); + // FIXME: Workaround JOGL/NEWT Bug 798 + if( 0 == keyCode ) { + keyCode = e.getKeySymbol(); + } + if( KeyEvent.VK_HOME == keyCode ) { + shouldReparent = true; + } + } + } + @Override + public void keyReleased(KeyEvent e) { + System.err.println(e); + } + } + } -- cgit v1.2.3