jogamp.newt.DisplayImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jogl-all Show documentation
Show all versions of jogl-all Show documentation
Java™ Binding for the OpenGL® API
/*
* Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
* Copyright (c) 2010 JogAmp Community. 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 jogamp.newt;
import com.jogamp.newt.Display;
import com.jogamp.newt.NewtFactory;
import com.jogamp.newt.event.NEWTEvent;
import com.jogamp.newt.event.NEWTEventConsumer;
import jogamp.newt.event.NEWTEventTask;
import com.jogamp.newt.util.EDTUtil;
import java.util.ArrayList;
import javax.media.nativewindow.AbstractGraphicsDevice;
import javax.media.nativewindow.NativeWindowException;
import javax.media.nativewindow.NativeWindowFactory;
public abstract class DisplayImpl extends Display {
private static int serialno = 1;
static {
NativeWindowFactory.addCustomShutdownHook(true /* head */, new Runnable() {
public void run() {
WindowImpl.shutdownAll();
ScreenImpl.shutdownAll();
DisplayImpl.shutdownAll();
}
});
}
/** Ensure static init has been run. */
/* pp */static void initSingleton() { }
private static Class> getDisplayClass(String type)
throws ClassNotFoundException
{
final Class> displayClass = NewtFactory.getCustomClass(type, "DisplayDriver");
if(null==displayClass) {
throw new ClassNotFoundException("Failed to find NEWT Display Class <"+type+".DisplayDriver>");
}
return displayClass;
}
/** Make sure to reuse a Display with the same name */
public static Display create(String type, String name, final long handle, boolean reuse) {
try {
final Class> displayClass = getDisplayClass(type);
final DisplayImpl display = (DisplayImpl) displayClass.newInstance();
name = display.validateDisplayName(name, handle);
synchronized(displayList) {
if(reuse) {
final Display display0 = Display.getLastDisplayOf(type, name, -1, true /* shared only */);
if(null != display0) {
if(DEBUG) {
System.err.println("Display.create() REUSE: "+display0+" "+getThreadName());
}
return display0;
}
}
display.exclusive = !reuse;
display.name = name;
display.type=type;
display.refCount=0;
display.id = serialno++;
display.fqname = getFQName(display.type, display.name, display.id);
display.hashCode = display.fqname.hashCode();
display.setEDTUtil( display.edtUtil ); // device's default if EDT is used, or null
Display.addDisplay2List(display);
}
if(DEBUG) {
System.err.println("Display.create() NEW: "+display+" "+getThreadName());
}
return display;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DisplayImpl other = (DisplayImpl) obj;
if (this.id != other.id) {
return false;
}
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if ((this.type == null) ? (other.type != null) : !this.type.equals(other.type)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public synchronized final void createNative()
throws NativeWindowException
{
if( null == aDevice ) {
if(DEBUG) {
System.err.println("Display.createNative() START ("+getThreadName()+", "+this+")");
}
final DisplayImpl f_dpy = this;
try {
runOnEDTIfAvail(true, new Runnable() {
public void run() {
f_dpy.createNativeImpl();
}});
} catch (Throwable t) {
throw new NativeWindowException(t);
}
if( null == aDevice ) {
throw new NativeWindowException("Display.createNative() failed to instanciate an AbstractGraphicsDevice");
}
synchronized(displayList) {
displaysActive++;
if(DEBUG) {
System.err.println("Display.createNative() END ("+getThreadName()+", "+this+", active "+displaysActive+")");
}
}
}
}
protected EDTUtil createEDTUtil() {
final EDTUtil def;
if(NewtFactory.useEDT()) {
def = new DefaultEDTUtil(Thread.currentThread().getThreadGroup(), "Display-"+getFQName(), dispatchMessagesRunnable);
if(DEBUG) {
System.err.println("Display.createNative("+getFQName()+") Create EDTUtil: "+def.getClass().getName());
}
} else {
def = null;
}
return def;
}
@Override
public synchronized EDTUtil setEDTUtil(final EDTUtil usrEDTUtil) {
final EDTUtil oldEDTUtil = edtUtil;
final EDTUtil newEDTUtil;
if( null != usrEDTUtil && usrEDTUtil == oldEDTUtil ) {
if( DEBUG ) {
System.err.println("Display.setEDTUtil: "+usrEDTUtil+" - keep!");
}
newEDTUtil = oldEDTUtil;
} else {
if(DEBUG) {
final String msg = ( null == usrEDTUtil ) ? "default" : "custom";
System.err.println("Display.setEDTUtil("+msg+"): "+oldEDTUtil+" -> "+usrEDTUtil);
}
stopEDT( oldEDTUtil, null );
newEDTUtil = ( null == usrEDTUtil ) ? createEDTUtil() : usrEDTUtil;
}
edtUtil = newEDTUtil;
return oldEDTUtil;
}
@Override
public final EDTUtil getEDTUtil() {
return edtUtil;
}
private static void stopEDT(final EDTUtil edtUtil, final Runnable task) {
if( null != edtUtil ) {
if( edtUtil.isRunning() ) {
final boolean res = edtUtil.invokeStop(true, task);
if( DEBUG ) {
if ( !res ) {
System.err.println("Warning: invokeStop() failed");
Thread.dumpStack();
}
}
}
edtUtil.waitUntilStopped();
// ready for restart ..
} else if( null != task ) {
task.run();
}
}
public void runOnEDTIfAvail(boolean wait, final Runnable task) {
final EDTUtil _edtUtil = edtUtil;
if( null != _edtUtil && !_edtUtil.isCurrentThreadEDT() ) {
if( !_edtUtil.isRunning() ) { // start EDT if not running yet
synchronized( this ) {
if( !_edtUtil.isRunning() ) { // // volatile dbl-checked-locking OK
_edtUtil.restart();
if( DEBUG ) {
System.err.println("Info: EDT started "+Thread.currentThread().getName()+", "+this);
Thread.dumpStack();
}
}
}
}
if( !_edtUtil.invoke(wait, task) ) {
if( DEBUG ) {
System.err.println("Warning: invoke(wait "+wait+", ..) on EDT failed .. invoke on current thread "+Thread.currentThread().getName());
Thread.dumpStack();
}
task.run();
}
} else {
task.run();
}
}
public boolean validateEDT() {
if( 0==refCount && null == aDevice ) {
final EDTUtil _edtUtil = edtUtil;
if( null != _edtUtil && _edtUtil.isRunning() ) {
synchronized( this ) {
if( null != edtUtil && edtUtil.isRunning() ) { // // volatile dbl-checked-locking OK
stopEDT( edtUtil, null );
return true;
}
}
}
}
return false;
}
@Override
public synchronized final void destroy() {
if(DEBUG) {
dumpDisplayList("Display.destroy("+getFQName()+") BEGIN");
}
synchronized(displayList) {
if(0 < displaysActive) {
displaysActive--;
}
if(DEBUG) {
System.err.println("Display.destroy(): "+this+", active "+displaysActive+" "+getThreadName());
}
}
final DisplayImpl f_dpy = this;
final AbstractGraphicsDevice f_aDevice = aDevice;
aDevice = null;
refCount=0;
stopEDT( edtUtil, new Runnable() { // blocks!
public void run() {
if ( null != f_aDevice ) {
f_dpy.closeNativeImpl(f_aDevice);
}
}
} );
if(DEBUG) {
dumpDisplayList("Display.destroy("+getFQName()+") END");
}
}
/** May be utilized at a shutdown hook, impl. does not block. */
/* pp */ static final void shutdownAll() {
final int dCount = displayList.size();
if(DEBUG) {
dumpDisplayList("Display.shutdownAll "+dCount+" instances, on thread "+getThreadName());
}
for(int i=0; i0; i++) { // be safe ..
final DisplayImpl d = (DisplayImpl) displayList.remove(0).get();
if(DEBUG) {
System.err.println("Display.shutdownAll["+(i+1)+"/"+dCount+"]: "+d+", GCed "+(null==d));
}
if( null != d ) { // GC'ed ?
if(0 < displaysActive) {
displaysActive--;
}
final EDTUtil edtUtil = d.getEDTUtil();
final AbstractGraphicsDevice f_aDevice = d.aDevice;
d.aDevice = null;
d.refCount=0;
final Runnable closeNativeTask = new Runnable() {
public void run() {
if ( null != d.getGraphicsDevice() ) {
d.closeNativeImpl(f_aDevice);
}
}
};
if(null != edtUtil) {
final long coopSleep = edtUtil.getPollPeriod() * 2;
if( edtUtil.isRunning() ) {
edtUtil.invokeStop(false, closeNativeTask); // don't block
}
try {
Thread.sleep( coopSleep < 50 ? coopSleep : 50 );
} catch (InterruptedException e) { }
} else {
closeNativeTask.run();
}
}
}
}
public synchronized final int addReference() {
if(DEBUG) {
System.err.println("Display.addReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount+1));
}
if ( 0 == refCount ) {
createNative();
}
if(null == aDevice) {
throw new NativeWindowException ("Display.addReference() (refCount "+refCount+") null AbstractGraphicsDevice");
}
return refCount++;
}
public synchronized final int removeReference() {
if(DEBUG) {
System.err.println("Display.removeReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount-1));
}
refCount--; // could become < 0, in case of manual destruction without actual creation/addReference
if(0>=refCount) {
destroy();
refCount=0; // fix < 0
}
return refCount;
}
public synchronized final int getReferenceCount() {
return refCount;
}
protected abstract void createNativeImpl();
protected abstract void closeNativeImpl(AbstractGraphicsDevice aDevice);
@Override
public final int getId() {
return id;
}
@Override
public final String getType() {
return type;
}
@Override
public final String getName() {
return name;
}
@Override
public final String getFQName() {
return fqname;
}
@Override
public final boolean isExclusive() {
return exclusive;
}
public static final String nilString = "nil" ;
public String validateDisplayName(String name, long handle) {
if(null==name && 0!=handle) {
name="wrapping-"+toHexString(handle);
}
return ( null == name ) ? nilString : name ;
}
private static String getFQName(String type, String name, int id) {
if(null==type) type=nilString;
if(null==name) name=nilString;
StringBuilder sb = new StringBuilder();
sb.append(type);
sb.append("_");
sb.append(name);
sb.append("-");
sb.append(id);
return sb.toString();
}
@Override
public final long getHandle() {
if(null!=aDevice) {
return aDevice.getHandle();
}
return 0;
}
@Override
public final AbstractGraphicsDevice getGraphicsDevice() {
return aDevice;
}
@Override
public synchronized final boolean isNativeValid() {
return null != aDevice;
}
@Override
public boolean isEDTRunning() {
final EDTUtil _edtUtil = edtUtil;
if( null != _edtUtil ) {
return _edtUtil.isRunning();
}
return false;
}
@Override
public String toString() {
final EDTUtil _edtUtil = edtUtil;
final boolean _edtUtilRunning = ( null != _edtUtil ) ? _edtUtil.isRunning() : false;
return "NEWT-Display["+getFQName()+", excl "+exclusive+", refCount "+refCount+", hasEDT "+(null!=_edtUtil)+", edtRunning "+_edtUtilRunning+", "+aDevice+"]";
}
/** Dispatch native Toolkit messageges */
protected abstract void dispatchMessagesNative();
private Object eventsLock = new Object();
private ArrayList events = new ArrayList();
private volatile boolean haveEvents = false;
final protected Runnable dispatchMessagesRunnable = new Runnable() {
public void run() {
DisplayImpl.this.dispatchMessages();
} };
final void dispatchMessage(final NEWTEvent event) {
try {
final Object source = event.getSource();
if(source instanceof NEWTEventConsumer) {
final NEWTEventConsumer consumer = (NEWTEventConsumer) source ;
if(!consumer.consumeEvent(event)) {
// enqueue for later execution
enqueueEvent(false, event);
}
} else {
throw new RuntimeException("Event source not NEWT: "+source.getClass().getName()+", "+source);
}
} catch (Throwable t) {
final RuntimeException re;
if(t instanceof RuntimeException) {
re = (RuntimeException) t;
} else {
re = new RuntimeException(t);
}
throw re;
}
}
final void dispatchMessage(final NEWTEventTask eventTask) {
final NEWTEvent event = eventTask.get();
try {
if(null == event) {
// Ooops ?
System.err.println("Warning: event of eventTask is NULL");
Thread.dumpStack();
return;
}
dispatchMessage(event);
} catch (RuntimeException re) {
if( eventTask.isCallerWaiting() ) {
// propagate exception to caller
eventTask.setException(re);
} else {
throw re;
}
}
eventTask.notifyCaller();
}
@Override
public void dispatchMessages() {
// System.err.println("Display.dispatchMessages() 0 "+this+" "+getThreadName());
if(0==refCount || // no screens
null==getGraphicsDevice() // no native device
)
{
return;
}
ArrayList _events = null;
if(haveEvents) { // volatile: ok
synchronized(eventsLock) {
if(haveEvents) {
// swap events list to free ASAP
_events = events;
events = new ArrayList();
haveEvents = false;
}
eventsLock.notifyAll();
}
if( null != _events ) {
for (int i=0; i < _events.size(); i++) {
dispatchMessage(_events.get(i));
}
}
}
// System.err.println("Display.dispatchMessages() NATIVE "+this+" "+getThreadName());
dispatchMessagesNative();
}
public void enqueueEvent(boolean wait, NEWTEvent e) {
final EDTUtil _edtUtil = edtUtil;
if( !_edtUtil.isRunning() ) {
// oops .. we are already dead
if(DEBUG) {
System.err.println("Warning: EDT already stopped: wait:="+wait+", "+e);
Thread.dumpStack();
}
return;
}
// can't wait if we are on EDT or NEDT -> consume right away
if(wait && _edtUtil.isCurrentThreadEDTorNEDT() ) {
dispatchMessage(e);
return;
}
final Object lock = new Object();
final NEWTEventTask eTask = new NEWTEventTask(e, wait?lock:null);
synchronized(lock) {
synchronized(eventsLock) {
events.add(eTask);
haveEvents = true;
eventsLock.notifyAll();
}
if( wait ) {
try {
lock.wait();
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
if( null != eTask.getException() ) {
throw eTask.getException();
}
}
}
}
public interface DisplayRunnable {
T run(long dpy);
}
public static final T runWithLockedDevice(AbstractGraphicsDevice device, DisplayRunnable action) {
T res;
device.lock();
try {
res = action.run(device.getHandle());
} finally {
device.unlock();
}
return res;
}
public final T runWithLockedDisplayDevice(DisplayRunnable action) {
final AbstractGraphicsDevice device = getGraphicsDevice();
if(null == device) {
throw new RuntimeException("null device - not initialized: "+this);
}
return runWithLockedDevice(device, action);
}
protected volatile EDTUtil edtUtil = null;
protected int id;
protected String name;
protected String type;
protected String fqname;
protected int hashCode;
protected int refCount; // number of Display references by Screen
protected boolean exclusive; // do not share this display, uses NullLock!
protected AbstractGraphicsDevice aDevice;
}