Exemplo n.º 1
0
/**
 * Convenient adapter forwarding AWT events to NEWT via the event listener model.<br>
 *
 * <p>You may attach an instance of this adapter to an AWT Component. When an event happen, it is
 * converted to a NEWT event and the given NEWT listener is being called.<br>
 *
 * <p>This adapter fullfills three use cases. First as a plain utility to write code AWT agnostic,
 * ie write an {@link javax.media.opengl.GLEvenListener} and some appropriate NEWT {@link
 * com.jogamp.newt.event.NEWTEventListener}.<br>
 *
 * <p>Attach the {@link javax.media.opengl.GLEvenListener} to a NEWT {@link
 * javax.media.opengl.GLAutoDrawable}, e.g. {@link com.jogamp.newt.opengl.GLWindow},<br>
 * or to an AWT {@link javax.media.opengl.GLAutoDrawable}, e.g. {@link
 * javax.media.opengl.awt.GLCanvas}.<br>
 * <br>
 * Attach the NEWT {@link com.jogamp.newt.event.NEWTEventListener} to a NEWT component, e.g. {@link
 * com.jogamp.newt.Window},<br>
 * or to an AWT component, e.g. {@link java.awt.Component}.<br>
 *
 * <p>Common:<br>
 * <code>
 * javax.media.opengl.GLEvenListener demo1 = new javax.media.opengl.GLEvenListener() { ... } ;<br>
 * com.jogamp.newt.event.MouseListener mouseListener = new com.jogamp.newt.event.MouseAdapter() { ... } ;<br>
 * </code>
 *
 * <p>NEWT Usage:<br>
 * <code>
 * GLWindow glWindow = GLWindow.create();<br>
 * glWindow.addGLEventListener(demo1);<br>
 * glWindow.addMouseListener(mouseListener);<br>
 * </code>
 *
 * <p>AWT Usage:<br>
 * <code>
 * GLCanvas glCanvas = new GLCanvas();<br>
 * glCanvas.addGLEventListener(demo1);<br>
 * <br>
 * new AWTMouseAdapter(mouseListener).addTo(glCanvas);<br>
 * </code>
 *
 * <p>AWT Usage (previous form in detail):<br>
 * <code>
 * AWTMouseAdapter mouseAdapter = new AWTMouseAdapter(mouseListener);<br>
 * glCanvas.addMouseListener(mouseAdapter);<br>
 * glCanvas.addMouseMotionListener(mouseAdapter);<br>
 * </code>
 *
 * <p>Second is just a litte variation, where we pass a NEWT Window <br>
 * to impersonate as the source of the event.<br>
 *
 * <p><code>
 * com.jogamp.newt.event.MouseListener mouseListener = new com.jogamp.newt.event.MouseAdapter() { ... } ;<br>
 * Component comp = ... ;                 // the AWT  component<br>
 * GLWindow glWindow = GLWindow.create(); // the NEWT component<br>
 * <br>
 * new AWTMouseAdapter(mouseListener, glWindow).addTo(comp);<br>
 * </code> Last but not least, the AWTAdapter maybe used as a general AWT event forwarder to NEWT.
 * <br>
 *
 * <p><code>
 * com.jogamp.newt.event.MouseListener mouseListener = new com.jogamp.newt.event.MouseAdapter() { ... } ;<br>
 * Component comp = ... ;                 // the AWT  component<br>
 * GLWindow glWindow = GLWindow.create(); // the NEWT component<br>
 * glWindow.addMouseListener(mouseListener); // add the custom EventListener to the NEWT component<br>
 * <br>
 * new AWTMouseAdapter(glWindow).addTo(comp); // forward all AWT events to glWindow, as NEWT events<br>
 * </code>
 *
 * @see #attachTo
 */
public abstract class AWTAdapter implements java.util.EventListener {
  public static final boolean DEBUG_IMPLEMENTATION = Debug.debug("Window");

  com.jogamp.newt.event.NEWTEventListener newtListener;
  com.jogamp.newt.Window newtWindow;

  /**
   * Simply wrap aroung a NEWT EventListener, exposed as an AWT EventListener.<br>
   * The NEWT EventListener will be called when an event happens.<br>
   */
  public AWTAdapter(com.jogamp.newt.event.NEWTEventListener newtListener) {
    if (null == newtListener) {
      throw new RuntimeException("Argument newtListener is null");
    }
    this.newtListener = newtListener;
    this.newtWindow = null;
  }

  /**
   * Wrap aroung a NEWT EventListener, exposed as an AWT EventListener,<br>
   * where the given NEWT Window impersonates as the event's source. The NEWT EventListener will be
   * called when an event happens.<br>
   */
  public AWTAdapter(
      com.jogamp.newt.event.NEWTEventListener newtListener, com.jogamp.newt.Window newtProxy) {
    if (null == newtListener) {
      throw new RuntimeException("Argument newtListener is null");
    }
    if (null == newtProxy) {
      throw new RuntimeException("Argument newtProxy is null");
    }
    this.newtListener = newtListener;
    this.newtWindow = newtProxy;
  }

  /**
   * Create a pipeline adapter, AWT EventListener.<br>
   * Once attached to an AWT component, it sends the converted AWT events to the NEWT downstream
   * window.<br>
   * This is only supported with EDT enabled!
   */
  public AWTAdapter(com.jogamp.newt.Window downstream) {
    if (null == downstream) {
      throw new RuntimeException("Argument downstream is null");
    }
    this.newtListener = null;
    this.newtWindow = downstream;
    if (null == newtWindow.getScreen().getDisplay().getEDTUtil()) {
      throw new RuntimeException("EDT not enabled");
    }
  }

  /**
   * Due to the fact that some NEWT {@link com.jogamp.newt.event.NEWTEventListener} are mapped to
   * more than one {@link java.util.EventListener}, this method is for your convenience to use this
   * Adapter as a listener for all types.<br>
   * E.g. {@link com.jogamp.newt.event.MouseListener} is mapped to {@link
   * java.awt.event.MouseListener} and {@link java.awt.event.MouseMotionListener}.
   */
  public abstract AWTAdapter addTo(java.awt.Component awtComponent);

  /** @see #addTo(java.awt.Component) */
  public abstract AWTAdapter removeFrom(java.awt.Component awtComponent);

  void enqueueEvent(com.jogamp.newt.event.NEWTEvent event) {
    enqueueEvent(false, event);
  }

  void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) {
    try {
      newtWindow.getScreen().getDisplay().enqueueEvent(wait, event);
    } catch (NullPointerException npe) {
      /* that's ok .. window might be down already */
    }
  }
}
Exemplo n.º 2
0
/**
 * NEWT Utility class MainThread
 *
 * <p>This class provides a startup singleton <i>main thread</i>, from which a new thread with the
 * users main class is launched.<br>
 * Such behavior is necessary for native windowing toolkits, where the windowing management must
 * happen on the so called <i>main thread</i> e.g. for Mac OS X !<br>
 * Utilizing this class as a launchpad, now you are able to use a NEWT multithreaded application
 * with window handling within the different threads, even on these restricted platforms.<br>
 * To support your NEWT Window platform, you have to pass your <i>main thread</i> actions to {@link
 * #invoke invoke(..)}, have a look at the {@link com.jogamp.newt.macosx.MacWindow MacWindow}
 * implementation.<br>
 * <i>TODO</i>: Some hardcoded dependencies exist in this implementation, where you have to patch
 * this code or factor it out.
 *
 * <p>If your platform is not Mac OS X, but you want to test your code without modifying this class,
 * you have to set the system property <code>newt.MainThread.force</code> to <code>true</code>.
 *
 * <p>The code is compatible with all other platform, which support multithreaded windowing
 * handling. Since those platforms won't trigger the <i>main thread</i> serialization, the main
 * method will be simply executed, in case you haven't set <code>newt.MainThread.force</code> to
 * <code>true</code>.
 *
 * <p>Test case on Mac OS X (or any other platform):
 *
 * <PRE>
 * java -XstartOnFirstThread com.jogamp.newt.util.MainThread demos.es1.RedSquare -GL2 -GL2 -GL2 -GL2
 * </PRE>
 *
 * Which starts 4 threads, each with a window and OpenGL rendering.<br>
 */
public class MainThread implements EDTUtil {
  private static AccessControlContext localACC = AccessController.getContext();
  public static final boolean MAIN_THREAD_CRITERIA =
      (!NativeWindowFactory.isAWTAvailable()
              && NativeWindowFactory.TYPE_MACOSX.equals(
                  NativeWindowFactory.getNativeWindowType(false)))
          || Debug.getBooleanProperty("newt.MainThread.force", true, localACC);

  protected static final boolean DEBUG = Debug.debug("MainThread");

  private static MainThread singletonMainThread = new MainThread(); // one singleton MainThread

  private static boolean isExit = false;
  private static volatile boolean isRunning = false;
  private static Object taskWorkerLock = new Object();
  private static boolean shouldStop;
  private static ArrayList tasks;
  private static Thread mainThread;

  private static Timer pumpMessagesTimer = null;
  private static TimerTask pumpMessagesTimerTask = null;
  private static Map /*<Display, Runnable>*/ pumpMessageDisplayMap = new HashMap();

  private static boolean useMainThread = false;
  private static Class cAWTEventQueue = null;
  private static Method mAWTInvokeAndWait = null;
  private static Method mAWTInvokeLater = null;
  private static Method mAWTIsDispatchThread = null;

  static class MainAction extends Thread {
    private String mainClassName;
    private String[] mainClassArgs;

    private Class mainClass;
    private Method mainClassMain;

    public MainAction(String mainClassName, String[] mainClassArgs) {
      this.mainClassName = mainClassName;
      this.mainClassArgs = mainClassArgs;
    }

    public void run() {
      if (useMainThread) {
        // we have to start first to provide the service ..
        singletonMainThread.waitUntilRunning();
      }

      // start user app ..
      try {
        Class mainClass = ReflectionUtil.getClass(mainClassName, true, getClass().getClassLoader());
        if (null == mainClass) {
          throw new RuntimeException(
              new ClassNotFoundException("MainThread couldn't find main class " + mainClassName));
        }
        try {
          mainClassMain = mainClass.getDeclaredMethod("main", new Class[] {String[].class});
          mainClassMain.setAccessible(true);
        } catch (Throwable t) {
          throw new RuntimeException(t);
        }
        if (DEBUG)
          System.err.println(
              "MainAction.run(): " + Thread.currentThread().getName() + " invoke " + mainClassName);
        mainClassMain.invoke(null, new Object[] {mainClassArgs});
      } catch (InvocationTargetException ite) {
        ite.getTargetException().printStackTrace();
      } catch (Throwable t) {
        t.printStackTrace();
      }

      if (DEBUG)
        System.err.println(
            "MainAction.run(): " + Thread.currentThread().getName() + " user app fin");

      if (useMainThread) {
        singletonMainThread.stop();
        if (DEBUG)
          System.err.println(
              "MainAction.run(): " + Thread.currentThread().getName() + " MainThread fin - stop");
        System.exit(0);
      }
    }
  }

  private static MainAction mainAction;

  /** Your new java application main entry, which pipelines your application */
  public static void main(String[] args) {
    useMainThread = MAIN_THREAD_CRITERIA;

    if (DEBUG)
      System.err.println(
          "MainThread.main(): "
              + Thread.currentThread().getName()
              + " useMainThread "
              + useMainThread);

    if (args.length == 0) {
      return;
    }

    String mainClassName = args[0];
    String[] mainClassArgs = new String[args.length - 1];
    if (args.length > 1) {
      System.arraycopy(args, 1, mainClassArgs, 0, args.length - 1);
    }

    NEWTJNILibLoader.loadNEWT();

    mainAction = new MainAction(mainClassName, mainClassArgs);

    if (NativeWindowFactory.TYPE_MACOSX.equals(NativeWindowFactory.getNativeWindowType(false))) {
      ReflectionUtil.callStaticMethod(
          "com.jogamp.newt.impl.macosx.MacDisplay",
          "initSingleton",
          null,
          null,
          MainThread.class.getClassLoader());
    }

    if (useMainThread) {
      shouldStop = false;
      tasks = new ArrayList();
      mainThread = Thread.currentThread();

      // dispatch user's main thread ..
      mainAction.start();

      // do our main thread task scheduling
      singletonMainThread.run();
    } else {
      // run user's main in this thread
      mainAction.run();
    }
  }

  public static final MainThread getSingleton() {
    return singletonMainThread;
  }

  public static Runnable removePumpMessage(Display dpy) {
    synchronized (pumpMessageDisplayMap) {
      return (Runnable) pumpMessageDisplayMap.remove(dpy);
    }
  }

  public static void addPumpMessage(Display dpy, Runnable pumpMessage) {
    if (useMainThread) {
      return; // error ?
    }
    if (null == pumpMessagesTimer) {
      synchronized (MainThread.class) {
        if (null == pumpMessagesTimer) {
          pumpMessagesTimer = new Timer();
          pumpMessagesTimerTask =
              new TimerTask() {
                public void run() {
                  synchronized (pumpMessageDisplayMap) {
                    for (Iterator i = pumpMessageDisplayMap.values().iterator(); i.hasNext(); ) {
                      ((Runnable) i.next()).run();
                    }
                  }
                }
              };
          pumpMessagesTimer.scheduleAtFixedRate(
              pumpMessagesTimerTask, 0, defaultEDTPollGranularity);
        }
      }
    }
    synchronized (pumpMessageDisplayMap) {
      pumpMessageDisplayMap.put(dpy, pumpMessage);
    }
  }

  private void initAWTReflection() {
    if (null == cAWTEventQueue) {
      ClassLoader cl = MainThread.class.getClassLoader();
      cAWTEventQueue = ReflectionUtil.getClass("java.awt.EventQueue", true, cl);
      mAWTInvokeAndWait =
          ReflectionUtil.getMethod(
              cAWTEventQueue, "invokeAndWait", new Class[] {java.lang.Runnable.class}, cl);
      mAWTInvokeLater =
          ReflectionUtil.getMethod(
              cAWTEventQueue, "invokeLater", new Class[] {java.lang.Runnable.class}, cl);
      mAWTIsDispatchThread =
          ReflectionUtil.getMethod(cAWTEventQueue, "isDispatchThread", new Class[] {}, cl);
    }
  }

  public void start() {
    // nop
  }

  public void stop() {
    if (DEBUG)
      System.err.println("MainThread.stop(): " + Thread.currentThread().getName() + " start");
    synchronized (taskWorkerLock) {
      if (isRunning) {
        shouldStop = true;
      }
      taskWorkerLock.notifyAll();
    }
    if (DEBUG)
      System.err.println("MainThread.stop(): " + Thread.currentThread().getName() + " end");
  }

  public boolean isCurrentThreadEDT() {
    if (NativeWindowFactory.isAWTAvailable()) {
      initAWTReflection();
      return ((Boolean) ReflectionUtil.callMethod(null, mAWTIsDispatchThread, null)).booleanValue();
    }
    return isRunning() && mainThread == Thread.currentThread();
  }

  public boolean isRunning() {
    if (useMainThread) {
      synchronized (taskWorkerLock) {
        return isRunning;
      }
    }
    return true; // AWT is always running
  }

  private void invokeLater(Runnable task) {
    synchronized (taskWorkerLock) {
      if (isRunning() && mainThread != Thread.currentThread()) {
        tasks.add(task);
        taskWorkerLock.notifyAll();
      } else {
        // if !running or isEDTThread, do it right away
        task.run();
      }
    }
  }

  /** invokes the given Runnable */
  public void invoke(boolean wait, Runnable r) {
    if (r == null) {
      return;
    }

    if (NativeWindowFactory.isAWTAvailable()) {
      initAWTReflection();

      // handover to AWT MainThread ..
      try {
        if (((Boolean) ReflectionUtil.callMethod(null, mAWTIsDispatchThread, null))
            .booleanValue()) {
          r.run();
          return;
        }
        if (wait) {
          ReflectionUtil.callMethod(null, mAWTInvokeAndWait, new Object[] {r});
        } else {
          ReflectionUtil.callMethod(null, mAWTInvokeLater, new Object[] {r});
        }
      } catch (Exception e) {
        throw new NativeWindowException(e);
      }
      return;
    }

    // if this main thread is not being used or
    // if this is already the main thread .. just execute.
    if (!isRunning() || mainThread == Thread.currentThread()) {
      r.run();
      return;
    }

    boolean doWait = wait && isRunning() && mainThread != Thread.currentThread();
    Object lock = new Object();
    RunnableTask rTask = new RunnableTask(r, doWait ? lock : null, true);
    Throwable throwable = null;
    synchronized (lock) {
      invokeLater(rTask);
      if (doWait) {
        try {
          lock.wait();
        } catch (InterruptedException ie) {
          throwable = ie;
        }
      }
    }
    if (null == throwable) {
      throwable = rTask.getThrowable();
    }
    if (null != throwable) {
      throw new RuntimeException(throwable);
    }
  }

  public void waitUntilIdle() {}

  public void waitUntilStopped() {}

  private void waitUntilRunning() {
    synchronized (taskWorkerLock) {
      if (isExit) return;

      while (!isRunning) {
        try {
          taskWorkerLock.wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }

  public void run() {
    if (DEBUG) System.err.println("MainThread.run(): " + Thread.currentThread().getName());
    synchronized (taskWorkerLock) {
      isRunning = true;
      taskWorkerLock.notifyAll();
    }
    while (!shouldStop) {
      try {
        // wait for something todo ..
        synchronized (taskWorkerLock) {
          while (!shouldStop && tasks.size() == 0) {
            try {
              taskWorkerLock.wait();
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }

          // take over the tasks ..
          if (!shouldStop && tasks.size() > 0) {
            Runnable task = (Runnable) tasks.remove(0);
            task.run(); // FIXME: could be run outside of lock
          }
          taskWorkerLock.notifyAll();
        }
      } catch (Throwable t) {
        // handle errors ..
        t.printStackTrace();
      } finally {
        // epilog - unlock locked stuff
      }
    }
    if (DEBUG) System.err.println("MainThread.run(): " + Thread.currentThread().getName() + " fin");
    synchronized (taskWorkerLock) {
      isRunning = false;
      isExit = true;
      taskWorkerLock.notifyAll();
    }
  }
}