diff options
author | Sven Gothel <[email protected]> | 2009-10-10 04:24:26 -0700 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2009-10-10 04:24:26 -0700 |
commit | 2268a6ce8a900ae7aa9f20d5f595f811185574a9 (patch) | |
tree | b4426ed20e1957bd5cba095d5a036df2bbc1736a /src/newt/classes | |
parent | 20c6d89bfc4f72144b8bcc48839da7ef9bc40681 (diff) |
NEWT: Add EventDispatchThread (EDT) pattern.
Due to limitations on Windows,
we need to standardize the one thread for
- window creation, and
- event dispatching
This was already mentioned in the previous implementation
but while integrating into another threading model (Plugin3),
it turned out that manual managing the thread is too much of a burden.
NEWT now uses a EDT per Display and Thread as the default,
where Display creation, Window creation and event dispatching is 'pipelined' into.
This can be switched off:
NewtFactory.setUseEDT(boolean onoff);
and queried via:
NewtFactory.useEDT();
Note this EDT impl. does not implicate a global lock or whatsoever.
The experimantal semantics of a current GL context
for input event dispatching is removed,
i.e. the GL context is no more made current for mouse/key listener.
This reduces the complexity and allows the proper impl. of
the external dispatch via EDT .. for example.
Removed:
GLWindow: setEventHandlerMode(int) .. etc
X11Display: XLockDisplay/XUnlockDisplay
needed to be utilized to allow the new
multithreading (EDT/Render) Display usage.
X11Window: lockSurface/unlockSurface
locks X11Display as well ..
+++++
NEWT: 'getSurfaceHandle()' semantics changed.
To allow usage of the surfaceHandle for OS
where it is allocated thread local (MS-Windows),
it shall be aquired/released while lockSurface/unlockSurface.
This is done in the Windows Window implementation.
GLWindow can no more query 'getSurfaceHandle()'
to verify if 'setRealized()' was successful.
NEWT: Window surface lock is recursive and blocking now,
as it shall be.
Diffstat (limited to 'src/newt/classes')
13 files changed, 530 insertions, 334 deletions
diff --git a/src/newt/classes/com/sun/javafx/newt/Display.java b/src/newt/classes/com/sun/javafx/newt/Display.java index 0be8aedbc..ad4664ac8 100755 --- a/src/newt/classes/com/sun/javafx/newt/Display.java +++ b/src/newt/classes/com/sun/javafx/newt/Display.java @@ -35,9 +35,10 @@ package com.sun.javafx.newt; import javax.media.nativewindow.*; import com.sun.javafx.newt.impl.Debug; +import com.sun.javafx.newt.util.EventDispatchThread; import java.util.*; -public abstract class Display implements Runnable { +public abstract class Display { public static final boolean DEBUG = Debug.debug("Display"); private static Class getDisplayClass(String type) @@ -62,6 +63,7 @@ public abstract class Display implements Runnable { return displayClass; } + // Unique Display for each thread private static ThreadLocal currentDisplayMap = new ThreadLocal(); /** Returns the thread local display map */ @@ -109,11 +111,11 @@ public abstract class Display implements Runnable { return display; } - private static void dumpDisplayMap(String prefix) { + public static void dumpDisplayMap(String prefix) { Map displayMap = getCurrentDisplayMap(); Set entrySet = displayMap.entrySet(); Iterator i = entrySet.iterator(); - System.err.println(prefix+" DisplayMap["+entrySet.size()+"] "+Thread.currentThread().getName()); + System.err.println(prefix+" DisplayMap["+entrySet.size()+"] "+Thread.currentThread()); for(int j=0; i.hasNext(); j++) { Map.Entry entry = (Map.Entry) i.next(); System.err.println(" ["+j+"] "+entry.getKey()+" -> "+entry.getValue()); @@ -128,58 +130,100 @@ public abstract class Display implements Runnable { /** Make sure to reuse a Display with the same name */ protected static Display create(String type, String name) { try { + if(DEBUG) { + dumpDisplayMap("Display.create("+name+") BEGIN"); + } Display display = getCurrentDisplay(name); if(null==display) { Class displayClass = getDisplayClass(type); display = (Display) displayClass.newInstance(); display.name=name; display.refCount=1; - display.createNative(); + + if(NewtFactory.useEDT()) { + Thread current = Thread.currentThread(); + display.eventDispatchThread = new EventDispatchThread(display, current.getThreadGroup(), current.getName()); + display.eventDispatchThread.start(); + final Display f_dpy = display; + display.eventDispatchThread.invokeAndWait(new Runnable() { + public void run() { + f_dpy.createNative(); + } + } ); + } else { + display.createNative(); + } if(null==display.aDevice) { throw new RuntimeException("Display.createNative() failed to instanciate an AbstractGraphicsDevice"); } setCurrentDisplay(display); if(DEBUG) { - System.err.println("Display.create("+name+") NEW: "+display+" "+Thread.currentThread().getName()); + System.err.println("Display.create("+name+") NEW: "+display+" "+Thread.currentThread()); } } else { synchronized(display) { display.refCount++; if(DEBUG) { - System.err.println("Display.create("+name+") REUSE: refCount "+display.refCount+", "+display+" "+Thread.currentThread().getName()); + System.err.println("Display.create("+name+") REUSE: refCount "+display.refCount+", "+display+" "+Thread.currentThread()); } } } + if(DEBUG) { + dumpDisplayMap("Display.create("+name+") END"); + } + return display; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected static Display wrapHandle(String type, String name, AbstractGraphicsDevice aDevice) { + try { + Class displayClass = getDisplayClass(type); + Display display = (Display) displayClass.newInstance(); + display.name=name; + display.aDevice=aDevice; return display; } catch (Exception e) { throw new RuntimeException(e); } } + public EventDispatchThread getEDT() { return eventDispatchThread; } + public synchronized void destroy() { + if(DEBUG) { + dumpDisplayMap("Display.destroy("+name+") BEGIN"); + } refCount--; if(0==refCount) { removeCurrentDisplay(name); if(DEBUG) { - System.err.println("Display.destroy("+name+") REMOVE: "+this+" "+Thread.currentThread().getName()); + System.err.println("Display.destroy("+name+") REMOVE: "+this+" "+Thread.currentThread()); + } + if(null!=eventDispatchThread) { + final Display f_dpy = this; + eventDispatchThread.invokeAndWait(new Runnable() { + public void run() { + f_dpy.closeNative(); + } + } ); + } else { + closeNative(); + } + aDevice = null; + if(null!=eventDispatchThread) { + eventDispatchThread.stop(); + eventDispatchThread.waitUntilStopped(); + eventDispatchThread=null; } - closeNative(); } else { if(DEBUG) { - System.err.println("Display.destroy("+name+") KEEP: refCount "+refCount+", "+this+" "+Thread.currentThread().getName()); + System.err.println("Display.destroy("+name+") KEEP: refCount "+refCount+", "+this+" "+Thread.currentThread()); } } - } - - protected static Display wrapHandle(String type, String name, AbstractGraphicsDevice aDevice) { - try { - Class displayClass = getDisplayClass(type); - Display display = (Display) displayClass.newInstance(); - display.name=name; - display.aDevice=aDevice; - return display; - } catch (Exception e) { - throw new RuntimeException(e); + if(DEBUG) { + dumpDisplayMap("Display.destroy("+name+") END"); } } @@ -191,32 +235,23 @@ public abstract class Display implements Runnable { } public long getHandle() { - return aDevice.getHandle(); + if(null!=aDevice) { + return aDevice.getHandle(); + } + return 0; } public AbstractGraphicsDevice getGraphicsDevice() { return aDevice; } - public synchronized void pumpMessages() { - dispatchMessages(); - } - - /** calls {@link #pumpMessages} */ - public void run() { - pumpMessages(); - } - - public static interface Action { - public void run(Display display); - } - - /** Calls {@link Display.Action#run(Display)} on all Display's - bound to the current thread. */ - public static void runCurrentThreadDisplaysAction(Display.Action action) { - Iterator iter = getCurrentDisplays().iterator(); // Thread local .. no sync necessary - while(iter.hasNext()) { - action.run((Display) iter.next()); + public void pumpMessages() { + if(null!=eventDispatchThread) { + dispatchMessages(); + } else { + synchronized(this) { + dispatchMessages(); + } } } @@ -226,6 +261,7 @@ public abstract class Display implements Runnable { protected abstract void dispatchMessages(); + protected EventDispatchThread eventDispatchThread = null; protected String name; protected int refCount; protected AbstractGraphicsDevice aDevice; diff --git a/src/newt/classes/com/sun/javafx/newt/DisplayActionThread.java b/src/newt/classes/com/sun/javafx/newt/DisplayActionThread.java deleted file mode 100644 index 8f02148a2..000000000 --- a/src/newt/classes/com/sun/javafx/newt/DisplayActionThread.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution 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 Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - */ -package com.sun.javafx.newt; - -import javax.media.nativewindow.*; -import com.sun.javafx.newt.impl.Debug; -import java.util.*; - -public class DisplayActionThread extends Thread { - private Object taskWorkerLock=new Object(); - private boolean isRunning = false; - private boolean shouldStop = false; - private List/*DisplayAction*/ displayActions = new ArrayList(); - - public synchronized void addAction(Display.Action da) { - List newListeners = (List) ((ArrayList) displayActions).clone(); - newListeners.add(da); - displayActions = newListeners; - } - - public synchronized void removeAction(Display.Action da) { - List newListeners = (List) ((ArrayList) displayActions).clone(); - newListeners.remove(da); - displayActions = newListeners; - } - - public boolean isRunning() { - synchronized(taskWorkerLock) { - return isRunning; - } - } - - public void exit() { - synchronized(taskWorkerLock) { - if(isRunning) { - shouldStop = true; - taskWorkerLock.notifyAll(); - } - } - Map displayMap = Display.getCurrentDisplayMap(); - synchronized(displayMap) { - displayMap.notifyAll(); - } - } - - public void start() { - synchronized(taskWorkerLock) { - if(!isRunning) { - shouldStop = false; - taskWorkerLock.notifyAll(); - super.start(); - } - } - } - - public void run() { - synchronized(taskWorkerLock) { - isRunning = true; - taskWorkerLock.notifyAll(); - } - while(!shouldStop) { - Map displayMap = Display.getCurrentDisplayMap(); - // wait for something todo .. - synchronized(displayMap) { - while(!shouldStop && displayMap.size()==0) { - try { - displayMap.wait(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - Iterator iter = Display.getCurrentDisplays().iterator(); - while(iter.hasNext()) { - Display display = (Display) iter.next(); - Iterator iterDA = displayActions.iterator(); - while(iterDA.hasNext()) { - ((Display.Action)iterDA.next()).run(display); - } - } - } - synchronized(taskWorkerLock) { - isRunning = false; - taskWorkerLock.notifyAll(); - } - } -} diff --git a/src/newt/classes/com/sun/javafx/newt/Event.java b/src/newt/classes/com/sun/javafx/newt/Event.java index 7d2b24ba5..3c045c52b 100644 --- a/src/newt/classes/com/sun/javafx/newt/Event.java +++ b/src/newt/classes/com/sun/javafx/newt/Event.java @@ -74,4 +74,13 @@ public class Event { public String toString() { return "Event[sys:"+isSystemEvent()+", source:"+getSource()+", when:"+getWhen()+"]"; } + + public static String toHexString(int hex) { + return "0x" + Integer.toHexString(hex); + } + + public static String toHexString(long hex) { + return "0x" + Long.toHexString(hex); + } + } diff --git a/src/newt/classes/com/sun/javafx/newt/KeyEvent.java b/src/newt/classes/com/sun/javafx/newt/KeyEvent.java index 7e224dbad..c7450da67 100644 --- a/src/newt/classes/com/sun/javafx/newt/KeyEvent.java +++ b/src/newt/classes/com/sun/javafx/newt/KeyEvent.java @@ -53,7 +53,7 @@ public class KeyEvent extends InputEvent public String toString() { return "KeyEvent["+getEventTypeString(getEventType())+ - ", code "+keyCode+"(0x"+Integer.toHexString(keyCode)+"), char <"+keyChar+"> (0x"+Integer.toHexString((int)keyChar)+"), isActionKey "+isActionKey()+", "+super.toString()+"]"; + ", code "+keyCode+"("+toHexString(keyCode)+"), char <"+keyChar+"> ("+toHexString((int)keyChar)+"), isActionKey "+isActionKey()+", "+super.toString()+"]"; } public static String getEventTypeString(int type) { diff --git a/src/newt/classes/com/sun/javafx/newt/NewtFactory.java b/src/newt/classes/com/sun/javafx/newt/NewtFactory.java index c4180eb46..6e25b19eb 100755 --- a/src/newt/classes/com/sun/javafx/newt/NewtFactory.java +++ b/src/newt/classes/com/sun/javafx/newt/NewtFactory.java @@ -57,6 +57,20 @@ public abstract class NewtFactory { return clazz; } + private static boolean useEDT = true; + + /** + * Toggles the usage of an EventDispatchThread while creating a Display.<br> + * The default is enabled.<br> + * The EventDispatchThread is thread local to the Display instance.<br> + */ + public static synchronized void setUseEDT(boolean onoff) { + useEDT = onoff; + } + + /** @see #setUseEDT(boolean) */ + public static boolean useEDT() { return useEDT; } + /** * Create a Display entity, incl native creation */ diff --git a/src/newt/classes/com/sun/javafx/newt/Screen.java b/src/newt/classes/com/sun/javafx/newt/Screen.java index e347a69c2..9edd0b719 100755 --- a/src/newt/classes/com/sun/javafx/newt/Screen.java +++ b/src/newt/classes/com/sun/javafx/newt/Screen.java @@ -84,6 +84,8 @@ public abstract class Screen { public synchronized void destroy() { closeNative(); + display = null; + aScreen = null; } protected static Screen wrapHandle(String type, Display display, AbstractGraphicsScreen aScreen) { diff --git a/src/newt/classes/com/sun/javafx/newt/Window.java b/src/newt/classes/com/sun/javafx/newt/Window.java index 85b8723f9..7bebe2125 100755 --- a/src/newt/classes/com/sun/javafx/newt/Window.java +++ b/src/newt/classes/com/sun/javafx/newt/Window.java @@ -34,6 +34,7 @@ package com.sun.javafx.newt; import com.sun.javafx.newt.impl.Debug; +import com.sun.javafx.newt.util.EventDispatchThread; import javax.media.nativewindow.*; import com.sun.nativewindow.impl.NWReflection; @@ -85,7 +86,7 @@ public abstract class Window implements NativeWindow return windowClass; } - protected static Window create(String type, long parentWindowHandle, Screen screen, Capabilities caps, boolean undecorated) { + protected static Window create(String type, final long parentWindowHandle, Screen screen, final Capabilities caps, boolean undecorated) { try { Class windowClass; if(caps.isOnscreen()) { @@ -97,7 +98,17 @@ public abstract class Window implements NativeWindow window.invalidate(); window.screen = screen; window.setUndecorated(undecorated||0!=parentWindowHandle); - window.createNative(parentWindowHandle, caps); + EventDispatchThread edt = screen.getDisplay().getEDT(); + if(null!=edt) { + final Window f_win = window; + edt.invokeAndWait(new Runnable() { + public void run() { + f_win.createNative(parentWindowHandle, caps); + } + } ); + } else { + window.createNative(parentWindowHandle, caps); + } return window; } catch (Throwable t) { t.printStackTrace(); @@ -105,7 +116,7 @@ public abstract class Window implements NativeWindow } } - protected static Window create(String type, Object[] cstrArguments, Screen screen, Capabilities caps, boolean undecorated) { + protected static Window create(String type, Object[] cstrArguments, Screen screen, final Capabilities caps, boolean undecorated) { try { Class windowClass = getWindowClass(type); Class[] cstrArgumentTypes = getCustomConstructorArgumentTypes(windowClass); @@ -120,7 +131,17 @@ public abstract class Window implements NativeWindow window.invalidate(); window.screen = screen; window.setUndecorated(undecorated); - window.createNative(0, caps); + EventDispatchThread edt = screen.getDisplay().getEDT(); + if(null!=edt) { + final Window f_win = window; + edt.invokeAndWait(new Runnable() { + public void run() { + f_win.createNative(0, caps); + } + } ); + } else { + window.createNative(0, caps); + } return window; } catch (Throwable t) { t.printStackTrace(); @@ -152,6 +173,25 @@ public abstract class Window implements NativeWindow } } + public static String toHexString(int hex) { + return "0x" + Integer.toHexString(hex); + } + + public static String toHexString(long hex) { + return "0x" + Long.toHexString(hex); + } + + protected Screen screen; + + protected AbstractGraphicsConfiguration config; + protected long windowHandle; + protected boolean fullscreen, visible; + protected int width, height, x, y; + protected int eventMask; + + protected String title = "Newt Window"; + protected boolean undecorated = false; + /** * Create native windowHandle, ie creates a new native invisible window. * @@ -173,8 +213,8 @@ public abstract class Window implements NativeWindow StringBuffer sb = new StringBuffer(); sb.append(getClass().getName()+"[config "+config+ - ", windowHandle 0x"+Long.toHexString(getWindowHandle())+ - ", surfaceHandle 0x"+Long.toHexString(getSurfaceHandle())+ + ", windowHandle "+toHexString(getWindowHandle())+ + ", surfaceHandle "+toHexString(getSurfaceHandle())+ ", pos "+getX()+"/"+getY()+", size "+getWidth()+"x"+getHeight()+ ", visible "+isVisible()+ ", undecorated "+undecorated+ @@ -202,18 +242,6 @@ public abstract class Window implements NativeWindow return sb.toString(); } - protected Screen screen; - - protected AbstractGraphicsConfiguration config; - protected long windowHandle; - protected Exception lockedStack = null; - protected boolean fullscreen, visible; - protected int width, height, x, y; - protected int eventMask; - - protected String title = "Newt Window"; - protected boolean undecorated = false; - public String getTitle() { return title; } @@ -236,31 +264,55 @@ public abstract class Window implements NativeWindow // // NativeWindow impl // + private Thread owner; + private int recursionCount; + protected Exception lockedStack = null; - public synchronized int lockSurface() throws NativeWindowException { + /** Recursive and blocking lockSurface() implementation */ + public synchronized int lockSurface() { // We leave the ToolkitLock lock to the specializtion's discretion, // ie the implicit JAWTWindow in case of AWTWindow - if (null!=lockedStack) { - lockedStack.printStackTrace(); - throw new NativeWindowException("NEWT Surface already locked - "+Thread.currentThread().getName()+" "+this); + Thread cur = Thread.currentThread(); + if (owner == cur) { + ++recursionCount; + return LOCK_SUCCESS; } - - lockedStack = new Exception("NEWT Surface previously locked by "+Thread.currentThread().getName()); + while (owner != null) { + try { + wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + owner = cur; + lockedStack = new Exception("NEWT Surface previously locked by "+Thread.currentThread()); return LOCK_SUCCESS; } - public synchronized void unlockSurface() { - if (null!=lockedStack) { - lockedStack = null; - } else { - throw new NativeWindowException("NEWT Surface not locked"); + /** Recursive and unblocking unlockSurface() implementation */ + public synchronized void unlockSurface() throws NativeWindowException { + Thread cur = Thread.currentThread(); + if (owner != cur) { + lockedStack.printStackTrace(); + throw new NativeWindowException(cur+": Not owner, owner is "+owner); } + if (recursionCount > 0) { + --recursionCount; + return; + } + owner = null; + lockedStack = null; + notifyAll(); // We leave the ToolkitLock unlock to the specializtion's discretion, // ie the implicit JAWTWindow in case of AWTWindow } public synchronized boolean isSurfaceLocked() { - return null!=lockedStack; + return null!=owner; + } + + public synchronized Thread getSurfaceLockOwner() { + return owner; } public synchronized Exception getLockedStack() { @@ -268,8 +320,13 @@ public abstract class Window implements NativeWindow } public synchronized void destroy() { + destroy(false); + } + + /** @param deep If true, the linked Screen and Display will be destroyed as well. */ + public synchronized void destroy(boolean deep) { if(DEBUG_WINDOW_EVENT) { - System.out.println("Window.destroy() start "+Thread.currentThread().getName()); + System.out.println("Window.destroy() start (deep "+deep+" - "+Thread.currentThread()); } synchronized(surfaceUpdatedListeners) { surfaceUpdatedListeners = new ArrayList(); @@ -283,16 +340,32 @@ public abstract class Window implements NativeWindow synchronized(keyListeners) { keyListeners = new ArrayList(); } - closeNative(); + Screen scr = screen; + Display dpy = screen.getDisplay(); + EventDispatchThread edt = dpy.getEDT(); + if(null!=edt) { + final Window f_win = this; + edt.invokeAndWait(new Runnable() { + public void run() { + f_win.closeNative(); + } + } ); + } else { + closeNative(); + } invalidate(); + if(deep) { + scr.destroy(); + dpy.destroy(); + } if(DEBUG_WINDOW_EVENT) { - System.out.println("Window.destroy() end "+Thread.currentThread().getName()); + System.out.println("Window.destroy() end "+Thread.currentThread()); } } public void invalidate() { if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { - Exception e = new Exception("!!! Window Invalidate "+Thread.currentThread().getName()); + Exception e = new Exception("!!! Window Invalidate "+Thread.currentThread()); e.printStackTrace(); } screen = null; @@ -415,7 +488,7 @@ public abstract class Window implements NativeWindow protected void windowDestroyNotify() { if(DEBUG_WINDOW_EVENT) { - System.out.println("Window.windowDestroyeNotify start "+Thread.currentThread().getName()); + System.out.println("Window.windowDestroyeNotify start "+Thread.currentThread()); } sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); @@ -425,13 +498,13 @@ public abstract class Window implements NativeWindow } if(DEBUG_WINDOW_EVENT) { - System.out.println("Window.windowDestroyeNotify end "+Thread.currentThread().getName()); + System.out.println("Window.windowDestroyeNotify end "+Thread.currentThread()); } } protected void windowDestroyed() { if(DEBUG_WINDOW_EVENT) { - System.out.println("Window.windowDestroyed "+Thread.currentThread().getName()); + System.out.println("Window.windowDestroyed "+Thread.currentThread()); } invalidate(); } diff --git a/src/newt/classes/com/sun/javafx/newt/opengl/GLWindow.java b/src/newt/classes/com/sun/javafx/newt/opengl/GLWindow.java index 52f368148..aebf8f9dd 100644 --- a/src/newt/classes/com/sun/javafx/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/sun/javafx/newt/opengl/GLWindow.java @@ -41,45 +41,22 @@ import java.util.*; /** * An implementation of {@link Window} which is customized for OpenGL - * use, and which implements the {@link - * javax.media.opengl.GLAutoDrawable} interface. For convenience, this - * window class guarantees that its OpenGL context is current inside - * the various EventListeners' callbacks (MouseListener, KeyListener, - * etc.).<P> - * - * Best performance is currently achieved with default settings, - * {@link #setEventHandlerMode} to {@link #EVENT_HANDLER_GL_NONE} - * and one thread per GLWindow. To ensure compatibility with the - * underlying platform, window shall also be created within your - * working thread. See comment at {@link #setRunPumpMessages}. + * use, and which implements the {@link javax.media.opengl.GLAutoDrawable} interface. + * <P> + * This implementation does not make the OpenGL context current<br> + * before calling the various input EventListener callbacks (MouseListener, KeyListener, + * etc.).<br> + * This design decision is made to favor a more performant and simplified + * implementation, as well as the event dispatcher shall be allowed + * not having a notion about OpenGL. + * <p> */ public class GLWindow extends Window implements GLAutoDrawable { - /** - * Event handling mode: EVENT_HANDLER_GL_NONE. - * No GL context is current, while calling the EventListener. - * This might be inconvenient, but don't impact the performance. - * Also - * - * @see com.sun.javafx.newt.GLWindow#setEventHandlerMode(int) - */ - public static final int EVENT_HANDLER_GL_NONE = 0; - - /** - * Event handling mode: EVENT_HANDLER_GL_CURRENT. - * The GL context is made current, while calling the EventListener. - * This might be convenient, but impacts the performance - * due to context switches. - * - * This is the default setting! - * - * @see com.sun.javafx.newt.GLWindow#setEventHandlerMode(int) - */ - public static final int EVENT_HANDLER_GL_CURRENT = (1 << 0); - private static List/*GLWindow*/ glwindows = new ArrayList(); + private boolean ownerOfDisplayAndScreen; private Window window; - private boolean runPumpMessages = true; + private boolean runPumpMessages; /** Constructor. Do not call this directly -- use {@link create()} instead. */ @@ -87,6 +64,7 @@ public class GLWindow extends Window implements GLAutoDrawable { this.ownerOfDisplayAndScreen = ownerOfDisplayAndScreen; this.window = window; this.window.setAutoDrawableClient(true); + this.runPumpMessages = ( null == getScreen().getDisplay().getEDT() ) ; window.addWindowListener(new WindowListener() { public void windowResized(WindowEvent e) { sendReshape = true; @@ -166,9 +144,16 @@ public class GLWindow extends Window implements GLAutoDrawable { * * Best performance has been achieved with one GLWindow per thread.<br> * + * Enabling local pump messages while using the EDT, + * {@link com.sun.javafx.newt.NewtFactory#setUseEDT(boolean)}, + * will result in an exception. + * * @deprecated EXPERIMENTAL, semantic is about to be removed after further verification. */ public void setRunPumpMessages(boolean onoff) { + if( onoff && null!=getScreen().getDisplay().getEDT() ) { + throw new GLException("GLWindow.setRunPumpMessages(true) - Can't do with EDT on"); + } runPumpMessages = onoff; } @@ -182,7 +167,7 @@ public class GLWindow extends Window implements GLAutoDrawable { protected void dispose(boolean regenerate) { if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) { - Exception e1 = new Exception("GLWindow.dispose("+regenerate+") "+Thread.currentThread().getName()+", 1: "+this); + Exception e1 = new Exception("GLWindow.dispose("+regenerate+") "+Thread.currentThread()+", 1: "+this); e1.printStackTrace(); } @@ -213,21 +198,22 @@ public class GLWindow extends Window implements GLAutoDrawable { } drawable = factory.createGLDrawable(nw); drawable.setRealized(true); - if(getSurfaceHandle()==0) { - throw new GLException("SurfaceHandle==NULL after setRealize(true) "+this); - } context = drawable.createContext(null); sendReshape = true; // ensure a reshape event is send .. } if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) { - System.out.println("GLWindow.dispose("+regenerate+") "+Thread.currentThread().getName()+", fin: "+this); + System.out.println("GLWindow.dispose("+regenerate+") "+Thread.currentThread()+", fin: "+this); } } public synchronized void destroy() { + destroy(false); + } + + public synchronized void destroy(boolean deep) { if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) { - Exception e1 = new Exception("GLWindow.destroy "+Thread.currentThread().getName()+", 1: "+this); + Exception e1 = new Exception("GLWindow.destroy "+Thread.currentThread()+", 1: "+this); e1.printStackTrace(); } @@ -240,17 +226,17 @@ public class GLWindow extends Window implements GLAutoDrawable { Screen _screen = null; Display _device = null; if(null!=window) { - if(ownerOfDisplayAndScreen) { + if(!deep && ownerOfDisplayAndScreen) { _screen = getScreen(); if(null != _screen) { _device = _screen.getDisplay(); } } - window.destroy(); + window.destroy(deep); } if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) { - System.out.println("GLWindow.destroy "+Thread.currentThread().getName()+", fin: "+this); + System.out.println("GLWindow.destroy "+Thread.currentThread()+", fin: "+this); } drawable = null; @@ -271,21 +257,11 @@ public class GLWindow extends Window implements GLAutoDrawable { perfLog = v; } - /** - * Sets the event handling mode. - * - * @see com.sun.javafx.newt.GLWindow#EVENT_HANDLER_GL_NONE - * @see com.sun.javafx.newt.GLWindow#EVENT_HANDLER_GL_CURRENT - */ - public void setEventHandlerMode(int mode) { - eventHandlerMode = mode; - } - - public int getEventHandlerMode() { - return eventHandlerMode; - } - public void setVisible(boolean visible) { + if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) { + System.out.println(Thread.currentThread()+" GLWindow.setVisible("+visible+") START ; isVisible "+this.visible+" ; has context "+(null!=context)); + } + this.visible=visible; window.setVisible(visible); if (visible && context == null) { NativeWindow nw; @@ -298,12 +274,12 @@ public class GLWindow extends Window implements GLAutoDrawable { factory = GLDrawableFactory.getFactory(glCaps.getGLProfile()); drawable = factory.createGLDrawable(nw); drawable.setRealized(true); - if(getSurfaceHandle()==0) { - throw new GLException("SurfaceHandle==NULL after setRealize(true) "+this); - } context = drawable.createContext(null); sendReshape = true; // ensure a reshape event is send .. } + if(Window.DEBUG_WINDOW_EVENT || window.DEBUG_IMPLEMENTATION) { + System.out.println(Thread.currentThread()+" GLWindow.setVisible("+visible+") END ; has context "+(null!=context)); + } } public Screen getScreen() { @@ -423,8 +399,6 @@ public class GLWindow extends Window implements GLAutoDrawable { // OpenGL-related methods and state // - private static int eventHandlerMode = EVENT_HANDLER_GL_CURRENT; - private static boolean autoSwapBufferMode = true; private GLDrawableFactory factory; private GLDrawable drawable; private GLContext context; @@ -469,72 +443,14 @@ public class GLWindow extends Window implements GLAutoDrawable { helper.removeGLEventListener(listener); } - class DisplayPumpMessage implements Display.Action { - public void run(Display display) { - if( 0 == (eventHandlerMode & EVENT_HANDLER_GL_CURRENT) ) { - display.run(); - } else { - helper.setAutoSwapBufferMode(false); - try { - helper.invokeGL(drawable, context, display, initAction); - } finally { - helper.setAutoSwapBufferMode(autoSwapBufferMode); - } - } - } - } - private DisplayPumpMessage displayPumpMessage = new DisplayPumpMessage(); - - static class DisplayPumpMessageStatic implements Display.Action { - public void run(Display display) { - display.run(); - } - } - private static DisplayPumpMessageStatic displayPumpMessageStatic = new DisplayPumpMessageStatic(); - - /** - * @see #setRunPumpMessages - * @deprecated EXPERIMENTAL, semantic is about to be removed after further verification. - */ - public static void runCurrentThreadPumpMessage() { - Exception safedE = null; - - if( 0 != (eventHandlerMode & EVENT_HANDLER_GL_CURRENT) ) { - // for all: setup / init / makeCurrent - for(Iterator i = glwindows.iterator(); i.hasNext(); ) { - GLWindow glw = (GLWindow) i.next(); - glw.helper.setAutoSwapBufferMode(false); - glw.helper.invokeGL(glw.drawable, glw.context, null, glw.initAction); - } - } - - try { - Display.runCurrentThreadDisplaysAction(displayPumpMessageStatic); - } catch (Exception e) { - safedE=e; - } - - if( 0 != (eventHandlerMode & EVENT_HANDLER_GL_CURRENT) ) { - // for all: reset - for(Iterator i = glwindows.iterator(); i.hasNext(); ) { - GLWindow glw = (GLWindow) i.next(); - glw.helper.setAutoSwapBufferMode(autoSwapBufferMode); - } - } - - if(null!=safedE) { - throw new GLException(safedE); - } - } - public void display() { display(false); } public void display(boolean forceReshape) { - if(getSurfaceHandle()!=0) { + if(window!=null && drawable!=null && context != null) { if(runPumpMessages) { - displayPumpMessage.run(window.getScreen().getDisplay()); + window.getScreen().getDisplay().pumpMessages(); } if(window.hasDeviceChanged() && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED) { dispose(true); @@ -552,24 +468,24 @@ public class GLWindow extends Window implements GLAutoDrawable { } private void sendDisposeEvent() { - if(disposeAction!=null && drawable!=null && context != null && window!=null && getSurfaceHandle()!=0) { + if(drawable!=null && context != null) { helper.invokeGL(drawable, context, disposeAction, null); } } /** This implementation uses a static value */ public void setAutoSwapBufferMode(boolean onOrOff) { - autoSwapBufferMode = onOrOff; + helper.setAutoSwapBufferMode(onOrOff); } /** This implementation uses a static value */ public boolean getAutoSwapBufferMode() { - return autoSwapBufferMode; + return helper.getAutoSwapBufferMode(); } public void swapBuffers() { - if(getSurfaceHandle()!=0) { - if (context != null && context != GLContext.getCurrent()) { + if(drawable!=null && context != null) { + if (context != GLContext.getCurrent()) { // Assume we should try to make the context current before swapping the buffers helper.invokeGL(drawable, context, swapBuffersAction, initAction); } else { @@ -638,7 +554,6 @@ public class GLWindow extends Window implements GLAutoDrawable { private long curTime = 0; private long lastCheck = 0; private int totalFrames = 0, lastFrames = 0; - private boolean ownerOfDisplayAndScreen; class SwapBuffersAction implements Runnable { public void run() { diff --git a/src/newt/classes/com/sun/javafx/newt/opengl/broadcom/egl/Window.java b/src/newt/classes/com/sun/javafx/newt/opengl/broadcom/egl/Window.java index 7d087416c..11672dde3 100755 --- a/src/newt/classes/com/sun/javafx/newt/opengl/broadcom/egl/Window.java +++ b/src/newt/classes/com/sun/javafx/newt/opengl/broadcom/egl/Window.java @@ -148,7 +148,7 @@ public class Window extends com.sun.javafx.newt.Window { throw new NativeWindowException("Error creating EGLGraphicsConfiguration from id: "+cfgID+", "+this); } if(DEBUG_IMPLEMENTATION) { - System.out.println("BCEGL Window.windowCreated(): 0x"+Integer.toHexString(cfgID)+", "+width+"x"+height+", "+config); + System.out.println("BCEGL Window.windowCreated(): "+toHexString(cfgID)+", "+width+"x"+height+", "+config); } } diff --git a/src/newt/classes/com/sun/javafx/newt/util/EventDispatchThread.java b/src/newt/classes/com/sun/javafx/newt/util/EventDispatchThread.java new file mode 100644 index 000000000..f8b205a8b --- /dev/null +++ b/src/newt/classes/com/sun/javafx/newt/util/EventDispatchThread.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution 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 Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +package com.sun.javafx.newt.util; + +import com.sun.javafx.newt.Display; +import java.util.*; + +public class EventDispatchThread { + private ThreadGroup threadGroup; + private volatile boolean shouldStop = false; + private TaskWorker taskWorker = null; + private Object taskWorkerLock = new Object(); + private ArrayList tasks = new ArrayList(); // one shot tasks + private Display display = null; + private String name; + private long edtPollGranularity = 10; + + public EventDispatchThread(Display display, ThreadGroup tg, String name) { + this.display = display; + this.threadGroup = tg; + this.name=new String("EDT-"+name); + } + + public String getName() { return name; } + + public ThreadGroup getThreadGroup() { return threadGroup; } + + public void start() { + start(false); + } + + /** + * @param externalStimuli true indicates that another thread stimulates, + * ie. calls this TaskManager's run() loop method. + * Hence no own thread is started in this case. + * + * @return The started Runnable, which handles the run-loop. + * Usefull in combination with externalStimuli=true, + * so an external stimuli can call it. + */ + public Runnable start(boolean externalStimuli) { + synchronized(taskWorkerLock) { + if(null==taskWorker) { + taskWorker = new TaskWorker(threadGroup, name); + } + if(!taskWorker.isRunning()) { + shouldStop = false; + taskWorker.start(externalStimuli); + } + taskWorkerLock.notifyAll(); + } + return taskWorker; + } + + public void stop() { + synchronized(taskWorkerLock) { + if(null!=taskWorker && taskWorker.isRunning()) { + shouldStop = true; + } + taskWorkerLock.notifyAll(); + } + } + + public boolean isEDTThread(Thread thread) { + return taskWorker == thread; + } + + public boolean isRunning() { + return null!=taskWorker && taskWorker.isRunning() ; + } + + public void invokeLater(Runnable task) { + if(task == null) { + return; + } + synchronized(taskWorkerLock) { + tasks.add(task); + taskWorkerLock.notifyAll(); + } + } + + public void invokeAndWait(Runnable task) { + if(task == null) { + return; + } + invokeLater(task); + waitOnWorker(); + } + + public void waitOnWorker() { + synchronized(taskWorkerLock) { + if(null!=taskWorker && taskWorker.isRunning() && tasks.size()>0 && taskWorker != Thread.currentThread() ) { + try { + taskWorkerLock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + public void waitUntilStopped() { + synchronized(taskWorkerLock) { + while(null!=taskWorker && taskWorker.isRunning() && taskWorker != Thread.currentThread() ) { + try { + taskWorkerLock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + class TaskWorker extends Thread { + boolean isRunning = false; + boolean externalStimuli = false; + + public TaskWorker(ThreadGroup tg, String name) { + super(tg, name); + } + + public synchronized boolean isRunning() { + return isRunning; + } + + public void start(boolean externalStimuli) throws IllegalThreadStateException { + synchronized(this) { + this.externalStimuli = externalStimuli; + isRunning = true; + } + if(!externalStimuli) { + super.start(); + } + } + + public void run() { + while(!shouldStop) { + try { + // wait for something todo .. + synchronized(taskWorkerLock) { + while(!shouldStop && tasks.size()==0) { + try { + display.pumpMessages(); // event dispatch + taskWorkerLock.wait(edtPollGranularity); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if(tasks.size()>0) { + Runnable task = (Runnable) tasks.remove(0); + task.run(); + taskWorkerLock.notifyAll(); + } + } + display.pumpMessages(); // event dispatch + } catch (Throwable t) { + // handle errors .. + t.printStackTrace(); + } finally { + // epilog - unlock locked stuff + } + if(externalStimuli) break; // no loop if called by external stimuli + } + synchronized(this) { + isRunning = !shouldStop; + } + if(!isRunning) { + synchronized(taskWorkerLock) { + taskWorkerLock.notifyAll(); + } + } + } + } +} + diff --git a/src/newt/classes/com/sun/javafx/newt/windows/WindowsWindow.java b/src/newt/classes/com/sun/javafx/newt/windows/WindowsWindow.java index d044d61b5..5749039aa 100755 --- a/src/newt/classes/com/sun/javafx/newt/windows/WindowsWindow.java +++ b/src/newt/classes/com/sun/javafx/newt/windows/WindowsWindow.java @@ -53,16 +53,40 @@ public class WindowsWindow extends Window { public WindowsWindow() { } - public long getSurfaceHandle() { - if (hdc == 0 && 0!=windowHandle) { + Thread hdcOwner = null; + + public synchronized int lockSurface() throws NativeWindowException { + int res = super.lockSurface(); + if(LOCK_SUCCESS==res && 0!=windowHandle) { + if(hdc!=0) { + throw new NativeWindowException("NEWT Surface handle set HDC "+toHexString(hdc)+" - "+Thread.currentThread().getName()+" ; "+this); + } hdc = GetDC(windowHandle); hmon = MonitorFromWindow(windowHandle); - if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { - Exception e = new Exception("!!! Window new surface handle "+Thread.currentThread().getName()+ - ", HWND 0x"+Long.toHexString(windowHandle)+", HDC 0x"+Long.toHexString(hdc)+", HMON 0x"+Long.toHexString(hmon)); - e.printStackTrace(); + hdcOwner = Thread.currentThread(); + } + return res; + } + + public synchronized void unlockSurface() { + // prevalidate, before we change data .. + Thread cur = Thread.currentThread(); + if ( getSurfaceLockOwner() != cur ) { + getLockedStack().printStackTrace(); + throw new NativeWindowException(cur+": Not owner, owner is "+getSurfaceLockOwner()); + } + if (0!=hdc && 0!=windowHandle) { + if(hdcOwner != cur) { + throw new NativeWindowException("NEWT Surface handle set HDC "+toHexString(hdc)+" by other thread "+hdcOwner+", this "+cur+" ; "+this); } + ReleaseDC(windowHandle, hdc); + hdc=0; + hdcOwner=null; } + super.unlockSurface(); + } + + public long getSurfaceHandle() { return hdc; } @@ -72,7 +96,7 @@ public class WindowsWindow extends Window { if (hmon != _hmon) { if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { Exception e = new Exception("!!! Window Device Changed "+Thread.currentThread().getName()+ - ", HMON 0x"+Long.toHexString(hmon)+" -> 0x"+Long.toHexString(_hmon)); + ", HMON "+toHexString(hmon)+" -> "+toHexString(_hmon)); e.printStackTrace(); } hmon = _hmon; @@ -87,7 +111,7 @@ public class WindowsWindow extends Window { ReleaseDC(windowHandle, hdc); hdc=0; if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { - Exception e = new Exception("!!! Window surface handle disposed "+Thread.currentThread().getName()); + Exception e = new Exception("!!! Window surface handle disposed "+Thread.currentThread().getName()+", "+Thread.currentThread()); e.printStackTrace(); } } @@ -110,7 +134,8 @@ public class WindowsWindow extends Window { windowHandleClose = windowHandle; if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { Exception e = new Exception("!!! Window new window handle "+Thread.currentThread().getName()+ - ", HWND 0x"+Long.toHexString(windowHandle)); + " (Parent HWND "+toHexString(parentWindowHandle)+ + ") : HWND "+toHexString(windowHandle)+", "+Thread.currentThread()); e.printStackTrace(); } } diff --git a/src/newt/classes/com/sun/javafx/newt/x11/X11Display.java b/src/newt/classes/com/sun/javafx/newt/x11/X11Display.java index 5a2e0ad5d..297f98edb 100755 --- a/src/newt/classes/com/sun/javafx/newt/x11/X11Display.java +++ b/src/newt/classes/com/sun/javafx/newt/x11/X11Display.java @@ -87,11 +87,23 @@ public class X11Display extends Display { protected long getJavaObjectAtom() { return javaObjectAtom; } protected long getWindowDeleteAtom() { return windowDeleteAtom; } + protected void lockDisplay() { + LockDisplay(getHandle()); + } + + protected void unlockDisplay() { + UnlockDisplay(getHandle()); + } + + //---------------------------------------------------------------------- // Internals only // private static native boolean initIDs(); + private native void LockDisplay(long handle); + private native void UnlockDisplay(long handle); + private native void CompleteDisplay(long handle); private native void DispatchMessages(long display, long javaObjectAtom, long windowDeleteAtom); diff --git a/src/newt/classes/com/sun/javafx/newt/x11/X11Window.java b/src/newt/classes/com/sun/javafx/newt/x11/X11Window.java index 46e797c21..94eb98299 100755 --- a/src/newt/classes/com/sun/javafx/newt/x11/X11Window.java +++ b/src/newt/classes/com/sun/javafx/newt/x11/X11Window.java @@ -85,6 +85,25 @@ public class X11Window extends Window { super.windowDestroyed(); } + public synchronized int lockSurface() throws NativeWindowException { + int res = super.lockSurface(); + if(LOCK_SUCCESS == res) { + ((X11Display)(screen.getDisplay())).lockDisplay(); + } + return res; + } + + public synchronized void unlockSurface() { + // prevalidate, before we change data .. + Thread cur = Thread.currentThread(); + if ( getSurfaceLockOwner() != cur ) { + getLockedStack().printStackTrace(); + throw new NativeWindowException(cur+": Not owner, owner is "+getSurfaceLockOwner()); + } + ((X11Display)(screen.getDisplay())).unlockDisplay(); + super.unlockSurface(); + } + public void setVisible(boolean visible) { if(0!=windowHandle && this.visible!=visible) { this.visible=visible; |