[fixed with code] problematic call in SWT bindings

As required by eclipse LWJGLSWTCanvas uses a Runnable to draw on the canvas.

The problem is that the Runnable does a Thread.sleep which sends the Eclipse main thread into sleep.

This should be avoid by either using timed runnables or an external thread monitoring the runnable execution.

I am currently poritn some of my stuff from Rifidi to JME 2 and would provide a solution for that.

Ok, i made a small change to JMESWTCanvas and added a RenderThread, following I attached both classes.

The eclipse thread won't be blocked by waiting between two render runs.

Code had some testing from me and we will test it more tomorrow.



modified LWJGLSWTCanvas:

/*
 * Copyright (c) 2003-2008 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.jmex.swt.lwjgl;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.opengl.GLCanvas;
import org.eclipse.swt.opengl.GLData;
import org.eclipse.swt.widgets.Composite;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.ARBMultisample;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;

import com.jme.input.InputSystem;
import com.jme.system.DisplaySystem;
import com.jme.system.canvas.JMECanvas;
import com.jme.system.canvas.JMECanvasImplementor;
import com.jme.system.lwjgl.LWJGLDisplaySystem;
import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;

/**
 * <code>LWJGLCanvas</code>
 *
 * @author Joshua Slack
 */
public class LWJGLSWTCanvas extends GLCanvas implements JMECanvas {

    private static final Logger logger = Logger.getLogger(LWJGLSWTCanvas.class
            .getName());

    private static final long serialVersionUID = 1L;

    private JMECanvasImplementor impl;

    private boolean updateInput = false;

    private double syncNS = 0f;

    private int syncRate = 0;

    private boolean inited = false;

    private long lastRender = 0;

    private boolean drawWhenDirty = false;
    private boolean dirty = true;
    private RenderThread renderThread;

    public LWJGLSWTCanvas(Composite parent, int style, GLData data)
            throws LWJGLException {
        super(parent, style, data);
        renderThread=new RenderThread("renderThread",30,this);
        renderThread.start();
    }

    public void setImplementor(JMECanvasImplementor impl) {
        this.impl = impl;
    }

    public void init() {
        try {
            LWJGLDisplaySystem display = (LWJGLDisplaySystem) DisplaySystem
                    .getDisplaySystem();
            display.switchContext(this);
            setCurrent();
            GLContext.useContext(this);

            // Complete canvas initialization.
            Point size = this.getSize();
            display.initForCanvas(Math.max(size.x, 1), Math.max(size.y, 1));

            // Perform game initialization.
            impl.doSetup();

            // TODO Should this be moved into initForCanvas?
            if (DisplaySystem.getDisplaySystem().getMinSamples() != 0
                    && GLContext.getCapabilities().GL_ARB_multisample) {
                GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
            }

            inited = true;
        } catch (Exception e) {
            logger.log(Level.SEVERE, "Exception in initGL()", e);
        }
    }

    public void render() {
       if(!inited){
          init();
       }
        if (!isDisposed()) {
            try {
                setCurrent();

                ((LWJGLDisplaySystem) DisplaySystem.getDisplaySystem())
                        .switchContext(this);
                GLContext.useContext(this);

                if (updateInput) {
                    InputSystem.update();
                }

                GameTaskQueueManager.getManager().getQueue(
                        GameTaskQueue.UPDATE).execute();

                impl.doUpdate();

                if (!drawWhenDirty || dirty) {
                    GameTaskQueueManager.getManager().getQueue(
                            GameTaskQueue.RENDER).execute();

                    impl.doRender();

                    swapBuffers();

                    dirty = false;
                }
            } catch (LWJGLException e) {
                logger.log(Level.SEVERE, "Exception in render()", e);
            }
        }
    }

    public boolean isUpdateInput() {
        return updateInput;
    }

    public void setUpdateInput(boolean doUpdate) {
        updateInput = doUpdate;
    }

    public void setTargetRate(int fps) {
       renderThread.setTargetRate(fps);
    }

    public int getTargetSyncRate() {
        return syncRate;
    }

    public void setDrawWhenDirty(boolean whenDirty) {
        this.drawWhenDirty = whenDirty;
    }

    public boolean isDrawWhenDirty() {
        return drawWhenDirty;
    }

    public void makeDirty() {
        dirty = true;
    }
}



the new render thread class:

/*
 * Copyright (c) 2003-2008 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.jmex.swt.lwjgl;

import org.eclipse.swt.widgets.Display;

/**
 * Thread for rendering to a GLCanvas in eclipse.
 *
 * @author Jochen Mader - jochen@pramari.com - Aug 30, 2008
 *
 */
public class RenderThread extends Thread {
   /*
    * Nanoseconds per frame.
    */
   private double syncNS = 0f;
   /*
    * Frames per second.
    */
   private int syncRate = 0;
   /*
    * Resuable render runnable.
    */
   private Runnable renderRunnable;
   /*
    * The canvas this thread is rendering to.
    */
   private LWJGLSWTCanvas canvas = null;
   /*
    * timestamp f last renderrun
    */
   private long lastRender = 0;
   /*
    * SWT display for sync exec.
    */
   private Display display;

   /**
    * @param name
    */
   public RenderThread(String name, int fps, LWJGLSWTCanvas targetcanvas) {
      super(name);
      setTargetRate(fps);
      this.canvas = targetcanvas;
      display = canvas.getDisplay();
      renderRunnable = new Runnable() {

         /*
          * (non-Javadoc)
          *
          * @see java.lang.Runnable#run()
          */
         @Override
         public void run() {
            canvas.render();
         }

      };
   }

   /**
    * @param group
    * @param name
    * @param fps
    */
   public RenderThread(ThreadGroup group, String name, int fps) {
      super(group, name);
      setTargetRate(fps);
   }

   /*
    * (non-Javadoc)
    *
    * @see java.lang.Thread#run()
    */
   @Override
   public void run() {
      while (!Thread.interrupted()) {
         if (syncRate > 0) {
            long sinceLast = System.nanoTime() - lastRender;
            if (sinceLast < syncNS) {
               try {
                  Thread.sleep((Math
                        .round((syncNS - sinceLast) / 1000000L)));
               } catch (InterruptedException e) {
                  // propagate interruption
                  Thread.currentThread().interrupt();
               }
            }
            lastRender = System.nanoTime();
         }
         if (!display.isDisposed()) {
            display.syncExec(renderRunnable);
         }
      }
   }

   /**
    * Set the target fps.
    *
    * @param fps
    */
   public void setTargetRate(int fps) {
      this.syncRate = fps;
      this.syncNS = 1000000000.0 / syncRate;
   }
}