/** * 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 */ } } }
/** * 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(); } } }