diff options
Diffstat (limited to 'src/newt/classes')
123 files changed, 20317 insertions, 7643 deletions
diff --git a/src/newt/classes/com/jogamp/newt/Display.java b/src/newt/classes/com/jogamp/newt/Display.java index 7b6849a30..4b38fcca5 100644 --- a/src/newt/classes/com/jogamp/newt/Display.java +++ b/src/newt/classes/com/jogamp/newt/Display.java @@ -28,21 +28,33 @@ package com.jogamp.newt; -import com.jogamp.newt.util.EDTUtil; -import jogamp.newt.Debug; - -import java.util.*; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.util.PixelRectangle; +import javax.media.nativewindow.util.PixelFormat; +import javax.media.nativewindow.util.PointImmutable; + +import jogamp.newt.Debug; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.newt.util.EDTUtil; public abstract class Display { public static final boolean DEBUG = Debug.debug("Display"); + protected static final boolean DEBUG_POINTER_ICON = Debug.debug("Display.PointerIcon"); /** return precomputed hashCode from FQN {@link #getFQName()} */ + @Override public abstract int hashCode(); /** return true if obj is of type Display and both FQN {@link #getFQName()} equals */ + @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof Display) { @@ -53,6 +65,167 @@ public abstract class Display { } /** + * Native PointerIcon handle. + * <p> + * Instances can be created via {@link Display}'s + * {@link Display#createPointerIcon(com.jogamp.common.util.IOUtil.ClassResources, int, int) createPointerIcon(pngResource, ..)} + * or {@link Display#createPointerIcon(PixelRectangle, int, int) createPointerIcon(pixelrect, ..)}. + * </p> + * <p> + * Instance is {@link #destroy()}'ed automatically if it's {@link #getDisplay() associated Display} is destroyed. + * </p> + * <p> + * Instance can be re-validated after destruction via {@link #validate()}. + * </p> + * <p> + * {@link PointerIcon} must not be {@link #destroy() destroyed} while in use! + * </p> + * <p> + * {@link PointerIcon} may be {@link #destroy() destroyed} manually after use, + * i.e. when no {@link Window} {@link Window#setPointerIcon(PointerIcon) uses them} anymore. + * However, this is not required. + * </p> + * <p> + * PointerIcons can be used via {@link Window#setPointerIcon(PointerIcon)}. + * </p> + */ + public static interface PointerIcon extends PixelRectangle { + /** + * Always neatly packed, i.e. width * bytes_per_pixel. + * <p> + * {@inheritDoc} + * </p> + */ + @Override + int getStride(); + + /** + * Always false, i.e. origin is TOP-LEFT. + * <p> + * {@inheritDoc} + * </p> + */ + boolean isGLOriented(); + + /** + * Computes a hash code over: + * <ul> + * <li>display</li> + * <li>pixelformat</li> + * <li>size</li> + * <li>stride</li> + * <li>isGLOriented</li> + * <li>pixels</li> + * <li>hotspot</li> + * </ul> + * Dismissing the native handle! + * <p> + * The hashCode shall be computed only once with first call + * and stored for later retrieval to enhance performance. + * </p> + * <p> + * {@inheritDoc} + * </p> + */ + @Override + int hashCode(); + + /** + * @return the associated Display + */ + Display getDisplay(); + + /** Returns the hotspot. */ + PointImmutable getHotspot(); + + /** + * Returns true if valid, otherwise false. + * <p> + * A PointerIcon instance becomes invalid if it's {@link #getDisplay() associated Display} is destroyed. + * </p> + */ + boolean isValid(); + + /** + * Returns true if instance {@link #isValid()} or validation was successful, otherwise false. + * <p> + * Validation, i.e. recreation, is required if instance became invalid, see {@link #isValid()}. + * </p> + */ + boolean validate(); + + /** + * Destroys this instance. + * <p> + * Will be called automatically if it's {@link #getDisplay() associated Display} is destroyed. + * </p> + */ + void destroy(); + } + + /** + * Returns the native platform's {@link PointerIcon.PixelFormat} for pointer-icon pixel data. + * <p> + * Using this value will avoid conversion within {@link #createPointerIcon(PixelRectangle, int, int)}. + * </p> + */ + public abstract PixelFormat getNativePointerIconPixelFormat(); + + /** + * Returns the native platform's direct NIO buffer requirement pointer-icon pixel data. + * <p> + * Using this value will avoid conversion within {@link #createPointerIcon(PixelRectangle, int, int)}. + * </p> + */ + public abstract boolean getNativePointerIconForceDirectNIO(); + + /** + * Returns the newly created {@link PointerIcon} or <code>null</code> if not implemented on platform. + * <p> + * See {@link PointerIcon} for lifecycle semantics. + * </p> + * + * @param pngResource single PNG resource for the {@link PointerIcon}. Only the first entry of {@link IOUtil.ClassResources#resourcePaths} is used. + * @param hotX pointer hotspot x-coord, origin is upper-left corner + * @param hotY pointer hotspot y-coord, origin is upper-left corner + * + * @throws IllegalArgumentException if pngResource is null or invalid + * @throws IllegalStateException if this Display instance is not {@link #isNativeValid() valid yet}. + * @throws IOException if the <code>pngResource</code> could not be {@link IOUtil.ClassResources#resolve(int) resolved} + * or via the PNG parser processing the input stream. + * + * @see PointerIcon + * @see Window#setPointerIcon(PointerIcon) + */ + public abstract PointerIcon createPointerIcon(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) + throws IllegalArgumentException, IllegalStateException, IOException; + + /** + * Returns the newly created {@link PointerIcon} or <code>null</code> if not implemented on platform. + * <p> + * See {@link PointerIcon} for lifecycle semantics. + * </p> + * <p> + * In case {@link #getNativePointerIconPixelFormat()} or {@link #getNativePointerIconForceDirectNIO()} + * is not matched by the given <code>pixelrect</code>, the <code>pixelrect</code> is converted + * into the required {@link PixelFormat} and NIO type. + * </p> + * + * @param pixelrect {@link PixelRectangle} source for the {@link PointerIcon} + * @param hotX pointer hotspot x-coord, origin is upper-left corner + * @param hotY pointer hotspot y-coord, origin is upper-left corner + * + * @throws IllegalArgumentException if pixelrect is null. + * @throws IllegalStateException if this Display instance is not {@link #isNativeValid() valid yet}. + * + * @see PointerIcon + * @see Window#setPointerIcon(PointerIcon) + * @see #getNativePointerIconPixelFormat() + * @see #getNativePointerIconForceDirectNIO() + */ + public abstract PointerIcon createPointerIcon(final PixelRectangle pixelrect, final int hotX, final int hotY) throws IllegalArgumentException, IllegalStateException; + + /** * Manual trigger the native creation, if it is not done yet.<br> * This is useful to be able to request the {@link javax.media.nativewindow.AbstractGraphicsDevice}, via * {@link #getGraphicsDevice()}.<br> @@ -77,7 +250,7 @@ public abstract class Display { * Stop the running EDT in case this display is destroyed already.<br> * @return true if EDT has been stopped (destroyed but running), otherwise false. */ - public abstract boolean validateEDT(); + public abstract boolean validateEDTStopped(); /** * @return true if the native display handle is valid and ready to operate, @@ -88,7 +261,7 @@ public abstract class Display { public abstract boolean isNativeValid(); /** - * @return number of references by Screen + * @return number of references */ public abstract int getReferenceCount(); @@ -96,7 +269,7 @@ public abstract class Display { * The 1st call will initiate native creation, * since we follow the lazy creation pattern. * - * @return number of references after adding one + * @return number of references post operation * @throws NativeWindowException if the native creation failed. * @see #removeReference() */ @@ -106,31 +279,37 @@ public abstract class Display { * The last call may destroy this instance, * if {@link #getDestroyWhenUnused()} returns <code>true</code>. * - * @return number of references after removing one + * @return number of references post operation * @see #addReference() * @see #getDestroyWhenUnused() * @see #setDestroyWhenUnused(boolean) */ public abstract int removeReference(); + /** + * Return the {@link AbstractGraphicsDevice} used for depending resources lifecycle, + * i.e. {@link Screen} and {@link Window}, as well as the event dispatching (EDT). */ public abstract AbstractGraphicsDevice getGraphicsDevice(); /** - * @return the fully qualified Display name, - * which is a key of {@link #getType()} + {@link #getName()} + {@link #getId()} + * Return the handle of the {@link AbstractGraphicsDevice} as returned by {@link #getGraphicsDevice()}. */ - public abstract String getFQName(); - public abstract long getHandle(); /** + * @return The fully qualified Display name, + * which is a key of {@link #getType()} + {@link #getName()} + {@link #getId()}. + */ + public abstract String getFQName(); + + /** * @return this display internal serial id */ public abstract int getId(); /** - * @return This display connection name as defined at creation time. - * The display connection name is a technical platform specific detail, see {@link AbstractGraphicsDevice#getConnection()}. + * @return This display connection name as defined at creation time. + * The display connection name is a technical platform specific detail, see {@link AbstractGraphicsDevice#getConnection()}. * * @see AbstractGraphicsDevice#getConnection() */ @@ -141,36 +320,60 @@ public abstract class Display { */ public abstract String getType(); + /** Return true if this instance is exclusive, i.e. will not be shared. */ + public abstract boolean isExclusive(); + + /** + * Sets a new {@link EDTUtil} and returns the previous one. + * <p> + * If <code>usrEDTUtil</code> is <code>null</code>, + * the device's default EDTUtil is created and used. + * </p> + * <p> + * If a previous one exists and it differs from <code>usrEDTUtil</code>, + * it's being stopped, wait-until-idle. + * </p> + * <p> + * If <code>usrEDTUtil</code> is not null and equals the previous one, + * no change is being made. + * </p> + */ + public abstract EDTUtil setEDTUtil(EDTUtil usrEDTUtil); + public abstract EDTUtil getEDTUtil(); + /** + * @return true if EDT is running and not subject to be stopped, otherwise false. + */ public abstract boolean isEDTRunning(); public abstract void dispatchMessages(); - + // Global Displays - protected static ArrayList<Display> displayList = new ArrayList<Display>(); + protected static final ArrayList<WeakReference<Display>> displayList = new ArrayList<WeakReference<Display>>(); protected static int displaysActive = 0; public static void dumpDisplayList(String prefix) { synchronized(displayList) { - Iterator<Display> i = displayList.iterator(); System.err.println(prefix+" DisplayList[] entries: "+displayList.size()+" - "+getThreadName()); - for(int j=0; i.hasNext(); j++) { - Display d = i.next(); - System.err.println(" ["+j+"] : "+d); + final Iterator<WeakReference<Display>> ri = displayList.iterator(); + for(int j=0; ri.hasNext(); j++) { + final Display d = ri.next().get(); + System.err.println(" ["+j+"] : "+d+", GC'ed "+(null==d)); } } } /** - * + * * @param type * @param name - * @param fromIndex start index, then increasing until found or end of list * - * @return + * @param fromIndex start index, then increasing until found or end of list + * @paran shared if true, only shared instances are found, otherwise also exclusive + * @return */ - public static Display getFirstDisplayOf(String type, String name, int fromIndex) { - return getDisplayOfImpl(type, name, fromIndex, 1); + public static Display getFirstDisplayOf(String type, String name, int fromIndex, boolean shared) { + return getDisplayOfImpl(type, name, fromIndex, 1, shared); } /** @@ -178,33 +381,69 @@ public abstract class Display { * @param type * @param name * @param fromIndex start index, then decreasing until found or end of list. -1 is interpreted as size - 1. + * @paran shared if true, only shared instances are found, otherwise also exclusive * @return */ - public static Display getLastDisplayOf(String type, String name, int fromIndex) { - return getDisplayOfImpl(type, name, fromIndex, -1); + public static Display getLastDisplayOf(String type, String name, int fromIndex, boolean shared) { + return getDisplayOfImpl(type, name, fromIndex, -1, shared); } - private static Display getDisplayOfImpl(String type, String name, int fromIndex, int incr) { + private static Display getDisplayOfImpl(String type, String name, final int fromIndex, final int incr, boolean shared) { synchronized(displayList) { int i = fromIndex >= 0 ? fromIndex : displayList.size() - 1 ; while( ( incr > 0 ) ? i < displayList.size() : i >= 0 ) { - Display display = (Display) displayList.get(i); - if( display.getType().equals(type) && - display.getName().equals(name) ) { - return display; + final Display display = displayList.get(i).get(); + if( null == display ) { + // Clear GC'ed dead reference entry! + displayList.remove(i); + if( incr < 0 ) { + // decrease + i+=incr; + } // else nop - remove shifted subsequent elements to the left + } else { + if( display.getType().equals(type) && + display.getName().equals(name) && + ( !shared || shared && !display.isExclusive() ) + ) { + return display; + } + i+=incr; } - i+=incr; } } return null; } + protected static void addDisplay2List(Display display) { + synchronized(displayList) { + // GC before add + int i=0; + while( i < displayList.size() ) { + if( null == displayList.get(i).get() ) { + displayList.remove(i); + } else { + i++; + } + } + displayList.add(new WeakReference<Display>(display)); + } + } + /** Returns the global display collection */ - @SuppressWarnings("unchecked") public static Collection<Display> getAllDisplays() { ArrayList<Display> list; synchronized(displayList) { - list = (ArrayList<Display>) displayList.clone(); + list = new ArrayList<Display>(); + int i = 0; + while( i < displayList.size() ) { + final Display d = displayList.get(i).get(); + if( null == d ) { + displayList.remove(i); + } else { + list.add( displayList.get(i).get() ); + i++; + } + } } return list; } diff --git a/src/newt/classes/com/jogamp/newt/MonitorDevice.java b/src/newt/classes/com/jogamp/newt/MonitorDevice.java new file mode 100644 index 000000000..28d9f53a1 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/MonitorDevice.java @@ -0,0 +1,239 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.newt; + +import java.util.List; + +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.Rectangle; +import javax.media.nativewindow.util.RectangleImmutable; + +import com.jogamp.common.util.ArrayHashSet; + +/** + * Visual output device, i.e. a CRT, LED ..consisting of it's components:<br> + * <ui> + * <li>Immutable + * <ul> + * <li>nativeId</li> + * <li>{@link DimensionImmutable} size in [mm]</li> + * <li>{@link MonitorMode} original mode</li> + * <li><code>List<MonitorMode></code> supportedModes</li> + * </ul></li> + * <li>Mutable + * <ul> + * <li>{@link MonitorMode} current mode</li> + * <li>{@link RectangleImmutable} viewport (rotated)</li> + * </ul></li> + * </ul> + */ +public abstract class MonitorDevice { + protected final Screen screen; // backref + protected final int nativeId; // unique monitor device ID + protected final DimensionImmutable sizeMM; // in [mm] + protected final MonitorMode originalMode; + protected final ArrayHashSet<MonitorMode> supportedModes; // FIXME: May need to support mutable mode, i.e. adding modes on the fly! + protected MonitorMode currentMode; + protected boolean modeChanged; + protected Rectangle viewport; + + protected MonitorDevice(Screen screen, int nativeId, DimensionImmutable sizeMM, Rectangle viewport, MonitorMode currentMode, ArrayHashSet<MonitorMode> supportedModes) { + this.screen = screen; + this.nativeId = nativeId; + this.sizeMM = sizeMM; + this.originalMode = currentMode; + this.supportedModes = supportedModes; + this.currentMode = currentMode; + this.viewport = viewport; + this.modeChanged = false; + } + + /** Returns the {@link Screen} owning this monitor. */ + public final Screen getScreen() { + return screen; + } + + /** + * Tests equality of two <code>MonitorDevice</code> objects + * by evaluating equality of it's components:<br> + * <ul> + * <li><code>nativeID</code></li> + * </ul> + * <br> + */ + @Override + public final boolean equals(Object obj) { + if (this == obj) { return true; } + if (obj instanceof MonitorDevice) { + MonitorDevice md = (MonitorDevice)obj; + return md.nativeId == nativeId; + } + return false; + } + + /** + * Returns a combined hash code of it's elements:<br> + * <ul> + * <li><code>nativeID</code></li> + * </ul> + */ + @Override + public final int hashCode() { + return nativeId; + } + + /** @return the immutable unique native Id of this monitor device. */ + public final int getId() { return nativeId; } + + /** + * @return the immutable monitor size in millimeters. + */ + public final DimensionImmutable getSizeMM() { + return sizeMM; + } + + /** + * Returns the immutable original {@link com.jogamp.newt.MonitorMode}, as used at NEWT initialization. + * <p> + * The returned {@link MonitorMode} is element of the lists {@link #getSupportedModes()} and {@link Screen#getMonitorModes()}. + * </p> + */ + public final MonitorMode getOriginalMode() { + return originalMode; + } + + /** + * Returns a list of immutable {@link MonitorMode}s supported by this monitor. + * <p> + * The list is ordered in descending order, + * see {@link MonitorMode#compareTo(MonitorMode)}. + * </p> + * <p> + * Use w/ care, it's not a copy! + * </p> + */ + public final List<MonitorMode> getSupportedModes() { + return supportedModes.getData(); + } + + /** Returns the {@link RectangleImmutable rectangular} portion of the rotated virtual {@link Screen} size represented by this monitor. */ + public final RectangleImmutable getViewport() { + return viewport; + } + + /** Returns <code>true</code> if given coordinates are contained by this {@link #getViewport() viewport}, otherwise <code>false</code>. */ + public final boolean contains(int x, int y) { + return x >= viewport.getX() && + x < viewport.getX() + viewport.getWidth() && + y >= viewport.getY() && + y < viewport.getY() + viewport.getHeight() ; + } + + /** + * Returns the coverage of given rectangle w/ this this {@link #getViewport() viewport}, i.e. between <code>0.0</code> and <code>1.0</code>. + * <p> + * Coverage is computed by: + * <pre> + * isect = viewport.intersection(r); + * coverage = area( isect ) / area( viewport ) ; + * </pre> + * </p> + */ + public final float coverage(RectangleImmutable r) { + return viewport.coverage(r); + } + + /** + * Returns the union of the given monitor's {@link #getViewport() viewport}. + * @param result storage for result, will be returned + * @param monitors given list of monitors + * @return viewport representing the union of given monitor's viewport. + */ + public static Rectangle unionOfViewports(final Rectangle result, final List<MonitorDevice> monitors) { + int x1=Integer.MAX_VALUE, y1=Integer.MAX_VALUE; + int x2=Integer.MIN_VALUE, y2=Integer.MIN_VALUE; + for(int i=monitors.size()-1; i>=0; i--) { + final RectangleImmutable vp = monitors.get(i).getViewport(); + x1 = Math.min(x1, vp.getX()); + x2 = Math.max(x2, vp.getX() + vp.getWidth()); + y1 = Math.min(y1, vp.getY()); + y2 = Math.max(y2, vp.getY() + vp.getHeight()); + } + result.set(x1, y1, x2 - x1, y2 - y1); + return result; + } + + public final boolean isOriginalMode() { + return currentMode.hashCode() == originalMode.hashCode(); + } + + /** + * Returns <code>true</true> if the {@link MonitorMode} + * has been changed programmatic via this API <i>only</i>, otherwise <code>false</code>. + * <p> + * Note: We cannot guarantee that we won't interfere w/ another running + * application's screen mode change or vice versa. + * </p> + */ + public final boolean isModeChangedByUs() { + return modeChanged && !isOriginalMode(); + } + + /** + * Returns the cached current {@link MonitorMode} w/o native query. + * <p> + * The returned {@link MonitorMode} is element of the lists {@link #getSupportedModes()} and {@link Screen#getMonitorModes()}. + * </p> + */ + public final MonitorMode getCurrentMode() { + return currentMode; + } + + /** + * Returns the current {@link MonitorMode} resulting from a native query. + * <p> + * The returned {@link MonitorMode} is element of the lists {@link #getSupportedModes()} and {@link Screen#getMonitorModes()}. + * </p> + */ + public abstract MonitorMode queryCurrentMode(); + + /** + * Set the current {@link com.jogamp.newt.MonitorMode}. + * @param mode to be made current, must be element of the list {@link #getSupportedModes()} and {@link Screen#getMonitorModes()}. + * @return true if successful, otherwise false + */ + public abstract boolean setCurrentMode(MonitorMode mode); + + @Override + public String toString() { + return "Monitor[Id "+Display.toHexString(nativeId)+", "+sizeMM+" mm, viewport "+viewport+ ", orig "+originalMode+", curr "+currentMode+ + ", modeChanged "+modeChanged+", modeCount "+supportedModes.size()+"]"; + } +} + diff --git a/src/newt/classes/com/jogamp/newt/MonitorMode.java b/src/newt/classes/com/jogamp/newt/MonitorMode.java new file mode 100644 index 000000000..9690f18db --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/MonitorMode.java @@ -0,0 +1,458 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.newt; + +import java.util.Comparator; + +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.RectangleImmutable; +import javax.media.nativewindow.util.SurfaceSize; + +import com.jogamp.newt.util.MonitorModeUtil; + + +/** + * Immutable MonitorMode Class, consisting of it's read only components:<br> + * <ul> + * <li>nativeId</li> + * <li>{@link SizeAndRRate}, consist out of non rotated {@link #getSurfaceSize() surface size}, {@link #getRefreshRate() refresh rate} and {@link #getFlags() flags}.</li> + * <li><code>rotation</code>, measured counter clockwise (CCW)</li> + * </ul> + * + * <i>Aquire and filter MonitorMode</i><br> + * <ul> + * <li>{@link MonitorDevice} Selection: + * <ul> + * <li>A List of all {@link MonitorDevice}s is accessible via {@link Screen#getMonitorDevices()}.</li> + * <li>The main monitor used by a windows is accessible via {@link Window#getMainMonitor()}.</li> + * <li>The main monitor covering an arbitrary rectnagle is accessible via {@link Screen#getMainMonitor(RectangleImmutable)}.</li> + * </ul></li> + * <li>The current MonitorMode can be obtained via {@link MonitorDevice#getCurrentMode()}.</li> + * <li>The original MonitorMode can be obtained via {@link MonitorDevice#getOriginalMode()}.</li> + * <li>{@link MonitorMode} Filtering: + * <ul> + * <li>A {@link MonitorDevice}'s MonitorModes is accessible via {@link MonitorDevice#getSupportedModes()}.</li> + * <li>You may utilize {@link MonitorModeUtil} to filter and select a desired MonitorMode.</li> + * </ul></li> + * </ul> + * <br> + * + * <i>Changing MonitorMode</i><br> + * <ul> + * <li> Use {@link MonitorDevice#setCurrentMode(MonitorMode)} + * to change the current MonitorMode for all {@link Screen}s referenced via the {@link Screen#getFQName() full qualified name (FQN)}.</li> + * <li> The {@link MonitorDevice#getOriginalMode() original mode} is restored when + * <ul> + * <li>the last FQN referenced Screen closes.</li> + * <li>the JVM shuts down.</li> + * </ul></li> + * </ul> + * <br> + * Example for changing the MonitorMode: + * <pre> + // Pick the monitor: + // Either the one used by a window .. + MonitorDevice monitor = window.getMainMonitor(); + + // Or arbitrary from the list .. + List<MonitorDevice> allMonitor = getMonitorDevices(); + MonitorDevice monitor = allMonitor.get(0); + + // Current and original modes .. + MonitorMode mmCurrent = monitor.queryCurrentMode(); + MonitorMode mmOrig = monitor.getOriginalMode(); + + // Target resolution + Dimension res = new Dimension(800, 600); + + // Target refresh rate shall be similar to current one .. + float freq = mmCurrent.getRefreshRate(); + + // Target rotation shall be similar to current one + int rot = mmCurrent.getRotation(); + + // Filter criterias sequential out of all available MonitorMode of the chosen MonitorDevice + List<MonitorMode> monitorModes = monitor.getSupportedModes(); + monitorModes = MonitorModeUtil.filterByFlags(monitorModes, 0); // no interlace, double-scan etc + monitorModes = MonitorModeUtil.filterByRotation(monitorModes, rot); + monitorModes = MonitorModeUtil.filterByResolution(monitorModes, res); + monitorModes = MonitorModeUtil.filterByRate(monitorModes, freq); + monitorModes = MonitorModeUtil.getHighestAvailableBpp(monitorModes); + + // pick 1st one and set to current .. + MonitorMode mm = monitorModes.get(0); + monitor.setCurrentMode(mm); + * </pre> + */ +public class MonitorMode implements Comparable<MonitorMode> { + + /** Comparator for 2 {@link MonitorMode}s, following comparison order as described in {@link MonitorMode#compareTo(MonitorMode)}, returning the ascending order. */ + public static final Comparator<MonitorMode> monitorModeComparator = new Comparator<MonitorMode>() { + @Override + public int compare(MonitorMode mm1, MonitorMode mm2) { + return mm1.compareTo(mm2); + } }; + + /** Comparator for 2 {@link MonitorMode}s, following comparison order as described in {@link MonitorMode#compareTo(MonitorMode)}, returning the descending order. */ + public static final Comparator<MonitorMode> monitorModeComparatorInv = new Comparator<MonitorMode>() { + @Override + public int compare(MonitorMode mm1, MonitorMode mm2) { + return mm2.compareTo(mm1); + } }; + + /** + * Immutable <i>surfaceSize, flags and refreshRate</i> Class, consisting of it's read only components:<br> + * <ul> + * <li>nativeId</li> + * <li>{@link SurfaceSize} surface memory size</li> + * <li><code>flags</code></li> + * <li><code>refresh rate</code></li> + * </ul> + */ + public static class SizeAndRRate implements Comparable<SizeAndRRate> { + /** Non rotated surface size */ + public final SurfaceSize surfaceSize; + /** Mode bitfield flags, i.e. {@link #FLAG_DOUBLESCAN}, {@link #FLAG_INTERLACE}, .. */ + public final int flags; + /** Vertical refresh rate */ + public final float refreshRate; + public final int hashCode; + + public SizeAndRRate(SurfaceSize surfaceSize, float refreshRate, int flags) { + if(null==surfaceSize) { + throw new IllegalArgumentException("surfaceSize must be set ("+surfaceSize+")"); + } + this.surfaceSize=surfaceSize; + this.flags = flags; + this.refreshRate=refreshRate; + this.hashCode = getHashCode(); + } + + private final static String STR_INTERLACE = "Interlace"; + private final static String STR_DOUBLESCAN = "DoubleScan"; + private final static String STR_SEP = ", "; + + public static final StringBuilder flags2String(int flags) { + final StringBuilder sb = new StringBuilder(); + boolean sp = false; + if( 0 != ( flags & FLAG_INTERLACE ) ) { + sb.append(STR_INTERLACE); + sp = true; + } + if( 0 != ( flags & FLAG_DOUBLESCAN ) ) { + if( sp ) { + sb.append(STR_SEP); + } + sb.append(STR_DOUBLESCAN); + sp = true; + } + return sb; + } + @Override + public final String toString() { + return surfaceSize+" @ "+refreshRate+" Hz, flags ["+flags2String(flags).toString()+"]"; + } + + /** + * <p> + * Compares {@link SurfaceSize#compareTo(SurfaceSize) surfaceSize} 1st, then {@link #flags}, then {@link #refreshRate}. + * </p> + * <p> + * Flags are compared as follows: + * <pre> + * NONE > DOUBLESCAN > INTERLACE + * </pre> + * </p> + * <p> + * Refresh rate differences of < 0.01 are considered equal (epsilon). + * </p> + * {@inheritDoc} + */ + @Override + public int compareTo(final SizeAndRRate sszr) { + final int rssz = surfaceSize.compareTo(sszr.surfaceSize); + if( 0 != rssz ) { + return rssz; + } + final int tflags = 0 == flags ? Integer.MAX_VALUE : flags; // normalize NONE + final int xflags = 0 == sszr.flags ? Integer.MAX_VALUE : sszr.flags; // normalize NONE + if( tflags == xflags ) { + final float refreshEpsilon = 0.01f; // reasonable sorting granularity of refresh rate + final float drate = refreshRate - sszr.refreshRate; + if( Math.abs(drate) < refreshEpsilon ) { + return 0; + } else if( drate > refreshEpsilon ) { + return 1; + } else { + return -1; + } + } else { + if(tflags > xflags) { + return 1; + } else if(tflags < xflags) { + return -1; + } + return 0; + } + } + + /** + * Tests equality of two {@link SizeAndRRate} objects + * by evaluating equality of it's components:<br/> + * <ul> + * <li><code>surfaceSize</code></li> + * <li><code>refreshRate</code></li> + * <li><code>flags</code></li> + * </ul> + */ + @Override + public final boolean equals(Object obj) { + if (this == obj) { return true; } + if (obj instanceof SizeAndRRate) { + final SizeAndRRate p = (SizeAndRRate)obj; + return surfaceSize.equals(p.surfaceSize) && + flags == p.flags && + refreshRate == p.refreshRate ; + } + return false; + } + + /** + * Returns a combined hash code of it's elements:<br/> + * <ul> + * <li><code>surfaceSize</code></li> + * <li><code>flags</code></li> + * <li><code>refreshRate</code></li> + * </ul> + */ + @Override + public final int hashCode() { + return hashCode; + } + private final int getHashCode() { + // 31 * x == (x << 5) - x + int hash = 31 + surfaceSize.hashCode(); + hash = ((hash << 5) - hash) + flags; + hash = ((hash << 5) - hash) + (int)(refreshRate*100.0f); + return hash; + } + } + + /** zero rotation, compared to normal settings */ + public static final int ROTATE_0 = 0; + + /** 90 degrees CCW rotation */ + public static final int ROTATE_90 = 90; + + /** 180 degrees CCW rotation */ + public static final int ROTATE_180 = 180; + + /** 270 degrees CCW rotation */ + public static final int ROTATE_270 = 270; + + /** Frame is split into two fields. See {@link #getFlags()}. */ + public static final int FLAG_INTERLACE = 1 << 0; + + /** Lines are doubled. See {@link #getFlags()}. */ + public static final int FLAG_DOUBLESCAN = 1 << 1; + + /** The immutable native Id of this instance, which may not be unique. */ + private final int nativeId; + private final SizeAndRRate sizeAndRRate; + private final int rotation; + private final int hashCode; + + public static boolean isRotationValid(int rotation) { + return rotation == MonitorMode.ROTATE_0 || rotation == MonitorMode.ROTATE_90 || + rotation == MonitorMode.ROTATE_180 || rotation == MonitorMode.ROTATE_270 ; + } + + /** + * @param sizeAndRRate the surface size and refresh rate mode + * @param rotation the screen rotation, measured counter clockwise (CCW) + */ + public MonitorMode(int nativeId, SizeAndRRate sizeAndRRate, int rotation) { + if ( !isRotationValid(rotation) ) { + throw new RuntimeException("invalid rotation: "+rotation); + } + this.nativeId = nativeId; + this.sizeAndRRate = sizeAndRRate; + this.rotation = rotation; + this.hashCode = getHashCode(); + } + + /** + * Creates a user instance w/o {@link #getId() identity} to filter our matching modes w/ identity. + * <p> + * See {@link com.jogamp.newt.util.MonitorModeUtil} for filter utilities. + * </p> + * @param surfaceSize + * @param refreshRate + * @param flags + * @param rotation + */ + public MonitorMode(SurfaceSize surfaceSize, float refreshRate, int flags, int rotation) { + this(0, new SizeAndRRate(surfaceSize, refreshRate, flags), rotation); + } + + /** @return the immutable native Id of this mode, may not be unique, may be 0. */ + public final int getId() { return nativeId; } + + /** Returns the <i>surfaceSize and refreshRate</i> instance. */ + public final SizeAndRRate getSizeAndRRate() { + return sizeAndRRate; + } + + /** Returns the unrotated {@link SurfaceSize} */ + public final SurfaceSize getSurfaceSize() { + return sizeAndRRate.surfaceSize; + } + + /** Returns the vertical refresh rate. */ + public final float getRefreshRate() { + return sizeAndRRate.refreshRate; + } + + /** Returns bitfield w/ flags, i.e. {@link #FLAG_DOUBLESCAN}, {@link #FLAG_INTERLACE}, .. */ + public final int getFlags() { + return sizeAndRRate.flags; + } + + /** Returns the CCW rotation of this mode */ + public final int getRotation() { + return rotation; + } + + /** Returns the rotated screen width, + * derived from <code>getMonitorMode().getSurfaceSize().getResolution()</code> + * and <code>getRotation()</code> + */ + public final int getRotatedWidth() { + return getRotatedWH(true); + } + + /** Returns the rotated screen height, + * derived from <code>getMonitorMode().getSurfaceSize().getResolution()</code> + * and <code>getRotation()</code> + */ + public final int getRotatedHeight() { + return getRotatedWH(false); + } + + @Override + public final String toString() { + return "[Id "+Display.toHexString(nativeId)+", " + sizeAndRRate + ", " + rotation + " degr]"; + } + + /** + * <p> + * Compares {@link SizeAndRRate#compareTo(SizeAndRRate) sizeAndRRate} 1st, then {@link #rotation}. + * </p> + * <p> + * Rotation is compared inverted, i.e. <code>360 - rotation</code>, + * so the lowest rotation reflects a higher value. + * </p> + * <p> + * Order of comparing MonitorMode: + * <ul> + * <li>resolution</li> + * <li>bits per pixel</li> + * <li>flags</li> + * <li>refresh rate</li> + * <li>rotation</li> + * </ul> + * </p> + * {@inheritDoc} + */ + @Override + public int compareTo(final MonitorMode mm) { + final int c = sizeAndRRate.compareTo(mm.sizeAndRRate); + if( 0 != c ) { + return c; + } + final int trot = 360 - rotation; // normalize rotation + final int xrot = 360 - mm.rotation; // normalize rotation + if(trot > xrot) { + return 1; + } else if(trot < xrot) { + return -1; + } + return 0; + } + + /** + * Tests equality of two {@link MonitorMode} objects + * by evaluating equality of it's components:<br/> + * <ul> + * <li><code>nativeId</code></li> + * <li><code>sizeAndRRate</code></li> + * <li><code>rotation</code></li> + * </ul> + */ + @Override + public final boolean equals(Object obj) { + if (this == obj) { return true; } + if (obj instanceof MonitorMode) { + MonitorMode sm = (MonitorMode)obj; + return sm.nativeId == this.nativeId && + sm.sizeAndRRate.equals(sizeAndRRate) && + sm.rotation == this.rotation ; + } + return false; + } + + /** + * Returns a combined hash code of it's elements:<br/> + * <ul> + * <li><code>nativeId</code></li> + * <li><code>sizeAndRRate</code></li> + * <li><code>rotation</code></li> + * </ul> + */ + @Override + public final int hashCode() { + return hashCode; + } + private final int getHashCode() { + // 31 * x == (x << 5) - x + int hash = 31 + getId(); + hash = ((hash << 5) - hash) + sizeAndRRate.hashCode(); + hash = ((hash << 5) - hash) + getRotation(); + return hash; + } + + private final int getRotatedWH(boolean width) { + final DimensionImmutable d = sizeAndRRate.surfaceSize.getResolution(); + final boolean swap = MonitorMode.ROTATE_90 == rotation || MonitorMode.ROTATE_270 == rotation ; + if ( ( width && swap ) || ( !width && !swap ) ) { + return d.getHeight(); + } + return d.getWidth(); + } +} diff --git a/src/newt/classes/com/jogamp/newt/NewtFactory.java b/src/newt/classes/com/jogamp/newt/NewtFactory.java index 61dbfb34c..9685200eb 100644 --- a/src/newt/classes/com/jogamp/newt/NewtFactory.java +++ b/src/newt/classes/com/jogamp/newt/NewtFactory.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,11 +29,15 @@ * 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.jogamp.newt; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; + import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.AbstractGraphicsScreen; @@ -41,6 +45,8 @@ import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowFactory; +import com.jogamp.common.util.IOUtil; + import jogamp.newt.Debug; import jogamp.newt.DisplayImpl; import jogamp.newt.ScreenImpl; @@ -49,17 +55,57 @@ import jogamp.newt.WindowImpl; public class NewtFactory { public static final boolean DEBUG_IMPLEMENTATION = Debug.debug("Window"); - // Work-around for initialization order problems on Mac OS X - // between native Newt and (apparently) Fmod + public static final String DRIVER_DEFAULT_ROOT_PACKAGE = "jogamp.newt.driver"; + + private static IOUtil.ClassResources defaultWindowIcons; + static { - NativeWindowFactory.initSingleton(false); // last resort .. - WindowImpl.init(NativeWindowFactory.getNativeWindowType(true)); + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + NativeWindowFactory.initSingleton(); // last resort .. + { + /** See API Doc in {@link Window} ! */ + final String[] paths = Debug.getProperty("newt.window.icons", true, "newt/data/jogamp-16x16.png newt/data/jogamp-32x32.png").split("\\s"); + if( paths.length < 2 ) { + throw new IllegalArgumentException("Property 'newt.window.icons' did not specify at least two PNG icons, but "+Arrays.toString(paths)); + } + final Class<?> clazz = NewtFactory.class; + defaultWindowIcons = new IOUtil.ClassResources(clazz, paths); + } + return null; + } } ); } + /** + * Returns the application window icon resources to be used. + * <p> + * Property <code>newt.window.icons</code> may define a list of PNG icons separated by a whitespace character. + * Shall reference at least two PNG icons, from lower (16x16) to higher (>= 32x32) resolution. + * </p> + * <p> + * Users may also specify application window icons using {@link #setWindowIcons(com.jogamp.common.util.IOUtil.ClassResources)}. + * </p> + */ + public static IOUtil.ClassResources getWindowIcons() { return defaultWindowIcons; } + + /** + * Allow user to set custom window icons, only applicable at application start before creating any NEWT instance. + * <p> + * Shall reference at least two PNG icons, from lower (16x16) to higher (>= 32x32) resolution. + * </p> + */ + public static void setWindowIcons(IOUtil.ClassResources cres) { defaultWindowIcons = cres; } + public static Class<?> getCustomClass(String packageName, String classBaseName) { Class<?> clazz = null; if(packageName!=null && classBaseName!=null) { - final String clazzName = packageName + "." + classBaseName ; + final String clazzName; + if( packageName.startsWith(".") ) { + clazzName = DRIVER_DEFAULT_ROOT_PACKAGE + packageName + "." + classBaseName ; + } else { + clazzName = packageName + "." + classBaseName ; + } try { clazz = Class.forName(clazzName); } catch (Throwable t) { @@ -74,7 +120,7 @@ public class NewtFactory { 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> @@ -92,7 +138,7 @@ public class NewtFactory { * Native creation is lazily done at usage, ie. {@link Display#addReference()}. * </p> * <p> - * An already existing display connection of the same <code>name</code> will be reused. + * An already existing display connection of the same <code>name</code> will be reused. * </p> * @param name the display connection name which is a technical platform specific detail, * see {@link AbstractGraphicsDevice#getConnection()}. Use <code>null</code> for default. @@ -198,7 +244,7 @@ public class NewtFactory { * </p> */ public static Window createWindow(Screen screen, CapabilitiesImmutable caps) { - return createWindowImpl(screen, caps); + return WindowImpl.create(null, 0, screen, caps); } /** @@ -225,6 +271,9 @@ public class NewtFactory { */ public static Window createWindow(NativeWindow parentWindow, CapabilitiesImmutable caps) { final String type = NativeWindowFactory.getNativeWindowType(true); + if( null == parentWindow ) { + return createWindowImpl(type, caps); + } Screen screen = null; Window newtParentWindow = null; @@ -245,7 +294,7 @@ public class NewtFactory { screen = NewtFactory.createScreen(display, 0); // screen 0 } } - final Window win = createWindowImpl(parentWindow, screen, caps); + final Window win = WindowImpl.create(parentWindow, 0, screen, caps); win.setSize(parentWindow.getWidth(), parentWindow.getHeight()); if ( null != newtParentWindow ) { @@ -255,19 +304,7 @@ public class NewtFactory { return win; } - protected static Window createWindowImpl(NativeWindow parentNativeWindow, Screen screen, CapabilitiesImmutable caps) { - return WindowImpl.create(parentNativeWindow, 0, screen, caps); - } - - protected static Window createWindowImpl(long parentWindowHandle, Screen screen, CapabilitiesImmutable caps) { - return WindowImpl.create(null, parentWindowHandle, screen, caps); - } - - protected static Window createWindowImpl(Screen screen, CapabilitiesImmutable caps) { - return WindowImpl.create(null, 0, screen, caps); - } - - protected static Window createWindowImpl(String type, CapabilitiesImmutable caps) { + private static Window createWindowImpl(String type, CapabilitiesImmutable caps) { Display display = NewtFactory.createDisplay(type, null, true); // local display Screen screen = NewtFactory.createScreen(display, 0); // screen 0 return WindowImpl.create(null, 0, screen, caps); @@ -276,11 +313,17 @@ public class NewtFactory { /** * Create a child Window entity attached to the given parent, incl native creation<br> * - * @param parentWindowObject the native parent window handle - * @param undecorated only impacts if the window is in top-level state, while attached to a parent window it's rendered undecorated always + * @param displayConnection the parent window's display connection + * @param screenIdx the desired screen index + * @param parentWindowHandle the native parent window handle + * @param caps the desired capabilities + * @return */ - public static Window createWindow(long parentWindowHandle, Screen screen, CapabilitiesImmutable caps) { - return createWindowImpl(parentWindowHandle, screen, caps); + public static Window createWindow(String displayConnection, int screenIdx, long parentWindowHandle, CapabilitiesImmutable caps) { + final String type = NativeWindowFactory.getNativeWindowType(true); + Display display = NewtFactory.createDisplay(type, displayConnection, true); + Screen screen = NewtFactory.createScreen(display, screenIdx); + return WindowImpl.create(null, parentWindowHandle, screen, caps); } /** @@ -298,14 +341,14 @@ public class NewtFactory { * Instantiate a Display entity using the native handle. */ public static Display createDisplay(String type, long handle, boolean reuse) { - return DisplayImpl.create(type, null, handle, false); + return DisplayImpl.create(type, null, handle, reuse); } public static boolean isScreenCompatible(NativeWindow parent, Screen childScreen) { // Get parent's NativeWindow details - AbstractGraphicsConfiguration parentConfig = (AbstractGraphicsConfiguration) parent.getGraphicsConfiguration(); - AbstractGraphicsScreen parentScreen = (AbstractGraphicsScreen) parentConfig.getScreen(); - AbstractGraphicsDevice parentDevice = (AbstractGraphicsDevice) parentScreen.getDevice(); + AbstractGraphicsConfiguration parentConfig = parent.getGraphicsConfiguration(); + AbstractGraphicsScreen parentScreen = parentConfig.getScreen(); + AbstractGraphicsDevice parentDevice = parentScreen.getDevice(); DisplayImpl childDisplay = (DisplayImpl) childScreen.getDisplay(); String parentDisplayName = childDisplay.validateDisplayName(null, parentDevice.getHandle()); @@ -326,9 +369,9 @@ public class NewtFactory { public static Screen createCompatibleScreen(NativeWindow parent, Screen childScreen) { // Get parent's NativeWindow details - AbstractGraphicsConfiguration parentConfig = (AbstractGraphicsConfiguration) parent.getGraphicsConfiguration(); - AbstractGraphicsScreen parentScreen = (AbstractGraphicsScreen) parentConfig.getScreen(); - AbstractGraphicsDevice parentDevice = (AbstractGraphicsDevice) parentScreen.getDevice(); + AbstractGraphicsConfiguration parentConfig = parent.getGraphicsConfiguration(); + AbstractGraphicsScreen parentScreen = parentConfig.getScreen(); + AbstractGraphicsDevice parentDevice = parentScreen.getDevice(); if(null != childScreen) { // check if child Display/Screen is compatible already diff --git a/src/newt/classes/com/jogamp/newt/NewtVersion.java b/src/newt/classes/com/jogamp/newt/NewtVersion.java index 961ffdf6a..e70d63f88 100644 --- a/src/newt/classes/com/jogamp/newt/NewtVersion.java +++ b/src/newt/classes/com/jogamp/newt/NewtVersion.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt; import com.jogamp.common.GlueGenVersion; @@ -46,9 +46,10 @@ public class NewtVersion extends JogampVersion { if(null == jogampCommonVersionInfo) { // volatile: ok synchronized(NewtVersion.class) { if( null == jogampCommonVersionInfo ) { - final String packageName = "com.jogamp.newt"; - final Manifest mf = VersionUtil.getManifest(NewtVersion.class.getClassLoader(), packageName); - jogampCommonVersionInfo = new NewtVersion(packageName, mf); + final String packageName1 = "com.jogamp.newt"; // atomic packaging - and identity + final String packageName2 = "javax.media.opengl"; // all packaging + final Manifest mf = VersionUtil.getManifest(NativeWindowVersion.class.getClassLoader(), new String[]{ packageName1, packageName2 } ); + jogampCommonVersionInfo = new NewtVersion(packageName1, mf); } } } diff --git a/src/newt/classes/com/jogamp/newt/Screen.java b/src/newt/classes/com/jogamp/newt/Screen.java index 26f19ad6b..ef62ec95d 100644 --- a/src/newt/classes/com/jogamp/newt/Screen.java +++ b/src/newt/classes/com/jogamp/newt/Screen.java @@ -27,14 +27,21 @@ */ package com.jogamp.newt; -import com.jogamp.newt.event.ScreenModeListener; +import com.jogamp.newt.event.MonitorModeListener; import jogamp.newt.Debug; + +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.util.Rectangle; +import javax.media.nativewindow.util.RectangleImmutable; +/** + * A screen may span multiple {@link MonitorDevice}s representing their combined virtual size. + */ public abstract class Screen { /** @@ -46,9 +53,11 @@ public abstract class Screen { public static final boolean DEBUG = Debug.debug("Screen"); /** return precomputed hashCode from FQN {@link #getFQName()} */ + @Override public abstract int hashCode(); /** return true if obj is of type Display and both FQN {@link #getFQName()} equals */ + @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof Screen) { @@ -59,7 +68,7 @@ public abstract class Screen { } /** - * Manual trigger the native creation, if it is not done yet..<br> + * Manual trigger the native creation, if not done yet..<br> * This is useful to be able to request the {@link javax.media.nativewindow.AbstractGraphicsScreen}, via * {@link #getGraphicsScreen()}.<br> * Otherwise the abstract device won't be available before the dependent component (Window) is realized. @@ -89,13 +98,14 @@ public abstract class Screen { public abstract boolean isNativeValid(); /** - * @return number of references by Window + * @return number of references */ public abstract int getReferenceCount(); /** * See {@link Display#addReference()} * + * @return number of references post operation * @throws NativeWindowException if the native creation failed. * @see #removeReference() * @see #setDestroyWhenUnused(boolean) @@ -106,6 +116,7 @@ public abstract class Screen { /** * See {@link Display#removeReference()} * + * @return number of references post operation * @see #addReference() * @see #setDestroyWhenUnused(boolean) * @see #getDestroyWhenUnused() @@ -120,81 +131,104 @@ public abstract class Screen { public abstract int getIndex(); /** - * @return the x position of the virtual top-left origin. + * @return the x position of the virtual viewport's top-left origin. */ public abstract int getX(); - + /** - * @return the y position of the virtual top-left origin. + * @return the y position of the virtual viewport's top-left origin. */ public abstract int getY(); - + /** - * @return the <b>rotated</b> virtual width. + * @return the <b>rotated</b> virtual viewport's width. */ public abstract int getWidth(); /** - * @return the <b>rotated</b> virtual height. + * @return the <b>rotated</b> virtual viewport's height. */ public abstract int getHeight(); /** + * @return the <b>rotated</b> virtual viewport, i.e. origin and size. + */ + public abstract RectangleImmutable getViewport(); + + /** * @return the associated Display */ public abstract Display getDisplay(); - /** - * @return the screen fully qualified Screen name, + /** + * @return The screen fully qualified Screen name, * which is a key of {@link com.jogamp.newt.Display#getFQName()} + {@link #getIndex()}. */ public abstract String getFQName(); /** - * @param sml ScreenModeListener to be added for ScreenMode change events + * Return a list of all {@link MonitorMode}s for all {@link MonitorDevice}s. + * <p> + * The list is ordered in descending order, + * see {@link MonitorMode#compareTo(MonitorMode)}. + * </p> */ - public abstract void addScreenModeListener(ScreenModeListener sml); + public abstract List<MonitorMode> getMonitorModes(); /** - * @param sml ScreenModeListener to be removed from ScreenMode change events + * Return a list of available {@link MonitorDevice}s. */ - public abstract void removeScreenModeListener(ScreenModeListener sml); + public abstract List<MonitorDevice> getMonitorDevices(); - /** - * Return a list of available {@link com.jogamp.newt.ScreenMode ScreenMode}s. + /** + * Returns the {@link MonitorDevice} which {@link MonitorDevice#getViewport() viewport} + * {@link MonitorDevice#coverage(RectangleImmutable) covers} the given rectangle the most. * <p> - * If {@link com.jogamp.newt.ScreenMode ScreenMode}s are not supported for this - * native type {@link com.jogamp.newt.Display#getType()}, it returns a list of size one with the current screen size.</p> - * - * @return a shallow copy of the internal immutable {@link com.jogamp.newt.ScreenMode ScreenMode}s. + * If no coverage is detected the first {@link MonitorDevice} is returned. + * </p> */ - public abstract List<ScreenMode> getScreenModes(); + public final MonitorDevice getMainMonitor(RectangleImmutable r) { + MonitorDevice res = null; + float maxCoverage = Float.MIN_VALUE; + final List<MonitorDevice> monitors = getMonitorDevices(); + for(int i=monitors.size()-1; i>=0; i--) { + final MonitorDevice monitor = monitors.get(i); + final float coverage = monitor.coverage(r); + if( coverage > maxCoverage ) { + maxCoverage = coverage; + res = monitor; + } + } + if( maxCoverage > 0.0f && null != res ) { + return res; + } + return monitors.get(0); + } /** - * Return the original {@link com.jogamp.newt.ScreenMode}, as used at NEWT initialization. - * @return original ScreenMode which is element of the list {@link #getScreenModes()}. + * Returns the union of all monitor's {@link MonitorDevice#getViewport() viewport}. + * <p> + * Should be equal to {@link #getX()}, {@link #getY()}, {@link #getWidth()} and {@link #getHeight()}, + * however, some native toolkits may choose a different virtual screen area. + * </p> + * @param result storage for result, will be returned */ - public abstract ScreenMode getOriginalScreenMode(); + public final Rectangle unionOfMonitorViewportSize(final Rectangle result) { + return MonitorDevice.unionOfViewports(result, getMonitorDevices()); + } /** - * Return the current {@link com.jogamp.newt.ScreenMode}. - * <p> - * If {@link com.jogamp.newt.ScreenMode ScreenMode}s are not supported for this - * native type {@link com.jogamp.newt.Display#getType()}, it returns one with the current screen size. </p> - * - * @return current ScreenMode which is element of the list {@link #getScreenModes()}. + * @param sml {@link MonitorModeListener} to be added for {@link MonitorEvent} */ - public abstract ScreenMode getCurrentScreenMode(); + public abstract void addMonitorModeListener(MonitorModeListener sml); /** - * Set the current {@link com.jogamp.newt.ScreenMode}. - * @param screenMode to be made current, must be element of the list {@link #getScreenModes()}. - * @return true if successful, otherwise false + * @param sml {@link MonitorModeListener} to be removed from {@link MonitorEvent} */ - public abstract boolean setCurrentScreenMode(ScreenMode screenMode); + public abstract void removeMonitorModeListener(MonitorModeListener sml); // Global Screens - protected static ArrayList<Screen> screenList = new ArrayList<Screen>(); + protected static final ArrayList<WeakReference<Screen>> screenList = new ArrayList<WeakReference<Screen>>(); protected static int screensActive = 0; /** @@ -223,21 +257,56 @@ public abstract class Screen { synchronized(screenList) { int i = fromIndex >= 0 ? fromIndex : screenList.size() - 1 ; while( ( incr > 0 ) ? i < screenList.size() : i >= 0 ) { - Screen screen = (Screen) screenList.get(i); - if( screen.getDisplay().equals(display) && - screen.getIndex() == idx ) { - return screen; + final Screen screen = (Screen) screenList.get(i).get(); + if( null == screen ) { + // Clear GC'ed dead reference entry! + screenList.remove(i); + if( incr < 0 ) { + // decrease + i+=incr; + } // else nop - remove shifted subsequent elements to the left + } else { + if( screen.getDisplay().equals(display) && + screen.getIndex() == idx ) { + return screen; + } + i+=incr; } - i+=incr; } } return null; } - /** Returns the global display collection */ + + protected static void addScreen2List(Screen screen) { + synchronized(screenList) { + // GC before add + int i=0; + while( i < screenList.size() ) { + if( null == screenList.get(i).get() ) { + screenList.remove(i); + } else { + i++; + } + } + screenList.add(new WeakReference<Screen>(screen)); + } + } + + /** Returns the global screen collection */ public static Collection<Screen> getAllScreens() { ArrayList<Screen> list; synchronized(screenList) { - list = (ArrayList<Screen>) screenList.clone(); + list = new ArrayList<Screen>(); + int i = 0; + while( i < screenList.size() ) { + final Screen s = screenList.get(i).get(); + if( null == s ) { + screenList.remove(i); + } else { + list.add( screenList.get(i).get() ); + i++; + } + } } return list; } diff --git a/src/newt/classes/com/jogamp/newt/ScreenMode.java b/src/newt/classes/com/jogamp/newt/ScreenMode.java deleted file mode 100644 index 1f12217bb..000000000 --- a/src/newt/classes/com/jogamp/newt/ScreenMode.java +++ /dev/null @@ -1,208 +0,0 @@ -/** - * Copyright 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: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ - -package com.jogamp.newt; - -import javax.media.nativewindow.util.DimensionImmutable; - -import com.jogamp.newt.util.MonitorMode; - -/** Immutable ScreenMode Class, consisting of it's read only components:<br> - * <ul> - * <li>{@link com.jogamp.newt.util.MonitorMode}, non rotated values</li> - * <li><code>rotation</code>, measured counter clockwise (CCW)</li> - * </ul> - * - * <i>Aquire and filter ScreenModes</i><br> - * <ul> - * <li>A List of read only ScreenMode's is being returned by {@link com.jogamp.newt.Screen#getScreenModes()}.</li> - * <li>You may utilize {@link com.jogamp.newt.util.ScreenModeUtil} to filter and select a desired ScreenMode.</li> - * <li>The current ScreenMode can be obtained via {@link com.jogamp.newt.Screen#getCurrentScreenMode()}.</li> - * <li>The initial original ScreenMode (at startup) can be obtained via {@link com.jogamp.newt.Screen#getOriginalScreenMode()}.</li> - * </ul> - * <br> - * - * <i>Changing ScreenModes</i><br> - * <ul> - * <li> Use {@link com.jogamp.newt.Screen#setCurrentScreenMode(com.jogamp.newt.ScreenMode)}</li> - * to change the current ScreenMode of all Screen's referenced via the full qualified name (FQN) - * {@link com.jogamp.newt.Screen#getFQName()}.</li> - * <li> When the last FQN referenced Screen closes, the original ScreenMode ({@link com.jogamp.newt.Screen#getOriginalScreenMode()}) - * is restored.</li> - * </ul> - * <br> - * Example for changing the ScreenMode: - * <pre> - // determine target refresh rate - ScreenMode orig = screen.getOriginalScreenMode(); - int freq = orig.getMonitorMode().getRefreshRate(); - - // target resolution - Dimension res = new Dimension(800, 600); - - // target rotation - int rot = 0; - - // filter available ScreenModes - List screenModes = screen.getScreenModes(); - screenModes = ScreenModeUtil.filterByRate(screenModes, freq); // get the nearest ones - screenModes = ScreenModeUtil.filterByRotation(screenModes, rot); - screenModes = ScreenModeUtil.filterByResolution(screenModes, res); // get the nearest ones - screenModes = ScreenModeUtil.getHighestAvailableBpp(screenModes); - - // pick 1st one .. - screen.setCurrentScreenMode((ScreenMode) screenModes.get(0)); - * </pre> - * - * X11 / AMD just works<br> - * <br> - * X11 / NVidia difficulties - * <pre> - NVidia RANDR RefreshRate Bug - If NVidia's 'DynamicTwinView' is enabled, all refresh rates are - unique, ie consequent numbers starting with the default refresh, ie 50, 51, .. - The only way to workaround it is to disable 'DynamicTwinView'. - Read: http://us.download.nvidia.com/XFree86/Linux-x86/260.19.12/README/configtwinview.html - - Check to see if 'DynamicTwinView' is enable: - nvidia-settings -q :0/DynamicTwinview - - To disable it (workaround), add the following option to your xorg.conf device section: - Option "DynamicTwinView" "False" - - NVidia RANDR Rotation: - To enable it, add the following option to your xorg.conf device section: - Option "RandRRotation" "on" - * </pre> - * - */ -public class ScreenMode { - /** zero rotation, compared to normal settings */ - public static final int ROTATE_0 = 0; - - /** 90 degrees CCW rotation */ - public static final int ROTATE_90 = 90; - - /** 180 degrees CCW rotation */ - public static final int ROTATE_180 = 180; - - /** 270 degrees CCW rotation */ - public static final int ROTATE_270 = 270; - - MonitorMode monitorMode; - int rotation; - - public static boolean isRotationValid(int rotation) { - return rotation == ScreenMode.ROTATE_0 || rotation == ScreenMode.ROTATE_90 || - rotation == ScreenMode.ROTATE_180 || rotation == ScreenMode.ROTATE_270 ; - } - - /** - * @param monitorMode the monitor mode - * @param rotation the screen rotation, measured counter clockwise (CCW) - */ - public ScreenMode(MonitorMode monitorMode, int rotation) { - if ( !isRotationValid(rotation) ) { - throw new RuntimeException("invalid rotation: "+rotation); - } - this.monitorMode = monitorMode; - this.rotation = rotation; - } - - /** Returns the unrotated <code>MonitorMode</code> */ - public final MonitorMode getMonitorMode() { - return monitorMode; - } - - /** Returns the CCW rotation of this mode */ - public final int getRotation() { - return rotation; - } - - /** Returns the rotated screen width, - * derived from <code>getMonitorMode().getSurfaceSize().getResolution()</code> - * and <code>getRotation()</code> - */ - public final int getRotatedWidth() { - return getRotatedWH(true); - } - - /** Returns the rotated screen height, - * derived from <code>getMonitorMode().getSurfaceSize().getResolution()</code> - * and <code>getRotation()</code> - */ - public final int getRotatedHeight() { - return getRotatedWH(false); - } - - public final String toString() { - return "[ " + getMonitorMode() + ", " + rotation + " degr ]"; - } - - /** - * Tests equality of two <code>ScreenMode</code> objects - * by evaluating equality of it's components:<br> - * <ul> - * <li><code>monitorMode</code></li> - * <li><code>rotation</code></li> - * </ul> - * <br> - */ - public final boolean equals(Object obj) { - if (this == obj) { return true; } - if (obj instanceof ScreenMode) { - ScreenMode sm = (ScreenMode)obj; - return sm.getMonitorMode().equals(getMonitorMode()) && - sm.getRotation() == this.getRotation() ; - } - return false; - } - - /** - * Returns a combined hash code of it's elements:<br> - * <ul> - * <li><code>monitorMode</code></li> - * <li><code>rotation</code></li> - * </ul> - */ - public final int hashCode() { - // 31 * x == (x << 5) - x - int hash = 31 + getMonitorMode().hashCode(); - hash = ((hash << 5) - hash) + getRotation(); - return hash; - } - - private final int getRotatedWH(boolean width) { - final DimensionImmutable d = getMonitorMode().getSurfaceSize().getResolution(); - final boolean swap = ScreenMode.ROTATE_90 == rotation || ScreenMode.ROTATE_270 == rotation ; - if ( ( width && swap ) || ( !width && !swap ) ) { - return d.getHeight(); - } - return d.getWidth(); - } -} diff --git a/src/newt/classes/com/jogamp/newt/Window.java b/src/newt/classes/com/jogamp/newt/Window.java index 3c5441bf7..4816e62e5 100644 --- a/src/newt/classes/com/jogamp/newt/Window.java +++ b/src/newt/classes/com/jogamp/newt/Window.java @@ -28,20 +28,56 @@ package com.jogamp.newt; +import java.util.List; + +import com.jogamp.newt.Display.PointerIcon; +import com.jogamp.newt.event.GestureHandler; +import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowListener; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.MouseListener; + import jogamp.newt.Debug; +import jogamp.newt.WindowImpl; + import javax.media.nativewindow.CapabilitiesChooser; import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.nativewindow.util.RectangleImmutable; /** - * Specifying the public Window functionality for the - * using a Window and for shadowing one like {@link com.jogamp.newt.opengl.GLWindow}. + * Specifying NEWT's Window functionality: + * <ul> + * <li>On- and offscreen windows</li> + * <li>Keyboard and multi-pointer input</li> + * <li>Native reparenting</li> + * <li>Toggable fullscreen and decoration mode</li> + * <li>Transparency</li> + * <li>... and more</li> + * </ul> + * <p> + * One use case is {@link com.jogamp.newt.opengl.GLWindow}, which delegates + * window operation to an instance of this interface while providing OpenGL + * functionality. + * </p> + * <a name="customwindowicons"><h5>Custom Window Icons</h5></a> + * <p> + * Custom window icons can be defined via system property <code>newt.window.icons</code>, + * which shall contain a space separated list of PNG icon locations from low- to high-resolution. + * The location must be resolvable via classpath, i.e. shall reference a location within the jar file. + * Example (our default): + * <pre> + * -Dnewt.window.icons="newt/data/jogamp-16x16.png newt/data/jogamp-32x32.png" + * -Djnlp.newt.window.icons="newt/data/jogamp-16x16.png newt/data/jogamp-32x32.png" + * </pre> + * The property can also be set programmatically, which must happen before any NEWT classes are <i>touched</i>: + * <pre> + * System.setProperty("newt.window.icons", "newt/data/jogamp-16x16.png newt/data/jogamp-32x32.png"); + * </pre> + * </p> */ public interface Window extends NativeWindow, WindowClosingProtocol { public static final boolean DEBUG_MOUSE_EVENT = Debug.debug("Window.MouseEvent"); @@ -65,13 +101,22 @@ public interface Window extends NativeWindow, WindowClosingProtocol { boolean isNativeValid(); /** - * @return The associated Screen + * @return The associated {@link Screen} */ Screen getScreen(); /** + * Returns the {@link MonitorDevice} which {@link MonitorDevice#getViewport() viewport} + * {@link MonitorDevice#coverage(RectangleImmutable) covers} this window the most. + * <p> + * If no coverage is detected the first {@link MonitorDevice} is returned. + * </p> + */ + MonitorDevice getMainMonitor(); + + /** * Set the CapabilitiesChooser to help determine the native visual type. - * + * * @param chooser the new CapabilitiesChooser * @return the previous CapabilitiesChooser */ @@ -86,37 +131,71 @@ public interface Window extends NativeWindow, WindowClosingProtocol { /** * Gets an immutable set of chosen capabilities. - * + * * @return the chosen capabilities */ CapabilitiesImmutable getChosenCapabilities(); /** - * Destroy the Window and it's children, incl. native destruction.<br> - * The Window can be recreate via {@link #setVisible(boolean) setVisible(true)}. - * <p>Visibility is set to false.</p> + * {@inheritDoc} + * <p> + * Also iterates through this window's children and destroys them. + * </p> + * <p> + * Visibility is set to false. + * </p> + * <p> + * Method sends out {@link WindowEvent#EVENT_WINDOW_DESTROY_NOTIFY pre-} and + * {@link WindowEvent#EVENT_WINDOW_DESTROYED post-} destruction events + * to all of it's {@link WindowListener}. + * </p> * <p> * This method invokes {@link Screen#removeReference()} after it's own destruction,<br> * which will issue {@link Screen#destroy()} if the reference count becomes 0.<br> * This destruction sequence shall end up in {@link Display#destroy()}, if all reference counts become 0. * </p> + * <p> + * The Window can be recreate via {@link #setVisible(boolean) setVisible(true)}. + * </p> * @see #destroy() * @see #setVisible(boolean) */ + @Override void destroy(); /** + * Set a custom action handling destruction issued by a {@link WindowImpl#windowDestroyNotify(boolean) toolkit triggered window destroy} + * replacing the default {@link #destroy()} action. * <p> - * <code>setVisible</code> makes the window and children visible if <code>visible</code> is true, - * otherwise the window and children becomes invisible.<br></p> + * The custom action shall call {@link #destroy()} + * but may perform further tasks before and after. + * </p> + */ + void setWindowDestroyNotifyAction(Runnable r); + + /** + * Calls {@link #setVisible(boolean, boolean) setVisible(true, visible)}, + * i.e. blocks until the window becomes visible. + * @see #setVisible(boolean, boolean) + */ + void setVisible(boolean visible); + + /** + * <code>setVisible(..)</code> makes the window and children visible if <code>visible</code> is true, + * otherwise the window and children becomes invisible. + * <p> + * <code>setVisible(wait, true)</code> is responsible to actual create the native window. + * </p> * <p> - * The <code>setVisible(true)</code> is responsible to actual create the native window.<br></p> + * If <code>wait</code> is true, method blocks until window is {@link #isVisible() visible} and {@link #isNativeValid() valid}, + * otherwise method returns immediately. + * </p> * <p> * Zero size semantics are respected, see {@link #setSize(int,int)}:<br> * <pre> * if ( 0 == windowHandle && visible ) { * this.visible = visible; - * if( 0 < width*height ) { + * if( 0 < width && 0 < height ) { * createNative(); * } * } else if ( this.visible != visible ) { @@ -125,23 +204,24 @@ public interface Window extends NativeWindow, WindowClosingProtocol { * } * </pre></p> * <p> - * In case this window is a child window and a parent {@link javax.media.nativewindow.NativeWindow} is being used,<br> - * the parent's {@link javax.media.nativewindow.NativeWindow} handle is retrieved via {@link javax.media.nativewindow.NativeWindow#getWindowHandle()}.<br> - * If this action fails, ie if the parent {@link javax.media.nativewindow.NativeWindow} is not valid yet,<br> - * no native window is created yet and <code>setVisible(true)</code> shall be repeated when it is.<br></p> + * In case this window is a child window and has a {@link javax.media.nativewindow.NativeWindow} parent,<br> + * <code>setVisible(wait, true)</code> has no effect as long the parent's is not valid yet, + * i.e. {@link javax.media.nativewindow.NativeWindow#getWindowHandle()} returns <code>null</code>.<br> + * <code>setVisible(wait, true)</code> shall be repeated when the parent becomes valid. + * </p> */ - void setVisible(boolean visible); + void setVisible(boolean wait, boolean visible); boolean isVisible(); - /** - * If the implementation uses delegation, return the delegated {@link Window} instance, + /** + * If the implementation uses delegation, return the delegated {@link Window} instance, * otherwise return <code>this</code> instance. */ Window getDelegatedWindow(); - + // // Child Window Management - // + // boolean addChild(NativeWindow win); @@ -153,13 +233,13 @@ public interface Window extends NativeWindow, WindowClosingProtocol { /** * Sets the size of the window's client area, excluding decorations. - * + * * <p> * Zero size semantics are respected, see {@link #setVisible(boolean)}:<br> * <pre> - * if ( 0 != windowHandle && 0 ≥ width*height && visible ) { + * if ( visible && 0 != windowHandle && ( 0 ≥ width || 0 ≥ height ) ) { * setVisible(false); - * } else if ( 0 == windowHandle && 0 < width*height && visible ) { + * } else if ( visible && 0 == windowHandle && 0 < width && 0 < height ) { * setVisible(true); * } else { * // as expected .. @@ -170,105 +250,121 @@ public interface Window extends NativeWindow, WindowClosingProtocol { * * @param width of the window's client area * @param height of the window's client area - * + * * @see #getInsets() */ void setSize(int width, int height); /** * Sets the size of the top-level window including insets (window decorations). - * + * * <p> * Note: Insets (if supported) are available only after the window is set visible and hence has been created. * </p> * * @param width of the top-level window area * @param height of the top-level window area - * + * * @see #setSize(int, int) * @see #getInsets() */ void setTopLevelSize(int width, int height); - + /** * Sets the location of the window's client area, excluding insets (window decorations).<br> - * + * * This call is ignored if in fullscreen mode.<br> * * @param x coord of the client-area's top left corner * @param y coord of the client-area's top left corner - * + * * @see #getInsets() */ void setPosition(int x, int y); - + /** * Sets the location of the top-level window inclusive insets (window decorations).<br> - * + * * <p> * Note: Insets (if supported) are available only after the window is set visible and hence has been created. * </p> - * + * * This call is ignored if in fullscreen mode.<br> * * @param x coord of the top-level left corner * @param y coord of the top-level left corner - * + * * @see #setPosition(int, int) * @see #getInsets() */ void setTopLevelPosition(int x, int y); void setUndecorated(boolean value); - + boolean isUndecorated(); - + void setAlwaysOnTop(boolean value); - + boolean isAlwaysOnTop(); - + void setTitle(String title); String getTitle(); + /** @see #setPointerVisible(boolean) */ boolean isPointerVisible(); - + /** * Makes the pointer visible or invisible. - * + * * @param pointerVisible defaults to <code>true</code> for platforms w/ visible pointer, * otherwise defaults to <code>true</code>, eg. Android. * @see #confinePointer(boolean) */ void setPointerVisible(boolean pointerVisible); + /** + * Returns the current {@link PointerIcon}, which maybe <code>null</code> for the default. + * @see #setPointerIcon(PointerIcon) + */ + PointerIcon getPointerIcon(); + + /** + * @param pi Valid {@link PointerIcon} reference or <code>null</code> to reset the pointer icon to default. + * + * @see PointerIcon + * @see Display#createPointerIcon(com.jogamp.common.util.IOUtil.ClassResources, int, int) + */ + void setPointerIcon(final PointerIcon pi); + + /** @see #confinePointer(boolean) */ boolean isPointerConfined(); - + /** * Confine the pointer to this window, ie. pointer jail. * <p> - * Before jailing the mouse pointer, + * Before jailing the mouse pointer, * the window request the focus and the pointer is centered in the window. * </p> * <p> - * In combination w/ {@link #warpPointer(int, int)} + * In combination w/ {@link #warpPointer(int, int)} * and maybe {@link #setPointerVisible(boolean)} a simple mouse * navigation can be realized.</p> - * + * * @param confine defaults to <code>false</code>. */ void confinePointer(boolean confine); - + /** * Moves the pointer to x/y relative to this window's origin. - * + * * @param x relative pointer x position within this window * @param y relative pointer y position within this window - * + * * @see #confinePointer(boolean) */ void warpPointer(int x, int y); - + /** Reparenting operation types */ public enum ReparentOperation { /** No native reparenting valid */ @@ -284,9 +380,14 @@ public interface Window extends NativeWindow, WindowClosingProtocol { ACTION_NATIVE_CREATION, /** Change Window tree only, native creation is pending */ - ACTION_NATIVE_CREATION_PENDING; + ACTION_NATIVE_CREATION_PENDING; } + /** Reparenting hint (bitfield value): Force destroy and hence {@link ReparentOperation#ACTION_NATIVE_CREATION re-creating} the window. */ + public static final int REPARENT_HINT_FORCE_RECREATION = 1 << 0; + /** Reparenting hint (bitfield value): Claim window becomes visible after reparenting, which is important for e.g. preserving the GL-states in case window is invisible while reparenting. */ + public static final int REPARENT_HINT_BECOMES_VISIBLE = 1 << 1; + /** * Change this window's parent window.<br> * <P> @@ -298,13 +399,72 @@ public interface Window extends NativeWindow, WindowClosingProtocol { * @param newParent The new parent NativeWindow. If null, this Window becomes a top level window. * * @return The issued reparent action type (strategy) as defined in Window.ReparentAction + * @see #reparentWindow(NativeWindow, int, int, boolean) + * @deprecated Use {@link #reparentWindow(NativeWindow, int, int, int)} */ ReparentOperation reparentWindow(NativeWindow newParent); - ReparentOperation reparentWindow(NativeWindow newParent, boolean forceDestroyCreate); + /** + * Change this window's parent window.<br> + * <P> + * In case the old parent is not null and a Window, + * this window is removed from it's list of children.<br> + * In case the new parent is not null and a Window, + * this window is added to it's list of children.<br></P> + * + * @param newParent The new parent NativeWindow. If null, this Window becomes a top level window. + * @param x new top-level position, use -1 for default position. + * @param y new top-level position, use -1 for default position. + * @param forceDestroyCreate if true, uses re-creation strategy for reparenting, default is <code>false</code>. + * + * @return The issued reparent action type (strategy) as defined in Window.ReparentAction + * @deprecated Use {@link #reparentWindow(NativeWindow, int, int, int)} + */ + ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, boolean forceDestroyCreate); + /** + * Change this window's parent window.<br> + * <P> + * In case the old parent is not null and a Window, + * this window is removed from it's list of children.<br> + * In case the new parent is not null and a Window, + * this window is added to it's list of children.<br></P> + * + * @param newParent The new parent NativeWindow. If null, this Window becomes a top level window. + * @param x new top-level position, use -1 for default position. + * @param y new top-level position, use -1 for default position. + * @param hints May contain hints (bitfield values) like {@link #REPARENT_HINT_FORCE_RECREATION} or {@link #REPARENT_HINT_BECOMES_VISIBLE}. + * + * @return The issued reparent action type (strategy) as defined in Window.ReparentAction + */ + ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, int hints); + + /** + * Enable or disable fullscreen mode for this window. + * <p> + * Fullscreen mode is established on the {@link #getMainMonitor() main monitor}. + * </p> + * @param fullscreen enable or disable fullscreen mode + * @return success + * @see #setFullscreen(List) + * @see #isFullscreen() + */ boolean setFullscreen(boolean fullscreen); - + + /** + * Enable fullscreen mode for this window spanning across the given {@link MonitorDevice}s + * or across all {@link MonitorDevice}s. + * <p> + * Disable fullscreen via {@link #setFullscreen(boolean)}. + * </p> + * @param monitors if <code>null</code> fullscreen will be spanned across all {@link MonitorDevice}s, + * otherwise across the given list of {@link MonitorDevice}. + * @return success + * @see #setFullscreen(boolean) + * @see #isFullscreen() + */ + boolean setFullscreen(List<MonitorDevice> monitors); + boolean isFullscreen(); static interface FocusRunnable { @@ -316,7 +476,7 @@ public interface Window extends NativeWindow, WindowClosingProtocol { } /** - * Sets a {@link FocusRunnable}, + * Sets a {@link FocusRunnable}, * which {@link FocusRunnable#run()} method is executed before the native focus is requested. * <p> * This allows notifying a covered window toolkit like AWT that the focus is requested, @@ -324,38 +484,42 @@ public interface Window extends NativeWindow, WindowClosingProtocol { * </p> */ void setFocusAction(FocusRunnable focusAction); - + /** * Sets a {@link KeyListener} allowing focus traversal with a covered window toolkit like AWT. * <p> * The {@link KeyListener} methods are invoked prior to all other {@link KeyListener}'s - * allowing to suppress the {@link KeyEvent} via the {@link InputEvent#consumedTag}. + * allowing to suppress the {@link KeyEvent} via the {@link InputEvent#consumedTag} + * and to perform focus traversal with a 3rd party toolkit. + * </p> + * <p> + * The {@link KeyListener} methods are not invoked for {@link KeyEvent#isAutoRepeat() auto-repeat} events. * </p> * @param l */ void setKeyboardFocusHandler(KeyListener l); - /** + /** * Request focus for this native window * <p> * The request is handled on this Window EDT and blocked until finished. * </p> - * + * * @see #requestFocus(boolean) */ void requestFocus(); - /** + /** * Request focus for this native window * <p> - * The request is handled on this Window EDT. + * The request is handled on this Window EDT. * </p> - * + * * @param wait true if waiting until the request is executed, otherwise false * @see #requestFocus() */ void requestFocus(boolean wait); - + void windowRepaint(int x, int y, int width, int height); void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event); @@ -367,10 +531,13 @@ public interface Window extends NativeWindow, WindowClosingProtocol { // WindowListener // + /** + * Send a {@link WindowEvent} to all {@link WindowListener}. + * @param eventType a {@link WindowEvent} type, e.g. {@link WindowEvent#EVENT_WINDOW_REPAINT}. + */ public void sendWindowEvent(int eventType); /** - * * Appends the given {@link com.jogamp.newt.event.WindowListener} to the end of * the list. */ @@ -399,6 +566,26 @@ public interface Window extends NativeWindow, WindowClosingProtocol { // KeyListener // + /** + * In case the platform supports or even requires a virtual on-screen keyboard, + * this method shows or hide it depending on whether <code>visible</code> is <code>true</code> + * or <code>false</code>. + * <p> + * One known platform where NEWT supports this feature is <code>Android</code>. + * </p> + */ + void setKeyboardVisible(boolean visible); + + /** + * Return <code>true</code> if the virtual on-screen keyboard is visible, otherwise <code>false</code>. + * <p> + * Currently on <code>Android</code>, the only supported platform right now, + * there is no way to reliably be notified of the current keyboard state.<br> + * It would be best, if your code does not rely on this information. + * </p> + * @see #setKeyboardVisible(boolean) + */ + boolean isKeyboardVisible(); /** * @@ -432,15 +619,12 @@ public interface Window extends NativeWindow, WindowClosingProtocol { // /** - * - * Appends the given {@link com.jogamp.newt.event.MouseListener} to the end of - * the list. + * Appends the given {@link MouseListener} to the end of the list. */ void addMouseListener(MouseListener l); /** - * - * Inserts the given {@link com.jogamp.newt.event.MouseListener} at the + * Inserts the given {@link MouseListener} at the * specified position in the list.<br> * * @param index Position where the listener will be inserted. @@ -451,10 +635,61 @@ public interface Window extends NativeWindow, WindowClosingProtocol { */ void addMouseListener(int index, MouseListener l); + /** + * Removes the given {@link MouseListener} from the list. + */ void removeMouseListener(MouseListener l); + /** + * Returns the {@link MouseListener} from the list at the given index. + */ MouseListener getMouseListener(int index); + /** + * Returns all {@link MouseListener} + */ MouseListener[] getMouseListeners(); + /** Enable or disable default {@link GestureHandler}. Default is enabled. */ + void setDefaultGesturesEnabled(boolean enable); + /** Return true if default {@link GestureHandler} are enabled. */ + boolean areDefaultGesturesEnabled(); + /** + * Appends the given {@link GestureHandler} to the end of the list. + */ + void addGestureHandler(GestureHandler gh); + /** + * Inserts the given {@link GestureHandler} at the + * specified position in the list.<br> + * + * @param index Position where the listener will be inserted. + * Should be within (0 <= index && index <= size()). + * An index value of -1 is interpreted as the end of the list, size(). + * @param l The listener object to be inserted + * @throws IndexOutOfBoundsException If the index is not within (0 <= index && index <= size()), or -1 + */ + void addGestureHandler(int index, GestureHandler gh); + /** + * Removes the given {@link GestureHandler} from the list. + */ + void removeGestureHandler(GestureHandler gh); + /** + * Appends the given {@link GestureHandler.GestureListener} to the end of the list. + */ + void addGestureListener(GestureHandler.GestureListener gl); + /** + * Inserts the given {@link GestureHandler.GestureListener} at the + * specified position in the list.<br> + * + * @param index Position where the listener will be inserted. + * Should be within (0 <= index && index <= size()). + * An index value of -1 is interpreted as the end of the list, size(). + * @param l The listener object to be inserted + * @throws IndexOutOfBoundsException If the index is not within (0 <= index && index <= size()), or -1 + */ + void addGestureListener(int index, GestureHandler.GestureListener gl); + /** + * Removes the given {@link GestureHandler.GestureListener} from the list. + */ + void removeGestureListener(GestureHandler.GestureListener gl); } diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java index 9af4a02ae..1ed628435 100644 --- a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,20 +20,26 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.awt; +import java.applet.Applet; import java.awt.AWTKeyStroke; import java.awt.Canvas; +import java.awt.Component; +import java.awt.EventQueue; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.KeyboardFocusManager; +import java.awt.geom.NoninvertibleTransformException; +import java.beans.Beans; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.reflect.Method; @@ -44,19 +50,28 @@ import java.util.Set; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.OffscreenLayerOption; import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; import javax.swing.MenuSelectionManager; import jogamp.nativewindow.awt.AWTMisc; +import jogamp.nativewindow.jawt.JAWTUtil; import jogamp.newt.Debug; +import jogamp.newt.WindowImpl; import jogamp.newt.awt.NewtFactoryAWT; import jogamp.newt.awt.event.AWTParentWindowAdapter; import jogamp.newt.driver.DriverClearFocus; +import jogamp.opengl.awt.AWTTilePainter; +import com.jogamp.common.util.awt.AWTEDTExecutor; +import com.jogamp.nativewindow.awt.AWTPrintLifecycle; import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; import com.jogamp.nativewindow.awt.JAWTWindow; import com.jogamp.newt.Display; import com.jogamp.newt.Window; -import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.WindowAdapter; @@ -65,32 +80,68 @@ import com.jogamp.newt.event.WindowListener; import com.jogamp.newt.event.awt.AWTAdapter; import com.jogamp.newt.event.awt.AWTKeyAdapter; import com.jogamp.newt.event.awt.AWTMouseAdapter; +import com.jogamp.opengl.util.GLDrawableUtil; +import com.jogamp.opengl.util.TileRenderer; +/** + * AWT {@link java.awt.Canvas Canvas} containing a NEWT {@link Window} using native parenting. + * + * <h5><A NAME="java2dgl">Offscreen Layer Remarks</A></h5> + * + * {@link OffscreenLayerOption#setShallUseOffscreenLayer(boolean) setShallUseOffscreenLayer(true)} + * maybe called to use an offscreen drawable (FBO or PBuffer) allowing + * the underlying JAWT mechanism to composite the image, if supported. + */ @SuppressWarnings("serial") -public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProtocol, OffscreenLayerOption { +public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProtocol, OffscreenLayerOption, AWTPrintLifecycle { public static final boolean DEBUG = Debug.debug("Window"); + private final Object sync = new Object(); private JAWTWindow jawtWindow = null; + private boolean isApplet = false; private boolean shallUseOffscreenLayer = false; private Window newtChild = null; + private boolean newtChildAttached = false; private boolean isOnscreen = true; private WindowClosingMode newtChildCloseOp; - private AWTAdapter awtAdapter = null; - private AWTAdapter awtMouseAdapter = null; - private AWTAdapter awtKeyAdapter = null; - - private AWTWindowClosingProtocol awtWindowClosingProtocol = + private final AWTParentWindowAdapter awtAdapter; + private final AWTAdapter awtMouseAdapter; + private final AWTAdapter awtKeyAdapter; + + /** Mitigates Bug 910 (IcedTea-Web), i.e. crash via removeNotify() invoked before Applet.destroy(). */ + private boolean destroyJAWTPending = false; + /** Mitigates Bug 910 (IcedTea-Web), i.e. crash via removeNotify() invoked before Applet.destroy(). */ + private boolean skipJAWTDestroy = false; + + /** Safeguard for AWTWindowClosingProtocol and 'removeNotify()' on other thread than AWT-EDT. */ + private volatile boolean componentAdded = false; + + private final AWTWindowClosingProtocol awtWindowClosingProtocol = new AWTWindowClosingProtocol(this, new Runnable() { + @Override public void run() { - NewtCanvasAWT.this.destroy(); + if( componentAdded ) { + NewtCanvasAWT.this.destroyImpl(false /* removeNotify */, true /* windowClosing */); + } + } + }, new Runnable() { + @Override + public void run() { + if( componentAdded && newtChild != null ) { + newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); + } } - }); + } ); /** * Instantiates a NewtCanvas without a NEWT child.<br> */ public NewtCanvasAWT() { super(); + awtMouseAdapter = new AWTMouseAdapter().addTo(this); + awtKeyAdapter = new AWTKeyAdapter().addTo(this); + awtAdapter = (AWTParentWindowAdapter) new AWTParentWindowAdapter().addTo(this); + awtAdapter.removeWindowClosingFrom(this); // we utilize AWTWindowClosingProtocol triggered destruction! } /** @@ -98,6 +149,10 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto */ public NewtCanvasAWT(GraphicsConfiguration gc) { super(gc); + awtMouseAdapter = new AWTMouseAdapter().addTo(this); + awtKeyAdapter = new AWTKeyAdapter().addTo(this); + awtAdapter = (AWTParentWindowAdapter) new AWTParentWindowAdapter().addTo(this); + awtAdapter.removeWindowClosingFrom(this); // we utilize AWTWindowClosingProtocol triggered destruction! } /** @@ -105,6 +160,10 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto */ public NewtCanvasAWT(Window child) { super(); + awtMouseAdapter = new AWTMouseAdapter().addTo(this); + awtKeyAdapter = new AWTKeyAdapter().addTo(this); + awtAdapter = (AWTParentWindowAdapter) new AWTParentWindowAdapter().addTo(this); + awtAdapter.removeWindowClosingFrom(this); // we utilize AWTWindowClosingProtocol triggered destruction! setNEWTChild(child); } @@ -113,142 +172,173 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto */ public NewtCanvasAWT(GraphicsConfiguration gc, Window child) { super(gc); + awtMouseAdapter = new AWTMouseAdapter().addTo(this); + awtKeyAdapter = new AWTKeyAdapter().addTo(this); + awtAdapter = (AWTParentWindowAdapter) new AWTParentWindowAdapter().addTo(this); + awtAdapter.removeWindowClosingFrom(this); // we utilize AWTWindowClosingProtocol triggered destruction! setNEWTChild(child); } - + + @Override public void setShallUseOffscreenLayer(boolean v) { shallUseOffscreenLayer = v; } - + + @Override public final boolean getShallUseOffscreenLayer() { - return shallUseOffscreenLayer; + return shallUseOffscreenLayer; } - - public final boolean isOffscreenLayerSurfaceEnabled() { - return jawtWindow.isOffscreenLayerSurfaceEnabled(); + + @Override + public final boolean isOffscreenLayerSurfaceEnabled() { + final JAWTWindow w = jawtWindow; + return null != w && w.isOffscreenLayerSurfaceEnabled(); } - - /** - * Returns true if the AWT component is parented to an {@link java.applet.Applet}, - * otherwise false. This information is valid only after {@link #addNotify()} is issued, - * ie. before adding the component to the AWT tree and make it visible. + + /** + * Returns true if the AWT component is parented to an {@link java.applet.Applet}, + * otherwise false. This information is valid only after {@link #addNotify()} is issued. */ - public boolean isApplet() { - return jawtWindow.isApplet(); + public final boolean isApplet() { + return isApplet; } - boolean isParent() { - return null!=newtChild && jawtWindow == newtChild.getParent(); + private final boolean isParent() { + final Window nw = newtChild; + return null!=nw && jawtWindow == nw.getParent(); } - - boolean isFullscreen() { - return null != newtChild && newtChild.isFullscreen(); + + private final boolean isFullscreen() { + final Window nw = newtChild; + return null != nw && nw.isFullscreen(); } - + class FocusAction implements Window.FocusRunnable { + @Override public boolean run() { final boolean isParent = isParent(); final boolean isFullscreen = isFullscreen(); if(DEBUG) { System.err.println("NewtCanvasAWT.FocusAction: "+Display.getThreadName()+", isOnscreen "+isOnscreen+", hasFocus "+hasFocus()+", isParent "+isParent+", isFS "+isFullscreen); } - if(isParent && !isFullscreen) { - // Newt-EDT -> AWT-EDT may freeze Window's native peer requestFocus. - if(!hasFocus()) { - // Acquire the AWT focus 1st for proper AWT traversal - NewtCanvasAWT.super.requestFocus(); - } - if(isOnscreen) { + if( isParent && !isFullscreen ) { // must be parent of newtChild _and_ newtChild not fullscreen + if( isOnscreen ) { // Remove the AWT focus in favor of the native NEWT focus - KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); + AWTEDTExecutor.singleton.invoke(false, awtClearGlobalFocusOwner); + } else if( !hasFocus() ) { + // In offscreen mode we require the focus! + // Newt-EDT -> AWT-EDT may freeze Window's native peer requestFocus. + NewtCanvasAWT.super.requestFocus(); } } return false; // NEWT shall proceed requesting the native focus } } - private FocusAction focusAction = new FocusAction(); - - WindowListener clearAWTMenusOnNewtFocus = new WindowAdapter() { + private final FocusAction focusAction = new FocusAction(); + + private static class ClearFocusOwner implements Runnable { + @Override + public void run() { + KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); + } + } + private static final Runnable awtClearGlobalFocusOwner = new ClearFocusOwner(); + + /** Must run on AWT-EDT non-blocking, since it invokes tasks on AWT-EDT w/ waiting otherwise. */ + private final Runnable awtClearSelectedMenuPath = new Runnable() { + @Override + public void run() { + MenuSelectionManager.defaultManager().clearSelectedPath(); + } + }; + private final WindowListener clearAWTMenusOnNewtFocus = new WindowAdapter() { + @Override + public void windowResized(WindowEvent e) { + updateLayoutSize(); + } @Override public void windowGainedFocus(WindowEvent arg0) { if( isParent() && !isFullscreen() ) { - MenuSelectionManager.defaultManager().clearSelectedPath(); + AWTEDTExecutor.singleton.invoke(false, awtClearSelectedMenuPath); } } }; class FocusTraversalKeyListener implements KeyListener { - boolean suppress = false; - + @Override public void keyPressed(KeyEvent e) { if( isParent() && !isFullscreen() ) { handleKey(e, false); } } + @Override public void keyReleased(KeyEvent e) { if( isParent() && !isFullscreen() ) { handleKey(e, true); } } - public void keyTyped(KeyEvent e) { - if(suppress) { - e.setAttachment(InputEvent.consumedTag); - suppress = false; // reset - } - } - - void handleKey(KeyEvent evt, boolean onRelease) { + + void handleKey(KeyEvent evt, boolean onRelease) { if(null == keyboardFocusManager) { throw new InternalError("XXX"); } final AWTKeyStroke ks = AWTKeyStroke.getAWTKeyStroke(evt.getKeyCode(), evt.getModifiers(), onRelease); + boolean suppress = false; if(null != ks) { - final Set<AWTKeyStroke> fwdKeys = keyboardFocusManager.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); - final Set<AWTKeyStroke> bwdKeys = keyboardFocusManager.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + final Set<AWTKeyStroke> fwdKeys = keyboardFocusManager.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + final Set<AWTKeyStroke> bwdKeys = keyboardFocusManager.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); if(fwdKeys.contains(ks)) { + final Component nextFocus = AWTMisc.getNextFocus(NewtCanvasAWT.this, true /* forward */); if(DEBUG) { - System.err.println("NewtCanvasAWT.focusKey (fwd): "+ks+", current focusOwner "+keyboardFocusManager.getFocusOwner()); + System.err.println("NewtCanvasAWT.focusKey (fwd): "+ks+", current focusOwner "+keyboardFocusManager.getFocusOwner()+", hasFocus: "+hasFocus()+", nextFocus "+nextFocus); } // Newt-EDT -> AWT-EDT may freeze Window's native peer requestFocus. - NewtCanvasAWT.this.transferFocus(); + nextFocus.requestFocus(); suppress = true; } else if(bwdKeys.contains(ks)) { + final Component prevFocus = AWTMisc.getNextFocus(NewtCanvasAWT.this, false /* forward */); if(DEBUG) { - System.err.println("NewtCanvasAWT.focusKey (bwd): "+ks+", current focusOwner "+keyboardFocusManager.getFocusOwner()); + System.err.println("NewtCanvasAWT.focusKey (bwd): "+ks+", current focusOwner "+keyboardFocusManager.getFocusOwner()+", hasFocus: "+hasFocus()+", prevFocus "+prevFocus); } // Newt-EDT -> AWT-EDT may freeze Window's native peer requestFocus. - NewtCanvasAWT.this.transferFocusBackward(); + prevFocus.requestFocus(); suppress = true; } } if(suppress) { - evt.setAttachment(InputEvent.consumedTag); + evt.setConsumed(true); } if(DEBUG) { System.err.println("NewtCanvasAWT.focusKey: XXX: "+ks); } } } - private final FocusTraversalKeyListener newtFocusTraversalKeyListener = new FocusTraversalKeyListener(); + private final FocusTraversalKeyListener newtFocusTraversalKeyListener = new FocusTraversalKeyListener(); class FocusPropertyChangeListener implements PropertyChangeListener { + @Override public void propertyChange(PropertyChangeEvent evt) { final Object oldF = evt.getOldValue(); final Object newF = evt.getNewValue(); final boolean isParent = isParent(); - final boolean isFullscreen = isFullscreen(); + final boolean isFullscreen = isFullscreen(); if(DEBUG) { System.err.println("NewtCanvasAWT.FocusProperty: "+evt.getPropertyName()+", src "+evt.getSource()+", "+oldF+" -> "+newF+", isParent "+isParent+", isFS "+isFullscreen); } if(isParent && !isFullscreen) { - if(oldF == NewtCanvasAWT.this && newF == null) { + if(newF == NewtCanvasAWT.this) { + if(DEBUG) { + System.err.println("NewtCanvasAWT.FocusProperty: AWT focus -> NEWT focus traversal"); + } + requestFocusNEWTChild(); + } else if(oldF == NewtCanvasAWT.this && newF == null) { // focus traversal to NEWT - NOP - if(DEBUG) { - System.err.println("NewtCanvasAWT.FocusProperty: NEWT focus traversal"); + if(DEBUG) { + System.err.println("NewtCanvasAWT.FocusProperty: NEWT focus"); } } else if(null != newF && newF != NewtCanvasAWT.this) { // focus traversal to another AWT component - if(DEBUG) { + if(DEBUG) { System.err.println("NewtCanvasAWT.FocusProperty: lost focus - clear focus"); } if(newtChild.getDelegatedWindow() instanceof DriverClearFocus) { @@ -256,22 +346,64 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } } } - } + } } private final FocusPropertyChangeListener focusPropertyChangeListener = new FocusPropertyChangeListener(); private volatile KeyboardFocusManager keyboardFocusManager = null; - /** sets a new NEWT child, provoking reparenting. */ - private NewtCanvasAWT setNEWTChild(Window child) { - if(newtChild!=child) { - newtChild = child; - if(isDisplayable()) { - // reparent right away, addNotify has been called already - final java.awt.Container cont = AWTMisc.getContainer(this); - reparentWindow( (null!=child) ? true : false, cont ); + private final void requestFocusNEWTChild() { + if(null!=newtChild) { + newtChild.setFocusAction(null); + if(isOnscreen) { + AWTEDTExecutor.singleton.invoke(false, awtClearGlobalFocusOwner); } + newtChild.requestFocus(); + newtChild.setFocusAction(focusAction); + } + } + + /** + * Sets a new NEWT child, provoking reparenting. + * <p> + * A previously detached <code>newChild</code> will be released to top-level status + * and made invisible. + * </p> + * <p> + * Note: When switching NEWT child's, detaching the previous first via <code>setNEWTChild(null)</code> + * produced much cleaner visual results. + * </p> + * @return the previous attached newt child. + */ + public Window setNEWTChild(Window newChild) { + synchronized(sync) { + final Window prevChild = newtChild; + if(DEBUG) { + System.err.println("NewtCanvasAWT.setNEWTChild.0: win "+newtWinHandleToHexString(prevChild)+" -> "+newtWinHandleToHexString(newChild)); + } + final java.awt.Container cont = AWTMisc.getContainer(this); + // remove old one + if(null != newtChild) { + detachNewtChild( cont ); + newtChild = null; + } + // add new one, reparent only if ready + newtChild = newChild; + + updateLayoutSize(); + // will be done later at paint/display/..: attachNewtChild(cont); + + return prevChild; + } + } + + private final void updateLayoutSize() { + final Window w = newtChild; + if( null != w ) { + // use NEWT child's size for min/pref size! + java.awt.Dimension minSize = new java.awt.Dimension(w.getWidth(), w.getHeight()); + setMinimumSize(minSize); + setPreferredSize(minSize); } - return this; } /** @return the current NEWT child */ @@ -282,145 +414,92 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto /** @return this AWT Canvas NativeWindow representation, may be null in case {@link #removeNotify()} has been called, * or {@link #addNotify()} hasn't been called yet.*/ public NativeWindow getNativeWindow() { return jawtWindow; } - + + @Override public WindowClosingMode getDefaultCloseOperation() { return awtWindowClosingProtocol.getDefaultCloseOperation(); } + @Override public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { return awtWindowClosingProtocol.setDefaultCloseOperation(op); } - /* package */ void configureNewtChild(boolean attach) { - if(null!=awtAdapter) { - awtAdapter.removeFrom(this); - awtAdapter=null; - } - if(null!=awtMouseAdapter) { - awtMouseAdapter.removeFrom(this); - awtMouseAdapter = null; - } - if(null!=awtKeyAdapter) { - awtKeyAdapter.removeFrom(this); - awtKeyAdapter = null; - } - newtChild.setKeyboardFocusHandler(null); - if(null != keyboardFocusManager) { - keyboardFocusManager.removePropertyChangeListener("focusOwner", focusPropertyChangeListener); - keyboardFocusManager = null; - } - - if( null != newtChild ) { - if(attach) { - if(null == jawtWindow.getGraphicsConfiguration()) { - throw new InternalError("XXX"); - } - isOnscreen = jawtWindow.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); - awtAdapter = new AWTParentWindowAdapter(jawtWindow, newtChild).addTo(this); - newtChild.addWindowListener(clearAWTMenusOnNewtFocus); - newtChild.setFocusAction(focusAction); // enable AWT focus traversal - newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); - awtWindowClosingProtocol.addClosingListenerOneShot(); - keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - keyboardFocusManager.addPropertyChangeListener("focusOwner", focusPropertyChangeListener); - if(isOnscreen) { - // onscreen newt child needs to fwd AWT focus - newtChild.setKeyboardFocusHandler(newtFocusTraversalKeyListener); - } else { - // offscreen newt child requires AWT to fwd AWT key/mouse event - awtMouseAdapter = new AWTMouseAdapter(newtChild).addTo(this); - awtKeyAdapter = new AWTKeyAdapter(newtChild).addTo(this); - } - } else { - newtChild.removeWindowListener(clearAWTMenusOnNewtFocus); - newtChild.setFocusAction(null); - newtChild.setDefaultCloseOperation(newtChildCloseOp); - awtWindowClosingProtocol.removeClosingListener(); - } + /** + * Mitigates Bug 910 (IcedTea-Web), i.e. crash via removeNotify() invoked before Applet.destroy(). + * <p> + * <code>skipJAWTDestroy</code> defaults to <code>false</code>. + * Due to above IcedTea-Web issue the <code>Applet</code> code needs to avoid JAWT destruction before + * <code>Applet.destroy()</code> is reached by setting <code>skipJAWTDestroy</code> to <code>true</code>. + * Afterwards the value should be reset to <code>false</code> and {@link #destroy()} needs to be called, + * which finally will perform the pending JAWT destruction. + * </p> + */ + public final void setSkipJAWTDestroy(boolean v) { skipJAWTDestroy = v; } + /** See {@link #setSkipJAWTDestroy(boolean)}. */ + public final boolean getSkipJAWTDestroy() { return skipJAWTDestroy; } + + private final void determineIfApplet() { + isApplet = false; + Component c = this; + while(!isApplet && null != c) { + isApplet = c instanceof Applet; + c = c.getParent(); } } @Override public void addNotify() { - - // before native peer is valid: X11 - disableBackgroundErase(); - - // creates the native peer - super.addNotify(); - - // after native peer is valid: Windows - disableBackgroundErase(); - - java.awt.Container cont = AWTMisc.getContainer(this); - if(DEBUG) { - // if ( isShowing() == false ) -> Container was not visible yet. - // if ( isShowing() == true ) -> Container is already visible. - System.err.println("NewtCanvasAWT.addNotify: "+newtChild+", "+this+", visible "+isVisible()+", showing "+isShowing()+ - ", displayable "+isDisplayable()+" -> "+cont); - } - reparentWindow(true, cont); + if( Beans.isDesignTime() ) { + super.addNotify(); + } else { + // before native peer is valid: X11 + disableBackgroundErase(); + + // creates the native peer + super.addNotify(); + + // after native peer is valid: Windows + disableBackgroundErase(); + + synchronized(sync) { + determineIfApplet(); + if(DEBUG) { + System.err.println("NewtCanvasAWT.addNotify.0 - isApplet "+isApplet+", addedOnAWTEDT "+EventQueue.isDispatchThread()+" @ "+currentThreadName()); + Thread.dumpStack(); + } + jawtWindow = NewtFactoryAWT.getNativeWindow(NewtCanvasAWT.this, null != newtChild ? newtChild.getRequestedCapabilities() : null); + jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); + awtWindowClosingProtocol.addClosingListener(); + componentAdded = true; // Bug 910 + if(DEBUG) { + // if ( isShowing() == false ) -> Container was not visible yet. + // if ( isShowing() == true ) -> Container is already visible. + System.err.println("NewtCanvasAWT.addNotify.X: twin "+newtWinHandleToHexString(newtChild)+ + ", comp "+this+", visible "+isVisible()+", showing "+isShowing()+ + ", displayable "+isDisplayable()+", cont "+AWTMisc.getContainer(this)); + } + } + } } @Override public void removeNotify() { - java.awt.Container cont = AWTMisc.getContainer(this); - if(DEBUG) { - System.err.println("NewtCanvasAWT.removeNotify: "+newtChild+", from "+cont); + if( Beans.isDesignTime() ) { + super.removeNotify(); + } else { + if(DEBUG) { + System.err.println("NewtCanvasAWT.removeNotify.0 - isApplet "+isApplet+" @ "+currentThreadName()); + Thread.dumpStack(); + } + componentAdded = false; // Bug 910 + awtWindowClosingProtocol.removeClosingListener(); + destroyImpl(true /* removeNotify */, false /* windowClosing */); + super.removeNotify(); + if(DEBUG) { + System.err.println("NewtCanvasAWT.removeNotify.X @ "+currentThreadName()); + } } - reparentWindow(false, cont); - super.removeNotify(); - } - - void reparentWindow(boolean add, java.awt.Container cont) { - if(null==newtChild) { - return; // nop - } - - newtChild.setFocusAction(null); // no AWT focus traversal .. - if(add) { - jawtWindow = NewtFactoryAWT.getNativeWindow(this, newtChild.getRequestedCapabilities()); - jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); - if(DEBUG) { - System.err.println("NewtCanvasAWT.reparentWindow: newtChild: "+newtChild); - } - final int w; - final int h; - if(isPreferredSizeSet()) { - java.awt.Dimension d = getPreferredSize(); - w = d.width; - h = d.height; - } else { - final java.awt.Dimension min; - if(this.isMinimumSizeSet()) { - min = getMinimumSize(); - } else { - min = new java.awt.Dimension(0, 0); - } - java.awt.Insets ins = cont.getInsets(); - w = Math.max(min.width, cont.getWidth() - ins.left - ins.right); - h = Math.max(min.height, cont.getHeight() - ins.top - ins.bottom); - } - setSize(w, h); - newtChild.setSize(w, h); - newtChild.reparentWindow(jawtWindow); - newtChild.setVisible(true); - configureNewtChild(true); - newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener - newtChild.windowRepaint(0, 0, w, h); - - // force this AWT Canvas to be focus-able, - // since this it is completely covered by the newtChild (z-order). - setFocusable(true); - } else { - configureNewtChild(false); - newtChild.setVisible(false); - newtChild.reparentWindow(null); - if(null != jawtWindow) { - NewtFactoryAWT.destroyNativeWindow(jawtWindow); - jawtWindow=null; - } - } } /** @@ -430,88 +509,419 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto * <li> Disconnects the NEWT Child from this Canvas NativeWindow, reparent to NULL </li> * <li> Issues <code>destroy()</code> on the NEWT Child</li> * <li> Remove reference to the NEWT Child</li> - * <li> Remove this Canvas from it's parent.</li> * </ul> * @see Window#destroy() */ public final void destroy() { - if(null!=newtChild) { - java.awt.Container cont = AWTMisc.getContainer(this); + if(DEBUG) { + System.err.println("NewtCanvasAWT.destroy() @ "+currentThreadName()); + Thread.dumpStack(); + } + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + @Override + public void run() { + destroyImpl(false /* removeNotify */, false /* windowClosing */); + } } ); + } + + private final void destroyImpl(boolean removeNotify, boolean windowClosing) { + synchronized(sync) { + final java.awt.Container cont = AWTMisc.getContainer(this); if(DEBUG) { - System.err.println("NewtCanvasAWT.destroy(): "+newtChild+", from "+cont); + System.err.println("NewtCanvasAWT.destroyImpl @ "+currentThreadName()); + System.err.println("NewtCanvasAWT.destroyImpl.0 - isApplet "+isApplet+", isOnAWTEDT "+EventQueue.isDispatchThread()+", skipJAWTDestroy "+skipJAWTDestroy+ + "; removeNotify "+removeNotify+", windowClosing "+windowClosing+", destroyJAWTPending "+destroyJAWTPending+ + ", hasJAWT "+(null!=jawtWindow)+", hasNEWT "+(null!=newtChild)+ + "): nw "+newtWinHandleToHexString(newtChild)+", from "+cont); } - configureNewtChild(false); - if(null!=jawtWindow) { - NewtFactoryAWT.destroyNativeWindow(jawtWindow); - jawtWindow=null; + if( null !=newtChild ) { + detachNewtChild(cont); + + if( !removeNotify ) { + final Window cWin = newtChild; + final Window dWin = cWin.getDelegatedWindow(); + newtChild=null; + if( windowClosing && dWin instanceof WindowImpl ) { + ((WindowImpl)dWin).windowDestroyNotify(true); + } else { + cWin.destroy(); + } + } } - newtChild.setVisible(false); - newtChild.reparentWindow(null); - newtChild.destroy(); - newtChild=null; - if(null!=cont) { - cont.remove(this); + if( ( destroyJAWTPending || removeNotify || windowClosing ) && null!=jawtWindow ) { + if( skipJAWTDestroy ) { + // Bug 910 - See setSkipJAWTDestroy(boolean) + destroyJAWTPending = true; + } else { + NewtFactoryAWT.destroyNativeWindow(jawtWindow); + jawtWindow=null; + destroyJAWTPending = false; + } } } - } + } @Override public void paint(Graphics g) { - awtWindowClosingProtocol.addClosingListenerOneShot(); - if(null!=newtChild) { - newtChild.windowRepaint(0, 0, getWidth(), getHeight()); + synchronized(sync) { + if( validateComponent(true) && !printActive ) { + newtChild.windowRepaint(0, 0, getWidth(), getHeight()); + } } } @Override public void update(Graphics g) { - awtWindowClosingProtocol.addClosingListenerOneShot(); - if(null!=newtChild) { - newtChild.windowRepaint(0, 0, getWidth(), getHeight()); - } + paint(g); } - private final void requestFocusNEWTChild() { - if(null!=newtChild) { - newtChild.setFocusAction(null); - if(isOnscreen) { - KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); + @SuppressWarnings("deprecation") + @Override + public void reshape(int x, int y, int width, int height) { + synchronized (getTreeLock()) { // super.reshape(..) claims tree lock, so we do extend it's lock over reshape + synchronized(sync) { + super.reshape(x, y, width, height); + if(DEBUG) { + System.err.println("NewtCanvasAWT.reshape: "+x+"/"+y+" "+width+"x"+height); + } + if( validateComponent(true) ) { + // newtChild.setSize(width, height); + } } - newtChild.requestFocus(); - newtChild.setFocusAction(focusAction); } } + private volatile boolean printActive = false; + private GLAnimatorControl printAnimator = null; + private GLAutoDrawable printGLAD = null; + private AWTTilePainter printAWTTiles = null; + + private final GLAutoDrawable getGLAD() { + if( null != newtChild && newtChild instanceof GLAutoDrawable ) { + return (GLAutoDrawable)newtChild; + } + return null; + } + @Override - public void requestFocus() { - super.requestFocus(); - requestFocusNEWTChild(); + public void setupPrint(double scaleMatX, double scaleMatY, int numSamples, int tileWidth, int tileHeight) { + printActive = true; + final int componentCount = isOpaque() ? 3 : 4; + final TileRenderer printRenderer = new TileRenderer(); + printAWTTiles = new AWTTilePainter(printRenderer, componentCount, scaleMatX, scaleMatY, numSamples, tileWidth, tileHeight, DEBUG); + AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, setupPrintOnEDT); } + private final Runnable setupPrintOnEDT = new Runnable() { + @Override + public void run() { + synchronized(sync) { + if( !validateComponent(true) ) { + if(DEBUG) { + System.err.println(currentThreadName()+": Info: NewtCanvasAWT setupPrint - skipped GL render, drawable not valid yet"); + } + printActive = false; + return; // not yet available .. + } + if( !isShowing() ) { + if(DEBUG) { + System.err.println(currentThreadName()+": Info: NewtCanvasAWT setupPrint - skipped GL render, drawable valid, canvas not showing"); + } + printActive = false; + return; // not yet available .. + } + final GLAutoDrawable glad = getGLAD(); + if( null == glad ) { + if( DEBUG ) { + System.err.println("AWT print.setup exit, newtChild not a GLAutoDrawable: "+newtChild); + } + printActive = false; + return; + } + printAnimator = glad.getAnimator(); + if( null != printAnimator ) { + printAnimator.remove(glad); + } + printGLAD = glad; // _not_ default, shall be replaced by offscreen GLAD + final GLCapabilities caps = (GLCapabilities)glad.getChosenGLCapabilities().cloneMutable(); + final int printNumSamples = printAWTTiles.getNumSamples(caps); + GLDrawable printDrawable = printGLAD.getDelegatedDrawable(); + final boolean reqNewGLADSamples = printNumSamples != caps.getNumSamples(); + final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getWidth() || + printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getHeight(); + final boolean reqNewGLADOnscrn = caps.isOnscreen(); + + // It is desired to use a new offscreen GLAD, however Bug 830 forbids this for AA onscreen context. + // Bug 830: swapGLContextAndAllGLEventListener and onscreen MSAA w/ NV/GLX + final boolean reqNewGLAD = !caps.getSampleBuffers() && ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize ); + if( DEBUG ) { + System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+"], "+ + ", drawableSize "+printDrawable.getWidth()+"x"+printDrawable.getHeight()+ + ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+ + ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+ + ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator); + } + if( reqNewGLAD ) { + caps.setDoubleBuffered(false); + caps.setOnscreen(false); + if( printNumSamples != caps.getNumSamples() ) { + caps.setSampleBuffers(0 < printNumSamples); + caps.setNumSamples(printNumSamples); + } + final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); + printGLAD = factory.createOffscreenAutoDrawable(null, caps, null, + printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE, + printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE); + GLDrawableUtil.swapGLContextAndAllGLEventListener(glad, printGLAD); + printDrawable = printGLAD.getDelegatedDrawable(); + } + printAWTTiles.setGLOrientation(printGLAD.isGLOriented(), printGLAD.isGLOriented()); + printAWTTiles.renderer.setTileSize(printDrawable.getWidth(), printDrawable.getHeight(), 0); + printAWTTiles.renderer.attachAutoDrawable(printGLAD); + if( DEBUG ) { + System.err.println("AWT print.setup "+printAWTTiles); + System.err.println("AWT print.setup AA "+printNumSamples+", "+caps); + System.err.println("AWT print.setup printGLAD: "+printGLAD.getWidth()+"x"+printGLAD.getHeight()+", "+printGLAD); + System.err.println("AWT print.setup printDraw: "+printDrawable.getWidth()+"x"+printDrawable.getHeight()+", "+printDrawable); + } + } + } + }; @Override - public boolean requestFocus(boolean temporary) { - final boolean res = super.requestFocus(temporary); - if(res) { - requestFocusNEWTChild(); + public void releasePrint() { + if( !printActive || null == printGLAD ) { + throw new IllegalStateException("setupPrint() not called"); } - return res; + // sendReshape = false; // clear reshape flag + AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, releasePrintOnEDT); + newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener } + private final Runnable releasePrintOnEDT = new Runnable() { + @Override + public void run() { + synchronized(sync) { + if( DEBUG ) { + System.err.println("AWT print.release "+printAWTTiles); + } + final GLAutoDrawable glad = getGLAD(); + printAWTTiles.dispose(); + printAWTTiles= null; + if( printGLAD != glad ) { + GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, glad); + printGLAD.destroy(); + } + printGLAD = null; + if( null != printAnimator ) { + printAnimator.add(glad); + printAnimator = null; + } + printActive = false; + } + } + }; @Override - public boolean requestFocusInWindow() { - final boolean res = super.requestFocusInWindow(); - if(res) { - requestFocusNEWTChild(); + public void print(Graphics graphics) { + synchronized(sync) { + if( !printActive || null == printGLAD ) { + throw new IllegalStateException("setupPrint() not called"); + } + if(DEBUG && !EventQueue.isDispatchThread()) { + System.err.println(currentThreadName()+": Warning: GLCanvas print - not called from AWT-EDT"); + // we cannot dispatch print on AWT-EDT due to printing internal locking .. + } + + final Graphics2D g2d = (Graphics2D)graphics; + try { + printAWTTiles.setupGraphics2DAndClipBounds(g2d, getWidth(), getHeight()); + final TileRenderer tileRenderer = printAWTTiles.renderer; + if( DEBUG ) { + System.err.println("AWT print.0: "+tileRenderer); + } + if( !tileRenderer.eot() ) { + try { + do { + tileRenderer.display(); + } while ( !tileRenderer.eot() ); + if( DEBUG ) { + System.err.println("AWT print.1: "+printAWTTiles); + } + tileRenderer.reset(); + } finally { + printAWTTiles.resetGraphics2D(); + } + } + } catch (NoninvertibleTransformException nte) { + System.err.println("Catched: Inversion failed of: "+g2d.getTransform()); + nte.printStackTrace(); + } + if( DEBUG ) { + System.err.println("AWT print.X: "+printAWTTiles); + } } - return res; } - @Override - public boolean requestFocusInWindow(boolean temporary) { - final boolean res = super.requestFocusInWindow(temporary); - if(res) { - requestFocusNEWTChild(); + private final boolean validateComponent(boolean attachNewtChild) { + if( Beans.isDesignTime() || !isDisplayable() ) { + return false; + } + if ( null == newtChild || null == jawtWindow ) { + return false; + } + if( 0 >= getWidth() || 0 >= getHeight() ) { + return false; + } + + if( attachNewtChild && !newtChildAttached && null != newtChild ) { + attachNewtChild(); + } + + return true; + } + + private final void configureNewtChild(boolean attach) { + awtAdapter.clear(); + awtMouseAdapter.clear(); + awtKeyAdapter.setConsumeAWTEvent(false); + awtMouseAdapter.clear(); + awtKeyAdapter.setConsumeAWTEvent(false); + + if(null != keyboardFocusManager) { + keyboardFocusManager.removePropertyChangeListener("focusOwner", focusPropertyChangeListener); + keyboardFocusManager = null; } - return res; + + if( null != newtChild ) { + newtChild.setKeyboardFocusHandler(null); + if(attach) { + if(null == jawtWindow.getGraphicsConfiguration()) { + throw new InternalError("XXX"); + } + isOnscreen = jawtWindow.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); + awtAdapter.setDownstream(jawtWindow, newtChild); + newtChild.addWindowListener(clearAWTMenusOnNewtFocus); + newtChild.setFocusAction(focusAction); // enable AWT focus traversal + newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); + keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + keyboardFocusManager.addPropertyChangeListener("focusOwner", focusPropertyChangeListener); + // force this AWT Canvas to be focus-able, + // since this it is completely covered by the newtChild (z-order). + setFocusable(true); + if(isOnscreen) { + // onscreen newt child needs to fwd AWT focus + newtChild.setKeyboardFocusHandler(newtFocusTraversalKeyListener); + } else { + // offscreen newt child requires AWT to fwd AWT key/mouse event + awtMouseAdapter.setDownstream(newtChild); + // We cannot consume AWT mouse click, since it would disable focus via mouse click! + // awtMouseAdapter.setConsumeAWTEvent(true); + awtKeyAdapter.setDownstream(newtChild); + // We manually transfer the focus via NEWT KeyListener, hence we can mark AWT keys as consumed! + awtKeyAdapter.setConsumeAWTEvent(true); + } + } else { + newtChild.removeWindowListener(clearAWTMenusOnNewtFocus); + newtChild.setFocusAction(null); + newtChild.setDefaultCloseOperation(newtChildCloseOp); + setFocusable(false); + } + } + } + + /** + * Returns <code>true</code> if Key and Mouse input events will be passed through AWT, + * otherwise only the {@link #getNEWTChild() NEWT child} will receive them. + * <p> + * Normally only the {@link #getNEWTChild() NEWT child} will receive Key and Mouse input events. + * In offscreen mode, e.g. OSX/CALayer, the AWT events will be received and translated into NEWT events + * and delivered to the NEWT child window.<br/> + * Note: AWT key events will {@link java.awt.event.InputEvent#consume() consumed} in pass-through mode. + * </p> + */ + public final boolean isAWTEventPassThrough() { + return !isOnscreen; + } + + private final void attachNewtChild() { + if( null == newtChild || null == jawtWindow || newtChildAttached ) { + return; // nop + } + if(DEBUG) { + // if ( isShowing() == false ) -> Container was not visible yet. + // if ( isShowing() == true ) -> Container is already visible. + System.err.println("NewtCanvasAWT.attachNewtChild.0 @ "+currentThreadName()); + System.err.println("\twin "+newtWinHandleToHexString(newtChild)+ + ", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()+ + ", comp "+this+", visible "+isVisible()+", showing "+isShowing()+", displayable "+isDisplayable()+ + ", cont "+AWTMisc.getContainer(this)); + } + + newtChildAttached = true; + newtChild.setFocusAction(null); // no AWT focus traversal .. + if(DEBUG) { + System.err.println("NewtCanvasAWT.attachNewtChild.1: newtChild: "+newtChild); + } + final int w = getWidth(); + final int h = getHeight(); + if(DEBUG) { + System.err.println("NewtCanvasAWT.attachNewtChild.2: size "+w+"x"+h); + } + newtChild.setVisible(false); + newtChild.setSize(w, h); + newtChild.reparentWindow(jawtWindow, -1, -1, Window.REPARENT_HINT_BECOMES_VISIBLE); + newtChild.addSurfaceUpdatedListener(jawtWindow); + if( jawtWindow.isOffscreenLayerSurfaceEnabled() && + 0 != ( JAWTUtil.JAWT_OSX_CALAYER_QUIRK_POSITION & JAWTUtil.getOSXCALayerQuirks() ) ) { + AWTEDTExecutor.singleton.invoke(false, forceRelayout); + } + newtChild.setVisible(true); + configureNewtChild(true); + newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener + + if(DEBUG) { + System.err.println("NewtCanvasAWT.attachNewtChild.X: win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()+", comp "+this); + } + } + private final Runnable forceRelayout = new Runnable() { + @Override + public void run() { + if(DEBUG) { + System.err.println("NewtCanvasAWT.forceRelayout.0"); + } + // Hack to force proper native AWT layout incl. CALayer components on OSX + final java.awt.Component component = NewtCanvasAWT.this; + final int cW = component.getWidth(); + final int cH = component.getHeight(); + component.setSize(cW+1, cH+1); + component.setSize(cW, cH); + if(DEBUG) { + System.err.println("NewtCanvasAWT.forceRelayout.X"); + } + } }; + + private final void detachNewtChild(java.awt.Container cont) { + if( null == newtChild || null == jawtWindow || !newtChildAttached ) { + return; // nop + } + if(DEBUG) { + // if ( isShowing() == false ) -> Container was not visible yet. + // if ( isShowing() == true ) -> Container is already visible. + System.err.println("NewtCanvasAWT.detachNewtChild.0: win "+newtWinHandleToHexString(newtChild)+ + ", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()+ + ", comp "+this+", visible "+isVisible()+", showing "+isShowing()+", displayable "+isDisplayable()+ + ", cont "+cont); + } + + newtChild.removeSurfaceUpdatedListener(jawtWindow); + newtChildAttached = false; + newtChild.setFocusAction(null); // no AWT focus traversal .. + configureNewtChild(false); + newtChild.setVisible(false); + + newtChild.reparentWindow(null, -1, -1, 0 /* hint */); // will destroy context (offscreen -> onscreen) and implicit detachSurfaceLayer + + if(DEBUG) { + System.err.println("NewtCanvasAWT.detachNewtChild.X: win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()+", comp "+this); + } } // Disables the AWT's erasing of this Canvas's background on Windows @@ -525,6 +935,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto if (!disableBackgroundEraseInitialized) { try { AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override public Object run() { try { Class<?> clazz = getToolkit().getClass(); @@ -563,5 +974,14 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } } } + + protected static String currentThreadName() { return "["+Thread.currentThread().getName()+", isAWT-EDT "+EventQueue.isDispatchThread()+"]"; } + + static String newtWinHandleToHexString(Window w) { + return null != w ? toHexString(w.getWindowHandle()) : "nil"; + } + static String toHexString(long l) { + return "0x"+Long.toHexString(l); + } } diff --git a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java index d06aca039..9cecca9c5 100755..100644 --- a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java +++ b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java @@ -32,6 +32,7 @@ import java.awt.BorderLayout; import java.awt.Button; import java.awt.Component; import java.awt.Container; +import java.awt.EventQueue; import java.awt.event.KeyListener; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; @@ -42,15 +43,19 @@ import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLProfile; +import jogamp.nativewindow.jawt.JAWTUtil; + +import com.jogamp.common.util.awt.AWTEDTExecutor; import com.jogamp.newt.awt.NewtCanvasAWT; import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.newt.util.applet.JOGLNewtAppletBase; -/** +/** * Simple GLEventListener deployment as an applet using JOGL. This demo must be * referenced from a web page via an <applet> tag. - * + * * <p> - * Example of an applet tag using GearsES2 within the applet area (normal case): + * Example of an applet tag using GearsES2 within the applet area (normal case): * <pre> <applet width=100 height=100> <param name="java_arguments" value="-Dsun.java2d.noddraw=true"> @@ -63,9 +68,9 @@ import com.jogamp.newt.opengl.GLWindow; </applet>Hello Gears ! * </pre> * </p> - * + * * <p> - * Example of an applet tag using GearsES2 in an undecorated, translucent, closeable and always-on-top window: + * Example of an applet tag using GearsES2 in an undecorated, translucent, closeable and always-on-top window: * <pre> <applet width=1 height=1> <param name="java_arguments" value="-Dsun.java2d.noddraw=true"> @@ -93,22 +98,22 @@ import com.jogamp.newt.opengl.GLWindow; @SuppressWarnings("serial") public class JOGLNewtApplet1Run extends Applet { public static final boolean DEBUG = JOGLNewtAppletBase.DEBUG; - - GLWindow glWindow; - NewtCanvasAWT newtCanvasAWT; - JOGLNewtAppletBase base; + + GLWindow glWindow = null; + NewtCanvasAWT newtCanvasAWT = null; + JOGLNewtAppletBase base = null; /** if valid glStandalone:=true (own window) ! */ - int glXd=Integer.MAX_VALUE, glYd=Integer.MAX_VALUE, glWidth=Integer.MAX_VALUE, glHeight=Integer.MAX_VALUE; - boolean glStandalone = false; + int glXd=Integer.MAX_VALUE, glYd=Integer.MAX_VALUE, glWidth=Integer.MAX_VALUE, glHeight=Integer.MAX_VALUE; + @Override public void init() { if(DEBUG) { - System.err.println("JOGLNewtApplet1Run.init() START"); + System.err.println("JOGLNewtApplet1Run.init() START - "+currentThreadName()); } if(!(this instanceof Container)) { throw new RuntimeException("This Applet is not a AWT Container"); } - Container container = (Container) this; + final Container container = this; String glEventListenerClazzName=null; String glProfileName=null; @@ -134,7 +139,7 @@ public class JOGLNewtApplet1Run extends Applet { glCloseable = JOGLNewtAppletBase.str2Bool(getParameter("gl_closeable"), glCloseable); glOpaque = JOGLNewtAppletBase.str2Bool(getParameter("gl_opaque"), glOpaque); glAlphaBits = JOGLNewtAppletBase.str2Int(getParameter("gl_alpha"), glAlphaBits); - glNumMultisampleBuffer = JOGLNewtAppletBase.str2Int(getParameter("gl_multisamplebuffer"), glNumMultisampleBuffer); + glNumMultisampleBuffer = JOGLNewtAppletBase.str2Int(getParameter("gl_multisamplebuffer"), glNumMultisampleBuffer); glXd = JOGLNewtAppletBase.str2Int(getParameter("gl_dx"), glXd); glYd = JOGLNewtAppletBase.str2Int(getParameter("gl_dy"), glYd); glWidth = JOGLNewtAppletBase.str2Int(getParameter("gl_width"), glWidth); @@ -147,7 +152,7 @@ public class JOGLNewtApplet1Run extends Applet { if(null==glEventListenerClazzName) { throw new RuntimeException("No applet parameter 'gl_event_listener_class'"); } - glStandalone = Integer.MAX_VALUE>glXd && Integer.MAX_VALUE>glYd && Integer.MAX_VALUE>glWidth && Integer.MAX_VALUE>glHeight; + final boolean glStandalone = Integer.MAX_VALUE>glXd && Integer.MAX_VALUE>glYd && Integer.MAX_VALUE>glWidth && Integer.MAX_VALUE>glHeight; if(DEBUG) { System.err.println("JOGLNewtApplet1Run Configuration:"); System.err.println("glStandalone: "+glStandalone); @@ -167,8 +172,8 @@ public class JOGLNewtApplet1Run extends Applet { System.err.println("glNumMultisampleBuffer: "+glNumMultisampleBuffer); System.err.println("glNoDefaultKeyListener: "+glNoDefaultKeyListener); } - - base = new JOGLNewtAppletBase(glEventListenerClazzName, + + base = new JOGLNewtAppletBase(glEventListenerClazzName, glSwapInterval, glNoDefaultKeyListener, glCloseable, @@ -190,10 +195,13 @@ public class JOGLNewtApplet1Run extends Applet { glWindow.setDefaultCloseOperation(glCloseable ? WindowClosingMode.DISPOSE_ON_CLOSE : WindowClosingMode.DO_NOTHING_ON_CLOSE); container.setLayout(new BorderLayout()); if(appletDebugTestBorder) { - container.add(new Button("North"), BorderLayout.NORTH); - container.add(new Button("South"), BorderLayout.SOUTH); - container.add(new Button("East"), BorderLayout.EAST); - container.add(new Button("West"), BorderLayout.WEST); + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + public void run() { + container.add(new Button("North"), BorderLayout.NORTH); + container.add(new Button("South"), BorderLayout.SOUTH); + container.add(new Button("East"), BorderLayout.EAST); + container.add(new Button("West"), BorderLayout.WEST); + } } ); } base.init(glWindow); if(base.isValid()) { @@ -209,32 +217,45 @@ public class JOGLNewtApplet1Run extends Applet { addKeyListener((KeyListener)glEventListener); } } - if(glStandalone) { - newtCanvasAWT = null; - } else { - newtCanvasAWT = new NewtCanvasAWT(glWindow); - container.add(newtCanvasAWT, BorderLayout.CENTER); - container.validate(); + if( !glStandalone ) { + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + public void run() { + newtCanvasAWT = new NewtCanvasAWT(glWindow); + newtCanvasAWT.setSkipJAWTDestroy(true); // Bug 910 + container.add(newtCanvasAWT, BorderLayout.CENTER); + container.validate(); + } } ); } } catch (Throwable t) { throw new RuntimeException(t); } if(DEBUG) { - System.err.println("JOGLNewtApplet1Run.init() END"); + System.err.println("JOGLNewtApplet1Run.init() END - "+currentThreadName()); } } + private static String currentThreadName() { return "["+Thread.currentThread().getName()+", isAWT-EDT "+EventQueue.isDispatchThread()+"]"; } + + @Override public void start() { if(DEBUG) { - System.err.println("JOGLNewtApplet1Run.start() START"); + System.err.println("JOGLNewtApplet1Run.start() START (isVisible "+isVisible()+", isDisplayable "+isDisplayable()+") - "+currentThreadName()); } - this.validate(); - this.setVisible(true); - - final java.awt.Point p0 = this.getLocationOnScreen(); - if(glStandalone) { + final java.awt.Point[] p0 = { null }; + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + public void run() { + setVisible(true); + p0[0] = getLocationOnScreen(); + if( null != newtCanvasAWT ) { + newtCanvasAWT.setFocusable(true); + newtCanvasAWT.requestFocus(); + } + } + }); + if( null == newtCanvasAWT ) { + glWindow.requestFocus(); glWindow.setSize(glWidth, glHeight); - glWindow.setPosition(p0.x+glXd, p0.y+glYd); + glWindow.setPosition(p0[0].x+glXd, p0[0].y+glYd); } if(DEBUG) { Component topC = this; @@ -251,34 +272,54 @@ public class JOGLNewtApplet1Run extends Applet { System.err.println("GLWindow: "+glWindow); } base.start(); + if( null != newtCanvasAWT && + newtCanvasAWT.isOffscreenLayerSurfaceEnabled() && + 0 != ( JAWTUtil.JAWT_OSX_CALAYER_QUIRK_POSITION & JAWTUtil.getOSXCALayerQuirks() ) ) { + // force relayout + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + public void run() { + final int cW = newtCanvasAWT.getWidth(); + final int cH = newtCanvasAWT.getHeight(); + newtCanvasAWT.setSize(cW+1, cH+1); + newtCanvasAWT.setSize(cW, cH); + } } ); + } if(DEBUG) { - System.err.println("JOGLNewtApplet1Run.start() END"); + System.err.println("JOGLNewtApplet1Run.start() END - "+currentThreadName()); } } + @Override public void stop() { if(DEBUG) { - System.err.println("JOGLNewtApplet1Run.stop() START"); + System.err.println("JOGLNewtApplet1Run.stop() START - "+currentThreadName()); } base.stop(); if(DEBUG) { - System.err.println("JOGLNewtApplet1Run.stop() END"); + System.err.println("JOGLNewtApplet1Run.stop() END - "+currentThreadName()); } } + @Override public void destroy() { if(DEBUG) { - System.err.println("JOGLNewtApplet1Run.destroy() START"); - } - glWindow.setVisible(false); // hide 1st - if(!glStandalone) { - glWindow.reparentWindow(null); // get out of newtCanvasAWT - this.remove(newtCanvasAWT); // remove newtCanvasAWT + System.err.println("JOGLNewtApplet1Run.destroy() START - "+currentThreadName()); } + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + public void run() { + glWindow.setVisible(false); // hide 1st + if( null != newtCanvasAWT ) { + newtCanvasAWT.setSkipJAWTDestroy(false); // Bug 910 + remove(newtCanvasAWT); // remove newtCanvasAWT incl. glWindow.reparentWindow(null) if not done yet! + newtCanvasAWT.destroy(); + } + } } ); base.destroy(); // destroy glWindow unrecoverable base=null; + glWindow=null; + newtCanvasAWT=null; if(DEBUG) { - System.err.println("JOGLNewtApplet1Run.destroy() END"); + System.err.println("JOGLNewtApplet1Run.destroy() END - "+currentThreadName()); } } } diff --git a/src/newt/classes/com/jogamp/newt/event/DoubleTapScrollGesture.java b/src/newt/classes/com/jogamp/newt/event/DoubleTapScrollGesture.java new file mode 100644 index 000000000..edb2429bb --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/DoubleTapScrollGesture.java @@ -0,0 +1,347 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.newt.event; + +import jogamp.newt.Debug; + +/** + * 2 pointer scroll/rotate gesture handler processing {@link MouseEvent}s + * while producing {@link MouseEvent#EVENT_MOUSE_WHEEL_MOVED} events if gesture is completed. + * <p> + * Criteria related to parameters: + * <pre> + * - doubleTapSlop (scaled in pixels): + * - Max 2 finger distance to start 'scroll' mode + * - Max. distance diff of current 2-pointer middle and initiated 2-pointer middle. + * + * - touchSlop (scaled in pixels): + * - Min. movement w/ 2 pointer within ScaledDoubleTapSlop starting 'scroll' mode + * + * - Avoid computation if not within gesture, especially for MOVE/DRAG + * + * - Only allow gesture to start with PRESS + * + * - Leave gesture completely with RELEASE of both/all fingers, or dist-diff exceeds doubleTapSlop + * + * - Tolerate temporary lift 1 of 2 pointer + * + * - Always validate pointer-id + * </pre> + * </p> + * Implementation uses a n-state to get detect gesture: + * <p> + * <table border="1"> + * <tr><th>from</th> <th>to</th> <th>action</th></tr> + * <tr><td>NONE</td> <td>1PRESS</td> <td>1-pointer-pressed</td></tr> + * <tr><td>1PRESS</td> <td>2PRESS_T</td> <td>2-pointer-pressed within doubleTapSlope</td></tr> + * <tr><td>2PRESS_T</td> <td>SCROLL</td> <td>2-pointer dragged, dist-diff within doubleTapSlop and scrollLen >= scrollSlop</td></tr> + * <tr><td>2PRESS_C</td> <td>SCROLL</td> <td>2-pointer dragged, dist-diff within doubleTapSlop</td></tr> + * <tr><td>SCROLL</td> <td>SCROLL</td> <td>2-pointer dragged, dist-diff within doubleTapSlop</td></tr> + * </table> + * State ST_2PRESS_C merely exist to pick up gesture after one pointer has been lost temporarily. + * </p> + * <p> + * {@link #isWithinGesture()} returns gestureState >= 2PRESS_C + * </p> + */ +public class DoubleTapScrollGesture implements GestureHandler { + /** Scroll threshold in pixels (fallback), defaults to 16 pixels. Can be overriden by integer property <code>newt.event.scroll_slop_pixel</code>.*/ + public static final int SCROLL_SLOP_PIXEL; + /** Two pointer 'double tap' slop in pixels (fallback), defaults to 104 pixels. Can be overriden by integer property <code>newt.event.double_tap_slop_pixel</code>.*/ + public static final int DOUBLE_TAP_SLOP_PIXEL; + + /** Scroll threshold in millimeter, defaults to 3 mm. Can be overriden by integer property <code>newt.event.scroll_slop_mm</code>.*/ + public static final float SCROLL_SLOP_MM; + /** Two pointer 'double tap' slop in millimeter, defaults to 20 mm. Can be overriden by integer property <code>newt.event.double_tap_slop_mm</code>.*/ + public static final float DOUBLE_TAP_SLOP_MM; + + static { + Debug.initSingleton(); + + SCROLL_SLOP_PIXEL = Debug.getIntProperty("newt.event.scroll_slop_pixel", true, 16); + DOUBLE_TAP_SLOP_PIXEL = Debug.getIntProperty("newt.event.double_tap_slop_pixel", true, 104); + SCROLL_SLOP_MM = Debug.getIntProperty("newt.event.scroll_slop_mm", true, 3); + DOUBLE_TAP_SLOP_MM = Debug.getIntProperty("newt.event.double_tap_slop_mm", true, 20); + } + + private static final int ST_NONE = 0; + private static final int ST_1PRESS = 1; + private static final int ST_2PRESS_T = 2; + private static final int ST_2PRESS_C = 3; + private static final int ST_SCROLL = 4; + + private final int scrollSlop, scrollSlopSquare, doubleTapSlop, doubleTapSlopSquare; + private final float[] scrollDistance = new float[] { 0f, 0f }; + private int[] pIds = new int[] { -1, -1 }; + /** See class docu */ + private int gestureState; + private int sqStartDist; + private int lastX, lastY; + private int pointerDownCount; + private MouseEvent hitGestureEvent; + + private static final int getSquareDistance(float x1, float y1, float x2, float y2) { + final int deltaX = (int) x1 - (int) x2; + final int deltaY = (int) y1 - (int) y2; + return deltaX * deltaX + deltaY * deltaY; + } + + private int gesturePointers(final MouseEvent e, final int excludeIndex) { + int j = 0; + for(int i=e.getPointerCount()-1; i>=0; i--) { + if( excludeIndex != i ) { + final int id = e.getPointerId(i); + if( pIds[0] == id || pIds[1] == id ) { + j++; + } + } + } + return j; + } + + /** + * scaledScrollSlop < scaledDoubleTapSlop + * @param scaledScrollSlop Distance a pointer can wander before we think the user is scrolling in <i>pixels</i>. + * @param scaledDoubleTapSlop Distance in <i>pixels</i> between the first touch and second touch to still be considered a double tap. + */ + public DoubleTapScrollGesture(int scaledScrollSlop, int scaledDoubleTapSlop) { + scrollSlop = scaledScrollSlop; + scrollSlopSquare = scaledScrollSlop * scaledScrollSlop; + doubleTapSlop = scaledDoubleTapSlop; + doubleTapSlopSquare = scaledDoubleTapSlop * scaledDoubleTapSlop; + pointerDownCount = 0; + clear(true); + if(DEBUG) { + System.err.println("DoubleTapScroll scrollSlop (scaled) "+scrollSlop); + System.err.println("DoubleTapScroll doubleTapSlop (scaled) "+doubleTapSlop); + } + } + + @Override + public String toString() { + return "DoubleTapScroll[state "+gestureState+", in "+isWithinGesture()+", has "+(null!=hitGestureEvent)+", pc "+pointerDownCount+"]"; + } + + @Override + public void clear(boolean clearStarted) { + scrollDistance[0] = 0f; + scrollDistance[1] = 0f; + hitGestureEvent = null; + if( clearStarted ) { + gestureState = ST_NONE; + sqStartDist = 0; + pIds[0] = -1; + pIds[1] = -1; + lastX = 0; + lastY = 0; + } + } + + @Override + public boolean isWithinGesture() { + return ST_2PRESS_C <= gestureState; + } + + @Override + public boolean hasGesture() { + return null != hitGestureEvent; + } + + @Override + public InputEvent getGestureEvent() { + if( null != hitGestureEvent ) { + final MouseEvent ge = hitGestureEvent; + int modifiers = ge.getModifiers(); + final float[] rotationXYZ = ge.getRotation(); + rotationXYZ[0] = scrollDistance[0] / scrollSlop; + rotationXYZ[1] = scrollDistance[1] / scrollSlop; + if( rotationXYZ[0]*rotationXYZ[0] > rotationXYZ[1]*rotationXYZ[1] ) { + // Horizontal scroll -> SHIFT + modifiers |= com.jogamp.newt.event.InputEvent.SHIFT_MASK; + } + return new MouseEvent(MouseEvent.EVENT_MOUSE_WHEEL_MOVED, ge.getSource(), ge.getWhen(), modifiers, + ge.getAllPointerTypes(), ge.getAllPointerIDs(), + ge.getAllX(), ge.getAllY(), ge.getAllPressures(), ge.getMaxPressure(), + ge.getButton(), ge.getClickCount(), rotationXYZ, scrollSlop); + } + return null; + } + + public final float[] getScrollDistanceXY() { + return scrollDistance; + } + + @Override + public boolean process(final InputEvent in) { + if( null != hitGestureEvent || !(in instanceof MouseEvent) ) { + return true; + } + final MouseEvent pe = (MouseEvent)in; + if( pe.getPointerType(0).getPointerClass() != MouseEvent.PointerClass.Onscreen ) { + return false; + } + pointerDownCount = pe.getPointerCount(); + final int eventType = pe.getEventType(); + final int x0 = pe.getX(0); + final int y0 = pe.getY(0); + switch ( eventType ) { + case MouseEvent.EVENT_MOUSE_PRESSED: { + int gPtr = 0; + if( ST_NONE == gestureState && 1 == pointerDownCount ) { + pIds[0] = pe.getPointerId(0); + pIds[1] = -1; + gestureState = ST_1PRESS; + } else if( ST_NONE < gestureState && 2 == pointerDownCount && 1 == gesturePointers(pe, 0) /* w/o pressed pointer */ ) { + final int x1 = pe.getX(1); + final int y1 = pe.getY(1); + final int xm = (x0+x1)/2; + final int ym = (y0+y1)/2; + + if( ST_1PRESS == gestureState ) { + final int sqDist = getSquareDistance(x0, y0, x1, y1); + final boolean isDistWithinDoubleTapSlop = sqDist < doubleTapSlopSquare; + if( isDistWithinDoubleTapSlop ) { + // very first 2-finger touch-down + gPtr = 2; + pIds[0] = pe.getPointerId(0); + pIds[1] = pe.getPointerId(1); + lastX = xm; + lastY = ym; + sqStartDist = sqDist; + gestureState = ST_2PRESS_T; + } + if(DEBUG) { + final int dist = (int)Math.round(Math.sqrt(sqDist)); + System.err.println(this+".pressed.1: dist "+dist+", gPtr "+gPtr+", distWithin2DTSlop "+isDistWithinDoubleTapSlop+", last "+lastX+"/"+lastY+", "+pe); + } + } else if( ST_2PRESS_C == gestureState ) { // pick up gesture after temp loosing one pointer + gPtr = gesturePointers(pe, -1); + if( 2 == gPtr ) { + // same pointers re-touch-down + lastX = xm; + lastY = ym; + } else { + // other 2 pointers .. should rarely happen! + clear(true); + } + } + } + if(DEBUG) { + System.err.println(this+".pressed: gPtr "+gPtr+", this "+lastX+"/"+lastY+", "+pe); + } + } break; + + case MouseEvent.EVENT_MOUSE_RELEASED: { + pointerDownCount--; // lifted + final int gPtr = gesturePointers(pe, 0); // w/o lifted pointer + if ( 1 == gPtr ) { + // tolerate lifting 1 of 2 gesture pointers temporary + gestureState = ST_2PRESS_C; + } else if( 0 == gPtr ) { + // all lifted + clear(true); + } + if(DEBUG) { + System.err.println(this+".released: gPtr "+gPtr+", "+pe); + } + } break; + + case MouseEvent.EVENT_MOUSE_DRAGGED: { + if( 2 == pointerDownCount && ST_1PRESS < gestureState ) { + final int gPtr = gesturePointers(pe, -1); + if( 2 == gPtr ) { + // same pointers + final int x1 = pe.getX(1); + final int y1 = pe.getY(1); + final int xm = (x0+x1)/2; + final int ym = (y0+y1)/2; + final int sqDist = getSquareDistance(x0, y0, x1, y1); + final boolean isDistDiffWithinDoubleTapSlop = Math.abs(sqDist - sqStartDist) <= doubleTapSlopSquare; + if( isDistDiffWithinDoubleTapSlop ) { + switch( gestureState ) { + case ST_2PRESS_T: { + final int sqScrollLen = getSquareDistance(lastX, lastY, xm, ym); + if( sqScrollLen > scrollSlopSquare ) { // min. scrolling threshold reached + gestureState = ST_SCROLL; + } + } break; + + case ST_2PRESS_C: + gestureState = ST_SCROLL; + break; + + case ST_SCROLL: + scrollDistance[0] = lastX - xm; + scrollDistance[1] = lastY - ym; + hitGestureEvent = pe; + break; + } + if(DEBUG) { + final boolean isDistWithinDoubleTapSlop = sqDist < doubleTapSlopSquare; + final int dist = (int)Math.round(Math.sqrt(sqDist)); + final int sqScrollLen = getSquareDistance(lastX, lastY, xm, ym); + final int scrollLen = (int)Math.round(Math.sqrt(sqScrollLen)); + System.err.println(this+".dragged.1: pDist "+dist+", scrollLen "+scrollLen+", gPtr "+gPtr+" ["+pIds[0]+", "+pIds[1]+"]"+ + ", diffDistWithinTapSlop "+isDistDiffWithinDoubleTapSlop+ + ", distWithin2DTSlop "+isDistWithinDoubleTapSlop+ + ", this "+xm+"/"+ym+", last "+lastX+"/"+lastY+", d "+scrollDistance[0]+"/"+scrollDistance[1]); + } + } else { + // distance too big .. + if(DEBUG) { + final boolean isDistWithinDoubleTapSlop = sqDist < doubleTapSlopSquare; + final int dist = (int)Math.round(Math.sqrt(sqDist)); + final int startDist = (int)Math.round(Math.sqrt(sqStartDist)); + System.err.println(this+".dragged.X1: pDist "+dist+", distStart "+startDist+", gPtr "+gPtr+" ["+pIds[0]+", "+pIds[1]+"]"+ + ", diffDistWithinTapSlop "+isDistDiffWithinDoubleTapSlop+ + ", distWithin2DTSlop "+isDistWithinDoubleTapSlop+ + ", this "+xm+"/"+ym+", last "+lastX+"/"+lastY+", d "+scrollDistance[0]+"/"+scrollDistance[1]); + } + clear(true); + } + if( ST_2PRESS_T < gestureState ) { + // state ST_2PRESS_T waits for min scroll threshold ! + lastX = xm; + lastY = ym; + } + } else { + // other 2 pointers .. should rarely happen! + if(DEBUG) { + System.err.println(this+".dragged.X2: gPtr "+gPtr+" ["+pIds[0]+", "+pIds[1]+"]"+ + ", last "+lastX+"/"+lastY+", d "+scrollDistance[0]+"/"+scrollDistance[1]); + } + clear(true); + } + } + } break; + + default: + } + return null != hitGestureEvent; + } +} diff --git a/src/newt/classes/com/jogamp/newt/event/GestureHandler.java b/src/newt/classes/com/jogamp/newt/event/GestureHandler.java new file mode 100644 index 000000000..2e98a8a01 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/GestureHandler.java @@ -0,0 +1,143 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.newt.event; + +import jogamp.newt.Debug; + +/** + * Generic gesture handler interface designed to allow pass-through + * filtering of {@link InputEvent}s. + * <p> + * To avoid negative impact on event processing, + * implementation shall restrict computation as much as possible + * and only within it's appropriate gesture states. + * </p> + * <p> + * To allow custom user events, other than the <i>normal</i> {@link InputEvent}s, + * a user may return a {@link GestureEvent} in it's implementation. + * </p> + */ +public interface GestureHandler { + public static final boolean DEBUG = Debug.debug("Window.MouseEvent"); + + /** A custom gesture event */ + @SuppressWarnings("serial") + public static class GestureEvent extends InputEvent { + /** A gesture has been detected. */ + public static final short EVENT_GESTURE_DETECTED = 400; + + private final GestureHandler handler; + + /** + * Creates a gesture event with default type {@link #EVENT_GESTURE_DETECTED}. + * + * @param source + * @param when + * @param modifiers + * @param handler + */ + public GestureEvent(Object source, long when, int modifiers, GestureHandler handler) { + super(EVENT_GESTURE_DETECTED, source, when, modifiers); + this.handler = handler; + } + + /** + * Creates a gesture event with custom <i>event_type</i> ! + * @param event_type must lie within [400..599] + * @param source + * @param when + * @param modifiers + * @param handler + */ + public GestureEvent(short event_type, Object source, long when, int modifiers, GestureHandler handler) { + super(event_type, source, when, modifiers); + this.handler = handler; + } + + /** Return the {@link GestureHandler}, which produced the event. */ + public final GestureHandler getHandler() { return handler; } + } + + /** + * Listener for {@link GestureEvent}s. + * + * @see GestureEvent + */ + public static interface GestureListener extends NEWTEventListener + { + /** {@link GestureHandler} {@link GestureHandler#hasGesture() has detected} the gesture. */ + public void gestureDetected(GestureEvent gh); + } + + /** + * Clears state of handler, i.e. resets all states incl. previous detected gesture. + * @param clearStarted if true, also clears {@link #isWithinGesture() started} state, + * otherwise stay within gesture - if appropriate. + * Staying within a gesture allows fluent continuous gesture sequence, + * e.g. for scrolling. + */ + public void clear(boolean clearStarted); + + /** + * Returns true if a previous {@link #process(InputEvent)} command produced a gesture, + * which has not been {@link #clear(boolean) cleared}. + * Otherwise returns false. + */ + public boolean hasGesture(); + + /** + * Returns the corresponding {@link InputEvent} for the gesture as detected by + * a previous {@link #process(InputEvent)}, which has not been {@link #clear(boolean) cleared}. + * Otherwise returns null. + * <p> + * Only implemented for gestures mapping to {@link InputEvent}s. + * </p> + */ + public InputEvent getGestureEvent(); + + /** + * Returns true if within a gesture as detected by a previous {@link #process(InputEvent)} command, + * which has not been {@link #clear(boolean) cleared}. + * Otherwise returns false. + */ + public boolean isWithinGesture(); + + /** + * Process the given {@link InputEvent} and returns true if it produced the gesture. + * Otherwise returns false. + * <p> + * If a gesture was already detected previously and has not been cleared, + * method does not process the event and returns true. + * </p> + * <p> + * Besides validation of the event's details, + * the handler may also validate the {@link InputEvent.InputClass} and/or {@link InputEvent.InputType}. + * </p> + */ + public boolean process(InputEvent e); +} diff --git a/src/newt/classes/com/jogamp/newt/event/InputEvent.java b/src/newt/classes/com/jogamp/newt/event/InputEvent.java index 819338ccb..f29b632e8 100644 --- a/src/newt/classes/com/jogamp/newt/event/InputEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/InputEvent.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,102 +29,202 @@ * 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.jogamp.newt.event; +import com.jogamp.common.util.IntBitfield; +import com.jogamp.newt.Window; + @SuppressWarnings("serial") public abstract class InputEvent extends NEWTEvent { - public static final int SHIFT_MASK = 1 << 0; - public static final int CTRL_MASK = 1 << 1; - public static final int META_MASK = 1 << 2; - public static final int ALT_MASK = 1 << 3; - public static final int ALT_GRAPH_MASK = 1 << 5; - public static final int BUTTON1_MASK = 1 << 6; - public static final int BUTTON2_MASK = 1 << 7; - public static final int BUTTON3_MASK = 1 << 8; - public static final int BUTTON4_MASK = 1 << 9; - public static final int BUTTON5_MASK = 1 << 10; - public static final int BUTTON6_MASK = 1 << 11; - public static final int CONFINED_MASK = 1 << 16; - public static final int INVISIBLE_MASK = 1 << 17; - - /** + /** Interface marking class of input types */ + public static interface InputClass { + } + + /** Interface marking type of input devices */ + public static interface InputType { + } + + public static final int SHIFT_MASK = 1 << 0; + public static final int CTRL_MASK = 1 << 1; + public static final int META_MASK = 1 << 2; + public static final int ALT_MASK = 1 << 3; + public static final int ALT_GRAPH_MASK = 1 << 4; + + public static final int BUTTON1_MASK = 1 << 5; + public static final int BUTTON2_MASK = 1 << 6; + public static final int BUTTON3_MASK = 1 << 7; + public static final int BUTTON4_MASK = 1 << 8; + public static final int BUTTON5_MASK = 1 << 9; + public static final int BUTTON6_MASK = 1 << 10; + public static final int BUTTON7_MASK = 1 << 11; + public static final int BUTTON8_MASK = 1 << 12; + public static final int BUTTON9_MASK = 1 << 13; + + public static final int BUTTONLAST_MASK = 1 << 20; // 16 buttons + public static final int BUTTONALL_MASK = 0xffff << 5 ; // 16 buttons + + /** Event is caused by auto-repeat. */ + public static final int AUTOREPEAT_MASK = 1 << 29; + + /** Pointer is confined, see {@link Window#confinePointer(boolean)}. */ + public static final int CONFINED_MASK = 1 << 30; + + /** Pointer is invisible, see {@link Window#setPointerVisible(boolean)}. */ + public static final int INVISIBLE_MASK = 1 << 31; + + /** * Returns the corresponding button mask for the given button. * <p> - * In case the given button lies outside - * of the valid range [{@link MouseEvent#BUTTON1} .. {@link MouseEvent#BUTTON6}], + * In case the given button lies outside + * of the valid range [{@link MouseEvent#BUTTON1} .. {@link MouseEvent#BUTTON_COUNT}], * null is returned. * </p> */ public static final int getButtonMask(int button) { - if( 0 < button && button <= MouseEvent.BUTTON_NUMBER ) { - return 1 << ( 5 + button ) ; + if( 0 < button && button <= MouseEvent.BUTTON_COUNT ) { + return 1 << ( 4 + button ) ; } return 0; } - - /** Object when attached via {@link #setAttachment(Object)} marks the event consumed, - * ie. stops propagating the event any further to the event listener. - */ - public static final Object consumedTag = new Object(); - - protected InputEvent(int eventType, Object source, long when, int modifiers) { + + protected InputEvent(short eventType, Object source, long when, int modifiers) { super(eventType, source, when); this.modifiers=modifiers; } + /** Return the modifier bits of this event, e.g. see {@link #SHIFT_MASK} .. etc. */ public int getModifiers() { return modifiers; } + /** {@link #getModifiers()} contains {@link #ALT_MASK}. */ public boolean isAltDown() { return (modifiers&ALT_MASK)!=0; } + /** {@link #getModifiers()} contains {@link #ALT_GRAPH_MASK}. */ public boolean isAltGraphDown() { return (modifiers&ALT_GRAPH_MASK)!=0; } + /** {@link #getModifiers()} contains {@link #CTRL_MASK}. */ public boolean isControlDown() { return (modifiers&CTRL_MASK)!=0; } + /** {@link #getModifiers()} contains {@link #META_MASK}. */ public boolean isMetaDown() { return (modifiers&META_MASK)!=0; } + /** {@link #getModifiers()} contains {@link #SHIFT_MASK}. */ public boolean isShiftDown() { return (modifiers&SHIFT_MASK)!=0; } + /** {@link #getModifiers()} contains {@link #AUTOREPEAT_MASK}. */ + public boolean isAutoRepeat() { + return (modifiers&AUTOREPEAT_MASK)!=0; + } + /** {@link #getModifiers()} contains {@link #CONFINED_MASK}. Pointer is confined, see {@link Window#confinePointer(boolean)}. */ public boolean isConfined() { return (modifiers&CONFINED_MASK)!=0; } + /** {@link #getModifiers()} contains {@link #INVISIBLE_MASK}. Pointer is invisible, see {@link Window#setPointerVisible(boolean)}. */ public boolean isInvisible() { return (modifiers&INVISIBLE_MASK)!=0; } + public StringBuilder getModifiersString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + sb.append("["); + boolean isFirst = true; + if(isShiftDown()) { if(!isFirst) { sb.append(", "); } isFirst = false; sb.append("shift"); } + if(isControlDown()) { if(!isFirst) { sb.append(", "); } isFirst = false; sb.append("ctrl"); } + if(isMetaDown()) { if(!isFirst) { sb.append(", "); } isFirst = false; sb.append("meta"); } + if(isAltDown()) { if(!isFirst) { sb.append(", "); } isFirst = false; sb.append("alt"); } + if(isAltGraphDown()) { if(!isFirst) { sb.append(", "); } isFirst = false; sb.append("altgr"); } + if(isAutoRepeat()) { if(!isFirst) { sb.append(", "); } isFirst = false; sb.append("repeat"); } + for(int i=1; i<=MouseEvent.BUTTON_COUNT; i++) { + if(isButtonDown(i)) { if(!isFirst) { sb.append(", "); } isFirst = false; sb.append("button").append(i); } + } + if(isConfined()) { if(!isFirst) { sb.append(", "); } isFirst = false; sb.append("confined"); } + if(isInvisible()) { if(!isFirst) { sb.append(", "); } isFirst = false; sb.append("invisible"); } + sb.append("]"); + + return sb; + } + /** - * @return Array of pressed mouse buttons [{@link MouseEvent#BUTTON1} .. {@link MouseEvent#BUTTON6}]. + * See also {@link MouseEvent}'s section about <i>Multiple-Pointer Events</i>. + * @return Array of pressed mouse buttons [{@link MouseEvent#BUTTON1} .. {@link MouseEvent#BUTTON6}]. * If none is down, the resulting array is of length 0. */ - public final int[] getButtonsDown() { - int len = 0; - for(int i=1; i<=MouseEvent.BUTTON_NUMBER; i++) { - if(isButtonDown(i)) { len++; } - } - - int[] res = new int[len]; + public final short[] getButtonsDown() { + final int len = getButtonDownCount(); + + final short[] res = new short[len]; int j = 0; - for(int i=1; i<=MouseEvent.BUTTON_NUMBER; i++) { - if(isButtonDown(i)) { res[j++] = ( MouseEvent.BUTTON1 - 1 ) + i; } + for(int i=1; i<=MouseEvent.BUTTON_COUNT; i++) { + if( isButtonDown(i) ) { res[j++] = (short) ( ( MouseEvent.BUTTON1 - 1 ) + i ); } } return res; } + /** + * See also {@link MouseEvent}'s section about <i>Multiple-Pointer Events</i>. + * @param button the button to test + * @return true if the given button is down + */ public final boolean isButtonDown(int button) { return ( modifiers & getButtonMask(button) ) != 0; } - + + /** + * Returns the number of pressed buttons by counting the set bits: + * <pre> + * getBitCount(modifiers & BUTTONALL_MASK); + * </pre> + * <p> + * See also {@link MouseEvent}'s section about <i>Multiple-Pointer Events</i>. + * </p> + * @see IntBitfield#getBitCount(int) + * @see #BUTTONALL_MASK + */ + public final int getButtonDownCount() { + return IntBitfield.getBitCount(modifiers & BUTTONALL_MASK); + } + + /** + * Returns true if at least one button is pressed, otherwise false: + * <pre> + * 0 != ( modifiers & BUTTONALL_MASK ) + * </pre> + * <p> + * See also {@link MouseEvent}'s section about <i>Multiple-Pointer Events</i>. + * </p> + * @see IntBitfield#getBitCount(int) + * @see #BUTTONALL_MASK + */ + public final boolean isAnyButtonDown() { + return 0 != ( modifiers & BUTTONALL_MASK ); + } + + @Override public String toString() { - return "InputEvent[modifiers: 0x"+Integer.toHexString(modifiers)+", "+super.toString()+"]"; + return toString(null).toString(); + } + + @Override + public StringBuilder toString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + sb.append("InputEvent[modifiers: "); + getModifiersString(sb); + sb.append(", "); + super.toString(sb).append("]"); + return sb; } private final int modifiers; diff --git a/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java b/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java index 93c8409b1..5cef734bb 100644 --- a/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java @@ -4,14 +4,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -21,21 +21,21 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event; public abstract class KeyAdapter implements KeyListener { + @Override public void keyPressed(KeyEvent e) { } + @Override public void keyReleased(KeyEvent e) { } - public void keyTyped(KeyEvent e) { - } } diff --git a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java index 44fcea49c..49aa4e054 100644 --- a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,712 +29,937 @@ * 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.jogamp.newt.event; +import com.jogamp.common.util.IntBitfield; + +/** + * <a name="eventDelivery"><h5>KeyEvent Delivery</h5></a> + * + * Key events are delivered in the following order: + * <p> + * <table border="0"> + * <tr><th>#</th><th>Event Type</th> <th>Constraints</th> <th>Notes</th></tr> + * <tr><td>1</td><td>{@link #EVENT_KEY_PRESSED} </td><td> <i> excluding {@link #isAutoRepeat() auto-repeat}-{@link #isModifierKey() modifier} keys</i></td><td></td></tr> + * <tr><td>2</td><td>{@link #EVENT_KEY_RELEASED} </td><td> <i> excluding {@link #isAutoRepeat() auto-repeat}-{@link #isModifierKey() modifier} keys</i></td><td></td></tr> + * </table> + * </p> + * In case the native platform does not + * deliver keyboard events in the above order or skip events, + * the NEWT driver will reorder and inject synthetic events if required. + * <p> + * Besides regular modifiers like {@link InputEvent#SHIFT_MASK} etc., + * the {@link InputEvent#AUTOREPEAT_MASK} bit is added if repetition is detected, following above constraints. + * </p> + * <p> + * Auto-Repeat shall behave as follow: + * <pre> + P = pressed, R = released + 0 = normal, 1 = auto-repeat + + P(0), [ R(1), P(1), R(1), ..], R(0) + * </pre> + * The idea is if you mask out auto-repeat in your event listener + * you just get one long pressed P/R tuple for {@link #isPrintableKey() printable} and {@link #isActionKey() Action} keys. + * </p> + * <p> + * {@link #isActionKey() Action} keys will produce {@link #EVENT_KEY_PRESSED pressed} + * and {@link #EVENT_KEY_RELEASED released} events including {@link #isAutoRepeat() auto-repeat}. + * </p> + * <p> + * {@link #isPrintableKey() Printable} keys will produce {@link #EVENT_KEY_PRESSED pressed} and {@link #EVENT_KEY_RELEASED released} events. + * </p> + * <p> + * {@link #isModifierKey() Modifier} keys will produce {@link #EVENT_KEY_PRESSED pressed} and {@link #EVENT_KEY_RELEASED released} events + * excluding {@link #isAutoRepeat() auto-repeat}. + * They will also influence subsequent event's {@link #getModifiers() modifier} bits while pressed. + * </p> + * + * <a name="unicodeMapping"><h5>Unicode Mapping</h5></a> + * <p> + * {@link #getKeyChar() Key-chars}, as well as + * {@link #isPrintableKey() printable} {@link #getKeyCode() key-codes} and {@link #getKeySymbol() key-symbols} + * use the UTF-16 unicode space w/o collision. + * + * </p> + * <p> + * Non-{@link #isPrintableKey() printable} {@link #getKeyCode() key-codes} and {@link #getKeySymbol() key-symbols}, + * i.e. {@link #isModifierKey() modifier-} and {@link #isActionKey() action-}keys, + * are mapped to unicode's control and private range and do not collide w/ {@link #isPrintableKey() printable} unicode values + * with the following exception. + * </p> + * + * <a name="unicodeCollision"><h5>Unicode Collision</h5></a> + * <p> + * The following {@link #getKeyCode() Key-code}s and {@link #getKeySymbol() key-symbol}s collide w/ unicode space:<br/> + * <table border="1"> + * <tr><th>unicode range</th> <th>virtual key code</th> <th>unicode character</th></tr> + * <tr><td>[0x61 .. 0x78]</td> <td>[{@link #VK_F1}..{@link #VK_F24}]</td> <td>['a'..'x']</td></tr> + * </table> + * </p> + * <p> + * Collision was chosen for {@link #getKeyCode() Key-code} and {@link #getKeySymbol() key-symbol} mapping + * to allow a minimal code range, i.e. <code>[0..255]</code>. + * The reduced code range in turn allows the implementation to utilize fast and small lookup tables, + * e.g. to implement a key-press state tracker. + * </p> + * <pre> + * http://www.utf8-chartable.de/unicode-utf8-table.pl + * http://www.unicode.org/Public/5.1.0/ucd/PropList.txt + * https://en.wikipedia.org/wiki/Mapping_of_Unicode_characters + * https://en.wikipedia.org/wiki/Unicode_control_characters + * https://en.wikipedia.org/wiki/Private_Use_%28Unicode%29#Private_Use_Areas + * </pre> + * </p> + */ @SuppressWarnings("serial") public class KeyEvent extends InputEvent { - public KeyEvent(int eventType, Object source, long when, int modifiers, int keyCode, char keyChar) { - super(eventType, source, when, modifiers); - this.keyCode=keyCode; - this.keyChar=keyChar; - } - - /** Only valid if delivered via {@link KeyListener#keyPressed(KeyEvent)} */ - public char getKeyChar() { - return keyChar; - } - - /** Always valid. */ - public int getKeyCode() { - return keyCode; - } - - public String toString() { - return "KeyEvent["+getEventTypeString(getEventType())+ - ", code "+keyCode+"("+toHexString(keyCode)+"), char '"+keyChar+"' ("+toHexString((int)keyChar)+"), isActionKey "+isActionKey()+", "+super.toString()+"]"; - } - - public static String getEventTypeString(int type) { - switch(type) { - case EVENT_KEY_PRESSED: return "EVENT_KEY_PRESSED"; - case EVENT_KEY_RELEASED: return "EVENT_KEY_RELEASED"; - case EVENT_KEY_TYPED: return "EVENT_KEY_TYPED"; - default: return "unknown (" + type + ")"; + private KeyEvent(short eventType, Object source, long when, int modifiers, short keyCode, short keySym, int keySymModMask, char keyChar) { + super(eventType, source, when, modifiers | keySymModMask); + this.keyCode=keyCode; + this.keySym=keySym; + this.keyChar=keyChar; + { // cache modifier and action flags + byte _flags = 0; + if( isPrintableKey(keySym, false) && isPrintableKey((short)keyChar, true) ) { + _flags |= F_PRINTABLE_MASK; + } else { + if( 0 != keySymModMask ) { + _flags |= F_MODIFIER_MASK; + } else { + // A = U - ( P + M ) + _flags |= F_ACTION_MASK; + } + } + flags = _flags; + + // + // Validate flags + // + final int pma_bits = flags & ( F_PRINTABLE_MASK | F_MODIFIER_MASK | F_ACTION_MASK ) ; + final int pma_count = IntBitfield.getBitCount(pma_bits); + if ( 1 != pma_count ) { + throw new InternalError("Key must be either of type printable, modifier or action - but it is of "+pma_count+" types: "+this); + } + } } - } - - public boolean isActionKey() { - switch (keyCode) { - case VK_HOME: - case VK_END: - case VK_PAGE_UP: - case VK_PAGE_DOWN: - case VK_UP: - case VK_DOWN: - case VK_LEFT: - case VK_RIGHT: - - case VK_F1: - case VK_F2: - case VK_F3: - case VK_F4: - case VK_F5: - case VK_F6: - case VK_F7: - case VK_F8: - case VK_F9: - case VK_F10: - case VK_F11: - case VK_F12: - case VK_F13: - case VK_F14: - case VK_F15: - case VK_F16: - case VK_F17: - case VK_F18: - case VK_F19: - case VK_F20: - case VK_F21: - case VK_F22: - case VK_F23: - case VK_F24: - case VK_PRINTSCREEN: - case VK_CAPS_LOCK: - case VK_PAUSE: - case VK_INSERT: - - case VK_HELP: - case VK_WINDOWS: - return true; - } - return false; - } - - private final int keyCode; - private final char keyChar; - public static final int EVENT_KEY_PRESSED = 300; - public static final int EVENT_KEY_RELEASED= 301; - public static final int EVENT_KEY_TYPED = 302; - - /* Virtual key codes. */ - - public static final int VK_ENTER = '\n'; - public static final int VK_BACK_SPACE = '\b'; - public static final int VK_TAB = '\t'; - public static final int VK_CANCEL = 0x03; - public static final int VK_CLEAR = 0x0C; - public static final int VK_SHIFT = 0x10; - public static final int VK_CONTROL = 0x11; - public static final int VK_ALT = 0x12; - public static final int VK_PAUSE = 0x13; - public static final int VK_CAPS_LOCK = 0x14; - public static final int VK_ESCAPE = 0x1B; - public static final int VK_SPACE = 0x20; - public static final int VK_PAGE_UP = 0x21; - public static final int VK_PAGE_DOWN = 0x22; - public static final int VK_END = 0x23; - public static final int VK_HOME = 0x24; + public static KeyEvent create(short eventType, Object source, long when, int modifiers, short keyCode, short keySym, char keyChar) { + return new KeyEvent(eventType, source, when, modifiers, keyCode, keySym, getModifierMask(keySym), keyChar); + } /** - * Constant for the non-numpad <b>left</b> arrow key. - * @see #VK_KP_LEFT + * Returns the <i>UTF-16</i> character reflecting the {@link #getKeySymbol() key symbol} + * incl. active {@link #isModifierKey() modifiers}. + * @see #getKeySymbol() + * @see #getKeyCode() */ - public static final int VK_LEFT = 0x25; + public final char getKeyChar() { + return keyChar; + } /** - * Constant for the non-numpad <b>up</b> arrow key. - * @see #VK_KP_UP + * Returns the virtual <i>key symbol</i> reflecting the current <i>keyboard layout</i>. + * <p> + * For {@link #isPrintableKey() printable keys}, the <i>key symbol</i> is the {@link #isModifierKey() unmodified} + * representation of the UTF-16 {@link #getKeyChar() key char}.<br/> + * E.g. symbol [{@link #VK_A}, 'A'] for char 'a'. + * </p> + * @see #isPrintableKey() + * @see #getKeyChar() + * @see #getKeyCode() */ - public static final int VK_UP = 0x26; + public final short getKeySymbol() { + return keySym; + } /** - * Constant for the non-numpad <b>right</b> arrow key. - * @see #VK_KP_RIGHT - */ - public static final int VK_RIGHT = 0x27; + * Returns the virtual <i>key code</i> using a fixed mapping to the <i>US keyboard layout</i>. + * <p> + * In contrast to {@link #getKeySymbol() key symbol}, <i>key code</i> + * uses a fixed <i>US keyboard layout</i> and therefore is keyboard layout independent. + * </p> + * <p> + * E.g. <i>virtual key code</i> {@link #VK_Y} denotes the same physical key + * regardless whether <i>keyboard layout</i> <code>QWERTY</code> or + * <code>QWERTZ</code> is active. The {@link #getKeySymbol() key symbol} of the former is + * {@link #VK_Y}, where the latter produces {@link #VK_Y}. + * </p> + * @see #getKeyChar() + * @see #getKeySymbol() + */ + public final short getKeyCode() { + return keyCode; + } + + @Override + public final String toString() { + return toString(null).toString(); + } + + @Override + public final StringBuilder toString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + sb.append("KeyEvent[").append(getEventTypeString(getEventType())).append(", code ").append(toHexString(keyCode)).append(", sym ").append(toHexString(keySym)).append(", char '").append(keyChar).append("' (").append(toHexString((short)keyChar)) + .append("), printable ").append(isPrintableKey()).append(", modifier ").append(isModifierKey()).append(", action ").append(isActionKey()).append(", "); + return super.toString(sb).append("]"); + } + + public static String getEventTypeString(short type) { + switch(type) { + case EVENT_KEY_PRESSED: return "EVENT_KEY_PRESSED"; + case EVENT_KEY_RELEASED: return "EVENT_KEY_RELEASED"; + default: return "unknown (" + type + ")"; + } + } /** - * Constant for the non-numpad <b>down</b> arrow key. - * @see #VK_KP_DOWN + * @param keyChar UTF16 value to map. It is expected that the incoming keyChar value is unshifted and unmodified, + * however, lower case a-z is mapped to {@link KeyEvent#VK_A} - {@link KeyEvent#VK_Z}. + * @return {@link KeyEvent} virtual key (VK) value. */ - public static final int VK_DOWN = 0x28; + public static short utf16ToVKey(char keyChar) { + if( 'a' <= keyChar && keyChar <= 'z' ) { + return (short) ( ( keyChar - 'a' ) + KeyEvent.VK_A ); + } + return (short) keyChar; + } /** - * Constant for the comma key, "," - */ - public static final int VK_COMMA = 0x2C; + * Returns <code>true</code> if the given <code>virtualKey</code> represents a modifier key, otherwise <code>false</code>. + * <p> + * A modifier key is one of {@link #VK_SHIFT}, {@link #VK_CONTROL}, {@link #VK_ALT}, {@link #VK_ALT_GRAPH}, {@link #VK_META}. + * </p> + */ + public static boolean isModifierKey(short vKey) { + switch (vKey) { + case VK_SHIFT: + case VK_CONTROL: + case VK_ALT: + case VK_ALT_GRAPH: + case VK_META: + return true; + default: + return false; + } + } /** - * Constant for the minus key, "-" - * @since 1.2 - */ - public static final int VK_MINUS = 0x2D; + * If <code>vKey</code> is a {@link #isModifierKey() modifier key}, method returns the corresponding modifier mask, + * otherwise 0. + */ + public static int getModifierMask(short vKey) { + switch (vKey) { + case VK_SHIFT: + return InputEvent.SHIFT_MASK; + case VK_CONTROL: + return InputEvent.CTRL_MASK; + case VK_ALT: + case VK_ALT_GRAPH: + return InputEvent.ALT_MASK; + case VK_META: + return InputEvent.META_MASK; + } + return 0; + } /** - * Constant for the period key, "." + * Returns <code>true</code> if {@link #getKeySymbol() key symbol} represents a modifier key, + * otherwise <code>false</code>. + * <p> + * See {@link #isModifierKey(short)} for details. + * </p> + * <p> + * Note: Implementation uses a cached value. + * </p> */ - public static final int VK_PERIOD = 0x2E; + public final boolean isModifierKey() { + return 0 != ( F_MODIFIER_MASK & flags ) ; + } /** - * Constant for the forward slash key, "/" + * Returns <code>true</code> if {@link #getKeySymbol() key symbol} represents a non-printable and + * non-{@link #isModifierKey(short) modifier} action key, otherwise <code>false</code>. + * <p> + * Hence it is the set A of all keys U w/o printable P and w/o modifiers M: + * <code> A = U - ( P + M ) </code> + * </p> + * @see #isPrintableKey() + * @see #isModifierKey() */ - public static final int VK_SLASH = 0x2F; - - /** VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) */ - public static final int VK_0 = 0x30; - public static final int VK_1 = 0x31; - public static final int VK_2 = 0x32; - public static final int VK_3 = 0x33; - public static final int VK_4 = 0x34; - public static final int VK_5 = 0x35; - public static final int VK_6 = 0x36; - public static final int VK_7 = 0x37; - public static final int VK_8 = 0x38; - public static final int VK_9 = 0x39; + public final boolean isActionKey() { + return 0 != ( F_ACTION_MASK & flags ) ; + } /** - * Constant for the semicolon key, ";" - */ - public static final int VK_SEMICOLON = 0x3B; + * Returns <code>true</code> if given <code>uniChar</code> represents a printable character, + * i.e. a value other than {@link #VK_UNDEFINED} and not a control or non-printable private code. + * <p> + * A printable character is neither a {@link #isModifierKey(short) modifier key}, nor an {@link #isActionKey(short) action key}. + * </p> + * <p> + * Otherwise returns <code>false</code>. + * </p> + * <p> + * Distinction of key character and virtual key code is made due to <a href="#unicodeCollision">unicode collision</a>. + * </p> + * + * @param uniChar the UTF-16 unicode value, which maybe a virtual key code or key character. + * @param isKeyChar true if <code>uniChar</code> is a key character, otherwise a virtual key code + */ + public static boolean isPrintableKey(final short uniChar, final boolean isKeyChar) { + if ( VK_BACK_SPACE == uniChar || VK_TAB == uniChar || VK_ENTER == uniChar ) { + return true; + } + if( !isKeyChar ) { + if( ( nonPrintableKeys[0].min <= uniChar && uniChar <= nonPrintableKeys[0].max ) || + ( nonPrintableKeys[1].min <= uniChar && uniChar <= nonPrintableKeys[1].max ) || + ( nonPrintableKeys[2].min <= uniChar && uniChar <= nonPrintableKeys[2].max ) || + ( nonPrintableKeys[3].min <= uniChar && uniChar <= nonPrintableKeys[3].max ) ) { + return false; + } + } else { + if( ( nonPrintableKeys[0].inclKeyChar && nonPrintableKeys[0].min <= uniChar && uniChar <= nonPrintableKeys[0].max ) || + ( nonPrintableKeys[1].inclKeyChar && nonPrintableKeys[1].min <= uniChar && uniChar <= nonPrintableKeys[1].max ) || + ( nonPrintableKeys[2].inclKeyChar && nonPrintableKeys[2].min <= uniChar && uniChar <= nonPrintableKeys[2].max ) || + ( nonPrintableKeys[3].inclKeyChar && nonPrintableKeys[3].min <= uniChar && uniChar <= nonPrintableKeys[3].max ) ) { + return false; + } + } + return VK_UNDEFINED != uniChar; + } /** - * Constant for the equals key, "=" + * Returns <code>true</code> if {@link #getKeySymbol() key symbol} and {@link #getKeyChar() key char} + * represents a printable character, i.e. a value other than {@link #VK_UNDEFINED} + * and not a control or non-printable private code. + * <p> + * A printable character is neither a {@link #isModifierKey(short) modifier key}, nor an {@link #isActionKey(short) action key}. + * </p> + * <p> + * Otherwise returns <code>false</code>. + * </p> */ - public static final int VK_EQUALS = 0x3D; - - /** VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */ - public static final int VK_A = 0x41; - public static final int VK_B = 0x42; - public static final int VK_C = 0x43; - public static final int VK_D = 0x44; - public static final int VK_E = 0x45; - public static final int VK_F = 0x46; - public static final int VK_G = 0x47; - public static final int VK_H = 0x48; - public static final int VK_I = 0x49; - public static final int VK_J = 0x4A; - public static final int VK_K = 0x4B; - public static final int VK_L = 0x4C; - public static final int VK_M = 0x4D; - public static final int VK_N = 0x4E; - public static final int VK_O = 0x4F; - public static final int VK_P = 0x50; - public static final int VK_Q = 0x51; - public static final int VK_R = 0x52; - public static final int VK_S = 0x53; - public static final int VK_T = 0x54; - public static final int VK_U = 0x55; - public static final int VK_V = 0x56; - public static final int VK_W = 0x57; - public static final int VK_X = 0x58; - public static final int VK_Y = 0x59; - public static final int VK_Z = 0x5A; + public final boolean isPrintableKey() { + return 0 != ( F_PRINTABLE_MASK & flags ) ; + } + + private final short keyCode; + private final short keySym; + private final char keyChar; + private final byte flags; + private static final byte F_MODIFIER_MASK = 1 << 0; + private static final byte F_ACTION_MASK = 1 << 1; + private static final byte F_PRINTABLE_MASK = 1 << 2; + + /** A key has been pressed, excluding {@link #isAutoRepeat() auto-repeat}-{@link #isModifierKey() modifier} keys. */ + public static final short EVENT_KEY_PRESSED = 300; + /** A key has been released, excluding {@link #isAutoRepeat() auto-repeat}-{@link #isModifierKey() modifier} keys. */ + public static final short EVENT_KEY_RELEASED= 301; /** - * Constant for the open bracket key, "[" + * This value, {@code '\0'}, is used to indicate that the keyChar is unknown or not printable. */ - public static final int VK_OPEN_BRACKET = 0x5B; + public static final char NULL_CHAR = '\0'; + + /* Virtual key codes. */ + public static class NonPrintableRange { + /** min. unicode value, inclusive */ + public short min; + /** max. unicode value, inclusive */ + public short max; + /** true if valid for keyChar values as well, otherwise only valid for keyCode and keySym due to collision. */ + public final boolean inclKeyChar; + private NonPrintableRange(short min, short max, boolean inclKeyChar) { + this.min = min; + this.max = max; + this.inclKeyChar = inclKeyChar; + } + }; /** - * Constant for the back slash key, "\" + * Non printable key ranges, currently fixed to an array of size 4. + * <p> + * Not included, queried upfront: + * <ul> + * <li>{@link #VK_BACK_SPACE}</li> + * <li>{@link #VK_TAB}</li> + * <li>{@link #VK_ENTER}</li> + * </ul> + * </p> */ - public static final int VK_BACK_SLASH = 0x5C; + public final static NonPrintableRange[] nonPrintableKeys = { + new NonPrintableRange( (short)0x0000, (short)0x001F, true ), // Unicode: Non printable controls: [0x00 - 0x1F], see exclusion above + new NonPrintableRange( (short)0x0061, (short)0x0078, false), // Small 'a' thru 'z' (0x61 - 0x7a) - Not used for keyCode / keySym - Re-used for Fn (collision) + new NonPrintableRange( (short)0x008F, (short)0x009F, true ), // Unicode: Non printable controls: [0x7F - 0x9F], Numpad keys [0x7F - 0x8E] are printable! + new NonPrintableRange( (short)0xE000, (short)0xF8FF, true ) // Unicode: Private 0xE000 - 0xF8FF (Marked Non-Printable) + }; + + // + // Unicode: Non printable controls: [0x00 - 0x1F] + // /** - * Constant for the close bracket key, "]" - */ - public static final int VK_CLOSE_BRACKET = 0x5D; - - public static final int VK_NUMPAD0 = 0x60; - public static final int VK_NUMPAD1 = 0x61; - public static final int VK_NUMPAD2 = 0x62; - public static final int VK_NUMPAD3 = 0x63; - public static final int VK_NUMPAD4 = 0x64; - public static final int VK_NUMPAD5 = 0x65; - public static final int VK_NUMPAD6 = 0x66; - public static final int VK_NUMPAD7 = 0x67; - public static final int VK_NUMPAD8 = 0x68; - public static final int VK_NUMPAD9 = 0x69; - public static final int VK_MULTIPLY = 0x6A; - public static final int VK_ADD = 0x6B; - - /** - * This constant is obsolete, and is included only for backwards - * compatibility. - * @see #VK_SEPARATOR + * This value, {@value}, is used to indicate that the keyCode is unknown. */ - public static final int VK_SEPARATER = 0x6C; + public static final short VK_UNDEFINED = (short) 0x0; - /** - * Constant for the Numpad Separator key. - * @since 1.4 - */ - public static final int VK_SEPARATOR = VK_SEPARATER; + static final short VK_FREE01 = (short) 0x01; - public static final int VK_SUBTRACT = 0x6D; - public static final int VK_DECIMAL = 0x6E; - public static final int VK_DIVIDE = 0x6F; - public static final int VK_DELETE = 0x7F; /* ASCII DEL */ - public static final int VK_NUM_LOCK = 0x90; - public static final int VK_SCROLL_LOCK = 0x91; + /** Constant for the HOME function key. ASCII: Start Of Text. */ + public static final short VK_HOME = (short) 0x02; - /** Constant for the F1 function key. */ - public static final int VK_F1 = 0x70; + /** Constant for the END function key. ASCII: End Of Text. */ + public static final short VK_END = (short) 0x03; - /** Constant for the F2 function key. */ - public static final int VK_F2 = 0x71; + /** Constant for the END function key. ASCII: End Of Transmission. */ + public static final short VK_FINAL = (short) 0x04; - /** Constant for the F3 function key. */ - public static final int VK_F3 = 0x72; + /** Constant for the PRINT function key. ASCII: Enquiry. */ + public static final short VK_PRINTSCREEN = (short) 0x05; - /** Constant for the F4 function key. */ - public static final int VK_F4 = 0x73; + static final short VK_FREE06 = (short) 0x06; + static final short VK_FREE07 = (short) 0x07; - /** Constant for the F5 function key. */ - public static final int VK_F5 = 0x74; + /** Constant for the BACK SPACE key "\b", matching ASCII. Printable! */ + public static final short VK_BACK_SPACE = (short) 0x08; - /** Constant for the F6 function key. */ - public static final int VK_F6 = 0x75; + /** Constant for the HORIZ TAB key "\t", matching ASCII. Printable! */ + public static final short VK_TAB = (short) 0x09; - /** Constant for the F7 function key. */ - public static final int VK_F7 = 0x76; + /** LINE_FEED "\n", matching ASCII, n/a on keyboard. */ + static final short VK_FREE0A = (short) 0x0A; - /** Constant for the F8 function key. */ - public static final int VK_F8 = 0x77; + /** Constant for the PAGE DOWN function key. ASCII: Vertical Tabulation. */ + public static final short VK_PAGE_DOWN = (short) 0x0B; - /** Constant for the F9 function key. */ - public static final int VK_F9 = 0x78; + /** Constant for the CLEAR key, i.e. FORM FEED, matching ASCII. */ + public static final short VK_CLEAR = (short) 0x0C; - /** Constant for the F10 function key. */ - public static final int VK_F10 = 0x79; + /** Constant for the ENTER key, i.e. CARRIAGE RETURN, matching ASCII. Printable! */ + public static final short VK_ENTER = (short) 0x0D; - /** Constant for the F11 function key. */ - public static final int VK_F11 = 0x7A; + static final short VK_FREE0E = (short) 0x0E; - /** Constant for the F12 function key. */ - public static final int VK_F12 = 0x7B; + /** Constant for the CTRL function key. ASCII: shift-in. */ + public static final short VK_SHIFT = (short) 0x0F; - /** - * Constant for the F13 function key. - * @since 1.2 - */ - /* F13 - F24 are used on IBM 3270 keyboard; use random range for constants. */ - public static final int VK_F13 = 0xF000; - - /** - * Constant for the F14 function key. - * @since 1.2 - */ - public static final int VK_F14 = 0xF001; - - /** - * Constant for the F15 function key. - * @since 1.2 - */ - public static final int VK_F15 = 0xF002; - - /** - * Constant for the F16 function key. - * @since 1.2 - */ - public static final int VK_F16 = 0xF003; - - /** - * Constant for the F17 function key. - * @since 1.2 - */ - public static final int VK_F17 = 0xF004; - - /** - * Constant for the F18 function key. - * @since 1.2 - */ - public static final int VK_F18 = 0xF005; - - /** - * Constant for the F19 function key. - * @since 1.2 - */ - public static final int VK_F19 = 0xF006; - - /** - * Constant for the F20 function key. - * @since 1.2 - */ - public static final int VK_F20 = 0xF007; - - /** - * Constant for the F21 function key. - * @since 1.2 - */ - public static final int VK_F21 = 0xF008; - - /** - * Constant for the F22 function key. - * @since 1.2 - */ - public static final int VK_F22 = 0xF009; - - /** - * Constant for the F23 function key. - * @since 1.2 - */ - public static final int VK_F23 = 0xF00A; - - /** - * Constant for the F24 function key. - * @since 1.2 - */ - public static final int VK_F24 = 0xF00B; - - public static final int VK_PRINTSCREEN = 0x9A; - public static final int VK_INSERT = 0x9B; - public static final int VK_HELP = 0x9C; - public static final int VK_META = 0x9D; + /** Constant for the PAGE UP function key. ASCII: Data Link Escape. */ + public static final short VK_PAGE_UP = (short) 0x10; - public static final int VK_BACK_QUOTE = 0xC0; - public static final int VK_QUOTE = 0xDE; + /** Constant for the CTRL function key. ASCII: device-ctrl-one. */ + public static final short VK_CONTROL = (short) 0x11; - /** - * Constant for the numeric keypad <b>up</b> arrow key. - * @see #VK_UP - * @since 1.2 - */ - public static final int VK_KP_UP = 0xE0; + /** Constant for the left ALT function key. ASCII: device-ctrl-two. */ + public static final short VK_ALT = (short) 0x12; - /** - * Constant for the numeric keypad <b>down</b> arrow key. - * @see #VK_DOWN - * @since 1.2 - */ - public static final int VK_KP_DOWN = 0xE1; + /** Constant for the ALT_GRAPH function key, i.e. right ALT key. ASCII: device-ctrl-three. */ + public static final short VK_ALT_GRAPH = (short) 0x13; - /** - * Constant for the numeric keypad <b>left</b> arrow key. - * @see #VK_LEFT - * @since 1.2 - */ - public static final int VK_KP_LEFT = 0xE2; + /** Constant for the CAPS LOCK function key. ASCII: device-ctrl-four. */ + public static final short VK_CAPS_LOCK = (short) 0x14; - /** - * Constant for the numeric keypad <b>right</b> arrow key. - * @see #VK_RIGHT - * @since 1.2 - */ - public static final int VK_KP_RIGHT = 0xE3; - - /* For European keyboards */ - /** @since 1.2 */ - public static final int VK_DEAD_GRAVE = 0x80; - /** @since 1.2 */ - public static final int VK_DEAD_ACUTE = 0x81; - /** @since 1.2 */ - public static final int VK_DEAD_CIRCUMFLEX = 0x82; - /** @since 1.2 */ - public static final int VK_DEAD_TILDE = 0x83; - /** @since 1.2 */ - public static final int VK_DEAD_MACRON = 0x84; - /** @since 1.2 */ - public static final int VK_DEAD_BREVE = 0x85; - /** @since 1.2 */ - public static final int VK_DEAD_ABOVEDOT = 0x86; - /** @since 1.2 */ - public static final int VK_DEAD_DIAERESIS = 0x87; - /** @since 1.2 */ - public static final int VK_DEAD_ABOVERING = 0x88; - /** @since 1.2 */ - public static final int VK_DEAD_DOUBLEACUTE = 0x89; - /** @since 1.2 */ - public static final int VK_DEAD_CARON = 0x8a; - /** @since 1.2 */ - public static final int VK_DEAD_CEDILLA = 0x8b; - /** @since 1.2 */ - public static final int VK_DEAD_OGONEK = 0x8c; - /** @since 1.2 */ - public static final int VK_DEAD_IOTA = 0x8d; - /** @since 1.2 */ - public static final int VK_DEAD_VOICED_SOUND = 0x8e; - /** @since 1.2 */ - public static final int VK_DEAD_SEMIVOICED_SOUND = 0x8f; - - /** @since 1.2 */ - public static final int VK_AMPERSAND = 0x96; - /** @since 1.2 */ - public static final int VK_ASTERISK = 0x97; - /** @since 1.2 */ - public static final int VK_QUOTEDBL = 0x98; - /** @since 1.2 */ - public static final int VK_LESS = 0x99; - - /** @since 1.2 */ - public static final int VK_GREATER = 0xa0; - /** @since 1.2 */ - public static final int VK_BRACELEFT = 0xa1; - /** @since 1.2 */ - public static final int VK_BRACERIGHT = 0xa2; + static final short VK_FREE15 = (short) 0x15; + + /** Constant for the PAUSE function key. ASCII: sync-idle. */ + public static final short VK_PAUSE = (short) 0x16; + + /** <b>scroll lock</b> key. ASCII: End Of Transmission Block. */ + public static final short VK_SCROLL_LOCK = (short) 0x17; + + /** Constant for the CANCEL function key. ASCII: Cancel. */ + public static final short VK_CANCEL = (short) 0x18; + + static final short VK_FREE19 = (short) 0x19; + + /** Constant for the INSERT function key. ASCII: Substitute. */ + public static final short VK_INSERT = (short) 0x1A; + + /** Constant for the ESCAPE function key. ASCII: Escape. */ + public static final short VK_ESCAPE = (short) 0x1B; + + /** Constant for the Convert function key, Japanese "henkan". ASCII: File Separator. */ + public static final short VK_CONVERT = (short) 0x1C; + + /** Constant for the Don't Convert function key, Japanese "muhenkan". ASCII: Group Separator.*/ + public static final short VK_NONCONVERT = (short) 0x1D; + + /** Constant for the Accept or Commit function key, Japanese "kakutei". ASCII: Record Separator.*/ + public static final short VK_ACCEPT = (short) 0x1E; + + /** Constant for the Mode Change (?). ASCII: Unit Separator.*/ + public static final short VK_MODECHANGE = (short) 0x1F; + + // + // Unicode: Printable [0x20 - 0x7E] + // NOTE: Collision of 'a' - 'x' [0x61 .. 0x78], used for keyCode/keySym Fn function keys + // + + /** Constant for the SPACE function key. ASCII: SPACE. */ + public static final short VK_SPACE = (short) 0x20; + + /** Constant for the "!" key. */ + public static final short VK_EXCLAMATION_MARK = (short) 0x21; + + /** Constant for the """ key. */ + public static final short VK_QUOTEDBL = (short) 0x22; + + /** Constant for the "#" key. */ + public static final short VK_NUMBER_SIGN = (short) 0x23; + + /** Constant for the "$" key. */ + public static final short VK_DOLLAR = (short) 0x24; + + /** Constant for the "%" key. */ + public static final short VK_PERCENT = (short) 0x25; + + /** Constant for the "&" key. */ + public static final short VK_AMPERSAND = (short) 0x26; + + /** Constant for the "'" key. */ + public static final short VK_QUOTE = (short) 0x27; + + /** Constant for the "(" key. */ + public static final short VK_LEFT_PARENTHESIS = (short) 0x28; + + /** Constant for the ")" key. */ + public static final short VK_RIGHT_PARENTHESIS = (short) 0x29; + + /** Constant for the "*" key */ + public static final short VK_ASTERISK = (short) 0x2A; + + /** Constant for the "+" key. */ + public static final short VK_PLUS = (short) 0x2B; + + /** Constant for the comma key, "," */ + public static final short VK_COMMA = (short) 0x2C; + + /** Constant for the minus key, "-" */ + public static final short VK_MINUS = (short) 0x2D; + + /** Constant for the period key, "." */ + public static final short VK_PERIOD = (short) 0x2E; + + /** Constant for the forward slash key, "/" */ + public static final short VK_SLASH = (short) 0x2F; + + /** VK_0 thru VK_9 are the same as UTF16/ASCII '0' thru '9' [0x30 - 0x39] */ + public static final short VK_0 = (short) 0x30; + /** See {@link #VK_0}. */ + public static final short VK_1 = (short) 0x31; + /** See {@link #VK_0}. */ + public static final short VK_2 = (short) 0x32; + /** See {@link #VK_0}. */ + public static final short VK_3 = (short) 0x33; + /** See {@link #VK_0}. */ + public static final short VK_4 = (short) 0x34; + /** See {@link #VK_0}. */ + public static final short VK_5 = (short) 0x35; + /** See {@link #VK_0}. */ + public static final short VK_6 = (short) 0x36; + /** See {@link #VK_0}. */ + public static final short VK_7 = (short) 0x37; + /** See {@link #VK_0}. */ + public static final short VK_8 = (short) 0x38; + /** See {@link #VK_0}. */ + public static final short VK_9 = (short) 0x39; + + /** Constant for the ":" key. */ + public static final short VK_COLON = (short) 0x3A; + + /** Constant for the semicolon key, ";" */ + public static final short VK_SEMICOLON = (short) 0x3B; + + /** Constant for the equals key, "<" */ + public static final short VK_LESS = (short) 0x3C; + + /** Constant for the equals key, "=" */ + public static final short VK_EQUALS = (short) 0x3D; + + /** Constant for the equals key, ">" */ + public static final short VK_GREATER = (short) 0x3E; + + /** Constant for the equals key, "?" */ + public static final short VK_QUESTIONMARK = (short) 0x3F; + + /** Constant for the equals key, "@" */ + public static final short VK_AT = (short) 0x40; + + /** VK_A thru VK_Z are the same as Capital UTF16/ASCII 'A' thru 'Z' (0x41 - 0x5A) */ + public static final short VK_A = (short) 0x41; + /** See {@link #VK_A}. */ + public static final short VK_B = (short) 0x42; + /** See {@link #VK_A}. */ + public static final short VK_C = (short) 0x43; + /** See {@link #VK_A}. */ + public static final short VK_D = (short) 0x44; + /** See {@link #VK_A}. */ + public static final short VK_E = (short) 0x45; + /** See {@link #VK_A}. */ + public static final short VK_F = (short) 0x46; + /** See {@link #VK_A}. */ + public static final short VK_G = (short) 0x47; + /** See {@link #VK_A}. */ + public static final short VK_H = (short) 0x48; + /** See {@link #VK_A}. */ + public static final short VK_I = (short) 0x49; + /** See {@link #VK_A}. */ + public static final short VK_J = (short) 0x4A; + /** See {@link #VK_A}. */ + public static final short VK_K = (short) 0x4B; + /** See {@link #VK_A}. */ + public static final short VK_L = (short) 0x4C; + /** See {@link #VK_A}. */ + public static final short VK_M = (short) 0x4D; + /** See {@link #VK_A}. */ + public static final short VK_N = (short) 0x4E; + /** See {@link #VK_A}. */ + public static final short VK_O = (short) 0x4F; + /** See {@link #VK_A}. */ + public static final short VK_P = (short) 0x50; + /** See {@link #VK_A}. */ + public static final short VK_Q = (short) 0x51; + /** See {@link #VK_A}. */ + public static final short VK_R = (short) 0x52; + /** See {@link #VK_A}. */ + public static final short VK_S = (short) 0x53; + /** See {@link #VK_A}. */ + public static final short VK_T = (short) 0x54; + /** See {@link #VK_A}. */ + public static final short VK_U = (short) 0x55; + /** See {@link #VK_A}. */ + public static final short VK_V = (short) 0x56; + /** See {@link #VK_A}. */ + public static final short VK_W = (short) 0x57; + /** See {@link #VK_A}. */ + public static final short VK_X = (short) 0x58; + /** See {@link #VK_A}. */ + public static final short VK_Y = (short) 0x59; + /** See {@link #VK_A}. */ + public static final short VK_Z = (short) 0x5A; + + /** Constant for the open bracket key, "[" */ + public static final short VK_OPEN_BRACKET = (short) 0x5B; + + /**Constant for the back slash key, "\" */ + public static final short VK_BACK_SLASH = (short) 0x5C; + + /** Constant for the close bracket key, "]" */ + public static final short VK_CLOSE_BRACKET = (short) 0x5D; + + /** Constant for the "^" key. */ + public static final short VK_CIRCUMFLEX = (short) 0x5E; + + /** Constant for the "_" key */ + public static final short VK_UNDERSCORE = (short) 0x5F; + + /** Constant for the "`" key */ + public static final short VK_BACK_QUOTE = (short) 0x60; + + /** Small UTF/ASCII 'a' thru 'z' (0x61 - 0x7a) - Not used for keyCode / keySym. */ + + /** + * Constant for the F<i>n</i> function keys. + * <p> + * F1..F24, i.e. F<i>n</i>, are mapped from on <code>0x60+n</code> -> <code>[0x61 .. 0x78]</code>. + * </p> + * <p> + * <b>Warning:</b> The F<i>n</i> function keys <b>do collide</b> with unicode characters small 'a' thru 'x'!<br/> + * See <a href="#unicodeCollision">Unicode Collision</a> for details. + * </p> + */ + public static final short VK_F1 = (short) ( 0x60+ 1 ); + + /** Constant for the F2 function key. See {@link #VK_F1}. */ + public static final short VK_F2 = (short) ( 0x60+ 2 ); + + /** Constant for the F3 function key. See {@link #VK_F1}. */ + public static final short VK_F3 = (short) ( 0x60+ 3 ); + + /** Constant for the F4 function key. See {@link #VK_F1}. */ + public static final short VK_F4 = (short) ( 0x60+ 4 ); + + /** Constant for the F5 function key. See {@link #VK_F1}. */ + public static final short VK_F5 = (short) ( 0x60+ 5 ); + + /** Constant for the F6 function key. See {@link #VK_F1}. */ + public static final short VK_F6 = (short) ( 0x60+ 6 ); + + /** Constant for the F7 function key. See {@link #VK_F1}. */ + public static final short VK_F7 = (short) ( 0x60+ 7 ); + + /** Constant for the F8 function key. See {@link #VK_F1}. */ + public static final short VK_F8 = (short) ( 0x60+ 8 ); + + /** Constant for the F9 function key. See {@link #VK_F1}. */ + public static final short VK_F9 = (short) ( 0x60+ 9 ); + + /** Constant for the F11 function key. See {@link #VK_F1}. */ + public static final short VK_F10 = (short) ( 0x60+10 ); + + /** Constant for the F11 function key. See {@link #VK_F1}. */ + public static final short VK_F11 = (short) ( 0x60+11 ); + + /** Constant for the F12 function key. See {@link #VK_F1}.*/ + public static final short VK_F12 = (short) ( 0x60+12 ); + + /** Constant for the F13 function key. See {@link #VK_F1}. */ + public static final short VK_F13 = (short) ( 0x60+13 ); + + /** Constant for the F14 function key. See {@link #VK_F1}. */ + public static final short VK_F14 = (short) ( 0x60+14 ); + + /** Constant for the F15 function key. See {@link #VK_F1}. */ + public static final short VK_F15 = (short) ( 0x60+15 ); + + /** Constant for the F16 function key. See {@link #VK_F1}. */ + public static final short VK_F16 = (short) ( 0x60+16 ); + + /** Constant for the F17 function key. See {@link #VK_F1}. */ + public static final short VK_F17 = (short) ( 0x60+17 ); + + /** Constant for the F18 function key. See {@link #VK_F1}. */ + public static final short VK_F18 = (short) ( 0x60+18 ); + + /** Constant for the F19 function key. See {@link #VK_F1}. */ + public static final short VK_F19 = (short) ( 0x60+19 ); + + /** Constant for the F20 function key. See {@link #VK_F1}. */ + public static final short VK_F20 = (short) ( 0x60+20 ); + + /** Constant for the F21 function key. See {@link #VK_F1}. */ + public static final short VK_F21 = (short) ( 0x60+21 ); + + /** Constant for the F22 function key. See {@link #VK_F1}. */ + public static final short VK_F22 = (short) ( 0x60+22 ); + + /** Constant for the F23 function key. See {@link #VK_F1}. */ + public static final short VK_F23 = (short) ( 0x60+23 ); + + /** Constant for the F24 function key. See {@link #VK_F1}. */ + public static final short VK_F24 = (short) ( 0x60+24 ); + + + /** Constant for the "{" key */ + public static final short VK_LEFT_BRACE = (short) 0x7B; + /** Constant for the "|" key */ + public static final short VK_PIPE = (short) 0x7C; + /** Constant for the "}" key */ + public static final short VK_RIGHT_BRACE = (short) 0x7D; + + /** Constant for the "~" key, matching ASCII */ + public static final short VK_TILDE = (short) 0x7E; + + // + // Unicode: Non printable controls: [0x7F - 0x9F] + // + // Numpad keys [0x7F - 0x8E] are printable + // + + /** Numeric keypad <b>decimal separator</b> key. Non printable UTF control. */ + public static final short VK_SEPARATOR = (short) 0x7F; + + /** Numeric keypad VK_NUMPAD0 thru VK_NUMPAD9 are mapped to UTF control (0x80 - 0x89). Non printable UTF control. */ + public static final short VK_NUMPAD0 = (short) 0x80; + /** See {@link #VK_NUMPAD0}. */ + public static final short VK_NUMPAD1 = (short) 0x81; + /** See {@link #VK_NUMPAD0}. */ + public static final short VK_NUMPAD2 = (short) 0x82; + /** See {@link #VK_NUMPAD0}. */ + public static final short VK_NUMPAD3 = (short) 0x83; + /** See {@link #VK_NUMPAD0}. */ + public static final short VK_NUMPAD4 = (short) 0x84; + /** See {@link #VK_NUMPAD0}. */ + public static final short VK_NUMPAD5 = (short) 0x85; + /** See {@link #VK_NUMPAD0}. */ + public static final short VK_NUMPAD6 = (short) 0x86; + /** See {@link #VK_NUMPAD0}. */ + public static final short VK_NUMPAD7 = (short) 0x87; + /** See {@link #VK_NUMPAD0}. */ + public static final short VK_NUMPAD8 = (short) 0x88; + /** See {@link #VK_NUMPAD0}. */ + public static final short VK_NUMPAD9 = (short) 0x89; + + /** Numeric keypad <b>decimal separator</b> key. Non printable UTF control. */ + public static final short VK_DECIMAL = (short) 0x8A; + + /** Numeric keypad <b>add</b> key. Non printable UTF control. */ + public static final short VK_ADD = (short) 0x8B; + + /** Numeric keypad <b>subtract</b> key. Non printable UTF control. */ + public static final short VK_SUBTRACT = (short) 0x8C; + + /** Numeric keypad <b>multiply</b> key. Non printable UTF control. */ + public static final short VK_MULTIPLY = (short) 0x8D; + + /** Numeric keypad <b>divide</b> key. Non printable UTF control. */ + public static final short VK_DIVIDE = (short) 0x8E; + + /** Constant for the DEL key, matching ASCII. Non printable UTF control. */ + public static final short VK_DELETE = (short) 0x93; + + /** Numeric keypad <b>num lock</b> key. Non printable UTF control. */ + public static final short VK_NUM_LOCK = (short) 0x94; + + /** Constant for the cursor- or numerical-pad <b>left</b> arrow key. Non printable UTF control. */ + public static final short VK_LEFT = (short) 0x95; + + /** Constant for the cursor- or numerical-pad <b>up</b> arrow key. Non printable UTF control. */ + public static final short VK_UP = (short) 0x96; + + /** Constant for the cursor- or numerical-pad <b>right</b> arrow key. Non printable UTF control. */ + public static final short VK_RIGHT = (short) 0x97; + + /** Constant for the cursor- or numerical pad <b>down</b> arrow key. Non printable UTF control. */ + public static final short VK_DOWN = (short) 0x98; + + /** Constant for the Context Menu key. Non printable UTF control. */ + public static final short VK_CONTEXT_MENU = (short) 0x99; /** - * Constant for the "@" key. - * @since 1.2 - */ - public static final int VK_AT = 0x0200; - - /** - * Constant for the ":" key. - * @since 1.2 - */ - public static final int VK_COLON = 0x0201; - - /** - * Constant for the "^" key. - * @since 1.2 - */ - public static final int VK_CIRCUMFLEX = 0x0202; - - /** - * Constant for the "$" key. - * @since 1.2 - */ - public static final int VK_DOLLAR = 0x0203; - - /** - * Constant for the Euro currency sign key. - * @since 1.2 - */ - public static final int VK_EURO_SIGN = 0x0204; - - /** - * Constant for the "!" key. - * @since 1.2 - */ - public static final int VK_EXCLAMATION_MARK = 0x0205; - - /** - * Constant for the inverted exclamation mark key. - * @since 1.2 - */ - public static final int VK_INVERTED_EXCLAMATION_MARK = 0x0206; - - /** - * Constant for the "(" key. - * @since 1.2 - */ - public static final int VK_LEFT_PARENTHESIS = 0x0207; - - /** - * Constant for the "#" key. - * @since 1.2 + * Constant for the MS "Windows" function key. + * It is used for both the left and right version of the key. */ - public static final int VK_NUMBER_SIGN = 0x0208; - + public static final short VK_WINDOWS = (short) 0x9A; + + /** Constant for the Meta function key. */ + public static final short VK_META = (short) 0x9B; + + /** Constant for the Help function key. */ + public static final short VK_HELP = (short) 0x9C; + + /** Constant for the Compose function key. */ + public static final short VK_COMPOSE = (short) 0x9D; + + /** Constant for the Begin function key. */ + public static final short VK_BEGIN = (short) 0x9E; + + /** Constant for the Stop function key. */ + public static final short VK_STOP = (short) 0x9F; + + // + // Unicode: Printable [0x00A0 - 0xDFFF] + // + + /** Constant for the inverted exclamation mark key. */ + public static final short VK_INVERTED_EXCLAMATION_MARK = (short) 0xA1; + + /** Constant for the Euro currency sign key. */ + public static final short VK_EURO_SIGN = (short) 0x20AC; + + // + // Unicode: Private 0xE000 - 0xF8FF (Marked Non-Printable) + // + + /* for Sun keyboards */ + public static final short VK_CUT = (short) 0xF879; + public static final short VK_COPY = (short) 0xF87A; + public static final short VK_PASTE = (short) 0xF87B; + public static final short VK_UNDO = (short) 0xF87C; + public static final short VK_AGAIN = (short) 0xF87D; + public static final short VK_FIND = (short) 0xF87E; + public static final short VK_PROPS = (short) 0xF87F; + + /* for input method support on Asian Keyboards */ + /** - * Constant for the "+" key. - * @since 1.2 + * Constant for the input method on/off key. */ - public static final int VK_PLUS = 0x0209; - + /* Japanese PC 106 keyboard: kanji. Japanese Solaris keyboard: nihongo */ + public static final short VK_INPUT_METHOD_ON_OFF = (short) 0xF890; + /** - * Constant for the ")" key. - * @since 1.2 + * Constant for the Code Input function key. */ - public static final int VK_RIGHT_PARENTHESIS = 0x020A; - + /* Japanese PC 106 keyboard - VK_ALPHANUMERIC + ALT: kanji bangou */ + public static final short VK_CODE_INPUT = (short) 0xF891; + /** - * Constant for the "_" key. - * @since 1.2 + * Constant for the Roman Characters function key. */ - public static final int VK_UNDERSCORE = 0x020B; - + /* Japanese PC 106 keyboard: roumaji */ + public static final short VK_ROMAN_CHARACTERS = (short) 0xF892; + /** - * Constant for the Microsoft Windows "Windows" key. - * It is used for both the left and right version of the key. - * @see #getKeyLocation() - * @since 1.5 + * Constant for the All Candidates function key. */ - public static final int VK_WINDOWS = 0x020C; - + /* Japanese PC 106 keyboard - VK_CONVERT + ALT: zenkouho */ + public static final short VK_ALL_CANDIDATES = (short) 0xF893; + /** - * Constant for the Microsoft Windows Context Menu key. - * @since 1.5 + * Constant for the Previous Candidate function key. */ - public static final int VK_CONTEXT_MENU = 0x020D; - - /* for input method support on Asian Keyboards */ - - /* not clear what this means - listed in Microsoft Windows API */ - public static final int VK_FINAL = 0x0018; - - /** Constant for the Convert function key. */ - /* Japanese PC 106 keyboard, Japanese Solaris keyboard: henkan */ - public static final int VK_CONVERT = 0x001C; - - /** Constant for the Don't Convert function key. */ - /* Japanese PC 106 keyboard: muhenkan */ - public static final int VK_NONCONVERT = 0x001D; - - /** Constant for the Accept or Commit function key. */ - /* Japanese Solaris keyboard: kakutei */ - public static final int VK_ACCEPT = 0x001E; - - /* not clear what this means - listed in Microsoft Windows API */ - public static final int VK_MODECHANGE = 0x001F; - - /* replaced by VK_KANA_LOCK for Microsoft Windows and Solaris; - might still be used on other platforms */ - public static final int VK_KANA = 0x0015; - - /* replaced by VK_INPUT_METHOD_ON_OFF for Microsoft Windows and Solaris; - might still be used for other platforms */ - public static final int VK_KANJI = 0x0019; + /* Japanese PC 106 keyboard - VK_CONVERT + SHIFT: maekouho */ + public static final short VK_PREVIOUS_CANDIDATE = (short) 0xF894; /** * Constant for the Alphanumeric function key. - * @since 1.2 */ /* Japanese PC 106 keyboard: eisuu */ - public static final int VK_ALPHANUMERIC = 0x00F0; - + public static final short VK_ALPHANUMERIC = (short) 0xF895; + /** * Constant for the Katakana function key. - * @since 1.2 */ /* Japanese PC 106 keyboard: katakana */ - public static final int VK_KATAKANA = 0x00F1; - + public static final short VK_KATAKANA = (short) 0xF896; + /** * Constant for the Hiragana function key. - * @since 1.2 */ /* Japanese PC 106 keyboard: hiragana */ - public static final int VK_HIRAGANA = 0x00F2; - + public static final short VK_HIRAGANA = (short) 0xF897; + /** * Constant for the Full-Width Characters function key. - * @since 1.2 */ /* Japanese PC 106 keyboard: zenkaku */ - public static final int VK_FULL_WIDTH = 0x00F3; - + public static final short VK_FULL_WIDTH = (short) 0xF898; + /** * Constant for the Half-Width Characters function key. - * @since 1.2 */ /* Japanese PC 106 keyboard: hankaku */ - public static final int VK_HALF_WIDTH = 0x00F4; - - /** - * Constant for the Roman Characters function key. - * @since 1.2 - */ - /* Japanese PC 106 keyboard: roumaji */ - public static final int VK_ROMAN_CHARACTERS = 0x00F5; - - /** - * Constant for the All Candidates function key. - * @since 1.2 - */ - /* Japanese PC 106 keyboard - VK_CONVERT + ALT: zenkouho */ - public static final int VK_ALL_CANDIDATES = 0x0100; - - /** - * Constant for the Previous Candidate function key. - * @since 1.2 - */ - /* Japanese PC 106 keyboard - VK_CONVERT + SHIFT: maekouho */ - public static final int VK_PREVIOUS_CANDIDATE = 0x0101; - - /** - * Constant for the Code Input function key. - * @since 1.2 - */ - /* Japanese PC 106 keyboard - VK_ALPHANUMERIC + ALT: kanji bangou */ - public static final int VK_CODE_INPUT = 0x0102; - + public static final short VK_HALF_WIDTH = (short) 0xF89A; + /** * Constant for the Japanese-Katakana function key. * This key switches to a Japanese input method and selects its Katakana input mode. - * @since 1.2 */ /* Japanese Macintosh keyboard - VK_JAPANESE_HIRAGANA + SHIFT */ - public static final int VK_JAPANESE_KATAKANA = 0x0103; - + public static final short VK_JAPANESE_KATAKANA = (short) 0xF89B; + /** * Constant for the Japanese-Hiragana function key. * This key switches to a Japanese input method and selects its Hiragana input mode. - * @since 1.2 */ /* Japanese Macintosh keyboard */ - public static final int VK_JAPANESE_HIRAGANA = 0x0104; - + public static final short VK_JAPANESE_HIRAGANA = (short) 0xF89C; + /** * Constant for the Japanese-Roman function key. * This key switches to a Japanese input method and selects its Roman-Direct input mode. - * @since 1.2 */ /* Japanese Macintosh keyboard */ - public static final int VK_JAPANESE_ROMAN = 0x0105; + public static final short VK_JAPANESE_ROMAN = (short) 0xF89D; /** * Constant for the locking Kana function key. * This key locks the keyboard into a Kana layout. - * @since 1.3 */ /* Japanese PC 106 keyboard with special Windows driver - eisuu + Control; Japanese Solaris keyboard: kana */ - public static final int VK_KANA_LOCK = 0x0106; + public static final short VK_KANA_LOCK = (short) 0xF89F; /** - * Constant for the input method on/off key. - * @since 1.3 + * Constant for Keyboard became invisible, e.g. Android's soft keyboard Back button hit while keyboard is visible. */ - /* Japanese PC 106 keyboard: kanji. Japanese Solaris keyboard: nihongo */ - public static final int VK_INPUT_METHOD_ON_OFF = 0x0107; - - /* for Sun keyboards */ - /** @since 1.2 */ - public static final int VK_CUT = 0xFFD1; - /** @since 1.2 */ - public static final int VK_COPY = 0xFFCD; - /** @since 1.2 */ - public static final int VK_PASTE = 0xFFCF; - /** @since 1.2 */ - public static final int VK_UNDO = 0xFFCB; - /** @since 1.2 */ - public static final int VK_AGAIN = 0xFFC9; - /** @since 1.2 */ - public static final int VK_FIND = 0xFFD0; - /** @since 1.2 */ - public static final int VK_PROPS = 0xFFCA; - /** @since 1.2 */ - public static final int VK_STOP = 0xFFC8; - - /** - * Constant for the Compose function key. - * @since 1.2 - */ - public static final int VK_COMPOSE = 0xFF20; - - /** - * Constant for the AltGraph function key. - * @since 1.2 - */ - public static final int VK_ALT_GRAPH = 0xFF7E; - - /** - * Constant for the Begin key. - * @since 1.5 - */ - public static final int VK_BEGIN = 0xFF58; + public static final short VK_KEYBOARD_INVISIBLE = (short) 0xF8FF; - /** - * This value is used to indicate that the keyCode is unknown. - * KEY_TYPED events do not have a keyCode value; this value - * is used instead. - */ - public static final int VK_UNDEFINED = 0x0; } diff --git a/src/newt/classes/com/jogamp/newt/event/KeyListener.java b/src/newt/classes/com/jogamp/newt/event/KeyListener.java index dae343d80..4b16ab61e 100644 --- a/src/newt/classes/com/jogamp/newt/event/KeyListener.java +++ b/src/newt/classes/com/jogamp/newt/event/KeyListener.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,15 +29,33 @@ * 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.jogamp.newt.event; +/** + * Listener for {@link KeyEvent}s. + * + * @see KeyEvent + */ public interface KeyListener extends NEWTEventListener { - public void keyPressed(KeyEvent e); - public void keyReleased(KeyEvent e); - public void keyTyped(KeyEvent e) ; + /** A key has been {@link KeyEvent#EVENT_KEY_PRESSED pressed}, excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys. See {@link KeyEvent}. */ + public void keyPressed(KeyEvent e); + + /** + * A key has been {@link KeyEvent#EVENT_KEY_RELEASED released}, excluding {@link #isAutoRepeat() auto-repeat} {@link #isModifierKey() modifier} keys. See {@link KeyEvent}. + * <p> + * To simulated the removed <code>keyTyped(KeyEvent e)</code> semantics, + * simply apply the following constraints upfront and bail out if not matched, i.e.: + * <pre> + if( !e.isPrintableKey() || e.isAutoRepeat() ) { + return; + } + * </pre> + * </p> + */ + public void keyReleased(KeyEvent e); } diff --git a/src/newt/classes/com/jogamp/newt/event/MonitorEvent.java b/src/newt/classes/com/jogamp/newt/event/MonitorEvent.java new file mode 100644 index 000000000..03242e147 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/MonitorEvent.java @@ -0,0 +1,73 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.newt.event; + +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +@SuppressWarnings("serial") +public class MonitorEvent extends OutputEvent { + public static final short EVENT_MONITOR_MODE_CHANGE_NOTIFY = 600; + public static final short EVENT_MONITOR_MODE_CHANGED = 601; + + private final MonitorMode mode; + + public MonitorEvent (short eventType, MonitorDevice source, long when, MonitorMode mode) { + super(eventType, source, when); + this.mode = mode; + } + + /** Returns the {@link #getSource() source}, which is a {@link MonitorDevice}. */ + public final MonitorDevice getMonitor() { return (MonitorDevice)source; } + + public final MonitorMode getMode() { return mode; } + + public static String getEventTypeString(short type) { + switch(type) { + case EVENT_MONITOR_MODE_CHANGE_NOTIFY: return "EVENT_MONITOR_MODE_CHANGE_NOTIFY"; + case EVENT_MONITOR_MODE_CHANGED: return "EVENT_MONITOR_MODE_CHANGED"; + default: return "unknown (" + type + ")"; + } + } + + @Override + public final String toString() { + return toString(null).toString(); + } + + @Override + public final StringBuilder toString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + sb.append("MonitorEvent[").append(getEventTypeString(getEventType())).append(", source ").append(source) + .append(", mode ").append(mode).append(", "); + return super.toString(sb).append("]"); + } +} diff --git a/src/newt/classes/com/jogamp/newt/event/ScreenModeListener.java b/src/newt/classes/com/jogamp/newt/event/MonitorModeListener.java index 7bca23cfe..11e23def1 100644 --- a/src/newt/classes/com/jogamp/newt/event/ScreenModeListener.java +++ b/src/newt/classes/com/jogamp/newt/event/MonitorModeListener.java @@ -28,12 +28,10 @@ package com.jogamp.newt.event; -import com.jogamp.newt.ScreenMode; +public interface MonitorModeListener { + /** called before the monitor mode will be changed */ + void monitorModeChangeNotify(MonitorEvent me); -public interface ScreenModeListener { - /** called before the screen mode will be changed */ - void screenModeChangeNotify(ScreenMode sm); - - /** called after the screen mode has been changed */ - void screenModeChanged(ScreenMode sm, boolean success); + /** called after the monitor mode has been changed */ + void monitorModeChanged(MonitorEvent me, boolean success); } diff --git a/src/newt/classes/com/jogamp/newt/event/MouseAdapter.java b/src/newt/classes/com/jogamp/newt/event/MouseAdapter.java index 3607ae634..98252fe14 100644 --- a/src/newt/classes/com/jogamp/newt/event/MouseAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/MouseAdapter.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,30 +20,38 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event; public abstract class MouseAdapter implements MouseListener { + @Override public void mouseClicked(MouseEvent e) { } + @Override public void mouseEntered(MouseEvent e) { } + @Override public void mouseExited(MouseEvent e) { } + @Override public void mousePressed(MouseEvent e) { } + @Override public void mouseReleased(MouseEvent e) { } + @Override public void mouseMoved(MouseEvent e) { } + @Override public void mouseDragged(MouseEvent e) { } + @Override public void mouseWheelMoved(MouseEvent e) { } } diff --git a/src/newt/classes/com/jogamp/newt/event/MouseEvent.java b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java index ceaf7d47a..272e4beb0 100644 --- a/src/newt/classes/com/jogamp/newt/event/MouseEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,186 +29,573 @@ * 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.jogamp.newt.event; +/** + * Pointer event of type {@link PointerType}. + * <p> + * The historical misleading class name may change in the future to <code>PointerEvent</code>. + * </p> + * <p> + * http://www.w3.org/Submission/pointer-events/#pointerevent-interface + * </p> + * <a name="multiPtrEvent"><h5>Multiple-Pointer Events</h5></a> + * <p> + * In case an instance represents a multiple-pointer event, i.e. {@link #getPointerCount()} is > 1, + * the first data element of the multiple-pointer fields represents the pointer triggering this event.<br/> + * For example {@link #getX(int) e.getX(0)} at {@link #EVENT_MOUSE_PRESSED} returns the data of the pressed pointer, etc. + * </p> + * <p> + * A {@link #getButton() button value} of <code>0</code> denotes no button activity, i.e. {@link PointerType#Mouse} move. + * </p> + * <p> + * A {@link #getPointerId(int) pointer-ID} of -1 denotes no pointer/button activity, i.e. {@link PointerType#Mouse} move. + * </p> + * <p> + * {@link #getButton() Button values} are mapped from and to {@link #getPointerId(int) pointer-IDs} as follows: + * <code> + * getPointerId(0) == getButton() - 1 + * </code>. + * </p> + * <p> + * If representing a multiple-pointer event, the {@link #getButton() button number} is mapped to the <i>first {@link #getPointerId(int) pointer ID}</i> + * triggering the event and the {@link InputEvent#BUTTON1_MASK button mask bits} in the {@link #getModifiers() modifiers} + * field represent the pressed pointer IDs.<br> + * Hence users can query the pressed button count as well as the pressed pointer count via {@link InputEvent#getButtonDownCount()} + * or use the simple query {@link InputEvent#isAnyButtonDown()}. + * </p> + */ @SuppressWarnings("serial") public class MouseEvent extends InputEvent { + /** Class of pointer types */ + public static enum PointerClass implements InputEvent.InputClass { + Offscreen, Onscreen, Undefined; + } + + /** Type of pointer devices */ + public static enum PointerType implements InputEvent.InputType { + /** {@link PointerClass#Offscreen} mouse. Ordinal 0. */ + Mouse(PointerClass.Offscreen), + /** {@link PointerClass#Offscreen} touch pad, usually using fingers. Ordinal 1. */ + TouchPad(PointerClass.Offscreen), + /** {@link PointerClass#Onscreen} touch screen, usually using fingers. Ordinal 2. */ + TouchScreen(PointerClass.Onscreen), + /** {@link PointerClass#Onscreen} pen usually on screen? Ordinal 3. FIXME*/ + Pen(PointerClass.Onscreen), + /** {@link PointerClass#Undefined} ?. Ordinal 4. */ + Undefined(PointerClass.Undefined); + + public PointerClass getPointerClass() { return pc; } + + /** + * Returns the matching PointerType value corresponding to the given PointerType's integer ordinal. + * <pre> + * given: + * ordinal = enumValue.ordinal() + * reverse: + * enumValue = EnumClass.values()[ordinal] + * </pre> + * @throws IllegalArgumentException if the given ordinal is out of range, i.e. not within [ 0 .. PointerType.values().length-1 ] + */ + public static PointerType valueOf(int ordinal) throws IllegalArgumentException { + final PointerType[] all = PointerType.values(); + if( 0 <= ordinal && ordinal < all.length ) { + return all[ordinal]; + } + throw new IllegalArgumentException("Ordinal "+ordinal+" out of range of PointerType.values()[0.."+(all.length-1)+"]"); + } + + /** + * Returns the PointerType array of matching PointerType values corresponding to the given PointerType's integer ordinal values. + * <p> + * See {@link #valueOf(int)}. + * </p> + * @throws IllegalArgumentException if one of the given ordinal values is out of range, i.e. not within [ 0 .. PointerType.values().length-1 ] + */ + public static PointerType[] valuesOf(int[] ordinals) throws IllegalArgumentException { + final int count = ordinals.length; + final PointerType[] types = new PointerType[count]; + for(int i=count-1; i>=0; i--) { + types[i] = PointerType.valueOf(ordinals[i]); + } + return types; + } + + private PointerType(PointerClass pc) { + this.pc = pc; + } + PointerClass pc; + } + /** ID for button 1, value <code>1</code> */ - public static final int BUTTON1 = 1; + public static final short BUTTON1 = 1; /** ID for button 2, value <code>2</code> */ - public static final int BUTTON2 = 2; + public static final short BUTTON2 = 2; /** ID for button 3, value <code>3</code> */ - public static final int BUTTON3 = 3; + public static final short BUTTON3 = 3; /** ID for button 4, value <code>4</code> */ - public static final int BUTTON4 = 4; + public static final short BUTTON4 = 4; /** ID for button 5, value <code>5</code> */ - public static final int BUTTON5 = 5; + public static final short BUTTON5 = 5; /** ID for button 6, value <code>6</code> */ - public static final int BUTTON6 = 6; - /** Number of buttons, value <code>6</code> */ - public static final int BUTTON_NUMBER = 6; + public static final short BUTTON6 = 6; + /** ID for button 6, value <code>7</code> */ + public static final short BUTTON7 = 7; + /** ID for button 6, value <code>8</code> */ + public static final short BUTTON8 = 8; + /** ID for button 6, value <code>9</code> */ + public static final short BUTTON9 = 9; + + /** Maximum number of buttons, value <code>16</code> */ + public static final short BUTTON_COUNT = 16; + + /** + * Maximum number of buttons, value <code>16</code>. + * @deprecated Use {@link #BUTTON_COUNT} .. semantics. + */ + public static final short BUTTON_NUMBER = 16; - public static final int getClickTimeout() { - return 300; + /** Returns the 3-axis XYZ rotation array by given rotation on Y axis or X axis (if SHIFT_MASK is given in mods). */ + public static final float[] getRotationXYZ(final float rotationXorY, final int mods) { + final float[] rotationXYZ = new float[] { 0f, 0f, 0f }; + if( 0 != ( mods & InputEvent.SHIFT_MASK ) ) { + rotationXYZ[0] = rotationXorY; + } else { + rotationXYZ[1] = rotationXorY; + } + return rotationXYZ; + } + + public static final short getClickTimeout() { + return 300; } - public MouseEvent(int eventType, Object source, long when, - int modifiers, int x, int y, int clickCount, int button, - int rotation) + /** + * Constructor for traditional one-pointer event. + * + * @param eventType + * @param source + * @param when + * @param modifiers + * @param x X-axis + * @param y Y-axis + * @param clickCount Mouse-button click-count + * @param button button number, e.g. [{@link #BUTTON1}..{@link #BUTTON_COUNT}-1]. + * A button value of <code>0</code> denotes no button activity, i.e. {@link PointerType#Mouse} move. + * @param rotationXYZ Rotation of all axis + * @param rotationScale Rotation scale + */ + public MouseEvent(short eventType, Object source, long when, + int modifiers, int x, int y, short clickCount, short button, + float[] rotationXYZ, float rotationScale) { - super(eventType, source, when, modifiers); + super(eventType, source, when, modifiers); this.x = new int[]{x}; this.y = new int[]{y}; - this.pressure = new float[]{0}; - this.pointerids = new int[]{-1}; + switch(eventType) { + case EVENT_MOUSE_CLICKED: + case EVENT_MOUSE_PRESSED: + case EVENT_MOUSE_DRAGGED: + this.pressure = constMousePressure1; + break; + default: + this.pressure = constMousePressure0; + } + this.maxPressure= 1.0f; + this.pointerID = new short[] { (short)(button - 1) }; this.clickCount=clickCount; this.button=button; - this.wheelRotation = rotation; + this.rotationXYZ = rotationXYZ; + this.rotationScale = rotationScale; + this.pointerType = constMousePointerTypes; } - public MouseEvent(int eventType, Object source, long when, - int modifiers, int[] x, int[] y, float[] pressure, int[] pointerids, int clickCount, int button, - int rotation) + /** + * Constructor for a multiple-pointer event. + * <p> + * First element of multiple-pointer arrays represents the pointer which triggered the event! + * </p> + * <p> + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * </p> + * + * @param eventType + * @param source + * @param when + * @param modifiers + * @param pointerType PointerType for each pointer (multiple pointer) + * @param pointerID Pointer ID for each pointer (multiple pointer). IDs start w/ 0 and are consecutive numbers. + * A pointer-ID of -1 may also denote no pointer/button activity, i.e. {@link PointerType#Mouse} move. + * @param x X-axis for each pointer (multiple pointer) + * @param y Y-axis for each pointer (multiple pointer) + * @param pressure Pressure for each pointer (multiple pointer) + * @param maxPressure Maximum pointer pressure for all pointer + * @param button Corresponding mouse-button + * @param clickCount Mouse-button click-count + * @param rotationXYZ Rotation of all axis + * @param rotationScale Rotation scale + */ + public MouseEvent(short eventType, Object source, long when, int modifiers, + PointerType pointerType[], short[] pointerID, + int[] x, int[] y, float[] pressure, float maxPressure, + short button, short clickCount, float[] rotationXYZ, float rotationScale) { - super(eventType, source, when, modifiers); + super(eventType, source, when, modifiers); this.x = x; this.y = y; - if(pointerids.length != pressure.length || - pointerids.length != x.length || - pointerids.length != y.length) { + final int pointerCount = pointerType.length; + if(pointerCount != pointerID.length || + pointerCount != x.length || + pointerCount != y.length || + pointerCount != pressure.length) { throw new IllegalArgumentException("All multiple pointer arrays must be of same size"); } + if( 0.0f >= maxPressure ) { + throw new IllegalArgumentException("maxPressure must be > 0.0f"); + } this.pressure = pressure; - this.pointerids = pointerids; + this.maxPressure= maxPressure; + this.pointerID = pointerID; this.clickCount=clickCount; this.button=button; - this.wheelRotation = rotation; + this.rotationXYZ = rotationXYZ; + this.rotationScale = rotationScale; + this.pointerType = pointerType; } - + + public MouseEvent createVariant(short newEventType) { + return new MouseEvent(newEventType, source, getWhen(), getModifiers(), pointerType, pointerID, + x, y, pressure, maxPressure, button, clickCount, rotationXYZ, rotationScale); + } + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. * @return the count of pointers involved in this event */ - public int getPointerCount() { - return x.length; + public final int getPointerCount() { + return pointerType.length; + } + + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @return the {@link PointerType} for the data at index or null if index not available. + */ + public final PointerType getPointerType(int index) { + if(0 > index || index >= pointerType.length) { + return null; + } + return pointerType[index]; } - + + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @return array of all {@link PointerType}s for all pointers + */ + public final PointerType[] getAllPointerTypes() { + return pointerType; + } + /** - * @return the pointer id for the data at index. - * return -1 if index not available. + * Return the pointer id for the given index or -1 if index not available. + * <p> + * IDs start w/ 0 and are consecutive numbers. + * </p> + * <p> + * A pointer-ID of -1 may also denote no pointer/button activity, i.e. {@link PointerType#Mouse} move. + * </p> + * <p> + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * </p> */ - public int getPointerId(int index) { - if(index >= pointerids.length) + public final short getPointerId(int index) { + if(0 > index || index >= pointerID.length) { return -1; - return pointerids[index]; + } + return pointerID[index]; } - - public int getButton() { + + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @return the pointer index for the given pointer id or -1 if id not available. + */ + public final int getPointerIdx(short id) { + if( id >= 0 ) { + for(int i=pointerID.length-1; i>=0; i--) { + if( pointerID[i] == id ) { + return i; + } + } + } + return -1; + } + + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @return array of all pointer IDs for all pointers. IDs start w/ 0 and are consecutive numbers. + */ + public final short[] getAllPointerIDs() { + return pointerID; + } + + /** + * Returns the button number, e.g. [{@link #BUTTON1}..{@link #BUTTON_COUNT}-1]. + * <p> + * A button value of <code>0</code> denotes no button activity, i.e. {@link PointerType#Mouse} move. + * </p> + * <p> + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * </p> + */ + public final short getButton() { return button; } - - public int getClickCount() { + + public final short getClickCount() { return clickCount; } - public int getX() { + + public final int getX() { return x[0]; } - - public int getY() { + + public final int getY() { return y[0]; } - /** - * @return x-coord at index where index refers to the - * data coming from a pointer. + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @param index pointer-index within [0 .. {@link #getPointerCount()}-1] + * @return X-Coord associated with the pointer-index. * @see getPointerId(index) */ - public int getX(int index) { + public final int getX(int index) { return x[index]; } - public int getY(int index) { + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @param index pointer-index within [0 .. {@link #getPointerCount()}-1] + * @return Y-Coord associated with the pointer-index. + * @see getPointerId(index) + */ + public final int getY(int index) { return y[index]; } - - public float getPressure(){ - return pressure[0]; + + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @return array of all X-Coords for all pointers + */ + public final int[] getAllX() { + return x; + } + + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @return array of all Y-Coords for all pointers + */ + public final int[] getAllY() { + return y; } - + /** - * @return the pressure associated with the pointer at index. - * the value of zero is return if not available. + * @param normalized if true, method returns the normalized pressure, i.e. <code>pressure / maxPressure</code> + * @return The pressure associated with the pointer-index 0. + * The value of zero is return if not available. + * @see #getMaxPressure() */ - public float getPressure(int index){ - return pressure[index]; + public final float getPressure(boolean normalized){ + return normalized ? pressure[0] / maxPressure : pressure[0]; } - + /** - * <i>Usually</i> a wheel rotation of <b>> 0 is up</b>, - * and <b>< 0 is down</b>.<br> + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @param index pointer-index within [0 .. {@link #getPointerCount()}-1] + * @param normalized if true, method returns the normalized pressure, i.e. <code>pressure / maxPressure</code> + * @return The pressure associated with the pointer-index. + * The value of zero is return if not available. + * @see #getMaxPressure() + */ + public final float getPressure(int index, boolean normalized){ + return normalized ? pressure[index] / maxPressure : pressure[index]; + } + + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @return array of all raw, un-normalized pressures for all pointers + */ + public final float[] getAllPressures() { + return pressure; + } + + /** + * Returns the maximum pressure known for the input device generating this event. + * <p> + * This value may be self calibrating on devices/OS, where no known maximum pressure is known. + * Hence subsequent events may return a higher value. + * </p> + * <p> + * Self calibrating maximum pressure is performed on: + * <ul> + * <li>Android</li> + * </ul> + * </p> + */ + public final float getMaxPressure() { + return maxPressure; + } + + /** + * Returns a 3-component float array filled with the values of the rotational axis + * in the following order: horizontal-, vertical- and z-axis. + * <p> + * A vertical rotation of <b>> 0.0f is up</b> and <b>< 0.0f is down</b>. + * </p> + * <p> + * A horizontal rotation of <b>> 0.0f is left</b> and <b>< 0.0f is right</b>. + * </p> + * <p> + * A z-axis rotation of <b>> 0.0f is back</b> and <b>< 0.0f is front</b>. + * </p> + * <p> * <i>However</i>, on some OS this might be flipped due to the OS <i>default</i> behavior. * The latter is true for OS X 10.7 (Lion) for example. + * </p> * <p> - * The events will be send usually in steps of one, ie. <i>-1</i> and <i>1</i>. + * On PointerClass {@link PointerClass#Onscreen onscreen} devices, i.e. {@link PointerType#TouchScreen touch screens}, + * rotation events are usually produced by a 2-finger movement, where horizontal and vertical rotation values are filled. + * </p> + * <p> + * On PointerClass {@link PointerClass#Offscreen offscreen} devices, i.e. {@link PointerType#Mouse mouse}, + * either the horizontal or the vertical rotation value is filled. + * </p> + * <p> + * The {@link InputEvent#SHIFT_MASK} modifier is set in case <b>|horizontal| > |vertical|</b> value.<br/> + * This can be utilized to implement only one 2d rotation direction, you may use {@link #isShiftDown()} to query it. + * </p> + * <p> + * In case the pointer type is {@link PointerType#Mouse mouse}, + * events are usually send in steps of one, ie. <i>-1.0f</i> and <i>1.0f</i>. * Higher values may result due to fast scrolling. + * Fractional values may result due to slow scrolling with high resolution devices.<br/> + * Here the button number refers to the wheel number. + * </p> + * <p> + * In case the pointer type is of class {@link PointerClass#Onscreen}, e.g. {@link PointerType#TouchScreen touch screen}, + * see {@link #getRotationScale()} for semantics. + * </p> + */ + public final float[] getRotation() { + return rotationXYZ; + } + + /** + * Returns the scale used to determine the {@link #getRotation() rotation value}, + * which semantics depends on the {@link #getPointerType() pointer type's} {@link PointerClass}. + * <p> + * For {@link PointerClass#Offscreen}, the scale is usually <code>1.0f</code> and denominates + * an abstract value without association to a physical value. * </p> * <p> - * The button number refers to the wheel number. - * </p> - * @return + * For {@link PointerClass#Onscreen}, the scale varies and denominates + * the divisor of the distance the finger[s] have moved on the screen. + * Hence <code>scale * rotation</code> reproduces the screen distance in pixels the finger[s] have moved. + * </p> */ - public int getWheelRotation() { - return wheelRotation; + public final float getRotationScale() { + return rotationScale; } - public String toString() { - StringBuilder sb = new StringBuilder(); + @Override + public final String toString() { + return toString(null).toString(); + } + + @Override + public final StringBuilder toString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } sb.append("MouseEvent[").append(getEventTypeString(getEventType())) .append(", ").append(x).append("/").append(y) .append(", button ").append(button).append(", count ") - .append(clickCount).append(", wheel rotation ").append(wheelRotation); - if(pointerids.length>0) { - sb.append(", pointer<").append(pointerids.length).append(">["); - for(int i=0; i<pointerids.length; i++) { + .append(clickCount).append(", rotation [").append(rotationXYZ[0]).append(", ").append(rotationXYZ[1]).append(", ").append(rotationXYZ[2]).append("] * ").append(rotationScale); + if(pointerID.length>0) { + sb.append(", pointer<").append(pointerID.length).append(">["); + for(int i=0; i<pointerID.length; i++) { if(i>0) { sb.append(", "); } - sb.append(pointerids[i]).append(": ") - .append(x[i]).append(" / ").append(y[i]).append(" ") - .append(pressure[i]).append("p"); + sb.append(pointerID[i]).append("/").append(pointerType[i]).append(": ") + .append(x[i]).append("/").append(y[i]).append(", ") + .append("p[").append(pressure[i]).append("/").append(maxPressure).append("=").append(pressure[i]/maxPressure).append("]"); } sb.append("]"); - } - sb.append(", ").append(super.toString()).append("]"); - return sb.toString(); + } + sb.append(", "); + return super.toString(sb).append("]"); } - public static String getEventTypeString(int type) { + public static String getEventTypeString(short type) { switch(type) { - case EVENT_MOUSE_CLICKED: return "EVENT_MOUSE_CLICKED"; - case EVENT_MOUSE_ENTERED: return "EVENT_MOUSE_ENTERED"; - case EVENT_MOUSE_EXITED: return "EVENT_MOUSE_EXITED"; - case EVENT_MOUSE_PRESSED: return "EVENT_MOUSE_PRESSED"; - case EVENT_MOUSE_RELEASED: return "EVENT_MOUSE_RELEASED"; - case EVENT_MOUSE_MOVED: return "EVENT_MOUSE_MOVED"; - case EVENT_MOUSE_DRAGGED: return "EVENT_MOUSE_DRAGGED"; - case EVENT_MOUSE_WHEEL_MOVED: return "EVENT_MOUSE_WHEEL_MOVED"; - default: return "unknown (" + type + ")"; + case EVENT_MOUSE_CLICKED: return "EVENT_MOUSE_CLICKED"; + case EVENT_MOUSE_ENTERED: return "EVENT_MOUSE_ENTERED"; + case EVENT_MOUSE_EXITED: return "EVENT_MOUSE_EXITED"; + case EVENT_MOUSE_PRESSED: return "EVENT_MOUSE_PRESSED"; + case EVENT_MOUSE_RELEASED: return "EVENT_MOUSE_RELEASED"; + case EVENT_MOUSE_MOVED: return "EVENT_MOUSE_MOVED"; + case EVENT_MOUSE_DRAGGED: return "EVENT_MOUSE_DRAGGED"; + case EVENT_MOUSE_WHEEL_MOVED: return "EVENT_MOUSE_WHEEL_MOVED"; + default: return "unknown (" + type + ")"; } } - private final int x[], y[], clickCount, button, wheelRotation; + + /** PointerType for each pointer (multiple pointer) */ + private final PointerType pointerType[]; + /** + * Pointer-ID for each pointer (multiple pointer). IDs start w/ 0 and are consecutive numbers. + * <p> + * A pointer-ID of -1 may also denote no pointer/button activity, i.e. {@link PointerType#Mouse} move. + * </p> + */ + private final short pointerID[]; + /** X-axis for each pointer (multiple pointer) */ + private final int x[]; + /** Y-axis for each pointer (multiple pointer) */ + private final int y[]; + /** Pressure for each pointer (multiple pointer) */ private final float pressure[]; - private final int pointerids[]; - - public static final int EVENT_MOUSE_CLICKED = 200; - public static final int EVENT_MOUSE_ENTERED = 201; - public static final int EVENT_MOUSE_EXITED = 202; - public static final int EVENT_MOUSE_PRESSED = 203; - public static final int EVENT_MOUSE_RELEASED = 204; - public static final int EVENT_MOUSE_MOVED = 205; - public static final int EVENT_MOUSE_DRAGGED = 206; - public static final int EVENT_MOUSE_WHEEL_MOVED = 207; + // private final short tiltX[], tiltY[]; // TODO: A generic way for pointer axis information, see Android MotionEvent! + private final short clickCount; + /** + * Returns the button number, e.g. [{@link #BUTTON1}..{@link #BUTTON_COUNT}-1]. + * <p> + * A button value of <code>0</code> denotes no button activity, i.e. {@link PointerType#Mouse} move. + * </p> + */ + private final short button; + /** Rotation around the X, Y and X axis */ + private final float[] rotationXYZ; + /** Rotation scale */ + private final float rotationScale; + private final float maxPressure; + + private static final float[] constMousePressure0 = new float[]{0f}; + private static final float[] constMousePressure1 = new float[]{1f}; + private static final PointerType[] constMousePointerTypes = new PointerType[] { PointerType.Mouse }; + + public static final short EVENT_MOUSE_CLICKED = 200; + /** Only generated for {@link PointerType#Mouse} */ + public static final short EVENT_MOUSE_ENTERED = 201; + /** Only generated for {@link PointerType#Mouse} */ + public static final short EVENT_MOUSE_EXITED = 202; + public static final short EVENT_MOUSE_PRESSED = 203; + public static final short EVENT_MOUSE_RELEASED = 204; + public static final short EVENT_MOUSE_MOVED = 205; + public static final short EVENT_MOUSE_DRAGGED = 206; + public static final short EVENT_MOUSE_WHEEL_MOVED = 207; } diff --git a/src/newt/classes/com/jogamp/newt/event/MouseListener.java b/src/newt/classes/com/jogamp/newt/event/MouseListener.java index 7668b755c..6e5142044 100644 --- a/src/newt/classes/com/jogamp/newt/event/MouseListener.java +++ b/src/newt/classes/com/jogamp/newt/event/MouseListener.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,22 +29,37 @@ * 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.jogamp.newt.event; +import com.jogamp.newt.event.MouseEvent.PointerType; + +/** + * Listener for {@link MouseEvent}s. + * + * @see MouseEvent + */ public interface MouseListener extends NEWTEventListener { public void mouseClicked(MouseEvent e); + /** Only generated for {@link PointerType#Mouse} */ public void mouseEntered(MouseEvent e); + /** Only generated for {@link PointerType#Mouse} */ public void mouseExited(MouseEvent e); public void mousePressed(MouseEvent e); public void mouseReleased(MouseEvent e); public void mouseMoved(MouseEvent e); public void mouseDragged(MouseEvent e); - - /** See {@link MouseEvent#getWheelRotation() } */ + + /** + * Traditional event name originally produced by a {@link PointerType#Mouse mouse} pointer type. + * <p> + * Triggered for any rotational pointer events, see + * {@link MouseEvent#getRotation()} and {@link MouseEvent#getRotationScale()}. + * </p> + */ public void mouseWheelMoved(MouseEvent e); } diff --git a/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java b/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java index 3f3817b91..af800e61e 100644 --- a/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,7 +29,7 @@ * 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.jogamp.newt.event; @@ -41,88 +41,35 @@ package com.jogamp.newt.event; * * Event type registry:<br> * <ul> - * <li> WindowEvent <code>100..10x</code></li> - * <li> MouseEvent <code>200..20x</code></li> - * <li> KeyEvent <code>300..30x</code></li> + * <li> WindowEvent <code>100..10x</code></li> + * <li> MouseEvent <code>200..20x</code></li> + * <li> KeyEvent <code>300..30x</code></li> + * <li> GestureEvent <code>400..5xx</code></li> + * <li> MonitorEvent <code>600..60x</code></li> * </ul><br> */ @SuppressWarnings("serial") public class NEWTEvent extends java.util.EventObject { - private final boolean isSystemEvent; - private final int eventType; + /** + * See {@link #setConsumed(boolean)} for description. + */ + public static final Object consumedTag = new Object(); + + private final short eventType; private final long when; private Object attachment; static final boolean DEBUG = false; - // 0: NEWTEvent.java - // 1: InputEvent.java - // 2: KeyEvent.java - // 3: com.jogamp.newt.Window - // 3: com.jogamp.newt.event.awt.AWTNewtEventFactory - // 2: MouseEvent.java - // 3: com.jogamp.newt.Window - // 3: com.jogamp.newt.event.awt.AWTNewtEventFactory - // 1: WindowEvent.java - // 2: com.jogamp.newt.Window - // 2: com.jogamp.newt.event.awt.AWTNewtEventFactory - // - // FIXME: verify the isSystemEvent evaluation - // - static final String WindowClazzName = "com.jogamp.newt.Window" ; - static final String AWTNewtEventFactoryClazzName = "com.jogamp.newt.event.awt.AWTNewtEventFactory" ; - - /** - static final boolean evaluateIsSystemEvent(NEWTEvent event, Throwable t) { - StackTraceElement[] stack = t.getStackTrace(); - if(stack.length==0 || null==stack[0]) { - return false; - } - if(DEBUG) { - for (int i = 0; i < stack.length && i<5; i++) { - System.err.println(i+": " + stack[i].getClassName()+ "." + stack[i].getMethodName()); - } - } - - String clazzName = null; - - if( event instanceof com.jogamp.newt.event.WindowEvent ) { - if ( stack.length > 2 ) { - clazzName = stack[2].getClassName(); - } - } else if( (event instanceof com.jogamp.newt.event.MouseEvent) || - (event instanceof com.jogamp.newt.event.KeyEvent) ) { - if ( stack.length > 3 ) { - clazzName = stack[3].getClassName(); - } - } - - boolean res = null!=clazzName && ( - clazzName.equals(WindowClazzName) || - clazzName.equals(AWTNewtEventFactoryClazzName) ) ; - if(DEBUG) { - System.err.println("system: "+res); - } - return res; - } */ - - protected NEWTEvent(int eventType, Object source, long when) { + protected NEWTEvent(short eventType, Object source, long when) { super(source); - // this.isSystemEvent = evaluateIsSystemEvent(this, new Throwable()); - this.isSystemEvent = false; // FIXME: Need a more efficient way to determine system events this.eventType = eventType; this.when = when; this.attachment=null; } - /** Indicates whether this event was produced by the system or - generated by user code. */ - public final boolean isSystemEvent() { - return isSystemEvent; - } - /** Returns the event type of this event. */ - public final int getEventType() { + public final short getEventType() { return eventType; } @@ -131,35 +78,72 @@ public class NEWTEvent extends java.util.EventObject { return when; } - /** + /** * Attach the passed object to this event.<br> * If an object was previously attached, it will be replaced.<br> * Attachments to NEWT events allow users to pass on information - * from one custom listener to another, ie custom listener to listener + * from one custom listener to another, ie custom listener to listener * communication. * @param attachment User application specific object */ public final void setAttachment(Object attachment) { - this.attachment=attachment; + this.attachment = attachment; } - /** + /** * @return The user application specific attachment, or null */ public final Object getAttachment() { return attachment; } - public String toString() { - return "NEWTEvent[sys:"+isSystemEvent()+", source:"+getSource().getClass().getName()+", when:"+getWhen()+" d "+(System.currentTimeMillis()-getWhen())+"ms]"; + /** + * Returns <code>true</code> if this events has been {@link #setConsumed(boolean) consumed}, + * otherwise <code>false</code>. + * @see #setConsumed(boolean) + */ + public final boolean isConsumed() { + return consumedTag == attachment; } - public static String toHexString(int hex) { - return "0x" + Integer.toHexString(hex); + /** + * If <code>consumed</code> is <code>true</code>, this event is marked as consumed, + * ie. the event will not be propagated any further to potential <i>other</i> event listener. + * Otherwise the event will be propagated to other event listener, the default. + * <p> + * The event is marked as being consumed while {@link #setAttachment(Object) attaching} + * the {@link #consumedTag}. + * </p> + * <p> + * Events with platform specific actions will be supressed if marked as consumed. + * Examples are: + * <ul> + * <li>{@link KeyEvent#VK_ESCAPE} on Android's BACK button w/ Activity::finish()</li> + * <li>{@link KeyEvent#VK_HOME} on Android's HOME button w/ Intend.ACTION_MAIN[Intend.CATEGORY_HOME]</li> + * </ul> + * </p> + */ + public final void setConsumed(boolean consumed) { + if( consumed ) { + setAttachment( consumedTag ); + } else if( consumedTag == attachment ) { + setAttachment( null ); + } + } + + @Override + public String toString() { + return toString(null).toString(); } - public static String toHexString(long hex) { - return "0x" + Long.toHexString(hex); + public StringBuilder toString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + return sb.append("NEWTEvent[source:").append(getSource().getClass().getName()).append(", consumed ").append(isConsumed()).append(", when:").append(getWhen()).append(" d ").append((System.currentTimeMillis()-getWhen())).append("ms]"); } + public static String toHexString(short hex) { + return "0x" + Integer.toHexString( (int)hex & 0x0000FFFF ); + } } diff --git a/src/newt/classes/com/jogamp/newt/event/NEWTEventConsumer.java b/src/newt/classes/com/jogamp/newt/event/NEWTEventConsumer.java index 6aa19e5f8..14fba6742 100644 --- a/src/newt/classes/com/jogamp/newt/event/NEWTEventConsumer.java +++ b/src/newt/classes/com/jogamp/newt/event/NEWTEventConsumer.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,18 +20,18 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event; public interface NEWTEventConsumer { - /** - * Consume the event + /** + * Consume the event * * @return true if the event has been consumed, * otherwise it returns false for later propagation. diff --git a/src/newt/classes/com/jogamp/newt/event/NEWTEventFiFo.java b/src/newt/classes/com/jogamp/newt/event/NEWTEventFiFo.java index fe224bba6..7dd56ad1e 100644 --- a/src/newt/classes/com/jogamp/newt/event/NEWTEventFiFo.java +++ b/src/newt/classes/com/jogamp/newt/event/NEWTEventFiFo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event; import java.util.LinkedList; diff --git a/src/newt/classes/com/jogamp/newt/event/NEWTEventListener.java b/src/newt/classes/com/jogamp/newt/event/NEWTEventListener.java index 677136573..f7ee3d739 100644 --- a/src/newt/classes/com/jogamp/newt/event/NEWTEventListener.java +++ b/src/newt/classes/com/jogamp/newt/event/NEWTEventListener.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,7 +29,7 @@ * 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.jogamp.newt.event; diff --git a/src/newt/classes/com/jogamp/newt/event/OutputEvent.java b/src/newt/classes/com/jogamp/newt/event/OutputEvent.java new file mode 100644 index 000000000..80c7780f8 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/OutputEvent.java @@ -0,0 +1,51 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.newt.event; + +@SuppressWarnings("serial") +public abstract class OutputEvent extends NEWTEvent +{ + protected OutputEvent(short eventType, Object source, long when) { + super(eventType, source, when); + } + + /** + public String toString() { + return toString(null).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + sb.append("OutputEvent["); + super.toString(sb).append("]"); + return sb; + } */ +} diff --git a/src/newt/classes/com/jogamp/newt/event/PinchToZoomGesture.java b/src/newt/classes/com/jogamp/newt/event/PinchToZoomGesture.java new file mode 100644 index 000000000..42f006f08 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/PinchToZoomGesture.java @@ -0,0 +1,228 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.newt.event; + +import javax.media.nativewindow.NativeSurface; + +import jogamp.newt.Debug; + +/** + * 2 pointer zoom, a.k.a. <i>pinch to zoom</i>, gesture handler processing {@link MouseEvent}s + * while producing {@link ZoomEvent}s if gesture is completed. + * <p> + * Zoom value lies within [0..2], with 1 as <i>1:1</i>. + * </p> + * <pre> + * - choosing the smallest surface edge (width/height -> x/y) + * - tolerating other fingers to be pressed and hence user to add functionality (scale, ..) + * </pre> + */ +public class PinchToZoomGesture implements GestureHandler { + public static final boolean DEBUG = Debug.debug("Window.MouseEvent"); + + /** A {@link GestureHandler.GestureEvent} denominating zoom. */ + @SuppressWarnings("serial") + public static class ZoomEvent extends GestureEvent { + private final MouseEvent pe; + private final float zoom; + public ZoomEvent(Object source, long when, int modifiers, GestureHandler handler, MouseEvent pe, float zoom) { + super(source, when, modifiers, handler); + this.pe = pe; + this.zoom = zoom; + } + /** Triggering {@link MouseEvent} */ + public final MouseEvent getTrigger() { return pe; } + /** Zoom value lies within [0..2], with 1 as <i>1:1</i>. */ + public final float getZoom() { return zoom; } + } + + private final NativeSurface surface; + private final boolean allowMorePointer; + private float zoom; + private int zoomLastEdgeDist; + private boolean zoomFirstTouch; + private boolean zoomMode; + private ZoomEvent zoomEvent; + private final short[] pIds = new short[] { -1, -1 }; + + /** + * @param surface the {@link NativeSurface}, which size is used to compute the relative zoom factor + * @param allowMorePointer if false, allow only 2 pressed pointers (safe and recommended), otherwise accept other pointer to be pressed. + */ + public PinchToZoomGesture(NativeSurface surface, boolean allowMorePointer) { + clear(true); + this.surface = surface; + this.allowMorePointer = allowMorePointer; + this.zoom = 1f; + } + + @Override + public String toString() { + return "PinchZoom[1stTouch "+zoomFirstTouch+", in "+isWithinGesture()+", has "+(null!=zoomEvent)+", zoom "+zoom+"]"; + } + + private int gesturePointers(final MouseEvent e, final int excludeIndex) { + int j = 0; + for(int i=e.getPointerCount()-1; i>=0; i--) { + if( excludeIndex != i ) { + final int id = e.getPointerId(i); + if( pIds[0] == id || pIds[1] == id ) { + j++; + } + } + } + return j; + } + + @Override + public void clear(boolean clearStarted) { + zoomEvent = null; + if( clearStarted ) { + zoomLastEdgeDist = 0; + zoomFirstTouch = true; + zoomMode = false; + pIds[0] = -1; + pIds[1] = -1; + } + } + + @Override + public boolean isWithinGesture() { + return zoomMode; + } + + @Override + public boolean hasGesture() { + return null != zoomEvent; + } + + @Override + public InputEvent getGestureEvent() { + return zoomEvent; + } + + /** Zoom value lies within [0..2], with 1 as <i>1:1</i>. */ + public final float getZoom() { + return zoom; + } + /** Set zoom value within [0..2], with 1 as <i>1:1</i>. */ + public final void setZoom(float zoom) { + this.zoom=zoom; + } + + @Override + public boolean process(final InputEvent in) { + if( null != zoomEvent || !(in instanceof MouseEvent) ) { + return true; + } + final MouseEvent pe = (MouseEvent)in; + final int pointerDownCount = pe.getPointerCount(); + + if( pe.getPointerType(0).getPointerClass() != MouseEvent.PointerClass.Onscreen || + ( !allowMorePointer && pointerDownCount > 2 ) ) { + return false; + } + + final int eventType = pe.getEventType(); + final boolean useY = surface.getWidth() >= surface.getHeight(); // use smallest dimension + switch ( eventType ) { + case MouseEvent.EVENT_MOUSE_PRESSED: { + if( 1 == pointerDownCount ) { + pIds[0] = pe.getPointerId(0); + pIds[1] = -1; + } else if ( 2 <= pointerDownCount ) { // && 1 == gesturePointers(pe, 0) /* w/o pressed pointer */) { + pIds[0] = pe.getPointerId(0); + pIds[1] = pe.getPointerId(1); + } + if(DEBUG) { + System.err.println("XXX1: id0 "+pIds[0]+" -> idx0 "+0+", id1 "+pIds[1]+" -> idx1 "+1); + System.err.println(this+".pressed: down "+pointerDownCount+", gPtr "+gesturePointers(pe, -1)+", event "+pe); + } + } break; + + case MouseEvent.EVENT_MOUSE_RELEASED: { + final int gPtr = gesturePointers(pe, 0); // w/o lifted pointer + if ( 1 == gPtr ) { + zoomFirstTouch = true; + zoomMode = false; + } else if( 0 == gPtr ) { + // all lifted + clear(true); + } + if(DEBUG) { + System.err.println(this+".released: down "+pointerDownCount+", gPtr "+gPtr+", event "+pe); + } + } break; + + case MouseEvent.EVENT_MOUSE_DRAGGED: { + if( 2 <= pointerDownCount ) { + final int gPtr = gesturePointers(pe, -1); + if( 2 == gPtr ) { + // same pointers + final int p0Idx = pe.getPointerIdx(pIds[0]); + final int p1Idx = pe.getPointerIdx(pIds[1]); + if( 0 <= p0Idx && 0 <= p1Idx ) { + final int edge0 = useY ? pe.getY(p0Idx) : pe.getX(p0Idx); + final int edge1 = useY ? pe.getY(p1Idx) : pe.getX(p1Idx); + // Diff. 1:1 Zoom: finger-distance to screen-coord + if(zoomFirstTouch) { + zoomLastEdgeDist = Math.abs(edge0-edge1); + zoomFirstTouch=false; + zoomMode = true; + } else if( zoomMode ) { + final int d = Math.abs(edge0-edge1); + final int dd = d - zoomLastEdgeDist; + final float screenEdge = useY ? surface.getHeight() : surface.getWidth(); + final float incr = dd / screenEdge; // [-1..1] + if(DEBUG) { + System.err.println("XXX2: id0 "+pIds[0]+" -> idx0 "+p0Idx+", id1 "+pIds[1]+" -> idx1 "+p1Idx); + System.err.println("XXX3: d "+d+", ld "+zoomLastEdgeDist+", dd "+dd+", screen "+screenEdge+" -> incr "+incr+", zoom "+zoom+" -> "+(zoom+incr)); + } + zoom += incr; + // clip value + if( 2f < zoom ) { + zoom = 2f; + } else if( 0 > zoom ) { + zoom = 0; + } + zoomLastEdgeDist = d; + zoomEvent = new ZoomEvent(pe.getSource(), pe.getWhen(), pe.getModifiers(), this, pe, zoom); + } + } + } + if(DEBUG) { + System.err.println(this+".dragged: down "+pointerDownCount+", gPtr "+gPtr+", event "+pe); + } + } + } break; + + default: + } + return null != zoomEvent; + } +} diff --git a/src/newt/classes/com/jogamp/newt/event/TraceKeyAdapter.java b/src/newt/classes/com/jogamp/newt/event/TraceKeyAdapter.java index 98ba5a24d..bbc170958 100644 --- a/src/newt/classes/com/jogamp/newt/event/TraceKeyAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/TraceKeyAdapter.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event; public class TraceKeyAdapter implements KeyListener { @@ -40,17 +40,15 @@ public class TraceKeyAdapter implements KeyListener { this.downstream = downstream; } + @Override public void keyPressed(KeyEvent e) { System.err.println(e); if(null!=downstream) { downstream.keyPressed(e); } } + @Override public void keyReleased(KeyEvent e) { System.err.println(e); if(null!=downstream) { downstream.keyReleased(e); } } - public void keyTyped(KeyEvent e) { - System.err.println(e); - if(null!=downstream) { downstream.keyTyped(e); } - } } diff --git a/src/newt/classes/com/jogamp/newt/event/TraceMouseAdapter.java b/src/newt/classes/com/jogamp/newt/event/TraceMouseAdapter.java index 14ee633a0..db8376034 100644 --- a/src/newt/classes/com/jogamp/newt/event/TraceMouseAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/TraceMouseAdapter.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event; public class TraceMouseAdapter implements MouseListener { @@ -40,34 +40,42 @@ public class TraceMouseAdapter implements MouseListener { this.downstream = downstream; } + @Override public void mouseClicked(MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseClicked(e); } } + @Override public void mouseEntered(MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseEntered(e); } } + @Override public void mouseExited(MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseExited(e); } } + @Override public void mousePressed(MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mousePressed(e); } } + @Override public void mouseReleased(MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseReleased(e); } } + @Override public void mouseMoved(MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseMoved(e); } } + @Override public void mouseDragged(MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseDragged(e); } } + @Override public void mouseWheelMoved(MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseWheelMoved(e); } diff --git a/src/newt/classes/com/jogamp/newt/event/TraceWindowAdapter.java b/src/newt/classes/com/jogamp/newt/event/TraceWindowAdapter.java index 8542820c4..7b844f059 100644 --- a/src/newt/classes/com/jogamp/newt/event/TraceWindowAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/TraceWindowAdapter.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event; public class TraceWindowAdapter implements WindowListener { @@ -40,30 +40,37 @@ public class TraceWindowAdapter implements WindowListener { this.downstream = downstream; } + @Override public void windowResized(WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowResized(e); } } + @Override public void windowMoved(WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowMoved(e); } } + @Override public void windowDestroyNotify(WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowDestroyNotify(e); } } + @Override public void windowDestroyed(WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowDestroyed(e); } } + @Override public void windowGainedFocus(WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowGainedFocus(e); } } + @Override public void windowLostFocus(WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowLostFocus(e); } } + @Override public void windowRepaint(WindowUpdateEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowRepaint(e); } diff --git a/src/newt/classes/com/jogamp/newt/event/WindowAdapter.java b/src/newt/classes/com/jogamp/newt/event/WindowAdapter.java index b9e487e9b..ccc627444 100644 --- a/src/newt/classes/com/jogamp/newt/event/WindowAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/WindowAdapter.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,28 +20,35 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event; public abstract class WindowAdapter implements WindowListener { + @Override public void windowResized(WindowEvent e) { } + @Override public void windowMoved(WindowEvent e) { } + @Override public void windowDestroyNotify(WindowEvent e) { } + @Override public void windowDestroyed(WindowEvent e) { } + @Override public void windowGainedFocus(WindowEvent e) { } + @Override public void windowLostFocus(WindowEvent e) { } + @Override public void windowRepaint(WindowUpdateEvent e) { } } diff --git a/src/newt/classes/com/jogamp/newt/event/WindowEvent.java b/src/newt/classes/com/jogamp/newt/event/WindowEvent.java index f3d62d8c6..2841fd0f6 100644 --- a/src/newt/classes/com/jogamp/newt/event/WindowEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/WindowEvent.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,7 +29,7 @@ * 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.jogamp.newt.event; @@ -39,20 +39,21 @@ package com.jogamp.newt.event; * NEWT will automatically handle component moves and resizes internally, regardless of whether a program is receiving these events or not. <br> * The actual event semantic, here move and resize, is processed before the event is send.<br> */ +@SuppressWarnings("serial") public class WindowEvent extends NEWTEvent { - public static final int EVENT_WINDOW_RESIZED = 100; - public static final int EVENT_WINDOW_MOVED = 101; - public static final int EVENT_WINDOW_DESTROY_NOTIFY = 102; - public static final int EVENT_WINDOW_GAINED_FOCUS = 103; - public static final int EVENT_WINDOW_LOST_FOCUS = 104; - public static final int EVENT_WINDOW_REPAINT = 105; - public static final int EVENT_WINDOW_DESTROYED = 106; + public static final short EVENT_WINDOW_RESIZED = 100; + public static final short EVENT_WINDOW_MOVED = 101; + public static final short EVENT_WINDOW_DESTROY_NOTIFY = 102; + public static final short EVENT_WINDOW_GAINED_FOCUS = 103; + public static final short EVENT_WINDOW_LOST_FOCUS = 104; + public static final short EVENT_WINDOW_REPAINT = 105; + public static final short EVENT_WINDOW_DESTROYED = 106; - public WindowEvent(int eventType, Object source, long when) { + public WindowEvent(short eventType, Object source, long when) { super(eventType, source, when); } - public static String getEventTypeString(int type) { + public static String getEventTypeString(short type) { switch(type) { case EVENT_WINDOW_RESIZED: return "WINDOW_RESIZED"; case EVENT_WINDOW_MOVED: return "WINDOW_MOVED"; @@ -64,8 +65,18 @@ public class WindowEvent extends NEWTEvent { default: return "unknown (" + type + ")"; } } + + @Override public String toString() { - return "WindowEvent["+getEventTypeString(getEventType()) + - ", " + super.toString() + "]"; + return toString(null).toString(); + } + + @Override + public StringBuilder toString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + sb.append("WindowEvent[").append(getEventTypeString(getEventType())).append(", "); + return super.toString(sb).append("]"); } } diff --git a/src/newt/classes/com/jogamp/newt/event/WindowListener.java b/src/newt/classes/com/jogamp/newt/event/WindowListener.java index e841a06cf..e097edf23 100644 --- a/src/newt/classes/com/jogamp/newt/event/WindowListener.java +++ b/src/newt/classes/com/jogamp/newt/event/WindowListener.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,11 +29,14 @@ * 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.jogamp.newt.event; +import javax.media.nativewindow.WindowClosingProtocol; + +/** NEWT {@link WindowEvent} listener. */ public interface WindowListener extends NEWTEventListener { /** Window is resized, your application shall respect the new window dimension. A repaint is recommended. */ public void windowResized(WindowEvent e); @@ -41,10 +44,19 @@ public interface WindowListener extends NEWTEventListener { /** Window has been moved. */ public void windowMoved(WindowEvent e); - /** Window will be destroyed. Release of resources is recommended. */ + /** + * Window destruction has been requested. + * <p> + * Depending on the {@link WindowClosingProtocol#getDefaultCloseOperation() default close operation}, + * the window maybe destroyed or not. + * </p> + * In case the window will be destroyed (see above), release of resources is recommended. + **/ public void windowDestroyNotify(WindowEvent e); - /** Window has been destroyed.*/ + /** + * Window has been destroyed. + */ public void windowDestroyed(WindowEvent e); /** Window gained focus. */ diff --git a/src/newt/classes/com/jogamp/newt/event/WindowUpdateEvent.java b/src/newt/classes/com/jogamp/newt/event/WindowUpdateEvent.java index 505939de2..9044517b5 100644 --- a/src/newt/classes/com/jogamp/newt/event/WindowUpdateEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/WindowUpdateEvent.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,20 +20,21 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event; import javax.media.nativewindow.util.Rectangle; +@SuppressWarnings("serial") public class WindowUpdateEvent extends WindowEvent { final Rectangle bounds; - public WindowUpdateEvent(int eventType, Object source, long when, Rectangle bounds) + public WindowUpdateEvent(short eventType, Object source, long when, Rectangle bounds) { super(eventType, source, when); this.bounds = bounds; @@ -43,7 +44,17 @@ public class WindowUpdateEvent extends WindowEvent { return bounds; } + @Override public String toString() { - return "WindowUpdateEvent["+super.toString()+", "+bounds+"]"; + return toString(null).toString(); + } + + @Override + public StringBuilder toString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + sb.append("WindowUpdateEvent[").append(bounds).append(", "); + return super.toString(sb).append("]"); } } diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTAdapter.java index 8991203d5..864db1f7b 100644 --- a/src/newt/classes/com/jogamp/newt/event/awt/AWTAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTAdapter.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event.awt; import jogamp.newt.Debug; @@ -48,10 +48,10 @@ import jogamp.newt.Debug; * Common:<br> * <pre> // your demo/render code - javax.media.opengl.GLEvenListener demo1 = new javax.media.opengl.GLEvenListener() { ... } ; + javax.media.opengl.GLEvenListener demo1 = new javax.media.opengl.GLEvenListener() { ... } ; // your AWT agnostic NEWT mouse listener code - com.jogamp.newt.event.MouseListener mouseListener = new com.jogamp.newt.event.MouseAdapter() { ... } ; + com.jogamp.newt.event.MouseListener mouseListener = new com.jogamp.newt.event.MouseAdapter() { ... } ; * </pre> </p> * <p> * Default NEWT use case, without using the AWTAdapter:<br> @@ -96,7 +96,7 @@ import jogamp.newt.Debug; <br> new AWTMouseAdapter(mouseListener, glWindow).addTo(comp);<br> * </pre> </p> - * + * * Last but not least, the AWTAdapter maybe used as a general AWT event forwarder to NEWT.<br> * * <p> @@ -108,7 +108,7 @@ import jogamp.newt.Debug; <br> new AWTMouseAdapter(glWindow).addTo(comp); // forward all AWT events to glWindow, as NEWT events<br> * </pre> </p> - * + * * @see #attachTo */ public abstract class AWTAdapter implements java.util.EventListener @@ -117,25 +117,29 @@ public abstract class AWTAdapter implements java.util.EventListener com.jogamp.newt.event.NEWTEventListener newtListener; com.jogamp.newt.Window newtWindow; + boolean consumeAWTEvent; + protected boolean isSetup; - /** + /** * 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) { + protected AWTAdapter(com.jogamp.newt.event.NEWTEventListener newtListener) { if(null==newtListener) { throw new RuntimeException("Argument newtListener is null"); } this.newtListener = newtListener; this.newtWindow = null; + this.consumeAWTEvent = false; + this.isSetup = true; } - /** + /** * 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) { + protected AWTAdapter(com.jogamp.newt.event.NEWTEventListener newtListener, com.jogamp.newt.Window newtProxy) { if(null==newtListener) { throw new RuntimeException("Argument newtListener is null"); } @@ -144,33 +148,65 @@ public abstract class AWTAdapter implements java.util.EventListener } this.newtListener = newtListener; this.newtWindow = newtProxy; + this.consumeAWTEvent = false; + this.isSetup = true; } - /** + /** * 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) { + protected AWTAdapter(com.jogamp.newt.Window downstream) { + this(); + setDownstream(downstream); + } + + public AWTAdapter() { + clear(); + this.consumeAWTEvent = false; + } + + /** + * Setup 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 synchronized AWTAdapter setDownstream(com.jogamp.newt.Window downstream) { if(null==downstream) { throw new RuntimeException("Argument downstream is null"); } this.newtListener = null; this.newtWindow = downstream; + this.consumeAWTEvent = false; if( null == newtWindow.getScreen().getDisplay().getEDTUtil() ) { throw new RuntimeException("EDT not enabled"); } + this.isSetup = true; + return this; + } + + /** Removes all references, downstream and NEWT-EventListener. */ + public synchronized AWTAdapter clear() { + this.newtListener = null; + this.newtWindow = null; + this.isSetup = false; + return this; } - public final com.jogamp.newt.Window getNewtWindow() { - return newtWindow; + public final synchronized void setConsumeAWTEvent(boolean v) { + this.consumeAWTEvent = v; } - - public final com.jogamp.newt.event.NEWTEventListener getNewtEventListener() { - return newtListener; + + public final synchronized com.jogamp.newt.Window getNewtWindow() { + return newtWindow; + } + + public final synchronized com.jogamp.newt.event.NEWTEventListener getNewtEventListener() { + return newtListener; } - - /** + + /** * 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> @@ -181,8 +217,13 @@ public abstract class AWTAdapter implements java.util.EventListener /** @see #addTo(java.awt.Component) */ public abstract AWTAdapter removeFrom(java.awt.Component awtComponent); + /** + * Enqueues the event to the {@link #getNewtWindow()} is not null. + */ void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { - newtWindow.enqueueEvent(wait, event); + if( null != newtWindow ) { + newtWindow.enqueueEvent(wait, event); + } } } diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java index 7b0f6ba97..d4226f53e 100644 --- a/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,16 +20,21 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event.awt; import jogamp.newt.awt.event.AWTNewtEventFactory; +/** + * AWT: + * printable: PRESSED (t0), TYPED (t0), RELEASED (t1) + * non-printable: PRESSED (t0), RELEASED (t1) + */ public class AWTKeyAdapter extends AWTAdapter implements java.awt.event.KeyListener { public AWTKeyAdapter(com.jogamp.newt.event.KeyListener newtListener) { @@ -44,18 +49,29 @@ public class AWTKeyAdapter extends AWTAdapter implements java.awt.event.KeyListe super(downstream); } - public AWTAdapter addTo(java.awt.Component awtComponent) { + public AWTKeyAdapter() { + super(); + } + + @Override + public synchronized AWTAdapter addTo(java.awt.Component awtComponent) { awtComponent.addKeyListener(this); return this; } - public AWTAdapter removeFrom(java.awt.Component awtComponent) { + @Override + public synchronized AWTAdapter removeFrom(java.awt.Component awtComponent) { awtComponent.removeKeyListener(this); return this; } - public void keyPressed(java.awt.event.KeyEvent e) { - com.jogamp.newt.event.KeyEvent event = AWTNewtEventFactory.createKeyEvent(e, newtWindow); + @Override + public synchronized void keyPressed(java.awt.event.KeyEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.KeyEvent event = AWTNewtEventFactory.createKeyEvent(com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED, e, newtWindow); + if( consumeAWTEvent ) { + e.consume(); + } if(null!=newtListener) { ((com.jogamp.newt.event.KeyListener)newtListener).keyPressed(event); } else { @@ -63,8 +79,13 @@ public class AWTKeyAdapter extends AWTAdapter implements java.awt.event.KeyListe } } - public void keyReleased(java.awt.event.KeyEvent e) { - com.jogamp.newt.event.KeyEvent event = AWTNewtEventFactory.createKeyEvent(e, newtWindow); + @Override + public synchronized void keyReleased(java.awt.event.KeyEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.KeyEvent event = AWTNewtEventFactory.createKeyEvent(com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED, e, newtWindow); + if( consumeAWTEvent ) { + e.consume(); + } if(null!=newtListener) { ((com.jogamp.newt.event.KeyListener)newtListener).keyReleased(event); } else { @@ -72,12 +93,11 @@ public class AWTKeyAdapter extends AWTAdapter implements java.awt.event.KeyListe } } - public void keyTyped(java.awt.event.KeyEvent e) { - com.jogamp.newt.event.KeyEvent event = AWTNewtEventFactory.createKeyEvent(e, newtWindow); - if(null!=newtListener) { - ((com.jogamp.newt.event.KeyListener)newtListener).keyTyped(event); - } else { - enqueueEvent(false, event); + @Override + public synchronized void keyTyped(java.awt.event.KeyEvent e) { + if( !isSetup ) { return; } + if( consumeAWTEvent ) { + e.consume(); } } } diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTMouseAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTMouseAdapter.java index 115743a0d..8ad1fa6ab 100644 --- a/src/newt/classes/com/jogamp/newt/event/awt/AWTMouseAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTMouseAdapter.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,17 +20,17 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event.awt; import jogamp.newt.awt.event.AWTNewtEventFactory; -public class AWTMouseAdapter extends AWTAdapter implements java.awt.event.MouseListener, +public class AWTMouseAdapter extends AWTAdapter implements java.awt.event.MouseListener, java.awt.event.MouseMotionListener, java.awt.event.MouseWheelListener { @@ -46,22 +46,33 @@ public class AWTMouseAdapter extends AWTAdapter implements java.awt.event.MouseL super(downstream); } - public AWTAdapter addTo(java.awt.Component awtComponent) { + public AWTMouseAdapter() { + super(); + } + + @Override + public synchronized AWTAdapter addTo(java.awt.Component awtComponent) { awtComponent.addMouseListener(this); awtComponent.addMouseMotionListener(this); awtComponent.addMouseWheelListener(this); return this; } - public AWTAdapter removeFrom(java.awt.Component awtComponent) { + @Override + public synchronized AWTAdapter removeFrom(java.awt.Component awtComponent) { awtComponent.removeMouseListener(this); awtComponent.removeMouseMotionListener(this); awtComponent.removeMouseWheelListener(this); return this; } - public void mouseClicked(java.awt.event.MouseEvent e) { + @Override + public synchronized void mouseClicked(java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); + if( consumeAWTEvent ) { + e.consume(); + } if(null!=newtListener) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseClicked(event); } else { @@ -69,8 +80,13 @@ public class AWTMouseAdapter extends AWTAdapter implements java.awt.event.MouseL } } - public void mouseEntered(java.awt.event.MouseEvent e) { + @Override + public synchronized void mouseEntered(java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); + if( consumeAWTEvent ) { + e.consume(); + } if(null!=newtListener) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseEntered(event); } else { @@ -78,8 +94,13 @@ public class AWTMouseAdapter extends AWTAdapter implements java.awt.event.MouseL } } - public void mouseExited(java.awt.event.MouseEvent e) { + @Override + public synchronized void mouseExited(java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); + if( consumeAWTEvent ) { + e.consume(); + } if(null!=newtListener) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseExited(event); } else { @@ -87,8 +108,13 @@ public class AWTMouseAdapter extends AWTAdapter implements java.awt.event.MouseL } } - public void mousePressed(java.awt.event.MouseEvent e) { + @Override + public synchronized void mousePressed(java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); + if( consumeAWTEvent ) { + e.consume(); + } if(null!=newtListener) { ((com.jogamp.newt.event.MouseListener)newtListener).mousePressed(event); } else { @@ -96,8 +122,13 @@ public class AWTMouseAdapter extends AWTAdapter implements java.awt.event.MouseL } } - public void mouseReleased(java.awt.event.MouseEvent e) { + @Override + public synchronized void mouseReleased(java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); + if( consumeAWTEvent ) { + e.consume(); + } if(null!=newtListener) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseReleased(event); } else { @@ -105,8 +136,13 @@ public class AWTMouseAdapter extends AWTAdapter implements java.awt.event.MouseL } } - public void mouseDragged(java.awt.event.MouseEvent e) { + @Override + public synchronized void mouseDragged(java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); + if( consumeAWTEvent ) { + e.consume(); + } if(null!=newtListener) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseDragged(event); } else { @@ -114,8 +150,13 @@ public class AWTMouseAdapter extends AWTAdapter implements java.awt.event.MouseL } } - public void mouseMoved(java.awt.event.MouseEvent e) { + @Override + public synchronized void mouseMoved(java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); + if( consumeAWTEvent ) { + e.consume(); + } if(null!=newtListener) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseMoved(event); } else { @@ -123,8 +164,13 @@ public class AWTMouseAdapter extends AWTAdapter implements java.awt.event.MouseL } } - public void mouseWheelMoved(java.awt.event.MouseWheelEvent e) { + @Override + public synchronized void mouseWheelMoved(java.awt.event.MouseWheelEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); + if( consumeAWTEvent ) { + e.consume(); + } if(null!=newtListener) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseWheelMoved(event); } else { diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTWindowAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTWindowAdapter.java index 69b0d0482..2e5527ee1 100644 --- a/src/newt/classes/com/jogamp/newt/event/awt/AWTWindowAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTWindowAdapter.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,18 +20,20 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.newt.event.awt; +import java.awt.Dimension; + import jogamp.newt.awt.event.AWTNewtEventFactory; -public class AWTWindowAdapter - extends AWTAdapter +public class AWTWindowAdapter + extends AWTAdapter implements java.awt.event.ComponentListener, java.awt.event.WindowListener, java.awt.event.FocusListener { WindowClosingListener windowClosingListener; @@ -47,15 +49,17 @@ public class AWTWindowAdapter public AWTWindowAdapter(com.jogamp.newt.Window downstream) { super(downstream); } + public AWTWindowAdapter() { + super(); + } - public AWTAdapter addTo(java.awt.Component awtComponent) { + @Override + public synchronized AWTAdapter addTo(java.awt.Component awtComponent) { java.awt.Window win = getWindow(awtComponent); awtComponent.addComponentListener(this); awtComponent.addFocusListener(this); - if( null == windowClosingListener ) { + if( null != win && null == windowClosingListener ) { windowClosingListener = new WindowClosingListener(); - } - if( null != win ) { win.addWindowListener(windowClosingListener); } if(awtComponent instanceof java.awt.Window) { @@ -64,13 +68,19 @@ public class AWTWindowAdapter return this; } - public AWTAdapter removeFrom(java.awt.Component awtComponent) { - awtComponent.removeFocusListener(this); - awtComponent.removeComponentListener(this); + public synchronized AWTAdapter removeWindowClosingFrom(java.awt.Component awtComponent) { java.awt.Window win = getWindow(awtComponent); if( null != win && null != windowClosingListener ) { win.removeWindowListener(windowClosingListener); } + return this; + } + + @Override + public synchronized AWTAdapter removeFrom(java.awt.Component awtComponent) { + awtComponent.removeFocusListener(this); + awtComponent.removeComponentListener(this); + removeWindowClosingFrom(awtComponent); if(awtComponent instanceof java.awt.Window) { ((java.awt.Window)awtComponent).removeWindowListener(this); } @@ -87,8 +97,13 @@ public class AWTWindowAdapter return null; } - public void focusGained(java.awt.event.FocusEvent e) { + @Override + public synchronized void focusGained(java.awt.event.FocusEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); + if(DEBUG_IMPLEMENTATION) { + System.err.println("AWT: focusGained: "+e+" -> "+event); + } if(null!=newtListener) { ((com.jogamp.newt.event.WindowListener)newtListener).windowGainedFocus(event); } else { @@ -96,8 +111,13 @@ public class AWTWindowAdapter } } - public void focusLost(java.awt.event.FocusEvent e) { + @Override + public synchronized void focusLost(java.awt.event.FocusEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); + if(DEBUG_IMPLEMENTATION) { + System.err.println("AWT: focusLost: "+e+" -> "+event); + } if(null!=newtListener) { ((com.jogamp.newt.event.WindowListener)newtListener).windowLostFocus(event); } else { @@ -105,10 +125,24 @@ public class AWTWindowAdapter } } - public void componentResized(java.awt.event.ComponentEvent e) { + @Override + public synchronized void componentResized(java.awt.event.ComponentEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); if(DEBUG_IMPLEMENTATION) { - System.err.println("AWT: componentResized: "+event); + final java.awt.Component c = e.getComponent(); + final java.awt.Dimension sz = c.getSize(); + final java.awt.Insets insets; + final java.awt.Dimension sz2; + if(c instanceof java.awt.Container) { + insets = ((java.awt.Container)c).getInsets(); + sz2 = new Dimension(sz.width - insets.left - insets.right, + sz.height - insets.top - insets.bottom); + } else { + insets = null; + sz2 = sz; + } + System.err.println("AWT: componentResized: "+sz+" ( "+insets+", "+sz2+" ), "+e+" -> "+event); } if(null!=newtListener) { ((com.jogamp.newt.event.WindowListener)newtListener).windowResized(event); @@ -117,10 +151,12 @@ public class AWTWindowAdapter } } - public void componentMoved(java.awt.event.ComponentEvent e) { + @Override + public synchronized void componentMoved(java.awt.event.ComponentEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); if(DEBUG_IMPLEMENTATION) { - System.err.println("AWT: componentMoved: "+event); + System.err.println("AWT: componentMoved: "+e+" -> "+event); } if(null!=newtListener) { ((com.jogamp.newt.event.WindowListener)newtListener).windowMoved(event); @@ -129,7 +165,9 @@ public class AWTWindowAdapter } } - public void componentShown(java.awt.event.ComponentEvent e) { + @Override + public synchronized void componentShown(java.awt.event.ComponentEvent e) { + if( !isSetup ) { return; } final java.awt.Component comp = e.getComponent(); if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: componentShown: "+comp); @@ -146,7 +184,9 @@ public class AWTWindowAdapter }*/ } - public void componentHidden(java.awt.event.ComponentEvent e) { + @Override + public synchronized void componentHidden(java.awt.event.ComponentEvent e) { + if( !isSetup ) { return; } final java.awt.Component comp = e.getComponent(); if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: componentHidden: "+comp); @@ -163,7 +203,9 @@ public class AWTWindowAdapter }*/ } - public void windowActivated(java.awt.event.WindowEvent e) { + @Override + public synchronized void windowActivated(java.awt.event.WindowEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); if(null!=newtListener) { ((com.jogamp.newt.event.WindowListener)newtListener).windowGainedFocus(event); @@ -172,11 +214,15 @@ public class AWTWindowAdapter } } - public void windowClosed(java.awt.event.WindowEvent e) { } + @Override + public synchronized void windowClosed(java.awt.event.WindowEvent e) { } - public void windowClosing(java.awt.event.WindowEvent e) { } + @Override + public synchronized void windowClosing(java.awt.event.WindowEvent e) { } - public void windowDeactivated(java.awt.event.WindowEvent e) { + @Override + public synchronized void windowDeactivated(java.awt.event.WindowEvent e) { + if( !isSetup ) { return; } com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); if(null!=newtListener) { ((com.jogamp.newt.event.WindowListener)newtListener).windowLostFocus(event); @@ -185,27 +231,50 @@ public class AWTWindowAdapter } } - public void windowDeiconified(java.awt.event.WindowEvent e) { } + @Override + public synchronized void windowDeiconified(java.awt.event.WindowEvent e) { } - public void windowIconified(java.awt.event.WindowEvent e) { } + @Override + public synchronized void windowIconified(java.awt.event.WindowEvent e) { } - public void windowOpened(java.awt.event.WindowEvent e) { } + @Override + public synchronized void windowOpened(java.awt.event.WindowEvent e) { } class WindowClosingListener implements java.awt.event.WindowListener { + @Override public void windowClosing(java.awt.event.WindowEvent e) { - com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); - if(null!=newtListener) { - ((com.jogamp.newt.event.WindowListener)newtListener).windowDestroyNotify(event); - } else { - enqueueEvent(true, event); + synchronized( AWTWindowAdapter.this ) { + if( !isSetup ) { return; } + com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); + if(null!=newtListener) { + ((com.jogamp.newt.event.WindowListener)newtListener).windowDestroyNotify(event); + } else { + enqueueEvent(true, event); + } + } + } + @Override + public void windowClosed(java.awt.event.WindowEvent e) { + synchronized( AWTWindowAdapter.this ) { + if( !isSetup ) { return; } + com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); + if(null!=newtListener) { + ((com.jogamp.newt.event.WindowListener)newtListener).windowDestroyed(event); + } else { + enqueueEvent(true, event); + } } } + @Override public void windowActivated(java.awt.event.WindowEvent e) { } - public void windowClosed(java.awt.event.WindowEvent e) { } + @Override public void windowDeactivated(java.awt.event.WindowEvent e) { } + @Override public void windowDeiconified(java.awt.event.WindowEvent e) { } + @Override public void windowIconified(java.awt.event.WindowEvent e) { } + @Override public void windowOpened(java.awt.event.WindowEvent e) { } } } diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index f89193754..4b740927b 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,105 +29,127 @@ * 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.jogamp.newt.opengl; -import java.io.PrintStream; - -import com.jogamp.common.GlueGenVersion; -import com.jogamp.common.util.VersionUtil; -import com.jogamp.newt.*; -import com.jogamp.newt.event.*; - -import jogamp.newt.WindowImpl; - -import javax.media.nativewindow.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.List; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.CapabilitiesChooser; +import javax.media.nativewindow.CapabilitiesImmutable; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.SurfaceUpdatedListener; import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; -import javax.media.opengl.*; +import javax.media.opengl.FPSCounter; +import javax.media.opengl.GL; +import javax.media.opengl.GL3; +import javax.media.opengl.GL4ES3; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLES2; +import javax.media.opengl.GLES3; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; +import javax.media.opengl.GLRunnable; -import jogamp.opengl.FPSCounterImpl; -import jogamp.opengl.GLDrawableHelper; +import jogamp.newt.WindowImpl; +import jogamp.opengl.GLAutoDrawableBase; +import jogamp.opengl.GLContextImpl; +import jogamp.opengl.GLDrawableImpl; + +import com.jogamp.common.GlueGenVersion; +import com.jogamp.common.util.VersionUtil; +import com.jogamp.common.util.locks.RecursiveLock; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.Screen; +import com.jogamp.newt.Window; +import com.jogamp.newt.Display.PointerIcon; +import com.jogamp.newt.event.GestureHandler; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.MouseListener; +import com.jogamp.newt.event.NEWTEvent; +import com.jogamp.newt.event.NEWTEventConsumer; +import com.jogamp.newt.event.NEWTEventListener; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.event.WindowListener; +import com.jogamp.newt.event.WindowUpdateEvent; import com.jogamp.opengl.JoglVersion; -import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.GLStateKeeper; /** * An implementation of {@link GLAutoDrawable} and {@link Window} interface, * using a delegated {@link Window} instance, which may be an aggregation (lifecycle: created and destroyed). * <P> + * This implementation supports {@link GLStateKeeper GL state preservation}, + * hence {@link #isGLStatePreservationSupported()} returns <code>true</code>. + * </P> + * <P> * This implementation does not make the OpenGL context current<br> - * before calling the various input EventListener callbacks, ie {@link com.jogamp.newt.event.MouseListener} etc.<br> + * before calling the various input EventListener callbacks, ie {@link MouseListener} etc.<br> * This design decision is made in favor of a more performant and simplified * implementation. Also the event dispatcher shall be implemented OpenGL agnostic.<br> - * To be able to use OpenGL commands from within such input {@link com.jogamp.newt.event.NEWTEventListener},<br> - * you can inject {@link javax.media.opengl.GLRunnable} objects - * via {@link #invoke(boolean, javax.media.opengl.GLRunnable)} to the OpenGL command stream.<br> - * <p> + * To be able to use OpenGL commands from within such input {@link NEWTEventListener},<br> + * you can inject {@link GLRunnable} objects + * via {@link #invoke(boolean, GLRunnable)} to the OpenGL command stream.<br> + * </p> */ -public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSCounter { +public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Window, NEWTEventConsumer, FPSCounter { private final WindowImpl window; /** * Constructor. Do not call this directly -- use {@link #create()} instead. */ protected GLWindow(Window window) { - resetFPSCounter(); + super(null, null, false /* always handle device lifecycle ourselves */); this.window = (WindowImpl) window; - ((WindowImpl)this.window).setHandleDestroyNotify(false); + this.window.setWindowDestroyNotifyAction( new Runnable() { + @Override + public void run() { + defaultWindowDestroyNotifyOp(); + } } ); window.addWindowListener(new WindowAdapter() { @Override public void windowRepaint(WindowUpdateEvent e) { - if( !GLWindow.this.window.isWindowLockedByOtherThread() && !GLWindow.this.helper.isAnimatorAnimating() ) { - display(); - } + defaultWindowRepaintOp(); } @Override public void windowResized(WindowEvent e) { - sendReshape = true; - if( !GLWindow.this.window.isWindowLockedByOtherThread() && !GLWindow.this.helper.isAnimatorAnimating() ) { - display(); - } + defaultWindowResizedOp(getWidth(), getHeight()); } - @Override - public void windowDestroyNotify(WindowEvent e) { - if( WindowClosingMode.DISPOSE_ON_CLOSE == GLWindow.this.getDefaultCloseOperation() ) { - // Is an animator thread perform rendering? - if (GLWindow.this.helper.isExternalAnimatorRunning()) { - // Pause animations before initiating safe destroy. - GLAnimatorControl ctrl = GLWindow.this.helper.getAnimator(); - boolean isPaused = ctrl.pause(); - destroy(); - if(isPaused) { - ctrl.resume(); - } - } else if (GLWindow.this.window.isWindowLockedByOtherThread()) { - // Window is locked by another thread - // Flag that destroy should be performed on the next - // attempt to display. - sendDestroy = true; - } else { - // Without an external thread animating or locking the - // surface, we are safe. - destroy (); - } - } - } }); this.window.setLifecycleHook(new GLLifecycleHook()); } + @Override + public final Object getUpstreamWidget() { + return window; + } + /** - * Creates a new GLWindow attaching a new Window referencing a + * Creates a new GLWindow attaching a new Window referencing a * new default Screen and default Display with the given GLCapabilities. * <p> * The lifecycle of this Window's Screen and Display is handled via {@link Screen#addReference()} * and {@link Screen#removeReference()}. * </p> - * The default Display will be reused if already instantiated. + * The default Display will be reused if already instantiated. */ public static GLWindow create(GLCapabilitiesImmutable caps) { return new GLWindow(NewtFactory.createWindow(caps)); @@ -145,7 +167,7 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC return new GLWindow(NewtFactory.createWindow(screen, caps)); } - /** + /** * Creates a new GLWindow attaching the given window. * <p> * The lifecycle of this Window's Screen and Display is handled via {@link Screen#addReference()} @@ -156,13 +178,13 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC return new GLWindow(window); } - /** - * Creates a new GLWindow attaching a new child Window + /** + * Creates a new GLWindow attaching a new child Window * of the given <code>parentNativeWindow</code> with the given GLCapabilities. * <p> * The Display/Screen will be compatible with the <code>parentNativeWindow</code>, * or even identical in case it's a Newt Window. - * An already instantiated compatible Display will be reused. + * An already instantiated compatible Display will be reused. * </p> * <p> * The lifecycle of this Window's Screen and Display is handled via {@link Screen#addReference()} @@ -176,10 +198,12 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC //---------------------------------------------------------------------- // WindowClosingProtocol implementation // + @Override public WindowClosingMode getDefaultCloseOperation() { return window.getDefaultCloseOperation(); } + @Override public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { return window.setDefaultCloseOperation(op); } @@ -188,165 +212,255 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC // Window Access // + @Override public CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) { return window.setCapabilitiesChooser(chooser); } + @Override public final CapabilitiesImmutable getChosenCapabilities() { - if (drawable == null) { - return window.getChosenCapabilities(); - } - - return drawable.getChosenGLCapabilities(); + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getChosenGLCapabilities() : window.getChosenCapabilities(); } + @Override public final CapabilitiesImmutable getRequestedCapabilities() { return window.getRequestedCapabilities(); } + @Override public final Window getDelegatedWindow() { return window.getDelegatedWindow(); } + @Override public final NativeWindow getParent() { return window.getParent(); } + @Override public final Screen getScreen() { return window.getScreen(); } + @Override + public final MonitorDevice getMainMonitor() { + return window.getMainMonitor(); + } + + @Override public final void setTitle(String title) { window.setTitle(title); } + @Override public final String getTitle() { return window.getTitle(); } + @Override public final boolean isPointerVisible() { return window.isPointerVisible(); } - + + @Override public final void setPointerVisible(boolean mouseVisible) { - window.setPointerVisible(mouseVisible); + window.setPointerVisible(mouseVisible); + } + + @Override + public final PointerIcon getPointerIcon() { + return window.getPointerIcon(); + } + + @Override + public final void setPointerIcon(final PointerIcon pi) { + window.setPointerIcon(pi); } - + + @Override public final boolean isPointerConfined() { return window.isPointerConfined(); } - + + @Override public final void confinePointer(boolean grab) { window.confinePointer(grab); } - + + @Override public final void setUndecorated(boolean value) { window.setUndecorated(value); } + @Override public final void warpPointer(int x, int y) { window.warpPointer(x, y); } + @Override public final boolean isUndecorated() { return window.isUndecorated(); } + @Override public final void setAlwaysOnTop(boolean value) { window.setAlwaysOnTop(value); } - + + @Override public final boolean isAlwaysOnTop() { return window.isAlwaysOnTop(); } - + + @Override public final void setFocusAction(FocusRunnable focusAction) { window.setFocusAction(focusAction); } - + + @Override public void setKeyboardFocusHandler(KeyListener l) { window.setKeyboardFocusHandler(l); } - + + @Override public final void requestFocus() { window.requestFocus(); } + @Override public final void requestFocus(boolean wait) { - window.requestFocus(wait); + window.requestFocus(wait); } - + + @Override public boolean hasFocus() { return window.hasFocus(); } - public final InsetsImmutable getInsets() { + @Override + public final InsetsImmutable getInsets() { return window.getInsets(); } - + + @Override + public final int getX() { + return window.getX(); + } + + @Override + public final int getY() { + return window.getY(); + } + + @Override + public final int getWidth() { + return window.getWidth(); + } + + @Override + public final int getHeight() { + return window.getHeight(); + } + + @Override public final void setPosition(int x, int y) { window.setPosition(x, y); } - public void setTopLevelPosition(int x, int y) { + @Override + public void setTopLevelPosition(int x, int y) { window.setTopLevelPosition(x, y); } + @Override public final boolean setFullscreen(boolean fullscreen) { return window.setFullscreen(fullscreen); } + @Override + public boolean setFullscreen(List<MonitorDevice> monitors) { + return window.setFullscreen(monitors); + } + + @Override public final boolean isFullscreen() { return window.isFullscreen(); } + @Override public final boolean isVisible() { return window.isVisible(); } @Override public final String toString() { - return "NEWT-GLWindow[ \n\tHelper: " + helper + ", \n\tDrawable: " + drawable + + return "NEWT-GLWindow[ \n\tHelper: " + helper + ", \n\tDrawable: " + drawable + ", \n\tContext: " + context + ", \n\tWindow: "+window+ /** ", \n\tFactory: "+factory+ */ "]"; } + @Override public final ReparentOperation reparentWindow(NativeWindow newParent) { return window.reparentWindow(newParent); } - public final ReparentOperation reparentWindow(NativeWindow newParent, boolean forceDestroyCreate) { - return window.reparentWindow(newParent, forceDestroyCreate); + @Override + public final ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, boolean forceDestroyCreate) { + return window.reparentWindow(newParent, x, y, forceDestroyCreate); + } + + @Override + public final ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, int hints) { + return window.reparentWindow(newParent, x, y, hints); } + @Override public final boolean removeChild(NativeWindow win) { return window.removeChild(win); } + @Override public final boolean addChild(NativeWindow win) { return window.addChild(win); } - + //---------------------------------------------------------------------- // Window.LifecycleHook Implementation // + @Override public final void destroy() { window.destroy(); } + @Override + public void setWindowDestroyNotifyAction(Runnable r) { + window.setWindowDestroyNotifyAction(r); + } + + @Override public final void setVisible(boolean visible) { window.setVisible(visible); } + @Override + public void setVisible(boolean wait, boolean visible) { + window.setVisible(wait, visible); + } + + @Override public final void setSize(int width, int height) { window.setSize(width, height); } + @Override public void setTopLevelSize(int width, int height) { - window.setTopLevelSize(width, height); + window.setTopLevelSize(width, height); } - + + @Override public final boolean isNativeValid() { return window.isNativeValid(); } + @Override public Point getLocationOnScreen(Point storage) { return window.getLocationOnScreen(storage); } @@ -354,82 +468,86 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC // Hide methods here .. protected class GLLifecycleHook implements WindowImpl.LifecycleHook { + @Override + public void preserveGLStateAtDestroy(boolean value) { + GLWindow.this.preserveGLStateAtDestroy(value); + } + + @Override public synchronized void destroyActionPreLock() { // nop } + @Override public synchronized void destroyActionInLock() { if(Window.DEBUG_IMPLEMENTATION) { - String msg = "GLWindow.destroy() "+Thread.currentThread()+", start"; + String msg = "GLWindow.destroy() "+WindowImpl.getThreadName()+", start"; System.err.println(msg); //Exception e1 = new Exception(msg); //e1.printStackTrace(); } - if( window.isNativeValid() && null != drawable && drawable.isRealized() ) { - if( null != context && context.isCreated() ) { - // Catch dispose GLExceptions by GLEventListener, just 'print' them - // so we can continue with the destruction. - try { - helper.disposeGL(GLWindow.this, drawable, context, null); - } catch (GLException gle) { - gle.printStackTrace(); - } - } - drawable.setRealized(false); - } - context = null; - drawable = null; - + destroyImplInLock(); + if(Window.DEBUG_IMPLEMENTATION) { - System.err.println("GLWindow.destroy() "+Thread.currentThread()+", fin"); + System.err.println("GLWindow.destroy() "+WindowImpl.getThreadName()+", fin"); } } + @Override public synchronized void resetCounter() { if(Window.DEBUG_IMPLEMENTATION) { - System.err.println("GLWindow.resetCounter() "+Thread.currentThread()); + System.err.println("GLWindow.resetCounter() "+WindowImpl.getThreadName()); } GLWindow.this.resetFPSCounter(); + final GLAnimatorControl animator = GLWindow.this.getAnimator(); + if( null != animator ) { + animator.resetFPSCounter(); + } } + @Override public synchronized void setVisibleActionPost(boolean visible, boolean nativeWindowCreated) { long t0; if(Window.DEBUG_IMPLEMENTATION) { t0 = System.nanoTime(); - System.err.println("GLWindow.setVisibleActionPost("+visible+", "+nativeWindowCreated+") "+Thread.currentThread()+", start"); + System.err.println("GLWindow.setVisibleActionPost("+visible+", "+nativeWindowCreated+") "+WindowImpl.getThreadName()+", start"); } else { t0 = 0; } - /* if (nativeWindowCreated && null != context) { - throw new GLException("InternalError: Native Windows has been just created, but context wasn't destroyed (is not null)"); - } */ - if (null == context && visible && 0 != window.getWindowHandle() && 0<getWidth()*getHeight()) { - NativeWindow nw; - if (window.getWrappedWindow() != null) { - nw = NativeWindowFactory.getNativeWindow(window.getWrappedWindow(), window.getPrivateGraphicsConfiguration()); - } else { - nw = window; - } - GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) nw.getGraphicsConfiguration().getChosenCapabilities(); - if(null==factory) { - factory = GLDrawableFactory.getFactory(glCaps.getGLProfile()); + if (null == drawable && visible && 0 != window.getWindowHandle() && 0<getWidth()*getHeight()) { + if( ( null != context ) ) { + throw new InternalError("GLWindow.LifecycleHook.setVisiblePost: "+WindowImpl.getThreadName()+" - Null drawable, but valid context - "+GLWindow.this); } - if(null==drawable) { - drawable = factory.createGLDrawable(nw); + final GLContext[] shareWith = { null }; + if( !helper.isSharedGLContextPending(shareWith) ) { + final NativeSurface ns; + { + final NativeSurface wrapped_ns = window.getWrappedSurface(); + ns = null != wrapped_ns ? wrapped_ns : window; + } + final GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) ns.getGraphicsConfiguration().getChosenCapabilities(); + if(null==factory) { + factory = GLDrawableFactory.getFactory(glCaps.getGLProfile()); + } + drawable = (GLDrawableImpl) factory.createGLDrawable(ns); + drawable.setRealized(true); + + if( !GLWindow.this.restoreGLEventListenerState() ) { + context = (GLContextImpl) drawable.createContext(shareWith[0]); + context.setContextCreationFlags(additionalCtxCreationFlags); + } } - drawable.setRealized(true); - context = drawable.createContext(sharedContext); - context.setContextCreationFlags(additionalCtxCreationFlags); } if(Window.DEBUG_IMPLEMENTATION) { - System.err.println("GLWindow.setVisibleActionPost("+visible+", "+nativeWindowCreated+") "+Thread.currentThread()+", fin: dt "+ (System.nanoTime()-t0)/1e6 +"ms"); + System.err.println("GLWindow.setVisibleActionPost("+visible+", "+nativeWindowCreated+") "+WindowImpl.getThreadName()+", fin: dt "+ (System.nanoTime()-t0)/1e6 +"ms"); } } - + private GLAnimatorControl savedAnimator = null; - + + @Override public synchronized boolean pauseRenderingAction() { boolean animatorPaused = false; savedAnimator = GLWindow.this.getAnimator(); @@ -439,302 +557,109 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC return animatorPaused; } + @Override public synchronized void resumeRenderingAction() { if ( null != savedAnimator && savedAnimator.isPaused() ) { savedAnimator.resume(); } } + + @SuppressWarnings("deprecation") + @Override + public void shutdownRenderingAction() { + final GLAnimatorControl anim = GLWindow.this.getAnimator(); + if ( null != anim && anim.isAnimating() ) { + final Thread animThread = anim.getThread(); + if( animThread == Thread.currentThread() ) { + anim.stop(); // on anim thread, non-blocking + } else { + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + if( anim.isAnimating() && null != animThread ) { + try { + animThread.stop(); + } catch(Throwable t) { + if( DEBUG ) { + System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage()); + t.printStackTrace(); + } + } + } + return null; + } } ); + } + } + } } //---------------------------------------------------------------------- // OpenGL-related methods and state // - private GLContext sharedContext = null; - private int additionalCtxCreationFlags = 0; - private GLDrawableFactory factory; - private GLDrawable drawable; - private GLContext context; - private GLDrawableHelper helper = new GLDrawableHelper(); - // To make reshape events be sent immediately before a display event - private boolean sendReshape=false; - private boolean sendDestroy=false; - private FPSCounterImpl fpsCounter = new FPSCounterImpl(); - - public GLDrawableFactory getFactory() { - return factory; - } - - /** - * Specifies an {@link javax.media.opengl.GLContext OpenGL context} to share with.<br> - * At native creation, {@link #setVisible(boolean) setVisible(true)}, - * a {@link javax.media.opengl.GLDrawable drawable} and {@link javax.media.opengl.GLContext context} is created besides the native Window itself,<br> - * hence you shall set the shared context before. - * - * @param sharedContext The OpenGL context shared by this GLWindow's one - */ - public void setSharedContext(GLContext sharedContext) { - this.sharedContext = sharedContext; - } - - public void setContext(GLContext newCtx) { - context = newCtx; - if(null != context) { - context.setContextCreationFlags(additionalCtxCreationFlags); - } - } - - public GLContext getContext() { - return context; - } - - public GL getGL() { - if (context == null) { - return null; - } - return context.getGL(); - } - - public GL setGL(GL gl) { - if (context != null) { - context.setGL(gl); - return gl; - } - return null; - } - - public void addGLEventListener(GLEventListener listener) { - if(null!=helper) { - helper.addGLEventListener(listener); - } - } - - public void addGLEventListener(int index, GLEventListener listener) { - if(null!=helper) { - helper.addGLEventListener(index, listener); - } - } - - public void removeGLEventListener(GLEventListener listener) { - if(null!=helper) { - helper.removeGLEventListener(listener); - } - } - - public void setAnimator(GLAnimatorControl animatorControl) { - if(null!=helper) { - helper.setAnimator(animatorControl); - } - } - - public GLAnimatorControl getAnimator() { - if(null!=helper) { - return helper.getAnimator(); - } - return null; - } - - public void invoke(boolean wait, GLRunnable glRunnable) { - if(null!=helper) { - helper.invoke(this, wait, glRunnable); - } + @Override + protected final RecursiveLock getLock() { + return window.getLock(); } + @Override public void display() { if( !isNativeValid() || !isVisible() ) { return; } - + if(sendDestroy || ( window.hasDeviceChanged() && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) ) { sendDestroy=false; destroy(); return; } - - if( null == context && 0<getWidth()*getHeight() ) { // TODO: Check memory sync - // retry drawable and context creation - setVisible(true); - } - if( null != context ) { // TODO: Check memory sync - // surface is locked/unlocked implicit by context's makeCurrent/release - helper.invokeGL(drawable, context, displayAction, initAction); - } - } - - public void setAutoSwapBufferMode(boolean enable) { - if(null!=helper) { - helper.setAutoSwapBufferMode(enable); - } - } - - public boolean getAutoSwapBufferMode() { - if(null!=helper) { - return helper.getAutoSwapBufferMode(); + final boolean done; + final RecursiveLock lock = window.getLock(); + lock.lock(); // sync: context/drawable could have been recreated/destroyed while animating + try { + if( null != context ) { + // surface is locked/unlocked implicit by context's makeCurrent/release + helper.invokeGL(drawable, context, defaultDisplayAction, defaultInitAction); + done = true; + } else { + done = false; + } + } finally { + lock.unlock(); } - return false; - } - - /** - * @param t the thread for which context release shall be skipped, usually the animation thread, - * ie. {@link Animator#getThread()}. - * @deprecated this is an experimental feature, - * intended for measuring performance in regards to GL context switch - */ - public void setSkipContextReleaseThread(Thread t) { - if(null!=helper) { - helper.setSkipContextReleaseThread(t); + if( !done && ( 0 < getWidth() && 0 < getHeight() ) ) { + // retry drawable and context creation, will itself issue resize -> display + setVisible(true); } } /** - * @deprecated see {@link #setSkipContextReleaseThread(Thread)} + * {@inheritDoc} + * <p> + * GLWindow supports GL state preservation, hence returns <code>true</code>. + * </p> */ - public Thread getSkipContextReleaseThread() { - if(null!=helper) { - return helper.getSkipContextReleaseThread(); - } - return null; - } - - public void swapBuffers() { - if(drawable!=null && context != null) { - drawable.swapBuffers(); - } - } - - public void setContextCreationFlags(int flags) { - additionalCtxCreationFlags = flags; - } - - public int getContextCreationFlags() { - return additionalCtxCreationFlags; - } - - private class InitAction implements Runnable { - public final void run() { - // Lock: Locked Surface/Window by MakeCurrent/Release - helper.init(GLWindow.this); - resetFPSCounter(); - } - } - private InitAction initAction = new InitAction(); - - private class DisplayAction implements Runnable { - public final void run() { - // Lock: Locked Surface/Window by display _and_ MakeCurrent/Release - if (sendReshape) { - helper.reshape(GLWindow.this, 0, 0, getWidth(), getHeight()); - sendReshape = false; - } - - helper.display(GLWindow.this); - - fpsCounter.tickFPS(); - } - } - private DisplayAction displayAction = new DisplayAction(); - - public final void setUpdateFPSFrames(int frames, PrintStream out) { - fpsCounter.setUpdateFPSFrames(frames, out); - } - - public final void resetFPSCounter() { - fpsCounter.resetFPSCounter(); - } - - public final int getUpdateFPSFrames() { - return fpsCounter.getUpdateFPSFrames(); - } - - public final long getFPSStartTime() { - return fpsCounter.getFPSStartTime(); - } - - public final long getLastFPSUpdateTime() { - return fpsCounter.getLastFPSUpdateTime(); - } - - public final long getLastFPSPeriod() { - return fpsCounter.getLastFPSPeriod(); - } - - public final float getLastFPS() { - return fpsCounter.getLastFPS(); - } - - public final int getTotalFPSFrames() { - return fpsCounter.getTotalFPSFrames(); - } - - public final long getTotalFPSDuration() { - return fpsCounter.getTotalFPSDuration(); - } - - public final float getTotalFPS() { - return fpsCounter.getTotalFPS(); - } + @Override + public final boolean isGLStatePreservationSupported() { return true; } //---------------------------------------------------------------------- // GLDrawable methods // + private GLDrawableFactory factory; - public final NativeSurface getNativeSurface() { - return null!=drawable ? drawable.getNativeSurface() : null; - } - - public final long getHandle() { - return null!=drawable ? drawable.getHandle() : 0; - } - - public final int getX() { - return window.getX(); - } - - public final int getY() { - return window.getY(); - } - - public final int getWidth() { - return window.getWidth(); - } - - public final int getHeight() { - return window.getHeight(); - } - - //---------------------------------------------------------------------- - // GLDrawable methods that are not really needed - // - - public final GLContext createContext(GLContext shareWith) { - return drawable.createContext(shareWith); - } - - public final void setRealized(boolean realized) { - } - - public final boolean isRealized() { - return ( null != drawable ) ? drawable.isRealized() : false; - } - - public final GLCapabilitiesImmutable getChosenGLCapabilities() { - if (drawable == null) { - throw new GLException("No drawable yet"); - } - - return drawable.getChosenGLCapabilities(); + @Override + public final GLDrawableFactory getFactory() { + return factory; } - public final GLProfile getGLProfile() { - if (drawable == null) { - throw new GLException("No drawable yet"); - } - - return drawable.getGLProfile(); + @Override + public final void swapBuffers() throws GLException { + defaultSwapBuffers(); } //---------------------------------------------------------------------- - // NEWTEventConsumer + // NEWTEventConsumer // + @Override public boolean consumeEvent(NEWTEvent event) { return window.consumeEvent(event); } @@ -742,179 +667,316 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC //---------------------------------------------------------------------- // Window completion // + @Override public final void windowRepaint(int x, int y, int width, int height) { window.windowRepaint(x, y, width, height); } + @Override public final void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { window.enqueueEvent(wait, event); } + @Override public final void runOnEDTIfAvail(boolean wait, final Runnable task) { window.runOnEDTIfAvail(wait, task); } - public final void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { - window.removeSurfaceUpdatedListener(l); - } - - public final void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { - window.addSurfaceUpdatedListener(l); - } - - public final void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException { - window.addSurfaceUpdatedListener(index, l); - } - + @Override public void sendWindowEvent(int eventType) { window.sendWindowEvent(eventType); } + @Override public final WindowListener getWindowListener(int index) { return window.getWindowListener(index); } + @Override public final WindowListener[] getWindowListeners() { return window.getWindowListeners(); } + @Override public final void removeWindowListener(WindowListener l) { window.removeWindowListener(l); } + @Override public final void addWindowListener(WindowListener l) { window.addWindowListener(l); } + @Override public final void addWindowListener(int index, WindowListener l) throws IndexOutOfBoundsException { window.addWindowListener(index, l); } + @Override + public final void setKeyboardVisible(boolean visible) { + window.setKeyboardVisible(visible); + } + + @Override + public final boolean isKeyboardVisible() { + return window.isKeyboardVisible(); + } + + @Override public final void addKeyListener(KeyListener l) { window.addKeyListener(l); } + @Override public final void addKeyListener(int index, KeyListener l) { window.addKeyListener(index, l); } + @Override public final void removeKeyListener(KeyListener l) { window.removeKeyListener(l); } + @Override public final KeyListener getKeyListener(int index) { return window.getKeyListener(index); } + @Override public final KeyListener[] getKeyListeners() { return window.getKeyListeners(); } + @Override public final void addMouseListener(MouseListener l) { window.addMouseListener(l); } + @Override public final void addMouseListener(int index, MouseListener l) { window.addMouseListener(index, l); } + @Override public final void removeMouseListener(MouseListener l) { window.removeMouseListener(l); } + @Override public final MouseListener getMouseListener(int index) { return window.getMouseListener(index); } + @Override public final MouseListener[] getMouseListeners() { return window.getMouseListeners(); } + @Override + public void setDefaultGesturesEnabled(boolean enable) { + window.setDefaultGesturesEnabled(enable); + } + @Override + public boolean areDefaultGesturesEnabled() { + return window.areDefaultGesturesEnabled(); + } + @Override + public final void addGestureHandler(GestureHandler gh) { + window.addGestureHandler(gh); + } + @Override + public final void addGestureHandler(int index, GestureHandler gh) { + window.addGestureHandler(index, gh); + } + @Override + public final void removeGestureHandler(GestureHandler gh) { + window.removeGestureHandler(gh); + } + @Override + public final void addGestureListener(GestureHandler.GestureListener gl) { + window.addGestureListener(-1, gl); + } + @Override + public final void addGestureListener(int index, GestureHandler.GestureListener gl) { + window.addGestureListener(index, gl); + } + @Override + public final void removeGestureListener(GestureHandler.GestureListener gl) { + window.removeGestureListener(gl); + } + //---------------------------------------------------------------------- // NativeWindow completion // - public final int lockSurface() { + @Override + public final int lockSurface() throws NativeWindowException, RuntimeException { return window.lockSurface(); } - public final void unlockSurface() throws NativeWindowException { + @Override + public final void unlockSurface() { window.unlockSurface(); } + @Override public final boolean isSurfaceLockedByOtherThread() { return window.isSurfaceLockedByOtherThread(); } - public final boolean isSurfaceLocked() { - return window.isSurfaceLocked(); - } - + @Override public final Thread getSurfaceLockOwner() { return window.getSurfaceLockOwner(); } + @Override public final boolean surfaceSwap() { return window.surfaceSwap(); } + @Override + public final void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { + window.removeSurfaceUpdatedListener(l); + } + + @Override + public final void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { + window.addSurfaceUpdatedListener(l); + } + + @Override + public final void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException { + window.addSurfaceUpdatedListener(index, l); + } + + @Override + public final void surfaceUpdated(Object updater, NativeSurface ns, long when) { + window.surfaceUpdated(updater, ns, when); + } + + @Override public final long getWindowHandle() { return window.getWindowHandle(); } + @Override public final long getSurfaceHandle() { return window.getSurfaceHandle(); } + @Override public final AbstractGraphicsConfiguration getGraphicsConfiguration() { return window.getGraphicsConfiguration(); } + @Override public final long getDisplayHandle() { return window.getDisplayHandle(); } + @Override public final int getScreenIndex() { return window.getScreenIndex(); } - public final void surfaceUpdated(Object updater, NativeSurface ns, long when) { - window.surfaceUpdated(updater, ns, when); - } - /** * A most simple JOGL AWT test entry */ public static void main(String args[]) { + final boolean forceES2; + final boolean forceES3; + final boolean forceGL3; + final boolean forceGL4ES3; + { + boolean _forceES2 = false; + boolean _forceES3 = false; + boolean _forceGL3 = false; + boolean _forceGL4ES3 = false; + if( null != args ) { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-es2")) { + _forceES2 = true; + } else if(args[i].equals("-es3")) { + _forceES3 = true; + } else if(args[i].equals("-gl3")) { + _forceGL3 = true; + } else if(args[i].equals("-gl4es3")) { + _forceGL4ES3 = true; + } + } + } + forceES2 = _forceES2; + forceES3 = _forceES3; + forceGL3 = _forceGL3; + forceGL4ES3 = _forceGL4ES3; + } + System.err.println("forceES2 "+forceES2); + System.err.println("forceES3 "+forceES3); + System.err.println("forceGL3 "+forceGL3); + System.err.println("forceGL4ES3 "+forceGL4ES3); + System.err.println(VersionUtil.getPlatformInfo()); System.err.println(GlueGenVersion.getInstance()); System.err.println(JoglVersion.getInstance()); - System.err.println(JoglVersion.getDefaultOpenGLInfo(null).toString()); - - final GLProfile glp = GLProfile.getDefault(); + System.err.println(JoglVersion.getDefaultOpenGLInfo(null, null, true).toString()); + + final GLProfile glp; + if(forceGL4ES3) { + glp = GLProfile.get(GLProfile.GL4ES3); + } else if(forceGL3) { + glp = GLProfile.get(GLProfile.GL3); + } else if(forceES3) { + glp = GLProfile.get(GLProfile.GLES3); + } else if(forceES2) { + glp = GLProfile.get(GLProfile.GLES2); + } else { + glp = GLProfile.getDefault(); + } final GLCapabilitiesImmutable caps = new GLCapabilities( glp ); + System.err.println("Requesting: "+caps); GLWindow glWindow = GLWindow.create(caps); glWindow.setSize(128, 128); glWindow.addGLEventListener(new GLEventListener() { + @Override public void init(GLAutoDrawable drawable) { GL gl = drawable.getGL(); System.err.println(JoglVersion.getGLInfo(gl, null)); System.err.println("Requested: "+drawable.getNativeSurface().getGraphicsConfiguration().getRequestedCapabilities()); System.err.println("Chosen : "+drawable.getChosenGLCapabilities()); + System.err.println("GL impl. class "+gl.getClass().getName()); + if( gl.isGL4ES3() ) { + GL4ES3 _gl = gl.getGL4ES3(); + System.err.println("GL4ES3 retrieved, impl. class "+_gl.getClass().getName()); + } + if( gl.isGL3() ) { + GL3 _gl = gl.getGL3(); + System.err.println("GL3 retrieved, impl. class "+_gl.getClass().getName()); + } + if( gl.isGLES3() ) { + GLES3 _gl = gl.getGLES3(); + System.err.println("GLES3 retrieved, impl. class "+_gl.getClass().getName()); + } + if( gl.isGLES2() ) { + GLES2 _gl = gl.getGLES2(); + System.err.println("GLES2 retrieved, impl. class "+_gl.getClass().getName()); + } } + @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { } + @Override public void display(GLAutoDrawable drawable) { } + @Override public void dispose(GLAutoDrawable drawable) { } }); diff --git a/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java new file mode 100644 index 000000000..43e56c874 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java @@ -0,0 +1,542 @@ +/** + * Copyright 2012 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.newt.swt; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.Capabilities; +import javax.media.nativewindow.CapabilitiesImmutable; +import javax.media.nativewindow.GraphicsConfigurationFactory; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.SurfaceUpdatedListener; +import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.nativewindow.util.Insets; +import javax.media.nativewindow.util.InsetsImmutable; +import javax.media.nativewindow.util.Point; +import javax.media.opengl.GLCapabilities; + +import jogamp.nativewindow.macosx.OSXUtil; +import jogamp.nativewindow.windows.GDIUtil; +import jogamp.nativewindow.x11.X11Lib; +import jogamp.newt.Debug; +import jogamp.newt.swt.SWTEDTUtil; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +import com.jogamp.nativewindow.swt.SWTAccessor; +import com.jogamp.newt.Display; +import com.jogamp.newt.Window; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.util.EDTUtil; + +/** + * SWT {@link Canvas} containing a NEWT {@link Window} using native parenting. + * <p> + * Implementation allows use of custom {@link GLCapabilities}. + * </p> + */ +public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { + private static final boolean DEBUG = Debug.debug("Window"); + + private final AbstractGraphicsScreen screen; + + private WindowClosingMode newtChildCloseOp = WindowClosingMode.DISPOSE_ON_CLOSE; + private volatile Rectangle clientArea; + + private volatile SWTNativeWindow nativeWindow; + private volatile Window newtChild = null; + private volatile boolean newtChildReady = false; // ready if SWTEDTUtil is set and newtChild parented + private volatile boolean postSetSize = false; // pending resize + + /** + * Creates an instance using {@link #NewtCanvasSWT(Composite, int, Window)} + * on the SWT thread. + * + * <p> + * Note: The NEWT child {@link Display}'s {@link EDTUtil} is being set to an SWT conform implementation + * via {@link Display#setEDTUtil(EDTUtil)}. + * </p> + * + * @param parent the SWT composite + * @param style additional styles to SWT#NO_BACKGROUND + * @param child optional preassigned {@link #Window}, maybe null + * @return a new instance + */ + public static NewtCanvasSWT create(final Composite parent, final int style, final Window child) { + final NewtCanvasSWT[] res = new NewtCanvasSWT[] { null }; + parent.getDisplay().syncExec( new Runnable() { + @Override + public void run() { + res[0] = new NewtCanvasSWT( parent, style, child); + } + }); + return res[0]; + } + + /** + * Instantiates a NewtCanvas with a NEWT child. + * + * <p> + * Note: The NEWT child {@link Display}'s {@link EDTUtil} is being set to an SWT conform implementation + * via {@link Display#setEDTUtil(EDTUtil)}. + * </p> + * + * @param parent the SWT composite + * @param style additional styles to SWT#NO_BACKGROUND + * @param child optional preassigned {@link #Window}, maybe null + */ + public NewtCanvasSWT(final Composite parent, final int style, Window child) { + super(parent, style | SWT.NO_BACKGROUND); + + SWTAccessor.setRealized(this, true); + + clientArea = getClientArea(); + + final AbstractGraphicsDevice device = SWTAccessor.getDevice(this); + screen = SWTAccessor.getScreen(device, -1 /* default */); + nativeWindow = null; + + if(null != child) { + setNEWTChild(child); + } + + final Listener listener = new Listener () { + @Override + public void handleEvent (Event event) { + switch (event.type) { + case SWT.Paint: + if( null != nativeWindow || validateNative() ) { + if( newtChildReady ) { + if( postSetSize ) { + newtChild.setSize(clientArea.width, clientArea.height); + postSetSize = false; + } + if( SWTAccessor.isOSX ) { + newtChild.setPosition(parent.getLocation().x,parent.getLocation().y); + } + newtChild.windowRepaint(0, 0, clientArea.width, clientArea.height); + } + } + break; + case SWT.Resize: + updateSizeCheck(); + break; + case SWT.Dispose: + NewtCanvasSWT.this.dispose(); + break; + } + } + }; + addListener (SWT.Resize, listener); + addListener (SWT.Paint, listener); + addListener (SWT.Dispose, listener); + } + + /** assumes nativeWindow == null ! */ + protected final boolean validateNative() { + updateSizeCheck(); + final Rectangle nClientArea = clientArea; + if(0 >= nClientArea.width || 0 >= nClientArea.height) { + return false; + } + screen.getDevice().open(); + + /* Native handle for the control, used to associate with GLContext */ + final long nativeWindowHandle = SWTAccessor.getWindowHandle(this); + final int visualID = SWTAccessor.getNativeVisualID(screen.getDevice(), nativeWindowHandle); + final boolean visualIDValid = NativeWindowFactory.isNativeVisualIDValidForProcessing(visualID); + if(DEBUG) { + System.err.println("NewtCanvasSWT.validateNative() windowHandle 0x"+Long.toHexString(nativeWindowHandle)+", visualID 0x"+Integer.toHexString(visualID)+", valid "+visualIDValid); + } + if( visualIDValid ) { + /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite). + * Note: SWT is owner of the native handle, hence no closing operation will be a NOP. */ + final CapabilitiesImmutable caps = new Capabilities(); + final GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(screen.getDevice(), caps); + final AbstractGraphicsConfiguration config = factory.chooseGraphicsConfiguration( caps, caps, null, screen, visualID ); + if(DEBUG) { + System.err.println("NewtCanvasSWT.validateNative() factory: "+factory+", windowHandle 0x"+Long.toHexString(nativeWindowHandle)+", visualID 0x"+Integer.toHexString(visualID)+", chosen config: "+config); + // Thread.dumpStack(); + } + if (null == config) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + + nativeWindow = new SWTNativeWindow(config, nativeWindowHandle); + reparentWindow( true ); + } + + return null != nativeWindow; + } + + protected final void updateSizeCheck() { + final Rectangle oClientArea = clientArea; + final Rectangle nClientArea = getClientArea(); + if ( nClientArea != null && + ( nClientArea.width != oClientArea.width || nClientArea.height != oClientArea.height ) + ) { + clientArea = nClientArea; // write back new value + if(DEBUG) { + final long nsh = newtChildReady ? newtChild.getSurfaceHandle() : 0; + System.err.println("NewtCanvasSWT.sizeChanged: ("+Thread.currentThread().getName()+"): newtChildReady "+newtChildReady+", "+nClientArea.x+"/"+nClientArea.y+" "+nClientArea.width+"x"+nClientArea.height+" - surfaceHandle 0x"+Long.toHexString(nsh)); + } + if( newtChildReady ) { + newtChild.setSize(clientArea.width, clientArea.height); + } else { + postSetSize = true; + } + } + } + + @Override + public void update() { + // don't paint background etc .. nop avoids flickering + } + + /** + * Destroys this resource: + * <ul> + * <li> Make the NEWT Child invisible </li> + * <li> Disconnects the NEWT Child from this Canvas NativeWindow, reparent to NULL </li> + * <li> Issues <code>destroy()</code> on the NEWT Child</li> + * <li> Remove reference to the NEWT Child</li> + * </ul> + * @see Window#destroy() + */ + @Override + public void dispose() { + if( null != newtChild ) { + if(DEBUG) { + System.err.println("NewtCanvasSWT.dispose.0: EDTUtil cur "+newtChild.getScreen().getDisplay().getEDTUtil()+ + ",\n\t"+newtChild); + } + configureNewtChild(false); + newtChild.setVisible(false); + newtChild.reparentWindow(null, -1, -1, 0 /* hint */); + newtChild.destroy(); + newtChild = null; + } + screen.getDevice().close(); + nativeWindow = null; + super.dispose(); + } + + private Rectangle getSWTCanvasPosition() { + return super.getBounds(); + } + /** @return this SWT Canvas NativeWindow representation, may be null in case it has not been realized. */ + public NativeWindow getNativeWindow() { return nativeWindow; } + + @Override + public WindowClosingMode getDefaultCloseOperation() { + return newtChildCloseOp; // TODO: implement ?! + } + + @Override + public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { + return newtChildCloseOp = op; // TODO: implement ?! + } + + + boolean isParent() { + return null!=newtChild ; + } + + boolean isFullscreen() { + return null != newtChild && newtChild.isFullscreen(); + } + + /** + * Sets a new NEWT child, provoking reparenting. + * <p> + * A previously detached <code>newChild</code> will be released to top-level status + * and made invisible. + * </p> + * <p> + * Note: When switching NEWT child's, detaching the previous first via <code>setNEWTChild(null)</code> + * produced much cleaner visual results. + * </p> + * <p> + * Note: The NEWT child {@link Display}'s {@link EDTUtil} is being set to an SWT conform implementation + * via {@link Display#setEDTUtil(EDTUtil)}. + * </p> + * @return the previous attached newt child. + */ + public Window setNEWTChild(final Window newChild) { + final Window prevChild = newtChild; + if(DEBUG) { + System.err.println("NewtCanvasSWT.setNEWTChild.0: win "+newtWinHandleToHexString(prevChild)+" -> "+newtWinHandleToHexString(newChild)); + } + // remove old one + if(null != newtChild) { + reparentWindow( false ); + newtChild = null; + } + // add new one, reparent only if ready + newtChild = newChild; + if(null != nativeWindow && null != newChild) { + reparentWindow( true ); + } + return prevChild; + } + + /** @return the current NEWT child */ + public Window getNEWTChild() { + return newtChild; + } + + @Override + public boolean setParent(Composite parent) { + return super.setParent(parent); + } + + /* package */ void configureNewtChild(boolean attach) { + newtChildReady = attach; + if( null != newtChild ) { + newtChild.setKeyboardFocusHandler(null); + if(attach) { + newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); + } else { + newtChild.setFocusAction(null); + newtChild.setDefaultCloseOperation(newtChildCloseOp); + } + } + } + + void reparentWindow(boolean add) { + if( null == newtChild ) { + return; // nop + } + if(DEBUG) { + System.err.println("NewtCanvasSWT.reparentWindow.0: add="+add+", win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()); + } + + newtChild.setFocusAction(null); // no AWT focus traversal .. + if(add) { + updateSizeCheck(); + final int w = clientArea.width; + final int h = clientArea.height; + + // set SWT EDT and start it + { + final Display newtDisplay = newtChild.getScreen().getDisplay(); + final EDTUtil edtUtil = new SWTEDTUtil(newtDisplay, getDisplay()); + edtUtil.start(); + newtDisplay.setEDTUtil( edtUtil ); + } + + newtChild.setSize(w, h); + newtChild.reparentWindow(nativeWindow, -1, -1, Window.REPARENT_HINT_BECOMES_VISIBLE); + newtChild.setVisible(true); + configureNewtChild(true); + newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener + + // force this SWT Canvas to be focus-able, + // since it is completely covered by the newtChild (z-order). + setEnabled(true); + } else { + configureNewtChild(false); + newtChild.setVisible(false); + newtChild.reparentWindow(null, -1, -1, 0 /* hints */); + } + if(DEBUG) { + System.err.println("NewtCanvasSWT.reparentWindow.X: add="+add+", win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()); + } + } + + private final void requestFocusNEWTChild() { + if( newtChildReady ) { + newtChild.setFocusAction(null); + newtChild.requestFocus(); + } + } + + @Override + public boolean forceFocus() { + final boolean res = NewtCanvasSWT.super.forceFocus(); + requestFocusNEWTChild(); + return res; + } + + private class SWTNativeWindow implements NativeWindow { + private final AbstractGraphicsConfiguration config; + private final long nativeWindowHandle; + private final InsetsImmutable insets; // only required to allow proper client position calculation on OSX + + public SWTNativeWindow(AbstractGraphicsConfiguration config, long nativeWindowHandle) { + this.config = config; + this.nativeWindowHandle = nativeWindowHandle; + if( SWTAccessor.isOSX ) { + this.insets = OSXUtil.GetInsets(nativeWindowHandle); + } else { + this.insets = new Insets(0, 0, 0, 0); + } + } + + @Override + public int lockSurface() throws NativeWindowException, RuntimeException { + return NativeSurface.LOCK_SUCCESS; + } + + @Override + public void unlockSurface() { } + + @Override + public boolean isSurfaceLockedByOtherThread() { + return false; + } + + @Override + public Thread getSurfaceLockOwner() { + return null; + } + + @Override + public boolean surfaceSwap() { + return false; + } + + @Override + public void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { } + + @Override + public void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException { + } + + @Override + public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { } + + @Override + public long getSurfaceHandle() { + return 0; + } + + @Override + public int getWidth() { + return clientArea.width; + } + + @Override + public int getHeight() { + return clientArea.height; + } + + @Override + public AbstractGraphicsConfiguration getGraphicsConfiguration() { + return config; + } + + @Override + public long getDisplayHandle() { + return config.getScreen().getDevice().getHandle(); + } + + @Override + public int getScreenIndex() { + return config.getScreen().getIndex(); + } + + @Override + public void surfaceUpdated(Object updater, NativeSurface ns, long when) { } + + @Override + public void destroy() { } + + @Override + public NativeWindow getParent() { + return null; + } + + @Override + public long getWindowHandle() { + return nativeWindowHandle; + } + + @Override + public InsetsImmutable getInsets() { + return insets; + } + + @Override + public int getX() { + return 0; + } + + @Override + public int getY() { + return 0; + } + + @Override + public Point getLocationOnScreen(Point point) { + final Point los; // client window location on screen + if( SWTAccessor.isOSX ) { + los = OSXUtil.GetLocationOnScreen(nativeWindowHandle, false, 0, 0); + // top-level position -> client window position: OSX needs to add SWT parent position incl. insets + final Rectangle swtCanvasPosition = getSWTCanvasPosition(); + los.translate(swtCanvasPosition.x + insets.getLeftWidth(), swtCanvasPosition.y + insets.getTopHeight()); + } else if (SWTAccessor.isX11) { + final AbstractGraphicsScreen s = config.getScreen(); + los = X11Lib.GetRelativeLocation(s.getDevice().getHandle(), s.getIndex(), nativeWindowHandle, 0 /*root win*/, 0, 0); + } else if (SWTAccessor.isWindows) { + los = GDIUtil.GetRelativeLocation( nativeWindowHandle, 0 /*root win*/, 0, 0); + } else { + // fall-back to 0/0 + los = new Point(0, 0); + } + if(null!=point) { + return point.translate(los); + } else { + return los; + } + } + + @Override + public boolean hasFocus() { + return isFocusControl(); + } + }; + + static String newtWinHandleToHexString(Window w) { + return null != w ? toHexString(w.getWindowHandle()) : "nil"; + } + static String toHexString(long l) { + return "0x"+Long.toHexString(l); + } +} + diff --git a/src/newt/classes/com/jogamp/newt/util/EDTUtil.java b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java index 4493e2781..582dc3e1f 100644 --- a/src/newt/classes/com/jogamp/newt/util/EDTUtil.java +++ b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java @@ -28,6 +28,9 @@ package com.jogamp.newt.util; +import jogamp.newt.DisplayImpl; +import com.jogamp.newt.event.NEWTEvent; + /** * EDT stands for Event Dispatch Thread. * <p> @@ -60,66 +63,106 @@ public interface EDTUtil { * @param ms poll period in milliseconds */ public void setPollPeriod(long ms); - + /** - * Create a new EDT. One should invoke <code>reset()</code><br> - * after <code>invokeStop(..)</code> in case another <code>start()</code> or <code>invoke(..)</code> - * is expected. + * Starts the EDT after it's creation or after {@link #invokeStop(boolean, Runnable) stopping}. + * <p> + * If the EDT is running, it must be {@link #invokeStop(boolean, Runnable) stopped} first + * and the caller should wait {@link #waitUntilStopped() until it's stopped}. + * </p> + * + * @return true if EDT has been successfully restarted, otherwise false + * @throws IllegalStateException if EDT is running and not subject to be stopped, i.e. {@link #isRunning()} returns true * - * @see #start() - * @see #invoke(boolean, java.lang.Runnable) - * @see #invokeStop(java.lang.Runnable) + * @see #invokeStop(boolean, java.lang.Runnable) + * @see #waitUntilStopped() */ - public void reset(); + public boolean start() throws IllegalStateException; /** - * Start the EDT + * Returns true if the current thread is the event dispatch thread (EDT). + * <p> + * The EDT is the platform specific thread dispatching toolkit-events + * and executing toolkit-tasks enqueued via {@link #invoke(boolean, Runnable)}. + * </p> + * <p> + * Usually it is the same thread as used to dequeue informal {@link NEWTEvent}s (NEDT), see {@link #isCurrentThreadNEDT()}, + * however, this may differ, e.g. SWT and AWT implementation. + * </p> */ - public void start(); + public boolean isCurrentThreadEDT(); /** - * @return True if the current thread is the EDT thread + * Returns true if the current thread is the internal NEWT event dequeue thread (NEDT). + * <p> + * The NEDT is the NEWT thread used to dequeue informal {@link NEWTEvent}s enqueued internally + * via {@link DisplayImpl#enqueueEvent(boolean, NEWTEvent)}. + * </p> + * <p> + * Usually it is the same thread as the EDT, see {@link #isCurrentThreadEDT()}, + * however, this may differ, e.g. SWT and AWT implementation. + * </p> */ - public boolean isCurrentThreadEDT(); + public boolean isCurrentThreadNEDT(); + + /** + * Returns <code>true</code> if either {@link #isCurrentThreadEDT()} or {@link #isCurrentThreadNEDT()} is <code>true</code>, + * otherwise <code>false</code>. + */ + public boolean isCurrentThreadEDTorNEDT(); /** - * @return True if EDT is running + * @return True if EDT is running and not subject to be stopped. */ public boolean isRunning(); - /** + /** * Append the final task to the EDT task queue, - * signals EDT to stop and wait until stopped.<br> + * signals EDT to stop. + * <p> + * If <code>wait</code> is <code>true</code> methods + * blocks until EDT is stopped. + * </p> + * <p> + * <code>task</code> maybe <code>null</code><br/> * Due to the nature of this method: * <ul> * <li>All previous queued tasks will be finished.</li> * <li>No new tasks are allowed, an Exception is thrown.</li> * <li>Can be issued from within EDT, ie from within an enqueued task.</li> - * <li>{@link #reset()} may follow immediately, ie creating a new EDT</li> + * <li>{@link #start()} may follow immediately, ie creating a new EDT</li> * </ul> + * </p> + * @return true if <code>task</code> has been executed or queued for later execution, otherwise false */ - public void invokeStop(Runnable finalTask); + public boolean invokeStop(boolean wait, Runnable finalTask); - /** - * Append task to the EDT task queue.<br> - * Wait until execution is finished if <code>wait == true</code>.<br> - * Shall start the thread if not running.<br> + /** + * Appends task to the EDT task queue if current thread is not EDT, + * otherwise execute task immediately. + * <p> + * Wait until execution is finished if <code>wait == true</code>. + * </p> * Can be issued from within EDT, ie from within an enqueued task.<br> - * - * @throws RuntimeException in case EDT is stopped and not {@link #reset()} + * @return true if <code>task</code> has been executed or queued for later execution, otherwise false */ - public void invoke(boolean wait, Runnable task); + public boolean invoke(boolean wait, Runnable task); - /** + /** * Wait until the EDT task queue is empty.<br> * The last task may still be in execution when this method returns. + * @return true if waited for idle, otherwise false, i.e. in case of current thread is EDT or NEDT */ - public void waitUntilIdle(); + public boolean waitUntilIdle(); /** * Wait until EDT task is stopped.<br> - * No <code>stop</code> action is performed, {@link #invokeStop(java.lang.Runnable)} should be used before. + * No <code>stop</code> action is performed, {@link #invokeStop(boolean, java.lang.Runnable)} should be used before. + * <p> + * If caller thread is EDT or NEDT, this call will not block. + * </p> + * @return true if stopped, otherwise false, i.e. in case of current thread is EDT or NEDT */ - public void waitUntilStopped(); + public boolean waitUntilStopped(); } diff --git a/src/newt/classes/com/jogamp/newt/util/MainThread.java b/src/newt/classes/com/jogamp/newt/util/MainThread.java index bbe415b2f..049320b21 100644 --- a/src/newt/classes/com/jogamp/newt/util/MainThread.java +++ b/src/newt/classes/com/jogamp/newt/util/MainThread.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2009 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 @@ -29,7 +29,7 @@ * 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. @@ -54,13 +54,13 @@ import jogamp.newt.NEWTJNILibLoader; * NEWT Utility class MainThread<P> * * <p> - * FIXME: Update this documentation! + * FIXME: Update this documentation! * This class just provides a main-thread utility, forking of a main java class * on another thread while being able to continue doing platform specific things * on the main-thread. The latter is essential for eg. MacOSX, where we continue * to run NSApp.run(). * </p> - * + * * This class provides a startup singleton <i>main thread</i>, * from which a new thread with the users main class is launched.<br> * @@ -72,17 +72,17 @@ import jogamp.newt.NEWTJNILibLoader; * use a NEWT multithreaded application with window handling within the different threads, * even on these restricted platforms.<br> * - * To support your NEWT Window platform, + * 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, + * have a look at the {@link jogamp.newt.driver.macosx.WindowDriver NEWT Mac OSX Window} driver 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 + * 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): @@ -92,39 +92,39 @@ import jogamp.newt.NEWTJNILibLoader; * Which starts 4 threads, each with a window and OpenGL rendering.<br> */ public class MainThread { - private static final String MACOSXDisplayClassName = "jogamp.newt.driver.macosx.MacDisplay"; + private static final String MACOSXDisplayClassName = "jogamp.newt.driver.macosx.DisplayDriver"; private static final Platform.OSType osType; private static final boolean isMacOSX; private static final ThreadGroup rootThreadGroup; - + /** if true, use the main thread EDT, otherwise AWT's EDT */ public static final boolean HINT_USE_MAIN_THREAD; - + static { - NativeWindowFactory.initSingleton(true); + NativeWindowFactory.initSingleton(); NEWTJNILibLoader.loadNEWT(); - HINT_USE_MAIN_THREAD = !NativeWindowFactory.isAWTAvailable() || + HINT_USE_MAIN_THREAD = !NativeWindowFactory.isAWTAvailable() || Debug.getBooleanProperty("newt.MainThread.force", true); osType = Platform.getOSType(); isMacOSX = osType == Platform.OSType.MACOS; rootThreadGroup = getRootThreadGroup(); } - + public static boolean useMainThread = false; - + protected static final boolean DEBUG = Debug.debug("MainThread"); private static final MainThread singletonMainThread = new MainThread(); // one singleton MainThread - + private static final ThreadGroup getRootThreadGroup() { ThreadGroup rootGroup = Thread.currentThread( ).getThreadGroup( ); ThreadGroup parentGroup; while ( ( parentGroup = rootGroup.getParent() ) != null ) { rootGroup = parentGroup; } - return rootGroup; + return rootGroup; } - + private static final Thread[] getAllThreads(int[] count) { int tn; Thread[] threads = new Thread[ rootThreadGroup.activeCount() ]; @@ -149,17 +149,17 @@ public class MainThread { t.printStackTrace(); } } - return res; + return res; } private static final int getNonDaemonThreadCount(List<Thread> ignoreThreads) { int res = 0; int[] tn = { 0 }; Thread[] threads = getAllThreads(tn); - + for(int i = tn[0] - 1; i >= 0; i--) { final Thread thread = threads[i]; try { - if(thread.isAlive() && !thread.isDaemon() && !ignoreThreads.contains(thread)) { + if(thread.isAlive() && !thread.isDaemon() && !ignoreThreads.contains(thread)) { res++; if(DEBUG) System.err.println("MainAction.run(): non daemon thread: "+thread); } @@ -167,9 +167,9 @@ public class MainThread { t.printStackTrace(); } } - return res; + return res; } - + static class UserApp extends Thread { private final String mainClassNameShort; private final String mainClassName; @@ -181,7 +181,7 @@ public class MainThread { super(); this.mainClassName=mainClassName; this.mainClassArgs=mainClassArgs; - + final Class<?> mainClass = ReflectionUtil.getClass(mainClassName, true, getClass().getClassLoader()); if(null==mainClass) { throw new ClassNotFoundException("MainAction couldn't find main class "+mainClassName); @@ -192,7 +192,7 @@ public class MainThread { setName(getName()+"-UserApp-"+mainClassNameShort); setDaemon(false); - + if(DEBUG) System.err.println("MainAction(): instantiated: "+getName()+", is daemon "+isDaemon()+", main-class: "+mainClass.getName()); } @@ -230,32 +230,32 @@ public class MainThread { if(isMacOSX) { try { if(DEBUG) { - System.err.println("MainAction.main(): "+Thread.currentThread()+" MainAction fin - stopNSApp.0"); + System.err.println("MainAction.main(): "+Thread.currentThread()+" MainAction fin - stopNSApp.0"); } - ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "stopNSApplication", + ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "stopNSApplication", null, null, MainThread.class.getClassLoader()); if(DEBUG) { - System.err.println("MainAction.main(): "+Thread.currentThread()+" MainAction fin - stopNSApp.X"); + System.err.println("MainAction.main(): "+Thread.currentThread()+" MainAction fin - stopNSApp.X"); } } catch (Exception e) { e.printStackTrace(); } } else { if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" MainAction fin - System.exit(0)"); - System.exit(0); - } + System.exit(0); + } } } } private static UserApp mainAction; - /** Your new java application main entry, which pipelines your application - * @throws ClassNotFoundException - * @throws NoSuchMethodException + /** Your new java application main entry, which pipelines your application + * @throws ClassNotFoundException + * @throws NoSuchMethodException * @throws SecurityException */ public static void main(String[] args) throws SecurityException, NoSuchMethodException, ClassNotFoundException { final Thread cur = Thread.currentThread(); - + useMainThread = HINT_USE_MAIN_THREAD; if(DEBUG) { @@ -268,7 +268,7 @@ public class MainThread { if(!useMainThread && !NativeWindowFactory.isAWTAvailable()) { throw new RuntimeException("!USE_MAIN_THREAD and no AWT available"); } - + if(args.length==0) { return; } @@ -282,7 +282,7 @@ public class MainThread { mainAction = new UserApp(mainClassName, mainClassArgs); if(isMacOSX) { - ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "initSingleton", + ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "initSingleton", null, null, MainThread.class.getClassLoader()); } @@ -290,24 +290,24 @@ public class MainThread { try { cur.setName(cur.getName()+"-MainThread"); } catch (Exception e) {} - + // dispatch user's main thread .. mainAction.start(); - + if(isMacOSX) { try { if(DEBUG) { - System.err.println("MainThread.main(): "+cur.getName()+"- runNSApp"); + System.err.println("MainThread.main(): "+cur.getName()+"- runNSApp"); } - ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "runNSApplication", + ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "runNSApplication", null, null, MainThread.class.getClassLoader()); } catch (Exception e) { e.printStackTrace(); } - } - if(DEBUG) { System.err.println("MainThread - wait until last non daemon thread ends ..."); } + } + if(DEBUG) { System.err.println("MainThread - wait until last non daemon thread ends ..."); } } else { - // run user's main in this thread + // run user's main in this thread mainAction.run(); } } diff --git a/src/newt/classes/com/jogamp/newt/util/MonitorMode.java b/src/newt/classes/com/jogamp/newt/util/MonitorMode.java deleted file mode 100644 index 8104f207a..000000000 --- a/src/newt/classes/com/jogamp/newt/util/MonitorMode.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright 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: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ - -package com.jogamp.newt.util; - -import javax.media.nativewindow.util.*; - -/** Immutable MonitorMode Class, consisting of it's read only components:<br> - * <ul> - * <li>{@link javax.media.nativewindow.util.SurfaceSize} surface memory size</li> - * <li>{@link javax.media.nativewindow.util.DimensionImmutable} size in [mm]</li> - * <li><code>refresh rate</code></li> - * </ul> - */ -public class MonitorMode { - SurfaceSize surfaceSize; - DimensionImmutable screenSizeMM; // in [mm] - int refreshRate; - - public MonitorMode(SurfaceSize surfaceSize, DimensionImmutable screenSizeMM, int refreshRate) { - // Don't validate screenSizeMM and refreshRate, since they may not be supported by the OS - if(null==surfaceSize) { - throw new IllegalArgumentException("surfaceSize must be set ("+surfaceSize+")"); - } - this.surfaceSize=surfaceSize; - this.screenSizeMM=screenSizeMM; - this.refreshRate=refreshRate; - } - - public final SurfaceSize getSurfaceSize() { - return surfaceSize; - } - - public final DimensionImmutable getScreenSizeMM() { - return screenSizeMM; - } - - public final int getRefreshRate() { - return refreshRate; - } - - public final String toString() { - return new String("[ "+surfaceSize+" x "+refreshRate+" Hz, "+screenSizeMM+" mm ]"); - } - - /** - * Checks whether two size objects are equal. Two instances - * of <code>MonitorMode</code> are equal if the three components - * <code>surfaceSize</code> and <code>refreshRate</code> - * are equal. <code>screenSizeMM</code> is kept out intentional to reduce the requirements for finding the current mode. - * @return <code>true</code> if the two dimensions are equal; - * otherwise <code>false</code>. - */ - public final boolean equals(Object obj) { - if (this == obj) { return true; } - if (obj instanceof MonitorMode) { - MonitorMode p = (MonitorMode)obj; - return getSurfaceSize().equals(p.getSurfaceSize()) && - /* getScreenSizeMM().equals(p.getScreenSizeMM()) && */ - getRefreshRate() == p.getRefreshRate() ; - } - return false; - } - - /** - * returns a hash code over <code>surfaceSize</code> and <code>refreshRate</code>. - * <code>screenSizeMM</code> is kept out intentional to reduce the requirements for finding the current mode. - */ - public final int hashCode() { - // 31 * x == (x << 5) - x - int hash = 31 + getSurfaceSize().hashCode(); - /* hash = ((hash << 5) - hash) + getScreenSizeMM().hashCode(); */ - hash = ((hash << 5) - hash) + getRefreshRate(); - return hash; - } -} - diff --git a/src/newt/classes/com/jogamp/newt/util/MonitorModeUtil.java b/src/newt/classes/com/jogamp/newt/util/MonitorModeUtil.java new file mode 100644 index 000000000..fdd7985fe --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/util/MonitorModeUtil.java @@ -0,0 +1,258 @@ +/** + * Copyright 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.newt.util; + +import com.jogamp.newt.MonitorMode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.SurfaceSize; + +/** + * Convenient {@link com.jogamp.newt.MonitorMode} utility methods, + * filters etc. + */ +public class MonitorModeUtil { + + public static int getIndex(List<MonitorMode> monitorModes, MonitorMode search) { + return monitorModes.indexOf(search); + } + + public static int getIndexByHashCode(List<MonitorMode> monitorModes, MonitorMode search) { + if( null!=monitorModes && monitorModes.size()>0 ) { + for (int i=0; i<monitorModes.size(); i++) { + if ( search.hashCode() == monitorModes.get(i).hashCode() ) { + return i; + } + } + } + return -1; + } + + public static MonitorMode getByNativeSizeRateIdAndRotation(List<MonitorMode> monitorModes, MonitorMode.SizeAndRRate sizeAndRate, int modeId, int rotation) { + if( null!=monitorModes && monitorModes.size()>0 ) { + for (int i=0; i<monitorModes.size(); i++) { + final MonitorMode mode = monitorModes.get(i); + if( mode.getSizeAndRRate().equals(sizeAndRate) && mode.getId() == modeId && mode.getRotation() == rotation ) { + return mode; + } + } + } + return null; + } + + /** Sort the given {@link MonitorMode} collection w/ {@link MonitorMode#compareTo(MonitorMode)} function. */ + public static void sort(List<MonitorMode> monitorModes, boolean ascendingOrder) { + if( ascendingOrder ) { + Collections.sort(monitorModes); + } else { + Collections.sort(monitorModes, MonitorMode.monitorModeComparatorInv); + } + } + + /** + * + * @param monitorModes + * @param surfaceSize + * @return modes with exact {@link SurfaceSize}. May return zero sized list for non. + */ + public static List<MonitorMode> filterBySurfaceSize(List<MonitorMode> monitorModes, SurfaceSize surfaceSize) { + final List<MonitorMode> out = new ArrayList<MonitorMode>(); + if( null!=monitorModes && monitorModes.size()>0 ) { + for (int i=0; null!=monitorModes && i<monitorModes.size(); i++) { + final MonitorMode mode = monitorModes.get(i); + if(mode.getSurfaceSize().equals(surfaceSize)) { + out.add(mode); + } + } + } + return out; + } + + /** + * + * @param monitorModes + * @param rotation + * @return modes with exact rotation. May return zero sized list for non. + */ + public static List<MonitorMode> filterByRotation(List<MonitorMode> monitorModes, int rotation) { + final List<MonitorMode> out = new ArrayList<MonitorMode>(); + if( null!=monitorModes && monitorModes.size()>0 ) { + for (int i=0; null!=monitorModes && i<monitorModes.size(); i++) { + final MonitorMode mode = monitorModes.get(i); + if(mode.getRotation() == rotation) { + out.add(mode); + } + } + } + return out; + } + + /** + * + * @param monitorModes + * @param bitsPerPixel + * @return modes with exact bpp. May return zero sized list for non. + */ + public static List<MonitorMode> filterByBpp(List<MonitorMode> monitorModes, int bitsPerPixel) { + final List<MonitorMode> out = new ArrayList<MonitorMode>(); + if( null!=monitorModes && monitorModes.size()>0 ) { + for (int i=0; null!=monitorModes && i<monitorModes.size(); i++) { + final MonitorMode mode = monitorModes.get(i); + if(mode.getSurfaceSize().getBitsPerPixel() == bitsPerPixel) { + out.add(mode); + } + } + } + return out; + } + + /** + * + * @param monitorModes + * @param flags + * @return modes with exact flags. May return zero sized list for non. + */ + public static List<MonitorMode> filterByFlags(List<MonitorMode> monitorModes, int flags) { + final List<MonitorMode> out = new ArrayList<MonitorMode>(); + if( null!=monitorModes && monitorModes.size()>0 ) { + for (int i=0; null!=monitorModes && i<monitorModes.size(); i++) { + final MonitorMode mode = monitorModes.get(i); + if(mode.getFlags() == flags) { + out.add(mode); + } + } + } + return out; + } + + /** + * @param monitorModes + * @param resolution + * @return modes with nearest resolution, or matching ones. May return zero sized list for non. + */ + public static List<MonitorMode> filterByResolution(List<MonitorMode> monitorModes, DimensionImmutable resolution) { + final List<MonitorMode> out = new ArrayList<MonitorMode>(); + if( null!=monitorModes && monitorModes.size()>0 ) { + final int resolution_sq = resolution.getHeight()*resolution.getWidth(); + int mode_dsq=Integer.MAX_VALUE, mode_dsq_idx=0; + + for (int i=0; null!=monitorModes && i<monitorModes.size(); i++) { + final MonitorMode mode = monitorModes.get(i); + final DimensionImmutable res = mode.getSurfaceSize().getResolution(); + final int dsq = Math.abs(resolution_sq - res.getHeight()*res.getWidth()); + if(dsq<mode_dsq) { + mode_dsq = dsq; + mode_dsq_idx = i; + } + if(res.equals(resolution)) { + out.add(mode); + } + } + if(out.size() == 0 && 0 <= mode_dsq_idx ) { + // nearest .. + out.add(monitorModes.get(mode_dsq_idx)); + } + } + return out; + } + + /** + * + * @param monitorModes + * @param refreshRate + * @return modes with nearest refreshRate, or matching ones. May return zero sized list for non. + */ + public static List<MonitorMode> filterByRate(List<MonitorMode> monitorModes, float refreshRate) { + final List<MonitorMode> out = new ArrayList<MonitorMode>(); + if( null!=monitorModes && monitorModes.size()>0 ) { + float mode_dr = Float.MAX_VALUE; + int mode_dr_idx = -1; + for (int i=0; null!=monitorModes && i<monitorModes.size(); i++) { + final MonitorMode mode = monitorModes.get(i); + float dr = Math.abs(refreshRate - mode.getRefreshRate()); + if(dr<mode_dr) { + mode_dr = dr; + mode_dr_idx = i; + } + if(0 == dr) { + out.add(mode); + } + } + if(out.size() == 0 && 0 <= mode_dr_idx ) { + // nearest .. + out.add(monitorModes.get(mode_dr_idx)); + } + } + return out; + } + + /** + * @param monitorModes + * @return modes with highest available bpp (color depth). May return zero sized list for non. + */ + public static List<MonitorMode> getHighestAvailableBpp(List<MonitorMode> monitorModes) { + if( null!=monitorModes && monitorModes.size()>0 ) { + int highest = -1; + for (int i=0; null!=monitorModes && i < monitorModes.size(); i++) { + final MonitorMode mode = monitorModes.get(i); + final int bpp = mode.getSurfaceSize().getBitsPerPixel(); + if (bpp > highest) { + highest = bpp; + } + } + return filterByBpp(monitorModes, highest); + } + return new ArrayList<MonitorMode>(); + } + + /** + * + * @param monitorModes + * @return modes with highest available refresh rate. May return zero sized list for non. + */ + public static List<MonitorMode> getHighestAvailableRate(List<MonitorMode> monitorModes) { + if( null!=monitorModes && monitorModes.size()>0 ) { + float highest = -1; + for (int i=0; null!=monitorModes && i < monitorModes.size(); i++) { + final MonitorMode mode = monitorModes.get(i); + final float rate = mode.getRefreshRate(); + if (rate > highest) { + highest = rate; + } + } + return filterByRate(monitorModes, highest); + } + return new ArrayList<MonitorMode>(); + } + +} diff --git a/src/newt/classes/com/jogamp/newt/util/ScreenModeUtil.java b/src/newt/classes/com/jogamp/newt/util/ScreenModeUtil.java deleted file mode 100644 index 93797c5fb..000000000 --- a/src/newt/classes/com/jogamp/newt/util/ScreenModeUtil.java +++ /dev/null @@ -1,341 +0,0 @@ -/** - * Copyright 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: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ - -package com.jogamp.newt.util; - -import com.jogamp.common.util.ArrayHashSet; -import com.jogamp.newt.ScreenMode; -import java.util.ArrayList; -import java.util.List; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.DimensionImmutable; -import javax.media.nativewindow.util.SurfaceSize; - -/** - * Convenient {@link com.jogamp.newt.ScreenMode} utility methods, - * filters etc. - */ -public class ScreenModeUtil { - /** WARNING: must be synchronized with ScreenMode.h, native implementation - * 2: width and height - */ - public static final int NUM_RESOLUTION_PROPERTIES = 2; - - /** WARNING: must be synchronized with ScreenMode.h, native implementation - * 1: bpp - */ - public static final int NUM_SURFACE_SIZE_PROPERTIES = 1; - - /** WARNING: must be synchronized with ScreenMode.h, native implementation - * 3: ScreenSizeMM[width, height], refresh-rate - */ - public static final int NUM_MONITOR_MODE_PROPERTIES = 3; - - /** WARNING: must be synchronized with ScreenMode.h, native implementation - * 1: rotation, native_mode_id - */ - public static final int NUM_SCREEN_MODE_PROPERTIES = 1; - - /** WARNING: must be synchronized with ScreenMode.h, native implementation - * count + all the above - */ - public static final int NUM_SCREEN_MODE_PROPERTIES_ALL = 8; - - public static int getIndex(List<ScreenMode> screenModes, ScreenMode search) { - return screenModes.indexOf(search); - } - - public static int getIndexByHashCode(List<ScreenMode> screenModes, ScreenMode search) { - for (int i=0; null!=screenModes && i<screenModes.size(); i++) { - if ( search.hashCode() == screenModes.get(i).hashCode() ) { - return i; - } - } - return -1; - } - - /** - * @param screenModes - * @param resolution - * @return modes with nearest resolution, or matching ones - */ - public static List<ScreenMode> filterByResolution(List<ScreenMode> screenModes, DimensionImmutable resolution) { - if(null==screenModes || screenModes.size()==0) { - return null; - } - List<ScreenMode> out = new ArrayList<ScreenMode>(); - int resolution_sq = resolution.getHeight()*resolution.getWidth(); - int sm_dsq=resolution_sq, sm_dsq_idx=0; - - for (int i=0; null!=screenModes && i<screenModes.size(); i++) { - ScreenMode sm = screenModes.get(i); - DimensionImmutable res = sm.getMonitorMode().getSurfaceSize().getResolution(); - int dsq = Math.abs(resolution_sq - res.getHeight()*res.getWidth()); - if(dsq<sm_dsq) { - sm_dsq = dsq; - sm_dsq_idx = i; - } - if(res.equals(resolution)) { - out.add(sm); - } - } - if(out.size()>0) { - return out; - } - // nearest .. - resolution = screenModes.get(sm_dsq_idx).getMonitorMode().getSurfaceSize().getResolution(); - return filterByResolution(screenModes, resolution); - } - - public static List<ScreenMode> filterBySurfaceSize(List<ScreenMode> screenModes, SurfaceSize surfaceSize) { - if(null==screenModes || screenModes.size()==0) { - return null; - } - List<ScreenMode> out = new ArrayList<ScreenMode>(); - for (int i=0; null!=screenModes && i<screenModes.size(); i++) { - ScreenMode sm = screenModes.get(i); - if(sm.getMonitorMode().getSurfaceSize().equals(surfaceSize)) { - out.add(sm); - } - } - return out.size()>0 ? out : null; - } - - public static List<ScreenMode> filterByRotation(List<ScreenMode> screenModes, int rotation) { - if(null==screenModes || screenModes.size()==0) { - return null; - } - List<ScreenMode> out = new ArrayList<ScreenMode>(); - for (int i=0; null!=screenModes && i<screenModes.size(); i++) { - ScreenMode sm = screenModes.get(i); - if(sm.getRotation() == rotation) { - out.add(sm); - } - } - return out.size()>0 ? out : null; - } - - public static List<ScreenMode> filterByBpp(List<ScreenMode> screenModes, int bitsPerPixel) { - if(null==screenModes || screenModes.size()==0) { - return null; - } - List<ScreenMode> out = new ArrayList<ScreenMode>(); - for (int i=0; null!=screenModes && i<screenModes.size(); i++) { - ScreenMode sm = screenModes.get(i); - if(sm.getMonitorMode().getSurfaceSize().getBitsPerPixel() == bitsPerPixel) { - out.add(sm); - } - } - return out.size()>0 ? out : null; - } - - /** - * - * @param screenModes - * @param refreshRate - * @return modes with nearest refreshRate, or matching ones - */ - public static List<ScreenMode> filterByRate(List<ScreenMode> screenModes, int refreshRate) { - if(null==screenModes || screenModes.size()==0) { - return null; - } - int sm_dr = refreshRate; - int sm_dr_idx = -1; - List<ScreenMode> out = new ArrayList<ScreenMode>(); - for (int i=0; null!=screenModes && i<screenModes.size(); i++) { - ScreenMode sm = screenModes.get(i); - int dr = Math.abs(refreshRate - sm.getMonitorMode().getRefreshRate()); - if(dr<sm_dr) { - sm_dr = dr; - sm_dr_idx = i; - } - if(0 == dr) { - out.add(sm); - } - } - if(out.size()>0) { - return out; - } - refreshRate = screenModes.get(sm_dr_idx).getMonitorMode().getRefreshRate(); - return filterByRate(screenModes, refreshRate); - } - - public static List<ScreenMode> getHighestAvailableBpp(List<ScreenMode> screenModes) { - if(null==screenModes || screenModes.size()==0) { - return null; - } - int highest = -1; - for (int i=0; null!=screenModes && i < screenModes.size(); i++) { - ScreenMode sm = screenModes.get(i); - int bpp = sm.getMonitorMode().getSurfaceSize().getBitsPerPixel(); - if (bpp > highest) { - highest = bpp; - } - } - return filterByBpp(screenModes, highest); - } - - public static List<ScreenMode> getHighestAvailableRate(List<ScreenMode> screenModes) { - if(null==screenModes || screenModes.size()==0) { - return null; - } - int highest = -1; - for (int i=0; null!=screenModes && i < screenModes.size(); i++) { - ScreenMode sm = screenModes.get(i); - int rate = sm.getMonitorMode().getRefreshRate(); - if (rate > highest) { - highest = rate; - } - } - return filterByRate(screenModes, highest); - } - - /** WARNING: must be synchronized with ScreenMode.h, native implementation */ - public static DimensionImmutable streamInResolution(int[] resolutionProperties, int offset) { - Dimension resolution = new Dimension(resolutionProperties[offset++], resolutionProperties[offset++]); - return resolution; - } - - /** WARNING: must be synchronized with ScreenMode.h, native implementation */ - public static SurfaceSize streamInSurfaceSize(DimensionImmutable resolution, int[] sizeProperties, int offset) { - SurfaceSize surfaceSize = new SurfaceSize(resolution, sizeProperties[offset++]); - return surfaceSize; - } - - /** WARNING: must be synchronized with ScreenMode.h, native implementation */ - public static MonitorMode streamInMonitorMode(SurfaceSize surfaceSize, DimensionImmutable screenSizeMM, int[] monitorProperties, int offset) { - int refreshRate = monitorProperties[offset++]; - return new MonitorMode(surfaceSize, screenSizeMM, refreshRate); - } - - /** WARNING: must be synchronized with ScreenMode.h, native implementation */ - public static ScreenMode streamInScreenMode(MonitorMode monitorMode, int[] modeProperties, int offset) { - int rotation = modeProperties[offset++]; - return new ScreenMode(monitorMode, rotation); - } - - /** - * WARNING: must be synchronized with ScreenMode.h, native implementation - * - * @param modeProperties the input data - * @param offset the offset to the input data - * @return ScreenMode element matching the input <code>modeProperties</code>, - * or null if input could not be processed. - */ - public static ScreenMode streamIn(int[] modeProperties, int offset) { - return streamInImpl(null, null, null, null, null, modeProperties, offset); - } - - /** - * WARNING: must be synchronized with ScreenMode.h, native implementation - * - * @param resolutionPool hash array of unique resolutions, no duplicates - * @param surfaceSizePool hash array of unique SurfaceSize, no duplicates - * @param monitorModePool hash array of unique MonitorMode, no duplicates - * @param screenModePool hash array of unique ScreenMode, no duplicates - * @param modeProperties the input data - * @param offset the offset to the input data - * @return index of the identical (old or new) ScreenMode element in <code>screenModePool</code>, - * matching the input <code>modeProperties</code>, or -1 if input could not be processed. - */ - public static int streamIn(ArrayHashSet<DimensionImmutable> resolutionPool, - ArrayHashSet<SurfaceSize> surfaceSizePool, - ArrayHashSet<DimensionImmutable> screenSizeMMPool, - ArrayHashSet<MonitorMode> monitorModePool, - ArrayHashSet<ScreenMode> screenModePool, - int[] modeProperties, int offset) { - ScreenMode screenMode = streamInImpl(resolutionPool, surfaceSizePool, screenSizeMMPool, monitorModePool, screenModePool, - modeProperties, offset); - return screenModePool.indexOf(screenMode); - } - - - private static ScreenMode streamInImpl(ArrayHashSet<DimensionImmutable> resolutionPool, - ArrayHashSet<SurfaceSize> surfaceSizePool, - ArrayHashSet<DimensionImmutable> screenSizeMMPool, - ArrayHashSet<MonitorMode> monitorModePool, - ArrayHashSet<ScreenMode> screenModePool, - int[] modeProperties, int offset) { - int count = modeProperties[offset]; - if(NUM_SCREEN_MODE_PROPERTIES_ALL != count) { - throw new RuntimeException("NUM_SCREEN_MODE_PROPERTIES should be "+NUM_SCREEN_MODE_PROPERTIES_ALL+", is "+count+", len "+(modeProperties.length-offset)); - } - if(NUM_SCREEN_MODE_PROPERTIES_ALL > modeProperties.length-offset) { - throw new RuntimeException("properties array too short, should be >= "+NUM_SCREEN_MODE_PROPERTIES_ALL+", is "+(modeProperties.length-offset)); - } - offset++; - DimensionImmutable resolution = ScreenModeUtil.streamInResolution(modeProperties, offset); - offset += ScreenModeUtil.NUM_RESOLUTION_PROPERTIES; - if(null!=resolutionPool) { - resolution = resolutionPool.getOrAdd(resolution); - } - - SurfaceSize surfaceSize = ScreenModeUtil.streamInSurfaceSize(resolution, modeProperties, offset); - offset += ScreenModeUtil.NUM_SURFACE_SIZE_PROPERTIES; - if(null!=surfaceSizePool) { - surfaceSize = surfaceSizePool.getOrAdd(surfaceSize); - } - - DimensionImmutable screenSizeMM = ScreenModeUtil.streamInResolution(modeProperties, offset); - offset += ScreenModeUtil.NUM_RESOLUTION_PROPERTIES; - if(null!=screenSizeMMPool) { - screenSizeMM = screenSizeMMPool.getOrAdd(screenSizeMM); - } - - MonitorMode monitorMode = ScreenModeUtil.streamInMonitorMode(surfaceSize, screenSizeMM, modeProperties, offset); - offset += ScreenModeUtil.NUM_MONITOR_MODE_PROPERTIES - ScreenModeUtil.NUM_RESOLUTION_PROPERTIES; - if(null!=monitorModePool) { - monitorMode = monitorModePool.getOrAdd(monitorMode); - } - - ScreenMode screenMode = ScreenModeUtil.streamInScreenMode(monitorMode, modeProperties, offset); - if(null!=screenModePool) { - screenMode = screenModePool.getOrAdd(screenMode); - } - return screenMode; - } - - /** WARNING: must be synchronized with ScreenMode.h, native implementation */ - public static int[] streamOut (ScreenMode screenMode) { - int[] data = new int[NUM_SCREEN_MODE_PROPERTIES_ALL]; - int idx=0; - data[idx++] = NUM_SCREEN_MODE_PROPERTIES_ALL; - data[idx++] = screenMode.getMonitorMode().getSurfaceSize().getResolution().getWidth(); - data[idx++] = screenMode.getMonitorMode().getSurfaceSize().getResolution().getHeight(); - data[idx++] = screenMode.getMonitorMode().getSurfaceSize().getBitsPerPixel(); - data[idx++] = screenMode.getMonitorMode().getScreenSizeMM().getWidth(); - data[idx++] = screenMode.getMonitorMode().getScreenSizeMM().getHeight(); - data[idx++] = screenMode.getMonitorMode().getRefreshRate(); - data[idx++] = screenMode.getRotation(); - if(NUM_SCREEN_MODE_PROPERTIES_ALL != idx) { - throw new InternalError("wrong number of attributes: got "+idx+" != should "+NUM_SCREEN_MODE_PROPERTIES_ALL); - } - return data; - } - -} diff --git a/src/newt/classes/com/jogamp/newt/util/applet/JOGLNewtApplet3Run.java b/src/newt/classes/com/jogamp/newt/util/applet/JOGLNewtApplet3Run.java new file mode 100644 index 000000000..8123126ee --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/util/applet/JOGLNewtApplet3Run.java @@ -0,0 +1,359 @@ +/** + * Copyright 2011 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.newt.util.applet; + +import java.util.Locale; + +import com.jogamp.plugin.applet.Applet3; +import com.jogamp.plugin.applet.Applet3Context; +import com.jogamp.plugin.ui.NativeWindowDownstream; +import com.jogamp.plugin.ui.NativeWindowUpstream; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.WindowClosingProtocol.WindowClosingMode; +import javax.media.nativewindow.util.PointImmutable; +import javax.media.opengl.FPSCounter; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; + +import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSizePos; +import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.Window; +import com.jogamp.newt.opengl.GLWindow; + +/** + * Simple GLEventListener deployment as an applet using JOGL. This demo must be + * referenced from a web page via an <applet> tag. + * + * <p> + * Example of an applet tag using GearsES2 within the applet area (normal case): + * <pre> + <applet width=100 height=100> + <param name="java_arguments" value="-Dsun.java2d.noddraw=true"> + <param name="gl_event_listener_class" value="com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2"> + <param name="gl_profile" value="GL2"> + <param name="gl_swap_interval" value="1"> + <param name="gl_debug" value="false"> + <param name="gl_trace" value="false"> + <param name="jnlp_href" value="jogl-newt-applet-runner.jnlp"> + </applet>Hello Gears ! + * </pre> + * </p> + * + * <p> + * Example of an applet tag using GearsES2 in an undecorated, translucent, closeable and always-on-top window: + * <pre> + <applet width=1 height=1> + <param name="java_arguments" value="-Dsun.java2d.noddraw=true"> + <param name="gl_event_listener_class" value="com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2"> + <param name="gl_profile" value="GL2"> + <param name="gl_swap_interval" value="1"> + <param name="gl_undecorated" value="true"> + <param name="gl_alwaysontop" value="true"> + <param name="gl_closeable" value="true"> + <param name="gl_alpha" value="1"> + <param name="gl_multisamplebuffer" value="0"> + <param name="gl_opaque" value="false"> + <param name="gl_dx" value="10"> + <param name="gl_dy" value="0"> + <param name="gl_width" value="100"> + <param name="gl_height" value="100"> + <param name="gl_nodefaultkeyListener" value="true"> + <param name="gl_debug" value="false"> + <param name="gl_trace" value="false"> + <param name="jnlp_href" value="jogl-newt-applet-runner.jnlp"> + </applet>Hello Gears ! + * </pre> + * </p> + */ +public class JOGLNewtApplet3Run implements Applet3 { + public static final boolean DEBUG = JOGLNewtAppletBase.DEBUG; + + GLWindow glWindow = null; + JOGLNewtAppletBase base = null; + /** if valid glStandalone:=true (own window) ! */ + int glXd=Integer.MAX_VALUE, glYd=Integer.MAX_VALUE, glWidth=Integer.MAX_VALUE, glHeight=Integer.MAX_VALUE; + Applet3Context ctx; + boolean glStandalone = false; + UpstreamSurfaceHookMutableSizePos upstreamSizePosHook; + PointImmutable upstreamLocOnScreen; + NativeWindow browserWin; + + final String getParameter(String name) { + return ctx.getParameter(name); + } + + @Override + public NativeWindowDownstream createNativeWindow(final Applet3Context ctx, final NativeWindowUpstream upstreamWin) { + this.ctx = ctx; + + String glProfileName=null; + boolean glOpaque=true; + int glAlphaBits=0; + int glNumMultisampleBuffer=0; + boolean glUndecorated=false; + boolean glAlwaysOnTop=false; + try { + glProfileName = getParameter("gl_profile"); + glOpaque = JOGLNewtAppletBase.str2Bool(getParameter("gl_opaque"), glOpaque); + glAlphaBits = JOGLNewtAppletBase.str2Int(getParameter("gl_alpha"), glAlphaBits); + glNumMultisampleBuffer = JOGLNewtAppletBase.str2Int(getParameter("gl_multisamplebuffer"), glNumMultisampleBuffer); + glXd = JOGLNewtAppletBase.str2Int(getParameter("gl_dx"), glXd); + glYd = JOGLNewtAppletBase.str2Int(getParameter("gl_dy"), glYd); + glWidth = JOGLNewtAppletBase.str2Int(getParameter("gl_width"), glWidth); + glHeight = JOGLNewtAppletBase.str2Int(getParameter("gl_height"), glHeight); + glUndecorated = JOGLNewtAppletBase.str2Bool(getParameter("gl_undecorated"), glUndecorated); + glAlwaysOnTop = JOGLNewtAppletBase.str2Bool(getParameter("gl_alwaysontop"), glAlwaysOnTop); + } catch (Exception e) { + e.printStackTrace(); + } + glStandalone = Integer.MAX_VALUE>glXd && Integer.MAX_VALUE>glYd && Integer.MAX_VALUE>glWidth && Integer.MAX_VALUE>glHeight; + final GLCapabilities caps = new GLCapabilities(GLProfile.get(glProfileName)); + caps.setAlphaBits(glAlphaBits); + if(0<glNumMultisampleBuffer) { + caps.setSampleBuffers(true); + caps.setNumSamples(glNumMultisampleBuffer); + } + caps.setBackgroundOpaque(glOpaque); + + final AbstractGraphicsDevice aDevice = NativeWindowFactory.createDevice(upstreamWin.getDisplayConnection(), + true /* own */); // open and own! (for upstreamLocOnScreen) + final AbstractGraphicsScreen aScreen = NativeWindowFactory.createScreen(aDevice, upstreamWin.getScreenIndex()); + upstreamSizePosHook = new UpstreamSurfaceHookMutableSizePos(upstreamWin.getX(), upstreamWin.getY(), + upstreamWin.getWidth(), upstreamWin.getHeight()); + browserWin = NativeWindowFactory.createWrappedWindow(aScreen, 0 /* surfaceHandle */, upstreamWin.getWindowHandle(), + upstreamSizePosHook); + upstreamLocOnScreen = NativeWindowFactory.getLocationOnScreen(browserWin); + if(DEBUG) { + System.err.println("JOGLNewtApplet3Run Configuration:"); + System.err.println("glStandalone: "+glStandalone); + System.err.println("glProfileName: "+glProfileName); + System.err.println("glOpaque: "+glOpaque); + System.err.println("glAlphaBits: "+glAlphaBits); + System.err.println("glNumMultisampleBuffer: "+glNumMultisampleBuffer); + System.err.println("glUndecorated: "+glUndecorated); + System.err.println("glAlwaysOnTop: "+glAlwaysOnTop); + System.err.println("UpstreamWin: "+upstreamWin+", LOS "+upstreamLocOnScreen); + if(glStandalone) { + System.err.println("pos-size: "+glXd+"/"+glYd+" "+glWidth+"x"+glHeight); + } + } + + final Window w = NewtFactory.createWindow(glStandalone ? null : browserWin, caps); + glWindow = GLWindow.create(w); + glWindow.setUndecorated(glUndecorated); + glWindow.setAlwaysOnTop(glAlwaysOnTop); + glWindow.setSize(browserWin.getWidth(), browserWin.getHeight()); + + return new NativeWindowDownstream() { + @Override + public void setVisible(boolean v) { + if( null != glWindow ) { + glWindow.setVisible(v); + } + } + + @Override + public void setSize(int width, int height) { + upstreamSizePosHook.setSize(width, height); + if( null != glWindow ) { + glWindow.setSize(width, height); + } + } + + @Override + public void requestFocus() { + if( null != glWindow ) { + glWindow.requestFocus(); + } + } + + @Override + public void destroy() { + if( null != glWindow ) { + glWindow.destroy(); + } + } + + @Override + public NativeWindowUpstream getParent() { + return upstreamWin; + } + + @Override + public long getWindowHandle() { + if( null != glWindow ) { + return glWindow.getWindowHandle(); + } else { + return 0; + } + } + + @Override + public void display() { + if( null != glWindow ) { + glWindow.display(); + } + } + + @Override + public void notifyPositionChanged(NativeWindowUpstream nw) { + upstreamSizePosHook.setPos(nw.getX(), nw.getY()); + if( null != glWindow ) { + glWindow.setPosition(nw.getX(), nw.getY()); + } + } + }; + } + + @Override + public void init(Applet3Context ctx) { + if(DEBUG) { + System.err.println("JOGLNewtApplet1Run.init() START - "+currentThreadName()); + } + this.ctx = ctx; + String glEventListenerClazzName=null; + int glSwapInterval=0; + boolean glDebug=false; + boolean glTrace=false; + boolean glNoDefaultKeyListener = false; + boolean glCloseable=false; + + try { + glEventListenerClazzName = getParameter("gl_event_listener_class"); + glSwapInterval = JOGLNewtAppletBase.str2Int(getParameter("gl_swap_interval"), glSwapInterval); + glDebug = JOGLNewtAppletBase.str2Bool(getParameter("gl_debug"), glDebug); + glTrace = JOGLNewtAppletBase.str2Bool(getParameter("gl_trace"), glTrace); + glNoDefaultKeyListener = JOGLNewtAppletBase.str2Bool(getParameter("gl_nodefaultkeyListener"), glNoDefaultKeyListener); + glCloseable = JOGLNewtAppletBase.str2Bool(getParameter("gl_closeable"), glCloseable); + } catch (Exception e) { + e.printStackTrace(); + } + if(null==glEventListenerClazzName) { + throw new RuntimeException("No applet parameter 'gl_event_listener_class'"); + } + if(DEBUG) { + System.err.println("JOGLNewtApplet1Run Configuration:"); + System.err.println("glEventListenerClazzName: "+glEventListenerClazzName); + System.err.println("glSwapInterval: "+glSwapInterval); + System.err.println("glDebug: "+glDebug); + System.err.println("glTrace: "+glTrace); + System.err.println("glNoDefaultKeyListener: "+glNoDefaultKeyListener); + System.err.println("glCloseable: "+glCloseable); + } + + base = new JOGLNewtAppletBase(glEventListenerClazzName, + glSwapInterval, + glNoDefaultKeyListener, + glCloseable, + glDebug, + glTrace); + + try { + glWindow.setUpdateFPSFrames(FPSCounter.DEFAULT_FRAMES_PER_INTERVAL, System.err); + glWindow.setDefaultCloseOperation(glCloseable ? WindowClosingMode.DISPOSE_ON_CLOSE : WindowClosingMode.DO_NOTHING_ON_CLOSE); + base.init(glWindow); + } catch (Throwable t) { + throw new RuntimeException(t); + } + if(DEBUG) { + System.err.println("JOGLNewtApplet1Run.init() END - "+currentThreadName()); + } + } + + private static String currentThreadName() { return "["+Thread.currentThread().getName()+"]"; } + + @Override + public void start() { + if(DEBUG) { + System.err.println("JOGLNewtApplet1Run.start() START (isVisible "+glWindow.isVisible()+") - "+currentThreadName()); + } + if( glStandalone ) { + glWindow.setSize(glWidth, glHeight); + glWindow.setPosition(upstreamLocOnScreen.getX()+glXd, upstreamLocOnScreen.getY()+glYd); + glWindow.setVisible(true); + glWindow.requestFocus(); + } + if(DEBUG) { + System.err.println("JOGLNewtApplet1Run start:"); + System.err.println("GLWindow Pos: "+glWindow.getX()+"/"+glWindow.getY()+" rel, "+glWindow.getLocationOnScreen(null)+" screen"); + System.err.println("GLWindow: "+glWindow); + } + base.start(); + if(DEBUG) { + System.err.println("JOGLNewtApplet1Run.start() END - "+currentThreadName()); + } + } + + @Override + public void stop() { + if(DEBUG) { + System.err.println("JOGLNewtApplet1Run.stop() START - "+currentThreadName()); + } + base.stop(); + if(DEBUG) { + System.err.println("JOGLNewtApplet1Run.stop() END - "+currentThreadName()); + } + } + + @Override + public void destroy() { + if(DEBUG) { + System.err.println("JOGLNewtApplet1Run.destroy() START - "+currentThreadName()); + } + glWindow.setVisible(false); // hide 1st + base.destroy(); // destroy glWindow unrecoverable + base=null; + glWindow=null; + browserWin.destroy(); // make sure the open display connection gets closed! + browserWin = null; + if(DEBUG) { + System.err.println("JOGLNewtApplet1Run.destroy() END - "+currentThreadName()); + } + } + + @Override + public String getAppletInfo() { + return null; + } + + @Override + public Locale getLocale() { + return null; + } + + @Override + public String[][] getParameterInfo() { + return null; + } + +} + diff --git a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java b/src/newt/classes/com/jogamp/newt/util/applet/JOGLNewtAppletBase.java index 082c01c23..bbe6b8527 100755..100644 --- a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java +++ b/src/newt/classes/com/jogamp/newt/util/applet/JOGLNewtAppletBase.java @@ -25,7 +25,7 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ -package com.jogamp.newt.awt.applet; +package com.jogamp.newt.util.applet; import java.lang.reflect.Field; import java.security.AccessController; @@ -33,6 +33,7 @@ import java.security.PrivilegedAction; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.WindowClosingProtocol.WindowClosingMode; +import javax.media.nativewindow.util.InsetsImmutable; import javax.media.opengl.FPSCounter; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; @@ -41,6 +42,10 @@ import javax.media.opengl.GLPipelineFactory; import jogamp.newt.Debug; +import com.jogamp.common.util.IOUtil; +import com.jogamp.newt.Display; +import com.jogamp.newt.Window; +import com.jogamp.newt.Display.PointerIcon; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.MouseListener; @@ -56,27 +61,28 @@ import com.jogamp.opengl.util.Animator; public class JOGLNewtAppletBase implements KeyListener, GLEventListener { public static final boolean DEBUG = Debug.debug("Applet"); - + String glEventListenerClazzName; int glSwapInterval; boolean noDefaultKeyListener; boolean glClosable; boolean glDebug; boolean glTrace; + PointerIcon pointerIconTest = null; GLEventListener glEventListener = null; GLWindow glWindow = null; Animator glAnimator=null; boolean isValid = false; - NativeWindow awtParent; + NativeWindow parentWin; - public JOGLNewtAppletBase(String glEventListenerClazzName, + public JOGLNewtAppletBase(String glEventListenerClazzName, int glSwapInterval, boolean noDefaultKeyListener, boolean glClosable, boolean glDebug, boolean glTrace) { - + this.glEventListenerClazzName=glEventListenerClazzName; this.glSwapInterval=glSwapInterval; this.noDefaultKeyListener = noDefaultKeyListener; @@ -111,6 +117,7 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { try { final Class<?> clazz = AccessController.doPrivileged(new PrivilegedAction<Class<?>>() { + @Override public Class<?> run() { final ClassLoader cl = Thread.currentThread().getContextClassLoader(); Class<?> clazz = null; @@ -160,22 +167,6 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { public void init(ThreadGroup tg, final GLWindow glWindow) { isValid = false; this.glWindow = glWindow; - this.glWindow.addWindowListener(new WindowAdapter() { - // Closing action: back to parent! - @Override - public void windowDestroyNotify(WindowEvent e) { - if( WindowClosingMode.DO_NOTHING_ON_CLOSE == glWindow.getDefaultCloseOperation() ) { - if(null == glWindow.getParent()) { - // we may be called directly by the native EDT - new Thread(new Runnable() { - public void run() { - glWindow.reparentWindow(awtParent); - } - }).start(); - } - } - } } ); - glEventListener = createInstance(glEventListenerClazzName); if(null == glEventListener) { return; @@ -200,7 +191,7 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { if(glEventListener instanceof KeyListener) { glWindow.addKeyListener((KeyListener)glEventListener); } - + if(!noDefaultKeyListener) { glWindow.addKeyListener(this); } @@ -208,26 +199,59 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { glWindow.setUpdateFPSFrames(FPSCounter.DEFAULT_FRAMES_PER_INTERVAL, System.err); // glAnimator = new FPSAnimator(canvas, 60); - glAnimator = new Animator(tg, glWindow); + glAnimator = new Animator(); + glAnimator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD); // No AWT thread involved! + glAnimator.setThreadGroup(tg); + glAnimator.add(glWindow); glAnimator.setUpdateFPSFrames(FPSCounter.DEFAULT_FRAMES_PER_INTERVAL, null); - + } catch (Throwable t) { throw new RuntimeException(t); } isValid = true; } + private final WindowListener reparentHomeListener = new WindowAdapter() { + // Closing action: back to parent! + @Override + public void windowDestroyNotify(WindowEvent e) { + if( isValid() && WindowClosingMode.DO_NOTHING_ON_CLOSE == glWindow.getDefaultCloseOperation() && + null == glWindow.getParent() && null != parentWin && 0 != parentWin.getWindowHandle() ) + { + // we may be called directly by the native EDT + new Thread(new Runnable() { + @Override + public void run() { + if( glWindow.isNativeValid() && null != parentWin && 0 != parentWin.getWindowHandle() ) { + glWindow.reparentWindow(parentWin, -1, -1, Window.REPARENT_HINT_BECOMES_VISIBLE); + } + } + }).start(); + } + } }; + public void start() { if(isValid) { glWindow.setVisible(true); glWindow.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); + if( null == pointerIconTest ) { + final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "newt/data/cross-grey-alpha-16x16.png" } ); + final Display disp = glWindow.getScreen().getDisplay(); + try { + pointerIconTest = disp.createPointerIcon(res, 8, 8); + } catch (Exception e) { + e.printStackTrace(); + } + } glAnimator.start(); - awtParent = glWindow.getParent(); + parentWin = glWindow.getParent(); + glWindow.addWindowListener(reparentHomeListener); } } public void stop() { if(null!=glAnimator) { + glWindow.removeWindowListener(reparentHomeListener); glAnimator.stop(); glWindow.setVisible(false); } @@ -250,30 +274,34 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { // *********************************************************************************** // *********************************************************************************** + @Override public void init(GLAutoDrawable drawable) { GL _gl = drawable.getGL(); if(glDebug) { try { _gl = _gl.getContext().setGL( GLPipelineFactory.create("javax.media.opengl.Debug", null, _gl, null) ); - } catch (Exception e) {e.printStackTrace();} + } catch (Exception e) {e.printStackTrace();} } if(glTrace) { try { // Trace .. _gl = _gl.getContext().setGL( GLPipelineFactory.create("javax.media.opengl.Trace", null, _gl, new Object[] { System.err } ) ); - } catch (Exception e) {e.printStackTrace();} + } catch (Exception e) {e.printStackTrace();} } if(glSwapInterval>=0) { _gl.setSwapInterval(glSwapInterval); } } + @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { } + @Override public void display(GLAutoDrawable drawable) { } + @Override public void dispose(GLAutoDrawable drawable) { } @@ -281,27 +309,82 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { // *********************************************************************************** // *********************************************************************************** - public void keyPressed(KeyEvent e) { - } - public void keyReleased(KeyEvent e) { - } - public void keyTyped(KeyEvent e) { + @Override + public void keyPressed(KeyEvent e) { + if( !e.isPrintableKey() || e.isAutoRepeat() ) { + return; + } if(e.getKeyChar()=='d') { - glWindow.setUndecorated(!glWindow.isUndecorated()); + new Thread() { + public void run() { + glWindow.setUndecorated(!glWindow.isUndecorated()); + } }.start(); } if(e.getKeyChar()=='f') { - glWindow.setFullscreen(!glWindow.isFullscreen()); + new Thread() { + public void run() { + glWindow.setFullscreen(!glWindow.isFullscreen()); + } }.start(); } else if(e.getKeyChar()=='a') { - glWindow.setAlwaysOnTop(!glWindow.isAlwaysOnTop()); - } else if(e.getKeyChar()=='r' && null!=awtParent) { - if(null == glWindow.getParent()) { - glWindow.reparentWindow(awtParent); - } else { - glWindow.reparentWindow(null); - if(glClosable) { - glWindow.setDefaultCloseOperation(WindowClosingMode.DISPOSE_ON_CLOSE); - } - } + new Thread() { + public void run() { + glWindow.setAlwaysOnTop(!glWindow.isAlwaysOnTop()); + } }.start(); + } else if(e.getKeyChar()=='r' && null!=parentWin) { + new Thread() { + public void run() { + if(null == glWindow.getParent()) { + glWindow.reparentWindow(parentWin, -1, -1, 0 /* hints */); + } else { + final InsetsImmutable insets = glWindow.getInsets(); + final int x, y; + if ( 0 >= insets.getTopHeight() ) { + // fail safe .. + x = 32; + y = 32; + } else { + x = insets.getLeftWidth(); + y = insets.getTopHeight(); + } + glWindow.reparentWindow(null, x, y, 0 /* hints */); + glWindow.setDefaultCloseOperation( glClosable ? WindowClosingMode.DISPOSE_ON_CLOSE : WindowClosingMode.DO_NOTHING_ON_CLOSE ); + } + } }.start(); + } else if(e.getKeyChar()=='c') { + new Thread() { + public void run() { + System.err.println("[set pointer-icon pre]"); + final PointerIcon currentPI = glWindow.getPointerIcon(); + glWindow.setPointerIcon( currentPI == pointerIconTest ? null : pointerIconTest); + System.err.println("[set pointer-icon post] "+currentPI+" -> "+glWindow.getPointerIcon()); + } }.start(); + } else if(e.getKeyChar()=='i') { + new Thread() { + public void run() { + System.err.println("[set mouse visible pre]: "+glWindow.isPointerVisible()); + glWindow.setPointerVisible(!glWindow.isPointerVisible()); + System.err.println("[set mouse visible post]: "+glWindow.isPointerVisible()); + } }.start(); + } else if(e.getKeyChar()=='j') { + new Thread() { + public void run() { + final Thread t = glWindow.setExclusiveContextThread(null); + System.err.println("[set mouse confined pre]: "+glWindow.isPointerConfined()); + glWindow.confinePointer(!glWindow.isPointerConfined()); + System.err.println("[set mouse confined post]: "+glWindow.isPointerConfined()); + glWindow.setExclusiveContextThread(t); + } }.start(); + } else if(e.getKeyChar()=='w') { + new Thread() { + public void run() { + System.err.println("[set mouse pos pre]"); + glWindow.warpPointer(glWindow.getWidth()/2, glWindow.getHeight()/2); + System.err.println("[set mouse pos post]"); + } }.start(); } } + + @Override + public void keyReleased(KeyEvent e) { + } } diff --git a/src/newt/classes/com/jogamp/newt/util/applet/VersionApplet3.java b/src/newt/classes/com/jogamp/newt/util/applet/VersionApplet3.java new file mode 100644 index 000000000..db0e8fc45 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/util/applet/VersionApplet3.java @@ -0,0 +1,226 @@ +package com.jogamp.newt.util.applet; + +import com.jogamp.plugin.applet.Applet3; +import com.jogamp.plugin.applet.Applet3Context; +import com.jogamp.plugin.ui.NativeWindowDownstream; +import com.jogamp.plugin.ui.NativeWindowUpstream; + +import java.util.List; +import java.util.Locale; + +import javax.media.opengl.GLProfile; +import javax.media.opengl.GL; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLEventListener; + +import com.jogamp.common.GlueGenVersion; +import com.jogamp.common.util.VersionUtil; +import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.Window; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.JoglVersion; + +public class VersionApplet3 implements Applet3 { + + public static void main(String[] args) { + VersionApplet3 va = new VersionApplet3(); + + final NativeWindowDownstream nwc = va.createNativeWindow(null, new NativeWindowUpstream() { + @Override + public long getWindowHandle() { + return 0; + } + @Override + public int getWidth() { + return 64; + } + @Override + public int getHeight() { + return 64; + } + @Override + public String getDisplayConnection() { + return null; // default + } + @Override + public int getScreenIndex() { + return 0; // default + } + @Override + public void notifySurfaceUpdated(NativeWindowDownstream swappedWin) { + // NOP + } + @Override + public int getX() { + return 0; + } + @Override + public int getY() { + return 0; + } + }); + va.init(null); + va.start(); + va.stop(); + va.destroy(); + nwc.destroy(); + } + + GLWindow canvas; + + @Override + public NativeWindowDownstream createNativeWindow(final Applet3Context ctx, final NativeWindowUpstream parentWin) { + final GLCapabilities caps = new GLCapabilities(GLProfile.getDefault()); + final Window w = NewtFactory.createWindow(parentWin.getDisplayConnection(), parentWin.getScreenIndex(), parentWin.getWindowHandle(), caps); + canvas = GLWindow.create(w); + canvas.setSize(parentWin.getWidth(), parentWin.getHeight()); + + return new NativeWindowDownstream() { + @Override + public void setVisible(boolean v) { + if( null != canvas ) { + canvas.setVisible(v); + } + } + + @Override + public void setSize(int width, int height) { + if( null != canvas ) { + canvas.setSize(width, height); + } + } + + @Override + public void requestFocus() { + if( null != canvas ) { + canvas.requestFocus(); + } + } + + @Override + public void destroy() { + if( null != canvas ) { + canvas.destroy(); + } + } + + @Override + public NativeWindowUpstream getParent() { + return parentWin; + } + + @Override + public long getWindowHandle() { + if( null != canvas ) { + return canvas.getWindowHandle(); + } else { + return 0; + } + } + + @Override + public void display() { + if( null != canvas ) { + canvas.display(); + } + } + + @Override + public void notifyPositionChanged(NativeWindowUpstream nw) { + if( null != canvas ) { + canvas.setPosition(nw.getX(), nw.getY()); + } + } + }; + } + + @Override + public void init(Applet3Context ctx) { + System.err.println("VersionApplet: init() - begin"); + canvas.addGLEventListener(new GLInfo()); + System.err.println("VersionApplet: init() - end"); + } + + @Override + public void start() { + System.err.println("VersionApplet: start() - begin"); + + String s; + + s = VersionUtil.getPlatformInfo().toString(); + System.err.println(s); + + s = GlueGenVersion.getInstance().toString(); + System.err.println(s); + + /* + s = NativeWindowVersion.getInstance().toString(); + System.err.println(s); + */ + + s = JoglVersion.getInstance().toString(); + System.err.println(s); + + GLDrawableFactory factory = GLDrawableFactory.getFactory(canvas.getGLProfile()); + List<GLCapabilitiesImmutable> availCaps = factory.getAvailableCapabilities(null); + for(int i=0; i<availCaps.size(); i++) { + s = availCaps.get(i).toString(); + System.err.println(s); + } + canvas.display(); + System.err.println("VersionApplet: start() - end"); + } + + @Override + public void stop() { + System.err.println("VersionApplet: stop() - begin"); + canvas.setVisible(false); + System.err.println("VersionApplet: stop() - end"); + } + + @Override + public void destroy() { + System.err.println("VersionApplet: destroy() - start"); + if(null!=canvas) { + canvas.destroy(); + canvas = null; + } + System.err.println("VersionApplet: destroy() - end"); + } + + @Override + public String getAppletInfo() { + return null; + } + + @Override + public Locale getLocale() { + return null; + } + + @Override + public String[][] getParameterInfo() { + return null; + } + + class GLInfo implements GLEventListener { + @Override + public void init(GLAutoDrawable drawable) { + GL gl = drawable.getGL(); + String s = JoglVersion.getGLInfo(gl, null).toString(); + System.err.println(s); + } + @Override + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + } + @Override + public void display(GLAutoDrawable drawable) { + } + @Override + public void dispose(GLAutoDrawable drawable) { + } + } +} diff --git a/src/newt/classes/jogamp/newt/Debug.java b/src/newt/classes/jogamp/newt/Debug.java index 3c83da4d9..7ef2d7ffc 100644 --- a/src/newt/classes/jogamp/newt/Debug.java +++ b/src/newt/classes/jogamp/newt/Debug.java @@ -1,21 +1,22 @@ /* - * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. - * + * Copyright (c) 2003 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 @@ -28,17 +29,20 @@ * 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. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.newt; +import java.security.AccessController; +import java.security.PrivilegedAction; + import com.jogamp.common.util.PropertyAccess; /** Helper routines for logging and debugging. */ @@ -47,9 +51,15 @@ public class Debug extends PropertyAccess { // Some common properties private static final boolean verbose; private static final boolean debugAll; - + static { - PropertyAccess.addTrustedPrefix("newt.", Debug.class); + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + PropertyAccess.addTrustedPrefix("newt."); + return null; + } } ); + verbose = isPropertyDefined("newt.verbose", true); debugAll = isPropertyDefined("newt.debug", true); if (verbose) { @@ -60,27 +70,18 @@ public class Debug extends PropertyAccess { } } - public static final boolean isPropertyDefined(final String property, final boolean jnlpAlias) { - return PropertyAccess.isPropertyDefined(property, jnlpAlias, null); - } - - public static final int getIntProperty(final String property, final boolean jnlpAlias, int defaultValue) { - return PropertyAccess.getIntProperty(property, jnlpAlias, null, defaultValue); - } - - public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias) { - return PropertyAccess.getBooleanProperty(property, jnlpAlias, null); - } - - public static boolean verbose() { + /** Ensures static init block has been issues, i.e. if calling through to {@link PropertyAccess#isPropertyDefined(String, boolean)}. */ + public static final void initSingleton() {} + + public static final boolean verbose() { return verbose; } - public static boolean debugAll() { + public static final boolean debugAll() { return debugAll; } - public static boolean debug(String subcomponent) { + public static final boolean debug(String subcomponent) { return debugAll() || isPropertyDefined("newt.debug." + subcomponent, true); } } diff --git a/src/newt/classes/jogamp/newt/DefaultEDTUtil.java b/src/newt/classes/jogamp/newt/DefaultEDTUtil.java index fd757fba6..d481ce8f9 100644 --- a/src/newt/classes/jogamp/newt/DefaultEDTUtil.java +++ b/src/newt/classes/jogamp/newt/DefaultEDTUtil.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2009 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 @@ -29,7 +29,7 @@ * 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. @@ -38,6 +38,7 @@ package jogamp.newt; import java.util.ArrayList; + import javax.media.nativewindow.NativeWindowException; import jogamp.common.util.locks.LockDebugUtil; @@ -49,127 +50,183 @@ import com.jogamp.newt.util.EDTUtil; public class DefaultEDTUtil implements EDTUtil { public static final boolean DEBUG = Debug.debug("EDT"); - private ThreadGroup threadGroup; - private EventDispatchThread edt = null; - private Object edtLock = new Object(); // locking the EDT start/stop state - private String name; - int start_iter=0; - private Runnable dispatchMessages; + /** Used to implement {@link #invokeStop(boolean, Runnable)}. */ + private static final Object TASK_ATTACHMENT_STOP = new Object(); + /** Used to provoke an exception on the EDT while waiting / blocking. Merely exists to test code.*/ + private static final Object TASK_ATTACHMENT_TEST_ERROR = new Object(); + + private final Object edtLock = new Object(); // locking the EDT start/stop state + private /* final */ ThreadGroup threadGroup; + private final String name; + private final Runnable dispatchMessages; + private NEDT edt = null; + private int start_iter=0; private static long pollPeriod = EDTUtil.defaultEDTPollPeriod; public DefaultEDTUtil(ThreadGroup tg, String name, Runnable dispatchMessages) { this.threadGroup = tg; this.name=Thread.currentThread().getName()+"-"+name+"-EDT-"; this.dispatchMessages=dispatchMessages; - this.edt = new EventDispatchThread(threadGroup, name); + this.edt = new NEDT(threadGroup, name); this.edt.setDaemon(true); // don't stop JVM from shutdown .. } + @Override final public long getPollPeriod() { return pollPeriod; } + @Override final public void setPollPeriod(long ms) { pollPeriod = ms; } - - public final void reset() { - synchronized(edtLock) { - waitUntilStopped(); + + @Override + public final boolean start() throws IllegalStateException { + synchronized(edtLock) { + if( edt.isRunning() ) { + throw new IllegalStateException("EDT still running and not subject to stop. Curr "+Thread.currentThread().getName()+", EDT "+edt.getName()+", isRunning "+edt.isRunning+", shouldStop "+edt.shouldStop); + } if(DEBUG) { if(edt.tasks.size()>0) { - System.err.println(Thread.currentThread()+": EDT reset, remaining tasks: "+edt.tasks.size()+" - "+edt); - // Thread.dumpStack(); + System.err.println(Thread.currentThread()+": Default-EDT reset, remaining tasks: "+edt.tasks.size()+" - "+edt); + } + System.err.println(Thread.currentThread()+": Default-EDT reset - edt: "+edt); + } + if( edt.getState() != Thread.State.NEW ) { + if( null != threadGroup && threadGroup.isDestroyed() ) { + // best thing we can do is to use this thread's TG + threadGroup = Thread.currentThread().getThreadGroup(); } - System.err.println(Thread.currentThread()+": EDT reset - edt: "+edt); + edt = new NEDT(threadGroup, name); + edt.setDaemon(true); // don't stop JVM from shutdown .. } - this.edt = new EventDispatchThread(threadGroup, name); - this.edt.setDaemon(true); // don't stop JVM from shutdown .. + startImpl(); } + return invoke(true, nullTask); } - public final void start() { - synchronized(edtLock) { - if(!edt.isRunning() && !edt.shouldStop) { - if(edt.isAlive()) { - throw new RuntimeException("EDT Thread.isAlive(): true, isRunning: "+edt.isRunning()+", edt: "+edt+", tasks: "+edt.tasks.size()); - } - start_iter++; - edt.setName(name+start_iter); - edt.shouldStop = false; - if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT START - edt: "+edt); - // Thread.dumpStack(); - } - edt.start(); - } + private final void startImpl() { + if(edt.isAlive()) { + throw new RuntimeException("Default-EDT Thread.isAlive(): true, isRunning: "+edt.isRunning+", shouldStop "+edt.shouldStop+", edt: "+edt+", tasks: "+edt.tasks.size()); } + start_iter++; + edt.setName(name+start_iter); + if(DEBUG) { + System.err.println(Thread.currentThread()+": Default-EDT START - edt: "+edt); + } + edt.start(); } + @Override public final boolean isCurrentThreadEDT() { - return edt == Thread.currentThread(); + return edt == Thread.currentThread(); // EDT == NEDT + } + + @Override + public final boolean isCurrentThreadNEDT() { + return edt == Thread.currentThread(); // EDT == NEDT } + @Override + public final boolean isCurrentThreadEDTorNEDT() { + return edt == Thread.currentThread(); // EDT == NEDT + } + + @Override public final boolean isRunning() { return edt.isRunning() ; } - public final void invokeStop(Runnable task) { - invokeImpl(true, task, true); + @Override + public final boolean invokeStop(boolean wait, Runnable task) { + if(DEBUG) { + System.err.println(Thread.currentThread()+": Default-EDT.invokeStop wait "+wait); + Thread.dumpStack(); + } + return invokeImpl(wait, task, true /* stop */, false /* provokeError */); } - public final void invoke(boolean wait, Runnable task) { - invokeImpl(wait, task, false); + public final boolean invokeAndWaitError(Runnable task) { + if(DEBUG) { + System.err.println(Thread.currentThread()+": Default-EDT.invokeAndWaitError"); + Thread.dumpStack(); + } + return invokeImpl(true /* wait */, task, false /* stop */, true /* provokeError */); } - private void invokeImpl(boolean wait, Runnable task, boolean stop) { - if(task == null) { - throw new RuntimeException("Null Runnable"); - } + @Override + public final boolean invoke(boolean wait, Runnable task) { + return invokeImpl(wait, task, false /* stop */, false /* provokeError */); + } + + private static Runnable nullTask = new Runnable() { + @Override + public void run() { } + }; + + private final boolean invokeImpl(boolean wait, Runnable task, boolean stop, boolean provokeError) { Throwable throwable = null; RunnableTask rTask = null; - Object rTaskLock = new Object(); + final Object rTaskLock = new Object(); synchronized(rTaskLock) { // lock the optional task execution synchronized(edtLock) { // lock the EDT status if( edt.shouldStop ) { // drop task .. + System.err.println(Thread.currentThread()+": Warning: Default-EDT about (1) to stop, won't enqueue new task: "+edt); if(DEBUG) { - System.err.println("Warning: EDT about (1) to stop, won't enqueue new task: "+edt); Thread.dumpStack(); } - return; - } - // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); - // Thread.dumpStack(); - if(stop) { - edt.shouldStop = true; - if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - tasks: "+edt.tasks.size()+" - "+edt); - // Thread.dumpStack(); - } + return false; } if( isCurrentThreadEDT() ) { - task.run(); + if(null != task) { + task.run(); + } wait = false; // running in same thread (EDT) -> no wait - if(stop && edt.tasks.size()>0) { - System.err.println("Warning: EDT about (2) to stop, having remaining tasks: "+edt.tasks.size()+" - "+edt); - if(DEBUG) { - Thread.dumpStack(); + if( stop ) { + edt.shouldStop = true; + if( edt.tasks.size()>0 ) { + System.err.println(Thread.currentThread()+": Warning: Default-EDT about (2) to stop, task executed. Remaining tasks: "+edt.tasks.size()+" - "+edt); + if(DEBUG) { + Thread.dumpStack(); + } } } } else { - synchronized(edt.tasks) { - start(); // start if not started yet and !shouldStop - wait = wait && edt.isRunning(); - rTask = new RunnableTask(task, - wait ? rTaskLock : null, - true /* always catch and report Exceptions, don't disturb EDT */); - if(stop) { - rTask.setAttachment(new Boolean(true)); // mark final task + if( !edt.isRunning ) { + if( null != task ) { + if( stop ) { + System.err.println(Thread.currentThread()+": Warning: Default-EDT is about (3) to stop and stopped already, dropping task. Remaining tasks: "+edt.tasks.size()+" - "+edt); + } else { + System.err.println(Thread.currentThread()+": Warning: Default-EDT is not running, dropping task. NEDT "+edt); + } + if(DEBUG) { + Thread.dumpStack(); + } } - // append task .. - edt.tasks.add(rTask); - edt.tasks.notifyAll(); + return false; + } else if( stop && null == task ) { + task = nullTask; // ensures execution triggering stop + } + + if(null != task) { + synchronized(edt.tasks) { + rTask = new RunnableTask(task, + wait ? rTaskLock : null, + true /* always catch and report Exceptions, don't disturb EDT */, + wait ? null : System.err); + if(stop) { + rTask.setAttachment(TASK_ATTACHMENT_STOP); // mark final task, will imply shouldStop:=true + } else if(provokeError) { + rTask.setAttachment(TASK_ATTACHMENT_TEST_ERROR); + } + // append task .. + edt.tasks.add(rTask); + edt.tasks.notifyAll(); + } + } else { + wait = false; } } } @@ -189,22 +246,26 @@ public class DefaultEDTUtil implements EDTUtil { throw new RuntimeException(throwable); } } - } - if(DEBUG && stop) { - System.err.println(Thread.currentThread()+": EDT signal STOP X edt: "+edt); + if(DEBUG) { + if( stop) { + System.err.println(Thread.currentThread()+": Default-EDT signal STOP X edt: "+edt); + } + } + return true; } } - final public void waitUntilIdle() { - final EventDispatchThread _edt; + @Override + final public boolean waitUntilIdle() { + final NEDT _edt; synchronized(edtLock) { _edt = edt; } - if(!_edt.isRunning() || _edt == Thread.currentThread()) { - return; + if(!_edt.isRunning || _edt == Thread.currentThread()) { + return false; } synchronized(_edt.tasks) { - while(_edt.isRunning() && _edt.tasks.size()>0) { + while(_edt.isRunning && _edt.tasks.size()>0) { try { _edt.tasks.notifyAll(); _edt.tasks.wait(); @@ -212,34 +273,39 @@ public class DefaultEDTUtil implements EDTUtil { e.printStackTrace(); } } + return true; } } - final public void waitUntilStopped() { + @Override + final public boolean waitUntilStopped() { synchronized(edtLock) { - if(edt.isRunning() && edt != Thread.currentThread() ) { - while(edt.isRunning()) { + if(edt.isRunning && edt != Thread.currentThread() ) { + while( edt.isRunning ) { try { edtLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } + return true; + } else { + return false; } } } - class EventDispatchThread extends Thread { + class NEDT extends Thread { volatile boolean shouldStop = false; volatile boolean isRunning = false; - ArrayList<RunnableTask> tasks = new ArrayList<RunnableTask>(); // one shot tasks + final ArrayList<RunnableTask> tasks = new ArrayList<RunnableTask>(); // one shot tasks - public EventDispatchThread(ThreadGroup tg, String name) { + public NEDT(ThreadGroup tg, String name) { super(tg, name); } final public boolean isRunning() { - return isRunning; + return isRunning && !shouldStop; } @Override @@ -249,24 +315,24 @@ public class DefaultEDTUtil implements EDTUtil { } private final void validateNoRecursiveLocksHold() { - if(Lock.DEBUG) { - if(LockDebugUtil.getRecursiveLockTrace().size()>0) { - LockDebugUtil.dumpRecursiveLockTrace(System.err); - throw new InternalError("XXX"); - } + if(LockDebugUtil.getRecursiveLockTrace().size()>0) { + LockDebugUtil.dumpRecursiveLockTrace(System.err); + throw new InternalError("XXX"); } } - - /** + + /** * Utilizing locking only on tasks and its execution, * not for event dispatching. */ @Override final public void run() { if(DEBUG) { - System.err.println(getName()+": EDT run() START "+ getName()); + System.err.println(getName()+": Default-EDT run() START "+ getName()); + } + if(Lock.DEBUG) { + validateNoRecursiveLocksHold(); } - validateNoRecursiveLocksHold(); RuntimeException error = null; try { do { @@ -289,13 +355,24 @@ public class DefaultEDTUtil implements EDTUtil { if(tasks.size()>0) { task = tasks.remove(0); tasks.notifyAll(); + final Object attachment = task.getAttachment(); + if( TASK_ATTACHMENT_STOP == attachment ) { + shouldStop = true; + } else if( TASK_ATTACHMENT_TEST_ERROR == attachment ) { + tasks.add(0, task); + task = null; + throw new RuntimeException("TASK_ATTACHMENT_TEST_ERROR"); + } } } if(null!=task) { task.run(); - validateNoRecursiveLocksHold(); + if(Lock.DEBUG) { + validateNoRecursiveLocksHold(); + } if(!task.hasWaiter() && null != task.getThrowable()) { // at least dump stack-trace in case nobody waits for result + System.err.println("DefaultEDT.run(): Catched exception occured on thread "+Thread.currentThread().getName()+": "+task.toString()); task.getThrowable().printStackTrace(); } } @@ -306,41 +383,30 @@ public class DefaultEDTUtil implements EDTUtil { if(t instanceof RuntimeException) { error = (RuntimeException) t; } else { - error = new RuntimeException("Within EDT", t); + error = new RuntimeException("Within Default-EDT", t); } } finally { + final String msg = getName()+": Default-EDT finished w/ "+tasks.size()+" left"; if(DEBUG) { - RunnableTask rt = ( tasks.size() > 0 ) ? tasks.get(0) : null ; - System.err.println(getName()+": EDT run() END "+ getName()+", tasks: "+tasks.size()+", "+rt+", "+error); + System.err.println(msg+", "+error); } synchronized(edtLock) { - if(null==error) { - synchronized(tasks) { - // drain remaining tasks (stop not on EDT), - // while having tasks and no previous-task, or previous-task is non final - RunnableTask task = null; - while ( ( null == task || task.getAttachment() == null ) && tasks.size() > 0 ) { - task = tasks.remove(0); - task.run(); - tasks.notifyAll(); - } - if(DEBUG) { - if(null!=task && task.getAttachment()==null) { - System.err.println(getName()+" Warning: EDT exit: Last task Not Final: "+tasks.size()+", "+task+" - "+edt); - } else if(tasks.size()>0) { - System.err.println(getName()+" Warning: EDT exit: Remaining tasks Post Final: "+tasks.size()); - } - Thread.dumpStack(); - } + int i = 0; + while( tasks.size() > 0 ) { + // notify all waiter + final String msg2 = msg+", task #"+i; + final Throwable t = null != error ? new Throwable(msg2, error) : new Throwable(msg2); + final RunnableTask rt = tasks.remove(0); + if( null != rt ) { + rt.flush(t); + i++; } } - isRunning = !shouldStop; - if(!isRunning) { - edtLock.notifyAll(); - } + isRunning = false; + edtLock.notifyAll(); } if(DEBUG) { - System.err.println(getName()+": EDT run() EXIT "+ getName()+", "+error); + System.err.println(msg+" EXIT, exception: "+error); } if(null!=error) { throw error; diff --git a/src/newt/classes/jogamp/newt/DisplayImpl.java b/src/newt/classes/jogamp/newt/DisplayImpl.java index a0bbcc264..952e611f2 100644 --- a/src/newt/classes/jogamp/newt/DisplayImpl.java +++ b/src/newt/classes/jogamp/newt/DisplayImpl.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,49 +29,236 @@ * 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.common.nio.Buffers; +import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.ReflectionUtil; 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 com.jogamp.opengl.util.PNGPixelRect; + +import java.io.IOException; +import java.net.URLConnection; +import java.nio.ByteBuffer; import java.util.ArrayList; + import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.util.PixelFormatUtil; +import javax.media.nativewindow.util.PixelRectangle; +import javax.media.nativewindow.util.PixelFormat; +import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.PointImmutable; public abstract class DisplayImpl extends Display { private static int serialno = 1; + private static final boolean pngUtilAvail; + + static { + NativeWindowFactory.addCustomShutdownHook(true /* head */, new Runnable() { + @Override + public void run() { + WindowImpl.shutdownAll(); + ScreenImpl.shutdownAll(); + DisplayImpl.shutdownAll(); + } + }); + + final ClassLoader cl = DisplayImpl.class.getClassLoader(); + pngUtilAvail = ReflectionUtil.isClassAvailable("com.jogamp.opengl.util.PNGPixelRect", cl); + } + + public static final boolean isPNGUtilAvailable() { return pngUtilAvail; } + + final ArrayList<PointerIconImpl> pointerIconList = new ArrayList<PointerIconImpl>(); + + /** Executed from EDT! */ + private void destroyAllPointerIconFromList(final long dpy) { + synchronized(pointerIconList) { + final int count = pointerIconList.size(); + for( int i=0; i < count; i++ ) { + final PointerIconImpl item = pointerIconList.get(i); + if(DEBUG) { + System.err.println("destroyAllPointerIconFromList: dpy "+toHexString(dpy)+", # "+i+"/"+count+": "+item+" @ "+getThreadName()); + } + if( null != item && item.isValid() ) { + item.destroyOnEDT(dpy); + } + } + pointerIconList.clear(); + } + } + + @Override + public PixelFormat getNativePointerIconPixelFormat() { return PixelFormat.BGRA8888; } + @Override + public boolean getNativePointerIconForceDirectNIO() { return false; } - private static Class<?> getDisplayClass(String type) - throws ClassNotFoundException + @Override + public final PointerIcon createPointerIcon(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) + throws IllegalArgumentException, IllegalStateException, IOException { - Class<?> displayClass = NewtFactory.getCustomClass(type, "Display"); - if(null==displayClass) { - if (NativeWindowFactory.TYPE_ANDROID == type) { - displayClass = Class.forName("jogamp.newt.driver.android.AndroidDisplay"); - } else if (NativeWindowFactory.TYPE_EGL == type) { - displayClass = Class.forName("jogamp.newt.driver.kd.KDDisplay"); - } else if (NativeWindowFactory.TYPE_WINDOWS == type) { - displayClass = Class.forName("jogamp.newt.driver.windows.WindowsDisplay"); - } else if (NativeWindowFactory.TYPE_MACOSX == type) { - displayClass = Class.forName("jogamp.newt.driver.macosx.MacDisplay"); - } else if (NativeWindowFactory.TYPE_X11 == type) { - displayClass = Class.forName("jogamp.newt.driver.x11.X11Display"); - } else if (NativeWindowFactory.TYPE_AWT == type) { - displayClass = Class.forName("jogamp.newt.driver.awt.AWTDisplay"); - } else { - throw new RuntimeException("Unknown display type \"" + type + "\""); + if( null == pngResource || 0 >= pngResource.resourceCount() ) { + throw new IllegalArgumentException("Null or invalid pngResource "+pngResource); + } + if( !pngUtilAvail ) { + return null; + } + final PointerIconImpl[] res = { null }; + runOnEDTIfAvail(true, new Runnable() { + public void run() { + try { + if( !DisplayImpl.this.isNativeValidAsync() ) { + throw new IllegalStateException("Display.createPointerIcon: Display invalid "+DisplayImpl.this); + } + final URLConnection urlConn = pngResource.resolve(0); + if( null == urlConn ) { + throw new IOException("Could not resolve "+pngResource.resourcePaths[0]); + } + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), + getNativePointerIconPixelFormat(), + getNativePointerIconForceDirectNIO(), + 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + final long handle = createPointerIconImplChecked(image.getPixelformat(), image.getSize().getWidth(), image.getSize().getHeight(), + image.getPixels(), hotX, hotY); + final PointImmutable hotspot = new Point(hotX, hotY); + if( DEBUG_POINTER_ICON ) { + System.err.println("createPointerIconPNG.0: "+image+", handle: "+toHexString(handle)+", hot "+hotspot); + } + if( 0 != handle ) { + res[0] = new PointerIconImpl(DisplayImpl.this, image, hotspot, handle); + if( DEBUG_POINTER_ICON ) { + System.err.println("createPointerIconPNG.0: "+res[0]); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } } ); + if( null != res[0] ) { + synchronized(pointerIconList) { + pointerIconList.add(res[0]); + } + } + return res[0]; + } + + @Override + public final PointerIcon createPointerIcon(final PixelRectangle pixelrect, final int hotX, final int hotY) + throws IllegalArgumentException, IllegalStateException + { + if( null == pixelrect ) { + throw new IllegalArgumentException("Null or pixelrect"); + } + final PixelRectangle fpixelrect; + if( getNativePointerIconPixelFormat() != pixelrect.getPixelformat() || pixelrect.isGLOriented() ) { + // conversion ! + fpixelrect = PixelFormatUtil.convert32(pixelrect, getNativePointerIconPixelFormat(), + 0 /* ddestStride */, false /* isGLOriented */, getNativePointerIconForceDirectNIO() ); + if( DEBUG_POINTER_ICON ) { + System.err.println("createPointerIconRES.0: Conversion-FMT "+pixelrect+" -> "+fpixelrect); + } + } else if( getNativePointerIconForceDirectNIO() && !Buffers.isDirect(pixelrect.getPixels()) ) { + // transfer to direct NIO + final ByteBuffer sBB = pixelrect.getPixels(); + final ByteBuffer dBB = Buffers.newDirectByteBuffer(sBB.array(), sBB.arrayOffset()); + fpixelrect = new PixelRectangle.GenericPixelRect(pixelrect.getPixelformat(), pixelrect.getSize(), pixelrect.getStride(), pixelrect.isGLOriented(), dBB); + if( DEBUG_POINTER_ICON ) { + System.err.println("createPointerIconRES.0: Conversion-NIO "+pixelrect+" -> "+fpixelrect); + } + } else { + fpixelrect = pixelrect; + if( DEBUG_POINTER_ICON ) { + System.err.println("createPointerIconRES.0: No conversion "+fpixelrect); } } + final PointerIconImpl[] res = { null }; + runOnEDTIfAvail(true, new Runnable() { + public void run() { + try { + if( !DisplayImpl.this.isNativeValidAsync() ) { + throw new IllegalStateException("Display.createPointerIcon: Display invalid "+DisplayImpl.this); + } + if( null != fpixelrect ) { + final long handle = createPointerIconImplChecked(fpixelrect.getPixelformat(), + fpixelrect.getSize().getWidth(), + fpixelrect.getSize().getHeight(), + fpixelrect.getPixels(), hotX, hotY); + if( 0 != handle ) { + res[0] = new PointerIconImpl(DisplayImpl.this, fpixelrect, new Point(hotX, hotY), handle); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } } ); + if( null != res[0] ) { + synchronized(pointerIconList) { + pointerIconList.add(res[0]); + } + } + return res[0]; + } + + /** + * Executed from EDT! + * + * @param pixelformat the <code>pixels</code>'s format + * @param width the <code>pixels</code>'s width + * @param height the <code>pixels</code>'s height + * @param pixels the <code>pixels</code> + * @param hotX the PointerIcon's hot-spot x-coord + * @param hotY the PointerIcon's hot-spot x-coord + * @return if successful a valid handle (not null), otherwise null. + */ + protected final long createPointerIconImplChecked(PixelFormat pixelformat, int width, int height, final ByteBuffer pixels, final int hotX, final int hotY) { + if( getNativePointerIconPixelFormat() != pixelformat ) { + throw new IllegalArgumentException("Pixelformat no "+getNativePointerIconPixelFormat()+", but "+pixelformat); + } + if( getNativePointerIconForceDirectNIO() && !Buffers.isDirect(pixels) ) { + throw new IllegalArgumentException("pixel buffer is not direct "+pixels); + } + return createPointerIconImpl(pixelformat, width, height, pixels, hotX, hotY); + } + + /** + * Executed from EDT! + * + * @param pixelformat the <code>pixels</code>'s format + * @param width the <code>pixels</code>'s width + * @param height the <code>pixels</code>'s height + * @param pixels the <code>pixels</code> + * @param hotX the PointerIcon's hot-spot x-coord + * @param hotY the PointerIcon's hot-spot x-coord + * @return if successful a valid handle (not null), otherwise null. + */ + protected long createPointerIconImpl(PixelFormat pixelformat, int width, int height, final ByteBuffer pixels, final int hotX, final int hotY) { + return 0; + } + + /** Executed from EDT! */ + protected void destroyPointerIconImpl(final long displayHandle, long piHandle) { } + + /** 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+".Display>"); + throw new ClassNotFoundException("Failed to find NEWT Display Class <"+type+".DisplayDriver>"); } return displayClass; } @@ -79,12 +266,12 @@ public abstract class DisplayImpl extends Display { /** Make sure to reuse a Display with the same name */ public static Display create(String type, String name, final long handle, boolean reuse) { try { - Class<?> displayClass = getDisplayClass(type); - DisplayImpl display = (DisplayImpl) displayClass.newInstance(); + final Class<?> displayClass = getDisplayClass(type); + final DisplayImpl display = (DisplayImpl) displayClass.newInstance(); name = display.validateDisplayName(name, handle); synchronized(displayList) { if(reuse) { - Display display0 = Display.getLastDisplayOf(type, name, -1); + final Display display0 = Display.getLastDisplayOf(type, name, -1, true /* shared only */); if(null != display0) { if(DEBUG) { System.err.println("Display.create() REUSE: "+display0+" "+getThreadName()); @@ -92,16 +279,17 @@ public abstract class DisplayImpl extends Display { return display0; } } + display.exclusive = !reuse; display.name = name; display.type=type; - display.destroyWhenUnused=false; display.refCount=0; display.id = serialno++; display.fqname = getFQName(display.type, display.name, display.id); display.hashCode = display.fqname.hashCode(); - displayList.add(display); + display.setEDTUtil( display.edtUtil ); // device's default if EDT is used, or null + Display.addDisplay2List(display); } - display.createEDTUtil(); + if(DEBUG) { System.err.println("Display.create() NEW: "+display+" "+getThreadName()); } @@ -132,118 +320,211 @@ public abstract class DisplayImpl extends Display { return true; } + @Override public int hashCode() { return hashCode; } - public synchronized final void createNative() + @Override + public synchronized final void createNative() throws NativeWindowException { - if(null==aDevice) { + if( null == aDevice ) { if(DEBUG) { System.err.println("Display.createNative() START ("+getThreadName()+", "+this+")"); } final DisplayImpl f_dpy = this; try { runOnEDTIfAvail(true, new Runnable() { + @Override public void run() { f_dpy.createNativeImpl(); }}); } catch (Throwable t) { throw new NativeWindowException(t); } - if(null==aDevice) { + if( null == aDevice ) { throw new NativeWindowException("Display.createNative() failed to instanciate an AbstractGraphicsDevice"); } - if(DEBUG) { - System.err.println("Display.createNative() END ("+getThreadName()+", "+this+")"); - } synchronized(displayList) { displaysActive++; + if(DEBUG) { + System.err.println("Display.createNative() END ("+getThreadName()+", "+this+", active "+displaysActive+")"); + } } } } - protected boolean shallRunOnEDT() { - return true; - } - - protected void createEDTUtil() { + protected EDTUtil createEDTUtil() { + final EDTUtil def; if(NewtFactory.useEDT()) { - edtUtil = new DefaultEDTUtil(Thread.currentThread().getThreadGroup(), "Display-"+getFQName(), dispatchMessagesRunnable); + def = new DefaultEDTUtil(Thread.currentThread().getThreadGroup(), "Display-"+getFQName(), dispatchMessagesRunnable); if(DEBUG) { - System.err.println("Display.createNative("+getFQName()+") Create EDTUtil: "+edtUtil.getClass().getName()); + System.err.println("Display.createEDTUtil("+getFQName()+"): "+def.getClass().getName()); } + } else { + def = null; } + return def; } + @Override + public synchronized EDTUtil setEDTUtil(final EDTUtil usrEDTUtil) { + final EDTUtil oldEDTUtil = edtUtil; + if( null != usrEDTUtil && usrEDTUtil == oldEDTUtil ) { + if( DEBUG ) { + System.err.println("Display.setEDTUtil: "+usrEDTUtil+" - keep!"); + } + return oldEDTUtil; + } + if(DEBUG) { + final String msg = ( null == usrEDTUtil ) ? "default" : "custom"; + System.err.println("Display.setEDTUtil("+msg+"): "+oldEDTUtil+" -> "+usrEDTUtil); + } + stopEDT( oldEDTUtil, null ); + edtUtil = ( null == usrEDTUtil ) ? createEDTUtil() : usrEDTUtil; + return oldEDTUtil; + } + + @Override public final EDTUtil getEDTUtil() { return edtUtil; } - private void stopEDT(final Runnable task) { - if( shallRunOnEDT() && null!=edtUtil ) { - edtUtil.invokeStop(task); - } else { + 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) { - if( shallRunOnEDT() && null!=edtUtil && !edtUtil.isCurrentThreadEDT()) { - edtUtil.invoke(wait, task); - } else { - task.run(); + final EDTUtil _edtUtil = edtUtil; + if( !_edtUtil.isRunning() ) { // start EDT if not running yet + synchronized( this ) { + if( !_edtUtil.isRunning() ) { // // volatile dbl-checked-locking OK + if( DEBUG ) { + System.err.println("Info: EDT start "+Thread.currentThread().getName()+", "+this); + Thread.dumpStack(); + } + _edtUtil.start(); + } + } + } + if( !_edtUtil.isCurrentThreadEDT() ) { + if( _edtUtil.invoke(wait, task) ) { + return; // done + } + if( DEBUG ) { + System.err.println("Warning: invoke(wait "+wait+", ..) on EDT failed .. invoke on current thread "+Thread.currentThread().getName()); + Thread.dumpStack(); + } } + task.run(); } - public boolean validateEDT() { - if(0==refCount && null==aDevice && null != edtUtil && edtUtil.isRunning()) { - stopEDT( new Runnable() { - public void run() { - // nop + @Override + public boolean validateEDTStopped() { + 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; + } } - } ); - edtUtil.waitUntilStopped(); - edtUtil.reset(); - return true; + } } return false; } + @Override public synchronized final void destroy() { if(DEBUG) { dumpDisplayList("Display.destroy("+getFQName()+") BEGIN"); } synchronized(displayList) { - displayList.remove(this); if(0 < displaysActive) { displaysActive--; } + if(DEBUG) { + System.err.println("Display.destroy(): "+this+", active "+displaysActive+" "+getThreadName()); + } } - if(DEBUG) { - System.err.println("Display.destroy(): "+this+" "+getThreadName()); - } - final AbstractGraphicsDevice f_aDevice = aDevice; final DisplayImpl f_dpy = this; - stopEDT( new Runnable() { + final AbstractGraphicsDevice f_aDevice = aDevice; + aDevice = null; + refCount=0; + stopEDT( edtUtil, new Runnable() { // blocks! + @Override public void run() { if ( null != f_aDevice ) { - f_dpy.closeNativeImpl(); + f_dpy.destroyAllPointerIconFromList(f_aDevice.getHandle()); + f_dpy.closeNativeImpl(f_aDevice); } } } ); - if(null!=edtUtil) { - edtUtil.waitUntilStopped(); - edtUtil.reset(); - } - aDevice = null; - refCount=0; 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; i<dCount && displayList.size()>0; 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() { + @Override + public void run() { + if ( null != d.getGraphicsDevice() ) { + d.destroyAllPointerIconFromList(f_aDevice.getHandle()); + 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(); + } + } + } + } + + @Override public synchronized final int addReference() { if(DEBUG) { System.err.println("Display.addReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount+1)); @@ -258,6 +539,7 @@ public abstract class DisplayImpl extends Display { } + @Override public synchronized final int removeReference() { if(DEBUG) { System.err.println("Display.removeReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount-1)); @@ -270,29 +552,39 @@ public abstract class DisplayImpl extends Display { return refCount; } + @Override public synchronized final int getReferenceCount() { return refCount; } protected abstract void createNativeImpl(); - protected abstract void closeNativeImpl(); + 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) { @@ -311,9 +603,10 @@ public abstract class DisplayImpl extends Display { sb.append(name); sb.append("-"); sb.append(id); - return sb.toString().intern(); + return sb.toString(); } + @Override public final long getHandle() { if(null!=aDevice) { return aDevice.getHandle(); @@ -321,65 +614,98 @@ public abstract class DisplayImpl extends Display { return 0; } + @Override public final AbstractGraphicsDevice getGraphicsDevice() { return aDevice; } + @Override public synchronized final boolean isNativeValid() { return null != aDevice; } + protected final boolean isNativeValidAsync() { + return null != aDevice; + } + @Override public boolean isEDTRunning() { - if(null!=edtUtil) { - return edtUtil.isRunning(); + final EDTUtil _edtUtil = edtUtil; + if( null != _edtUtil ) { + return _edtUtil.isRunning(); } return false; } @Override public String toString() { - return "NEWT-Display["+getFQName()+", refCount "+refCount+", hasEDT "+(null!=edtUtil)+", edtRunning "+isEDTRunning()+", "+aDevice+"]"; + 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 final Object eventsLock = new Object(); private ArrayList<NEWTEventTask> events = new ArrayList<NEWTEventTask>(); private volatile boolean haveEvents = false; - class DispatchMessagesRunnable implements Runnable { + final protected Runnable dispatchMessagesRunnable = new Runnable() { + @Override 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; } } - DispatchMessagesRunnable dispatchMessagesRunnable = new DispatchMessagesRunnable(); final void dispatchMessage(final NEWTEventTask eventTask) { - NEWTEvent event = eventTask.get(); - if(null == event) { - // Ooops ? - System.err.println("Warning: event of eventTask is NULL"); - Thread.dumpStack(); - return; - } - Object source = event.getSource(); - if(source instanceof NEWTEventConsumer) { - NEWTEventConsumer consumer = (NEWTEventConsumer) source ; - if(!consumer.consumeEvent(event)) { - // enqueue for later execution - enqueueEvent(false, event); + 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; } - } else { - throw new RuntimeException("Event source not NEWT: "+source.getClass().getName()+", "+source); } - eventTask.notifyIssuer(); + eventTask.notifyCaller(); } - + + @Override public void dispatchMessages() { // System.err.println("Display.dispatchMessages() 0 "+this+" "+getThreadName()); - if(0==refCount || // no screens + if(0==refCount || // no screens null==getGraphicsDevice() // no native device - ) + ) { return; } @@ -408,23 +734,24 @@ public abstract class DisplayImpl extends Display { } public void enqueueEvent(boolean wait, NEWTEvent e) { - if(!isEDTRunning()) { + final EDTUtil _edtUtil = edtUtil; + if( !_edtUtil.isRunning() ) { // oops .. we are already dead if(DEBUG) { - Throwable t = new Throwable("Warning: EDT already stopped: wait:="+wait+", "+e); - t.printStackTrace(); + System.err.println("Warning: EDT already stopped: wait:="+wait+", "+e); + Thread.dumpStack(); } return; } - - // can't wait if we are on EDT -> consume right away - if(wait && edtUtil.isCurrentThreadEDT()) { - dispatchMessage(new NEWTEventTask(e, null)); + + // can't wait if we are on EDT or NEDT -> consume right away + if(wait && _edtUtil.isCurrentThreadEDTorNEDT() ) { + dispatchMessage(e); return; } - - Object lock = new Object(); - NEWTEventTask eTask = new NEWTEventTask(e, wait?lock:null); + + final Object lock = new Object(); + final NEWTEventTask eTask = new NEWTEventTask(e, wait?lock:null); synchronized(lock) { synchronized(eventsLock) { events.add(eTask); @@ -437,36 +764,42 @@ public abstract class DisplayImpl extends Display { } catch (InterruptedException ie) { throw new RuntimeException(ie); } + if( null != eTask.getException() ) { + throw eTask.getException(); + } } } } public interface DisplayRunnable<T> { T run(long dpy); - } - public final <T> T runWithLockedDisplayHandle(DisplayRunnable<T> action) { - final AbstractGraphicsDevice aDevice = getGraphicsDevice(); - if(null == aDevice) { - throw new RuntimeException("null device - not initialized: "+this); - } + } + public static final <T> T runWithLockedDevice(AbstractGraphicsDevice device, DisplayRunnable<T> action) { T res; - aDevice.lock(); + device.lock(); try { - res = action.run(aDevice.getHandle()); + res = action.run(device.getHandle()); } finally { - aDevice.unlock(); + device.unlock(); } return res; } - - protected EDTUtil edtUtil = null; + public final <T> T runWithLockedDisplayDevice(DisplayRunnable<T> 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 destroyWhenUnused; + protected boolean exclusive; // do not share this display, uses NullLock! protected AbstractGraphicsDevice aDevice; } diff --git a/src/newt/classes/jogamp/newt/MonitorDeviceImpl.java b/src/newt/classes/jogamp/newt/MonitorDeviceImpl.java new file mode 100644 index 000000000..9e10879c4 --- /dev/null +++ b/src/newt/classes/jogamp/newt/MonitorDeviceImpl.java @@ -0,0 +1,147 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt; + +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.Rectangle; + +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; +import com.jogamp.newt.Screen; + +public class MonitorDeviceImpl extends MonitorDevice { + + public MonitorDeviceImpl(ScreenImpl screen, int nativeId, DimensionImmutable sizeMM, Rectangle viewport, MonitorMode currentMode, ArrayHashSet<MonitorMode> supportedModes) { + super(screen, nativeId, sizeMM, viewport, currentMode, supportedModes); + } + + @Override + public final MonitorMode queryCurrentMode() { + final ScreenImpl screenImpl = (ScreenImpl)screen; + final ScreenMonitorState sms = screenImpl.getScreenMonitorStatus(true); + sms.lock(); + try { + final MonitorMode mm0 = screenImpl.queryCurrentMonitorModeIntern(this); + if(null == mm0) { + throw new InternalError("getCurrentMonitorModeIntern() == null"); + } + MonitorMode mmU = supportedModes.get(mm0); // unified instance + if( null == mmU ) { + // add new mode avoiding exception! + mmU = sms.getMonitorModes().getOrAdd(mm0); + mmU = supportedModes.getOrAdd(mmU); + if( Screen.DEBUG ) { + System.err.println("Adding new mode: "+mm0+" -> "+mmU); + } + } + // if mode has changed somehow, update it .. + if( getCurrentMode().hashCode() != mmU.hashCode() ) { + setCurrentModeValue(mmU); + sms.fireMonitorModeChanged(this, mmU, true); + } + return mmU; + } finally { + sms.unlock(); + } + } + + @Override + public final boolean setCurrentMode(MonitorMode mode) { + if(Screen.DEBUG) { + System.err.println("Screen.setCurrentMode.0: "+this+" -> "+mode); + } + final ScreenImpl screenImpl = (ScreenImpl)screen; + final ScreenMonitorState sms = screenImpl.getScreenMonitorStatus(true); + sms.lock(); + try { + final MonitorMode mmC = queryCurrentMode(); + final MonitorMode mmU = supportedModes.get(mode); // unify via value hash + if( null == mmU ) { + throw new IllegalArgumentException("Given mode not in set of modes. Current mode "+mode+", "+this); + } + if( mmU.equals( mmC ) ) { + if(Screen.DEBUG) { + System.err.println("Screen.setCurrentMode: 0.0 is-current (skip) "+mmU+" == "+mmC); + } + return true; + } + final long tStart; + if(Screen.DEBUG) { + tStart = System.currentTimeMillis(); + } else { + tStart = 0; + } + + sms.fireMonitorModeChangeNotify(this, mmU); + if(Screen.DEBUG) { + System.err.println("Screen.setCurrentMode ("+(System.currentTimeMillis()-tStart)+"ms): fireModeChangeNotify() "+mmU); + } + + boolean success = screenImpl.setCurrentMonitorModeImpl(this, mmU); + if(success) { + if(Screen.DEBUG) { + System.err.println("Screen.setCurrentMode ("+(System.currentTimeMillis()-tStart)+"ms): setCurrentModeImpl() "+mmU+", success(1): "+success); + } + } else { + // 2nd attempt validate! + final MonitorMode queriedCurrent = queryCurrentMode(); // may fireModeChanged(..) if successful and differs! + success = queriedCurrent.hashCode() == mmU.hashCode() ; + if(Screen.DEBUG) { + System.err.println("Screen.setCurrentMode.2: queried "+queriedCurrent); + System.err.println("Screen.setCurrentMode ("+(System.currentTimeMillis()-tStart)+"ms): setCurrentModeImpl() "+mmU+", success(2): "+success); + } + } + if( success ) { + setCurrentModeValue(mmU); + modeChanged = !isOriginalMode(); + } + sms.fireMonitorModeChanged(this, mmU, success); + if(Screen.DEBUG) { + System.err.println("Screen.setCurrentMode ("+(System.currentTimeMillis()-tStart)+"ms): X.X: success "+success+": "+this); + } + return success; + } finally { + sms.unlock(); + } + } + + private final void setCurrentModeValue(MonitorMode currentMode) { + this.currentMode = currentMode; + } + + /* pp */ final void setViewportValue(Rectangle viewport) { + this.viewport = viewport; + } + + /* pp */ ArrayHashSet<MonitorMode> getSupportedModesImpl() { + return supportedModes; + } + +} diff --git a/src/newt/classes/jogamp/newt/MonitorModeProps.java b/src/newt/classes/jogamp/newt/MonitorModeProps.java new file mode 100644 index 000000000..9d8f4919c --- /dev/null +++ b/src/newt/classes/jogamp/newt/MonitorModeProps.java @@ -0,0 +1,355 @@ +/** + * Copyright 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt; + +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +import java.util.List; +import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.Rectangle; +import javax.media.nativewindow.util.SurfaceSize; + +import jogamp.newt.MonitorDeviceImpl; +import jogamp.newt.ScreenImpl; + +/** + * Encodes and decodes {@link MonitorMode} and {@link MonitorDevice} properties. + */ +public class MonitorModeProps { + /** WARNING: must be synchronized with ScreenMode.h, native implementation + * 2: width, height + */ + public static final int NUM_RESOLUTION_PROPERTIES = 2; + + /** WARNING: must be synchronized with ScreenMode.h, native implementation + * 1: bpp + */ + public static final int NUM_SURFACE_SIZE_PROPERTIES = 1; + + /** WARNING: must be synchronized with ScreenMode.h, native implementation + * 2: refresh-rate (Hz*100), flags + */ + public static final int NUM_SIZEANDRATE_PROPERTIES = 2; + + /** WARNING: must be synchronized with ScreenMode.h, native implementation + * 2: id, rotation + */ + public static final int NUM_MONITOR_MODE_PROPERTIES = 2; + + /** WARNING: must be synchronized with ScreenMode.h, native implementation + * count + all the above + */ + public static final int NUM_MONITOR_MODE_PROPERTIES_ALL = 8; + + public static final int IDX_MONITOR_MODE_BPP = 1 // count + + MonitorModeProps.NUM_RESOLUTION_PROPERTIES + ; + public static final int IDX_MONITOR_MODE_ROT = 1 // count + + MonitorModeProps.NUM_RESOLUTION_PROPERTIES + + MonitorModeProps.NUM_SURFACE_SIZE_PROPERTIES + + MonitorModeProps.NUM_SIZEANDRATE_PROPERTIES + + 1 // id of MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES + ; + + /** WARNING: must be synchronized with ScreenMode.h, native implementation + * 10: count + id, ScreenSizeMM[width, height], rotated Viewport[x, y, width, height], currentMonitorModeId, rotation, supportedModeId+ + */ + public static final int MIN_MONITOR_DEVICE_PROPERTIES = 11; + + public static final int IDX_MONITOR_DEVICE_VIEWPORT = 1 // count + + 1 // native mode + + MonitorModeProps.NUM_RESOLUTION_PROPERTIES // sizeMM + ; + + public static class Cache { + public final ArrayHashSet<DimensionImmutable> resolutions = new ArrayHashSet<DimensionImmutable>(); + public final ArrayHashSet<SurfaceSize> surfaceSizes = new ArrayHashSet<SurfaceSize>(); + public final ArrayHashSet<MonitorMode.SizeAndRRate> sizeAndRates = new ArrayHashSet<MonitorMode.SizeAndRRate>(); + public final ArrayHashSet<MonitorMode> monitorModes = new ArrayHashSet<MonitorMode>(); + public final ArrayHashSet<MonitorDevice> monitorDevices = new ArrayHashSet<MonitorDevice>(); + } + + /** WARNING: must be synchronized with ScreenMode.h, native implementation */ + private static DimensionImmutable streamInResolution(int[] resolutionProperties, int offset) { + Dimension resolution = new Dimension(resolutionProperties[offset++], resolutionProperties[offset++]); + return resolution; + } + + /** WARNING: must be synchronized with ScreenMode.h, native implementation */ + private static SurfaceSize streamInSurfaceSize(DimensionImmutable resolution, int[] sizeProperties, int offset) { + SurfaceSize surfaceSize = new SurfaceSize(resolution, sizeProperties[offset++]); + return surfaceSize; + } + + /** WARNING: must be synchronized with ScreenMode.h, native implementation */ + private static MonitorMode.SizeAndRRate streamInSizeAndRRate(SurfaceSize surfaceSize, int[] sizeAndRRateProperties, int offset) { + final float refreshRate = sizeAndRRateProperties[offset++]/100.0f; + final int flags = sizeAndRRateProperties[offset++]; + return new MonitorMode.SizeAndRRate(surfaceSize, refreshRate, flags); + } + + /** WARNING: must be synchronized with ScreenMode.h, native implementation */ + private static MonitorMode streamInMonitorMode0(MonitorMode.SizeAndRRate sizeAndRate, int[] modeProperties, int offset) { + final int id = modeProperties[offset++]; + final int rotation = modeProperties[offset++]; + return new MonitorMode(id, sizeAndRate, rotation); + } + + /** + * WARNING: must be synchronized with ScreenMode.h, native implementation + * + * @param mode_idx if not null and cache is given, returns the index of resulting {@link MonitorMode} within {@link Cache#monitorModes}. + * @param cache optional hash arrays of unique {@link MonitorMode} components and {@link MonitorDevice}s, allowing to avoid duplicates + * @param modeProperties the input data + * @param offset the offset to the input data + * @return {@link MonitorMode} of the identical (old or new) element in {@link Cache#monitorModes}, + * matching the input <code>modeProperties</code>, or null if input could not be processed. + */ + public static MonitorMode streamInMonitorMode(int[] mode_idx, Cache cache, + int[] modeProperties, int offset) { + final int count = modeProperties[offset]; + if(NUM_MONITOR_MODE_PROPERTIES_ALL != count) { + throw new RuntimeException("property count should be "+NUM_MONITOR_MODE_PROPERTIES_ALL+", but is "+count+", len "+(modeProperties.length-offset)); + } + if(NUM_MONITOR_MODE_PROPERTIES_ALL > modeProperties.length-offset) { + throw new RuntimeException("properties array too short, should be >= "+NUM_MONITOR_MODE_PROPERTIES_ALL+", is "+(modeProperties.length-offset)); + } + offset++; + DimensionImmutable resolution = MonitorModeProps.streamInResolution(modeProperties, offset); + offset += MonitorModeProps.NUM_RESOLUTION_PROPERTIES; + if(null!=cache) { + resolution = cache.resolutions.getOrAdd(resolution); + } + + SurfaceSize surfaceSize = MonitorModeProps.streamInSurfaceSize(resolution, modeProperties, offset); + offset += MonitorModeProps.NUM_SURFACE_SIZE_PROPERTIES; + if(null!=cache) { + surfaceSize = cache.surfaceSizes.getOrAdd(surfaceSize); + } + + MonitorMode.SizeAndRRate sizeAndRate = MonitorModeProps.streamInSizeAndRRate(surfaceSize, modeProperties, offset); + offset += MonitorModeProps.NUM_SIZEANDRATE_PROPERTIES; + if(null!=cache) { + sizeAndRate = cache.sizeAndRates.getOrAdd(sizeAndRate); + } + + MonitorMode monitorMode = MonitorModeProps.streamInMonitorMode0(sizeAndRate, modeProperties, offset); + if(null!=cache) { + monitorMode = cache.monitorModes.getOrAdd(monitorMode); + } + if( null != mode_idx && null!=cache) { + int _modeIdx = cache.monitorModes.indexOf(monitorMode); + if( 0 > _modeIdx ) { + throw new InternalError("Invalid index of current unified mode "+monitorMode); + } + mode_idx[0] = _modeIdx; + } + return monitorMode; + } + + /** WARNING: must be synchronized with ScreenMode.h, native implementation */ + public static int[] streamOutMonitorMode (MonitorMode monitorMode) { + int[] data = new int[NUM_MONITOR_MODE_PROPERTIES_ALL]; + int idx=0; + data[idx++] = NUM_MONITOR_MODE_PROPERTIES_ALL; + data[idx++] = monitorMode.getSurfaceSize().getResolution().getWidth(); + data[idx++] = monitorMode.getSurfaceSize().getResolution().getHeight(); + data[idx++] = monitorMode.getSurfaceSize().getBitsPerPixel(); + data[idx++] = (int)(monitorMode.getRefreshRate()*100.0f); // Hz*100 + data[idx++] = monitorMode.getFlags(); + data[idx++] = monitorMode.getId(); + data[idx++] = monitorMode.getRotation(); + if(NUM_MONITOR_MODE_PROPERTIES_ALL != idx) { + throw new InternalError("wrong number of attributes: got "+idx+" != should "+NUM_MONITOR_MODE_PROPERTIES_ALL); + } + return data; + } + + /** + * WARNING: must be synchronized with ScreenMode.h, native implementation + * <p> + * Note: This variant only works for impl. w/ a unique mode key pair <i>modeId, rotation</i>. + * </p> + * @param mode_idx if not null, returns the index of resulting {@link MonitorDevice} within {@link Cache#monitorDevices}. + * @param cache hash arrays of unique {@link MonitorMode} components and {@link MonitorDevice}s, allowing to avoid duplicates + * @param modeProperties the input data + * @param offset the offset to the input data + * @return {@link MonitorDevice} of the identical (old or new) element in {@link Cache#monitorDevices}, + * matching the input <code>modeProperties</code>, or null if input could not be processed. + */ + public static MonitorDevice streamInMonitorDevice(int[] monitor_idx, Cache cache, ScreenImpl screen, int[] monitorProperties, int offset) { + // min 11: count, id, ScreenSizeMM[width, height], Viewport[x, y, width, height], currentMonitorModeId, rotation, supportedModeId+ + final int count = monitorProperties[offset]; + if(MIN_MONITOR_DEVICE_PROPERTIES > count) { + throw new RuntimeException("property count should be >= "+MIN_MONITOR_DEVICE_PROPERTIES+", but is "+count+", len "+(monitorProperties.length-offset)); + } + if(MIN_MONITOR_DEVICE_PROPERTIES > monitorProperties.length-offset) { + throw new RuntimeException("properties array too short (min), should be >= "+MIN_MONITOR_DEVICE_PROPERTIES+", is "+(monitorProperties.length-offset)); + } + if(count > monitorProperties.length-offset) { + throw new RuntimeException("properties array too short (count), should be >= "+count+", is "+(monitorProperties.length-offset)); + } + final int limit = offset + count; + offset++; + final List<MonitorMode> allMonitorModes = cache.monitorModes.getData(); + final int id = monitorProperties[offset++]; + final DimensionImmutable sizeMM = streamInResolution(monitorProperties, offset); offset+=NUM_RESOLUTION_PROPERTIES; + final Rectangle viewport = new Rectangle(monitorProperties[offset++], monitorProperties[offset++], monitorProperties[offset++], monitorProperties[offset++]); + final MonitorMode currentMode; + { + final int modeId = monitorProperties[offset++]; + final int rotation = monitorProperties[offset++]; + currentMode = getByNativeIdAndRotation(allMonitorModes, modeId, rotation); + } + final ArrayHashSet<MonitorMode> supportedModes = new ArrayHashSet<MonitorMode>(); + while( offset < limit ) { + final int modeId = monitorProperties[offset++]; + for (int i=0; i<allMonitorModes.size(); i++) { + final MonitorMode mode = allMonitorModes.get(i); + if( mode.getId() == modeId ) { + supportedModes.add(mode); + } + } + } + MonitorDevice monitorDevice = new MonitorDeviceImpl(screen, id, sizeMM, viewport, currentMode, supportedModes); + if(null!=cache) { + monitorDevice = cache.monitorDevices.getOrAdd(monitorDevice); + } + if( null != monitor_idx ) { + int _monitorIdx = cache.monitorDevices.indexOf(monitorDevice); + if( 0 > _monitorIdx ) { + throw new InternalError("Invalid index of current unified mode "+monitorDevice); + } + monitor_idx[0] = _monitorIdx; + } + return monitorDevice; + } + private static MonitorMode getByNativeIdAndRotation(List<MonitorMode> monitorModes, int modeId, int rotation) { + if( null!=monitorModes && monitorModes.size()>0 ) { + for (int i=0; i<monitorModes.size(); i++) { + final MonitorMode mode = monitorModes.get(i); + if( mode.getId() == modeId && mode.getRotation() == rotation ) { + return mode; + } + } + } + return null; + } + + /** + * WARNING: must be synchronized with ScreenMode.h, native implementation + * <p> + * This variant expects <code>count</code> to be <code>{@link MIN_MONITOR_DEVICE_PROPERTIES} - 1 - {@link NUM_MONITOR_MODE_PROPERTIES}</code>, + * due to lack of supported mode and current mode. + * </p> + * + * @param mode_idx if not null, returns the index of resulting {@link MonitorDevice} within {@link Cache#monitorDevices}. + * @param cache hash arrays of unique {@link MonitorMode} components and {@link MonitorDevice}s, allowing to avoid duplicates + * @param supportedModes pre-assembled list of supported {@link MonitorMode}s from cache. + * @param currentMode pre-fetched current {@link MonitorMode}s from cache. + * @param modeProperties the input data minus supported modes! + * @param offset the offset to the input data + * @return {@link MonitorDevice} of the identical (old or new) element in {@link Cache#monitorDevices}, + * matching the input <code>modeProperties</code>, or null if input could not be processed. + */ + public static MonitorDevice streamInMonitorDevice(int[] monitor_idx, Cache cache, ScreenImpl screen, ArrayHashSet<MonitorMode> supportedModes, MonitorMode currentMode, int[] monitorProperties, int offset) { + // min 11: count, id, ScreenSizeMM[width, height], Viewport[x, y, width, height], currentMonitorModeId, rotation, supportedModeId+ + final int count = monitorProperties[offset]; + if(MIN_MONITOR_DEVICE_PROPERTIES - 1 - NUM_MONITOR_MODE_PROPERTIES != count) { + throw new RuntimeException("property count should be == "+(MIN_MONITOR_DEVICE_PROPERTIES-1-NUM_MONITOR_MODE_PROPERTIES)+", but is "+count+", len "+(monitorProperties.length-offset)); + } + if(MIN_MONITOR_DEVICE_PROPERTIES - 1 - NUM_MONITOR_MODE_PROPERTIES > monitorProperties.length-offset) { + throw new RuntimeException("properties array too short (min), should be >= "+(MIN_MONITOR_DEVICE_PROPERTIES-1-NUM_MONITOR_MODE_PROPERTIES)+", is "+(monitorProperties.length-offset)); + } + if(count > monitorProperties.length-offset) { + throw new RuntimeException("properties array too short (count), should be >= "+count+", is "+(monitorProperties.length-offset)); + } + offset++; + final int id = monitorProperties[offset++]; + final DimensionImmutable sizeMM = streamInResolution(monitorProperties, offset); offset+=NUM_RESOLUTION_PROPERTIES; + final Rectangle viewport = new Rectangle(monitorProperties[offset++], monitorProperties[offset++], monitorProperties[offset++], monitorProperties[offset++]); + MonitorDevice monitorDevice = new MonitorDeviceImpl(screen, id, sizeMM, viewport, currentMode, supportedModes); + if(null!=cache) { + monitorDevice = cache.monitorDevices.getOrAdd(monitorDevice); + } + if( null != monitor_idx ) { + int _monitorIdx = cache.monitorDevices.indexOf(monitorDevice); + if( 0 > _monitorIdx ) { + throw new InternalError("Invalid index of current unified mode "+monitorDevice); + } + monitor_idx[0] = _monitorIdx; + } + return monitorDevice; + } + + /** WARNING: must be synchronized with ScreenMode.h, native implementation */ + public static int[] streamOutMonitorDevice (MonitorDevice monitorDevice) { + // min 11: count, id, ScreenSizeMM[width, height], Viewport[x, y, width, height], currentMonitorModeId, rotation, supportedModeId+ + int supportedModeCount = monitorDevice.getSupportedModes().size(); + if( 0 == supportedModeCount ) { + throw new RuntimeException("no supported modes: "+monitorDevice); + } + int[] data = new int[MIN_MONITOR_DEVICE_PROPERTIES + supportedModeCount - 1]; + int idx=0; + data[idx++] = data.length; + data[idx++] = monitorDevice.getId(); + data[idx++] = monitorDevice.getSizeMM().getWidth(); + data[idx++] = monitorDevice.getSizeMM().getHeight(); + data[idx++] = monitorDevice.getViewport().getX(); + data[idx++] = monitorDevice.getViewport().getY(); + data[idx++] = monitorDevice.getViewport().getWidth(); + data[idx++] = monitorDevice.getViewport().getHeight(); + data[idx++] = monitorDevice.getCurrentMode().getId(); + data[idx++] = monitorDevice.getCurrentMode().getRotation(); + final List<MonitorMode> supportedModes = monitorDevice.getSupportedModes(); + for(int i=0; i<supportedModes.size(); i++) { + data[idx++] = supportedModes.get(i).getId(); + } + if(data.length != idx) { + throw new InternalError("wrong number of attributes: got "+idx+" != should "+data.length); + } + return data; + } + + public final void swapRotatePair(int rotation, int[] pairs, int offset, int numPairs) { + if( MonitorMode.ROTATE_0 == rotation || MonitorMode.ROTATE_180 == rotation ) { + // nop + return; + } + for(int i=0; i<numPairs; i++, offset+=2) { + final int tmp = pairs[offset]; + pairs[offset] = pairs[offset+1]; + pairs[offset+1] = tmp; + } + } + +} diff --git a/src/newt/classes/jogamp/newt/NEWTJNILibLoader.java b/src/newt/classes/jogamp/newt/NEWTJNILibLoader.java index 2db9d8d05..9364ada30 100644 --- a/src/newt/classes/jogamp/newt/NEWTJNILibLoader.java +++ b/src/newt/classes/jogamp/newt/NEWTJNILibLoader.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 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 @@ -28,11 +28,11 @@ * 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. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -42,24 +42,24 @@ package jogamp.newt; import java.security.AccessController; import java.security.PrivilegedAction; +import jogamp.nativewindow.NWJNILibLoader; + import com.jogamp.common.jvm.JNILibLoaderBase; import com.jogamp.common.os.Platform; import com.jogamp.common.util.cache.TempJarCache; public class NEWTJNILibLoader extends JNILibLoaderBase { - - public static void loadNEWT() { - AccessController.doPrivileged(new PrivilegedAction<Object>() { - public Object run() { - Platform.initSingleton(); - final String libName = "newt"; - if(TempJarCache.isInitialized() && null == TempJarCache.findLibrary(libName)) { - addNativeJarLibs(NEWTJNILibLoader.class, "jogl-all", new String[] { "nativewindow", "newt" } ); - } - loadLibrary(libName, false, NEWTJNILibLoader.class.getClassLoader()); - return null; - } - }); - } - + public static boolean loadNEWT() { + return AccessController.doPrivileged(new PrivilegedAction<Boolean>() { + @Override + public Boolean run() { + Platform.initSingleton(); + final String libName = "newt"; + if(TempJarCache.isInitialized() && null == TempJarCache.findLibrary(libName)) { + JNILibLoaderBase.addNativeJarLibsJoglCfg(new Class<?>[] { NWJNILibLoader.class, NEWTJNILibLoader.class }); + } + return Boolean.valueOf(loadLibrary(libName, false, NEWTJNILibLoader.class.getClassLoader())); + } + }).booleanValue(); + } } diff --git a/src/newt/classes/jogamp/newt/OffscreenWindow.java b/src/newt/classes/jogamp/newt/OffscreenWindow.java index 050e24b6c..2478b1e5d 100644 --- a/src/newt/classes/jogamp/newt/OffscreenWindow.java +++ b/src/newt/classes/jogamp/newt/OffscreenWindow.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,31 +29,42 @@ * 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 javax.media.nativewindow.*; +import java.util.List; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.GraphicsConfigurationFactory; +import javax.media.nativewindow.MutableSurface; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.VisualIDHolder; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; -public class OffscreenWindow extends WindowImpl implements SurfaceChangeable { +import com.jogamp.newt.MonitorDevice; + +public class OffscreenWindow extends WindowImpl implements MutableSurface { - long surfaceHandle = 0; + long surfaceHandle; public OffscreenWindow() { + surfaceHandle = 0; } static long nextWindowHandle = 0x100; // start here - a marker + @Override protected void createNativeImpl() { if(capsRequested.isOnscreen()) { throw new NativeWindowException("Capabilities is onscreen"); } final AbstractGraphicsScreen aScreen = getScreen().getGraphicsScreen(); - final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(aScreen.getDevice()).chooseGraphicsConfiguration( - capsRequested, capsRequested, capabilitiesChooser, aScreen); + final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(aScreen.getDevice(), capsRequested).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, aScreen, VisualIDHolder.VID_UNDEFINED); if (null == cfg) { throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); } @@ -62,22 +73,21 @@ public class OffscreenWindow extends WindowImpl implements SurfaceChangeable { synchronized(OffscreenWindow.class) { setWindowHandle(nextWindowHandle++); } + visibleChanged(false, true); } + @Override protected void closeNativeImpl() { // nop } - public void surfaceSizeChanged(int width, int height) { - sizeChanged(false, width, height, false); - } - @Override public synchronized void destroy() { super.destroy(); surfaceHandle = 0; } + @Override public void setSurfaceHandle(long handle) { surfaceHandle = handle ; } @@ -87,6 +97,7 @@ public class OffscreenWindow extends WindowImpl implements SurfaceChangeable { return surfaceHandle; } + @Override protected void requestFocusImpl(boolean reparented) { } @@ -94,16 +105,22 @@ public class OffscreenWindow extends WindowImpl implements SurfaceChangeable { public void setPosition(int x, int y) { // nop } - + @Override public boolean setFullscreen(boolean fullscreen) { - // nop - return false; + return false; // nop } + @Override + public boolean setFullscreen(List<MonitorDevice> monitors) { + return false; // nop + } + + + @Override protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { + sizeChanged(false, width, height, false); if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { - sizeChanged(false, width, height, false); visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); } else { /** @@ -120,19 +137,20 @@ public class OffscreenWindow extends WindowImpl implements SurfaceChangeable { @Override public Point getLocationOnScreen(Point storage) { if(null!=storage) { - storage.setX(0); - storage.setY(0); + storage.set(0, 0); return storage; } return new Point(0,0); } - + + @Override protected Point getLocationOnScreenImpl(int x, int y) { return new Point(x,y); } - + + @Override protected void updateInsetsImpl(Insets insets) { - // nop .. + // nop .. } } diff --git a/src/newt/classes/jogamp/newt/PointerIconImpl.java b/src/newt/classes/jogamp/newt/PointerIconImpl.java new file mode 100644 index 000000000..840799d55 --- /dev/null +++ b/src/newt/classes/jogamp/newt/PointerIconImpl.java @@ -0,0 +1,171 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.newt; + +import java.nio.ByteBuffer; + +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.PixelFormat; +import javax.media.nativewindow.util.PixelRectangle; +import javax.media.nativewindow.util.PointImmutable; + +import com.jogamp.newt.Display; +import com.jogamp.newt.Display.PointerIcon; + +public class PointerIconImpl implements PointerIcon { + private final DisplayImpl display; + private final PixelFormat pixelformat; + private final DimensionImmutable size; + private final ByteBuffer pixels; + private final PointImmutable hotspot; + private long handle; + private int hashCode = 0; + private volatile boolean hashCodeComputed = false; + + public PointerIconImpl(final DisplayImpl display, final PixelFormat pixelformat, final DimensionImmutable size, final ByteBuffer pixels, final PointImmutable hotspot, final long handle) { + this.display = display; + this.pixelformat = pixelformat; + this.size = size; + this.pixels = pixels; + this.hotspot = hotspot; + + this.handle=handle; + } + public PointerIconImpl(final DisplayImpl display, final PixelRectangle pixelrect, final PointImmutable hotspot, final long handle) { + this.display = display; + this.pixelformat = pixelrect.getPixelformat(); + this.size = pixelrect.getSize(); + this.pixels = pixelrect.getPixels(); + this.hotspot = hotspot; + this.handle=handle; + } + + @Override + public int hashCode() { + if( !hashCodeComputed ) { // DBL CHECKED OK VOLATILE + synchronized (this) { + if( !hashCodeComputed ) { + // 31 * x == (x << 5) - x + int hash = 31 + display.getFQName().hashCode(); + hash = ((hash << 5) - hash) + pixelformat.hashCode(); + hash = ((hash << 5) - hash) + size.hashCode(); + hash = ((hash << 5) - hash) + getStride(); + hash = ((hash << 5) - hash) + ( isGLOriented() ? 1 : 0); + hash = ((hash << 5) - hash) + pixels.hashCode(); + hashCode = ((hash << 5) - hash) + hotspot.hashCode(); + } + } + } + return hashCode; + } + + public synchronized final long getHandle() { return handle; } + public synchronized final long validatedHandle() { + synchronized(display.pointerIconList) { + if( !display.pointerIconList.contains(this) ) { + display.pointerIconList.add(this); + } + } + if( 0 == handle ) { + try { + handle = display.createPointerIconImpl(pixelformat, size.getWidth(), size.getHeight(), pixels, hotspot.getX(), hotspot.getY()); + return handle; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } else { + return handle; + } + } + @Override + public final Display getDisplay() { return display; } + @Override + public final PixelFormat getPixelformat() { return pixelformat; } + @Override + public final ByteBuffer getPixels() { return pixels; } + @Override + public synchronized final boolean isValid() { return 0 != handle; } + @Override + public synchronized final boolean validate() { + if( 0 == handle ) { + return 0 != validatedHandle(); + } + return true; + } + + @Override + public synchronized void destroy() { + if(DisplayImpl.DEBUG) { + System.err.println("PointerIcon.destroy: "+this+", "+display+", "+DisplayImpl.getThreadName()); + } + if( 0 != handle ) { + synchronized(display.pointerIconList) { + display.pointerIconList.remove(this); + } + display.runOnEDTIfAvail(false, new Runnable() { + public void run() { + if( !display.isNativeValidAsync() ) { + destroyOnEDT(display.getHandle()); + } + } } ); + } + } + + /** No checks, assume execution on EDT */ + synchronized void destroyOnEDT(final long dpy) { + final long h = handle; + handle = 0; + try { + display.destroyPointerIconImpl(dpy, h); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public final DimensionImmutable getSize() { + return size; + } + @Override + public final int getStride() { + return size.getWidth() * pixelformat.bytesPerPixel(); + } + @Override + public final boolean isGLOriented() { + return false; + } + @Override + public final PointImmutable getHotspot() { + return hotspot; + } + @Override + public final String toString() { + return "PointerIcon[obj 0x"+Integer.toHexString(super.hashCode())+", "+display.getFQName()+", 0x"+Long.toHexString(handle)+", "+pixelformat+", "+size+", "+hotspot+", pixels "+pixels+"]"; + } +}
\ No newline at end of file diff --git a/src/newt/classes/jogamp/newt/ScreenImpl.java b/src/newt/classes/jogamp/newt/ScreenImpl.java index cf614b6f1..d7e6c641c 100644 --- a/src/newt/classes/jogamp/newt/ScreenImpl.java +++ b/src/newt/classes/jogamp/newt/ScreenImpl.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,92 +29,72 @@ * 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 java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.NativeWindowException; -import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.DimensionImmutable; -import javax.media.nativewindow.util.Point; -import javax.media.nativewindow.util.SurfaceSize; - +import javax.media.nativewindow.util.Rectangle; +import javax.media.nativewindow.util.RectangleImmutable; import com.jogamp.common.util.ArrayHashSet; -import com.jogamp.common.util.IntIntHashMap; import com.jogamp.newt.Display; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Screen; -import com.jogamp.newt.ScreenMode; -import com.jogamp.newt.event.ScreenModeListener; -import com.jogamp.newt.util.MonitorMode; -import com.jogamp.newt.util.ScreenModeUtil; - -public abstract class ScreenImpl extends Screen implements ScreenModeListener { - protected static final boolean DEBUG_TEST_SCREENMODE_DISABLED = Debug.isPropertyDefined("newt.test.Screen.disableScreenMode", true); - - protected static final int default_sm_bpp = 32; - protected static final int default_sm_widthmm = 519; - protected static final int default_sm_heightmm = 324; - protected static final int default_sm_rate = 60; - protected static final int default_sm_rotation = 0; - +import com.jogamp.newt.event.MonitorEvent; +import com.jogamp.newt.event.MonitorModeListener; +import com.jogamp.newt.util.MonitorModeUtil; + +public abstract class ScreenImpl extends Screen implements MonitorModeListener { + protected static final boolean DEBUG_TEST_SCREENMODE_DISABLED; + + static { + Debug.initSingleton(); + DEBUG_TEST_SCREENMODE_DISABLED = Debug.isPropertyDefined("newt.test.Screen.disableScreenMode", true); + } + + public static final int default_sm_bpp = 32; + public static final int default_sm_widthmm = 519; + public static final int default_sm_heightmm = 324; + public static final int default_sm_rate = 60; + public static final int default_sm_rotation = 0; + + static { + DisplayImpl.initSingleton(); + } + + /** Ensure static init has been run. */ + /* pp */static void initSingleton() { } + protected DisplayImpl display; protected int screen_idx; protected String fqname; protected int hashCode; protected AbstractGraphicsScreen aScreen; protected int refCount; // number of Screen references by Window - protected Point vOrigin = new Point(0, 0); // virtual top-left origin - protected Dimension vSize = new Dimension(0, 0); // virtual rotated screen size + protected Rectangle vOriginSize = new Rectangle(0, 0, 0, 0); // virtual rotated screen origin and size protected static Dimension usrSize = null; // property values: newt.ws.swidth and newt.ws.sheight protected static volatile boolean usrSizeQueried = false; - private ArrayList<ScreenModeListener> referencedScreenModeListener = new ArrayList<ScreenModeListener>(); + private ArrayList<MonitorModeListener> refMonitorModeListener = new ArrayList<MonitorModeListener>(); + private long tCreated; // creationTime - static { - AccessController.doPrivileged(new PrivilegedAction<Object>() { - public Object run() { - registerShutdownHook(); - return null; - } - }); - } - - @SuppressWarnings("unchecked") - private static Class<? extends Screen> getScreenClass(String type) throws ClassNotFoundException + private static Class<?> getScreenClass(String type) throws ClassNotFoundException { - Class<?> screenClass = NewtFactory.getCustomClass(type, "Screen"); + final Class<?> screenClass = NewtFactory.getCustomClass(type, "ScreenDriver"); if(null==screenClass) { - if (NativeWindowFactory.TYPE_ANDROID == type) { - screenClass = Class.forName("jogamp.newt.driver.android.AndroidScreen"); - } else if (NativeWindowFactory.TYPE_EGL == type) { - screenClass = Class.forName("jogamp.newt.driver.kd.KDScreen"); - } else if (NativeWindowFactory.TYPE_WINDOWS == type) { - screenClass = Class.forName("jogamp.newt.driver.windows.WindowsScreen"); - } else if (NativeWindowFactory.TYPE_MACOSX == type) { - screenClass = Class.forName("jogamp.newt.driver.macosx.MacScreen"); - } else if (NativeWindowFactory.TYPE_X11 == type) { - screenClass = Class.forName("jogamp.newt.driver.x11.X11Screen"); - } else if (NativeWindowFactory.TYPE_AWT == type) { - screenClass = Class.forName("jogamp.newt.driver.awt.AWTScreen"); - } else { - throw new RuntimeException("Unknown window type \"" + type + "\""); - } + throw new ClassNotFoundException("Failed to find NEWT Screen Class <"+type+".ScreenDriver>"); } - if(null==screenClass) { - throw new ClassNotFoundException("Failed to find NEWT Screen Class <"+type+".Screen>"); - } - return (Class<? extends Screen>)screenClass; + return screenClass; } public static Screen create(Display display, int idx) { @@ -124,7 +104,7 @@ public abstract class ScreenImpl extends Screen implements ScreenModeListener { if(!usrSizeQueried) { usrSizeQueried = true; final int w = Debug.getIntProperty("newt.ws.swidth", true, 0); - final int h = Debug.getIntProperty("newt.ws.sheight", true, 0); + final int h = Debug.getIntProperty("newt.ws.sheight", true, 0); if(w>0 && h>0) { usrSize = new Dimension(w, h); System.err.println("User screen size "+usrSize); @@ -133,7 +113,7 @@ public abstract class ScreenImpl extends Screen implements ScreenModeListener { } } synchronized(screenList) { - Class<? extends Screen> screenClass = getScreenClass(display.getType()); + Class<?> screenClass = getScreenClass(display.getType()); ScreenImpl screen = (ScreenImpl) screenClass.newInstance(); screen.display = (DisplayImpl) display; idx = screen.validateScreenIndex(idx); @@ -148,14 +128,14 @@ public abstract class ScreenImpl extends Screen implements ScreenModeListener { } } screen.screen_idx = idx; - screen.fqname = (display.getFQName()+idx).intern(); + screen.fqname = display.getFQName()+"-s"+idx; screen.hashCode = screen.fqname.hashCode(); - screenList.add(screen); + Screen.addScreen2List(screen); if(DEBUG) { System.err.println("Screen.create() NEW: "+screen+" "+Display.getThreadName()); } return screen; - } + } } catch (Exception e) { throw new RuntimeException(e); } @@ -179,13 +159,15 @@ public abstract class ScreenImpl extends Screen implements ScreenModeListener { return true; } + @Override public int hashCode() { return hashCode; } + @Override public synchronized final void createNative() throws NativeWindowException - { + { if(null == aScreen) { if(DEBUG) { tCreated = System.nanoTime(); @@ -193,38 +175,38 @@ public abstract class ScreenImpl extends Screen implements ScreenModeListener { } else { tCreated = 0; } - display.addReference(); - + createNativeImpl(); if(null == aScreen) { throw new NativeWindowException("Screen.createNative() failed to instanciate an AbstractGraphicsScreen"); } - - initScreenModeStatus(); - updateVirtualScreenOriginAndSize(); - if(DEBUG) { - System.err.println("Screen.createNative() END ("+DisplayImpl.getThreadName()+", "+this+"), total "+ (System.nanoTime()-tCreated)/1e6 +"ms"); - } + + initMonitorState(); synchronized(screenList) { screensActive++; + if(DEBUG) { + System.err.println("Screen.createNative() END ("+DisplayImpl.getThreadName()+", "+this+"), active "+screensActive+", total "+ (System.nanoTime()-tCreated)/1e6 +"ms"); + } } + ScreenMonitorState.getScreenMonitorState(this.getFQName()).addListener(this); } - ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); - sms.addListener(this); } + @Override public synchronized final void destroy() { - releaseScreenModeStatus(); - synchronized(screenList) { - screenList.remove(this); if(0 < screensActive) { screensActive--; } + if(DEBUG) { + System.err.println("Screen.destroy() ("+DisplayImpl.getThreadName()+"): active "+screensActive); + // Thread.dumpStack(); + } } if ( null != aScreen ) { + releaseMonitorState(); closeNativeImpl(); aScreen = null; } @@ -232,6 +214,7 @@ public abstract class ScreenImpl extends Screen implements ScreenModeListener { display.removeReference(); } + @Override public synchronized final int addReference() throws NativeWindowException { if(DEBUG) { System.err.println("Screen.addReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount+1)); @@ -239,13 +222,13 @@ public abstract class ScreenImpl extends Screen implements ScreenModeListener { } if ( 0 == refCount ) { createNative(); - } - if(null == aScreen) { + } else if(null == aScreen) { throw new NativeWindowException("Screen.addReference() (refCount "+refCount+") null AbstractGraphicsScreen"); } return ++refCount; } + @Override public synchronized final int removeReference() { if(DEBUG) { System.err.println("Screen.removeReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount-1)); @@ -259,13 +242,14 @@ public abstract class ScreenImpl extends Screen implements ScreenModeListener { return refCount; } + @Override public synchronized final int getReferenceCount() { return refCount; } protected abstract void createNativeImpl(); protected abstract void closeNativeImpl(); - + /** * Returns the validated screen index, which is either the passed <code>idx</code> * value or <code>0</code>. @@ -274,18 +258,24 @@ public abstract class ScreenImpl extends Screen implements ScreenModeListener { * </p> */ protected abstract int validateScreenIndex(int idx); - + /** * Stores the virtual origin and virtual <b>rotated</b> screen size. * <p> - * This method is called after the ScreenMode has been set, + * This method is called after the MonitorMode has been set or changed, * hence you may utilize it. - * </p> - * @param virtualOrigin the store for the virtual origin - * @param virtualSize the store for the virtual rotated size + * </p> + * <p> + * Default implementation uses the union of all monitor's viewport, + * calculated via {@link #unionOfMonitorViewportSize()}. + * </p> + * @param vOriginSize storage for result */ - protected abstract void getVirtualScreenOriginAndSize(Point virtualOrigin, Dimension virtualSize); - + protected void calcVirtualScreenOriginAndSize(final Rectangle vOriginSize) { + unionOfMonitorViewportSize(vOriginSize); + } + + @Override public final String getFQName() { return fqname; } @@ -294,401 +284,399 @@ public abstract class ScreenImpl extends Screen implements ScreenModeListener { * Updates the <b>rotated</b> virtual ScreenSize using the native impl. */ protected void updateVirtualScreenOriginAndSize() { - getVirtualScreenOriginAndSize(vOrigin, vSize); - if(DEBUG) { - System.err.println("Detected screen origin "+vOrigin+", size "+vSize); + if(null != usrSize ) { + vOriginSize.set(0, 0, usrSize.getWidth(), usrSize.getHeight()); + if(DEBUG) { + System.err.println("Update user virtual screen viewport @ "+Thread.currentThread().getName()+": "+vOriginSize); + } + } else { + calcVirtualScreenOriginAndSize(vOriginSize); + if(DEBUG) { + System.err.println("Updated virtual screen viewport @ "+Thread.currentThread().getName()+": "+vOriginSize); + } } } + @Override public final Display getDisplay() { return display; } + @Override public final int getIndex() { return screen_idx; } + @Override public final AbstractGraphicsScreen getGraphicsScreen() { return aScreen; } + @Override public synchronized final boolean isNativeValid() { return null != aScreen; } - public int getX() { return vOrigin.getX(); } - public int getY() { return vOrigin.getY(); } - - public final int getWidth() { - return (null != usrSize) ? usrSize.getWidth() : vSize.getWidth(); - } - - public final int getHeight() { - return (null != usrSize) ? usrSize.getHeight() : vSize.getHeight(); - } + @Override + public final int getX() { return vOriginSize.getX(); } + @Override + public final int getY() { return vOriginSize.getY(); } + @Override + public final int getWidth() { return vOriginSize.getWidth(); } + @Override + public final int getHeight() { return vOriginSize.getHeight(); } + @Override + public final RectangleImmutable getViewport() { return vOriginSize; } @Override public String toString() { - return "NEWT-Screen["+getFQName()+", idx "+screen_idx+", refCount "+refCount+", "+getWidth()+"x"+getHeight()+", "+aScreen+", "+display+"]"; + return "NEWT-Screen["+getFQName()+", idx "+screen_idx+", refCount "+refCount+", vsize "+vOriginSize+", "+aScreen+", "+display+ + ", monitors: "+getMonitorDevices()+"]"; } - public final List<ScreenMode> getScreenModes() { - ArrayHashSet<ScreenMode> screenModes = getScreenModesOrig(); - if(null != screenModes && 0 < screenModes.size()) { - return screenModes.toArrayList(); - } - return null; - } + // + // MonitorDevice and MonitorMode + // - public ScreenMode getOriginalScreenMode() { - ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); - return ( null != sms ) ? sms.getOriginalScreenMode() : null ; + /** + * To be implemented by the native specification.<br> + * Is called within a thread safe environment.<br> + * Is called only to collect the {@link MonitorMode}s and {@link MonitorDevice}s, usually at startup setting up modes.<br> + * <br> + * <b>WARNING</b>: must be synchronized with + * <ul> + * <li>{@link MonitorModeProps#NUM_SCREEN_MODE_PROPERTIES} and </li> + * <li>{@link MonitorModeProps#MIN_MONITOR_DEVICE_PROPERTIES}</li> + * </ul>, i.e. + * <ul> + * <li>{@link MonitorModeProps#streamInMonitorDevice(int[], jogamp.newt.MonitorModeProps.Cache, ScreenImpl, int[], int)}</li> + * <li>{@link MonitorModeProps#streamInMonitorDevice(int[], jogamp.newt.MonitorModeProps.Cache, ScreenImpl, ArrayHashSet, int[], int)}</li> + * <li>{@link MonitorModeProps#streamInMonitorMode(int[], jogamp.newt.MonitorModeProps.Cache, int[], int)}</li> + * </ul> + * @param cache memory pool caching the result + */ + protected abstract void collectNativeMonitorModesAndDevicesImpl(MonitorModeProps.Cache cache); + + protected Rectangle getNativeMonitorDeviceViewportImpl(MonitorDevice monitor) { return null; } + + /** + * To be implemented by the native specification.<br> + * Is called within a thread safe environment.<br> + * <p> + * Implementation shall not unify the result w/ monitor's supported modes or a locally + * saved {@link MonitorModeProps.Cache}, since caller will perform such tasks. + * </p> + */ + protected abstract MonitorMode queryCurrentMonitorModeImpl(MonitorDevice monitor); + + /** + * To be implemented by the native specification.<br> + * Is called within a thread safe environment.<br> + */ + protected abstract boolean setCurrentMonitorModeImpl(MonitorDevice monitor, MonitorMode mode); + + @Override + public final List<MonitorMode> getMonitorModes() { + final ScreenMonitorState sms = getScreenMonitorStatus(false); + return null != sms ? sms.getMonitorModes().getData() : null; } - public ScreenMode getCurrentScreenMode() { - ScreenMode smU = null; - ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); - if(null == sms) { - throw new InternalError("ScreenModeStatus.getScreenModeStatus("+this.getFQName()+") == null"); - } - ScreenMode sm0 = getCurrentScreenModeIntern(); - if(null == sm0) { - throw new InternalError("getCurrentScreenModeImpl() == null"); - } - sms.lock(); - try { - smU = sms.getScreenModes().getOrAdd(sm0); // unified instance, maybe new + @Override + public final List<MonitorDevice> getMonitorDevices() { + final ScreenMonitorState sms = getScreenMonitorStatus(false); + return null != sms ? sms.getMonitorDevices().getData() : null; + } - // if mode has changed somehow, update it .. - if( sms.getCurrentScreenMode().hashCode() != smU.hashCode() ) { - sms.fireScreenModeChanged(smU, true); - } - } finally { - sms.unlock(); + final ScreenMonitorState getScreenMonitorStatus(boolean throwException) { + final String key = this.getFQName(); + final ScreenMonitorState res = ScreenMonitorState.getScreenMonitorState(key); + if(null == res & throwException) { + throw new InternalError("ScreenMonitorStatus.getMonitorModeStatus("+key+") == null"); } - return smU; + return res; } - public boolean setCurrentScreenMode(ScreenMode screenMode) { - final ScreenMode smC = getCurrentScreenMode(); - ScreenMode smU = getScreenModesOrig().get(screenMode); // unify via value hash - if(smU.equals(smC)) { - if(DEBUG) { - System.err.println("Screen.setCurrentScreenMode ("+(System.nanoTime()-tCreated)+"): 0.0 is-current (skip) "+smU+" == "+smC); - } - return true; + @Override + public void monitorModeChangeNotify(MonitorEvent me) { + if(DEBUG) { + System.err.println("monitorModeChangeNotify @ "+Thread.currentThread().getName()+": "+me); } - ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); - if(null == sms) { - throw new InternalError("ScreenModeStatus.getScreenModeStatus("+this.getFQName()+") == null"); + for(int i=0; i<refMonitorModeListener.size(); i++) { + ((MonitorModeListener)refMonitorModeListener.get(i)).monitorModeChangeNotify(me); } - boolean success; - sms.lock(); - try { - final long tStart; - if(DEBUG) { - tStart = System.nanoTime(); - } else { - tStart = 0; - } - - sms.fireScreenModeChangeNotify(smU); - if(DEBUG) { - System.err.println("Screen.setCurrentScreenMode ("+(System.nanoTime()-tStart)/1e6+"ms): fireScreenModeChangeNotify() "+smU); - } + } - success = setCurrentScreenModeImpl(smU); - if(DEBUG) { - System.err.println("Screen.setCurrentScreenMode ("+(System.nanoTime()-tStart)/1e6+"ms): setCurrentScreenModeImpl() "+smU+", success: "+success); + private void updateNativeMonitorDevicesViewport() { + final List<MonitorDevice> monitors = getMonitorDevices(); + for(int i=monitors.size()-1; i>=0; i--) { + final MonitorDeviceImpl monitor = (MonitorDeviceImpl) monitors.get(i); + final Rectangle newViewport = getNativeMonitorDeviceViewportImpl(monitor); + if( DEBUG ) { + System.err.println("Screen.updateMonitorViewport["+i+"] @ "+Thread.currentThread().getName()+": "+monitor.getViewport()+" -> "+newViewport); } - - sms.fireScreenModeChanged(smU, success); - if(DEBUG) { - System.err.println("Screen.setCurrentScreenMode ("+(System.nanoTime()-tStart)/1e6+"ms): X.X "+smU+", success: "+success); + if( null != newViewport ) { + monitor.setViewportValue(newViewport); } - } finally { - sms.unlock(); } - return success; } - public void screenModeChangeNotify(ScreenMode sm) { - for(int i=0; i<referencedScreenModeListener.size(); i++) { - ((ScreenModeListener)referencedScreenModeListener.get(i)).screenModeChangeNotify(sm); - } - } - - public void screenModeChanged(ScreenMode sm, boolean success) { + @Override + public void monitorModeChanged(MonitorEvent me, boolean success) { if(success) { + updateNativeMonitorDevicesViewport(); updateVirtualScreenOriginAndSize(); } - for(int i=0; i<referencedScreenModeListener.size(); i++) { - ((ScreenModeListener)referencedScreenModeListener.get(i)).screenModeChanged(sm, success); + if(DEBUG) { + System.err.println("monitorModeChangeNotify @ "+Thread.currentThread().getName()+": success "+success+", "+me); } - } - - public synchronized final void addScreenModeListener(ScreenModeListener sml) { - referencedScreenModeListener.add(sml); - } - - public synchronized final void removeScreenModeListener(ScreenModeListener sml) { - referencedScreenModeListener.remove(sml); - } - - /** ScreenModeStatus bridge to native implementation */ - protected final ArrayHashSet<ScreenMode> getScreenModesOrig() { - ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); - if(null!=sms) { - return sms.getScreenModes(); + for(int i=0; i<refMonitorModeListener.size(); i++) { + ((MonitorModeListener)refMonitorModeListener.get(i)).monitorModeChanged(me, success); } - return null; } - /** ScreenModeStatus bridge to native implementation */ - protected final IntIntHashMap getScreenModesIdx2NativeIdx() { - ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); - if(null!=sms) { - return sms.getScreenModesIdx2NativeIdx(); - } - return null; + @Override + public synchronized final void addMonitorModeListener(MonitorModeListener sml) { + refMonitorModeListener.add(sml); } - /** - * To be implemented by the native specification.<br> - * Is called within a thread safe environment.<br> - * Is called only to collect the ScreenModes, usually at startup setting up modes.<br> - * <br> - * <b>WARNING</b>: must be synchronized with {@link com.jogamp.newt.util.ScreenModeUtil#NUM_SCREEN_MODE_PROPERTIES}, - * ie {@link com.jogamp.newt.util.ScreenModeUtil#streamIn(com.jogamp.common.util.ArrayHashSet, com.jogamp.common.util.ArrayHashSet, com.jogamp.common.util.ArrayHashSet, com.jogamp.common.util.ArrayHashSet, int[], int)}<br> - * <br> - * <b>Note</b>: Additional 1st element is native mode id. - */ - protected int[] getScreenModeFirstImpl() { - return null; + @Override + public synchronized final void removeMonitorModeListener(MonitorModeListener sml) { + refMonitorModeListener.remove(sml); } /** - * To be implemented by the native specification.<br> - * Is called within a thread safe environment.<br> - * Is called only to collect the ScreenModes, usually at startup setting up modes.<br> - * <br> - * <b>WARNING</b>: must be synchronized with {@link com.jogamp.newt.util.ScreenModeUtil#NUM_SCREEN_MODE_PROPERTIES}, - * ie {@link com.jogamp.newt.util.ScreenModeUtil#streamIn(com.jogamp.common.util.ArrayHashSet, com.jogamp.common.util.ArrayHashSet, com.jogamp.common.util.ArrayHashSet, com.jogamp.common.util.ArrayHashSet, int[], int)}<br> - * <br> - * <b>Note</b>: Additional 1st element is native mode id. + * + * @param cache optional .. + * @param modeId + * @return */ - protected int[] getScreenModeNextImpl() { - return null; + private final MonitorMode getVirtualMonitorMode(MonitorModeProps.Cache cache, int modeId) { + final int[] props = new int[MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL]; + int i = 0; + props[i++] = MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL; + props[i++] = getWidth(); // width + props[i++] = getHeight(); // height + props[i++] = default_sm_bpp; + props[i++] = default_sm_rate * 100; + props[i++] = 0; // flags + props[i++] = modeId; + props[i++] = default_sm_rotation; + if( MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL != i ) { + throw new InternalError("XX"); + } + return MonitorModeProps.streamInMonitorMode(null, cache, props, 0); } /** - * To be implemented by the native specification.<br> - * Is called within a thread safe environment.<br> + * + * @param cache mandatory ! + * @param monitorId + * @param currentMode + * @return */ - protected ScreenMode getCurrentScreenModeImpl() { - return null; + private final MonitorDevice getVirtualMonitorDevice(MonitorModeProps.Cache cache, int monitorId, MonitorMode currentMode) { + int[] props = new int[MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES]; + int i = 0; + props[i++] = MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES; + props[i++] = monitorId; + props[i++] = default_sm_widthmm; + props[i++] = default_sm_heightmm; + props[i++] = 0; // rotated viewport x + props[i++] = 0; // rotated viewport y + props[i++] = currentMode.getRotatedWidth(); // rotated viewport width + props[i++] = currentMode.getRotatedHeight(); // rotated viewport height + props[i++] = currentMode.getId(); // current mode id + props[i++] = currentMode.getRotation(); + props[i++] = currentMode.getId(); // supported mode id #1 + if( MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES != i ) { + throw new InternalError("XX"); + } + return MonitorModeProps.streamInMonitorDevice(null, cache, this, props, 0); } - + /** - * Utilizes {@link #getCurrentScreenModeImpl()}, if the latter returns null it uses + * Utilizes {@link #getCurrentMonitorModeImpl()}, if the latter returns null it uses * the current screen size and dummy values. */ - protected ScreenMode getCurrentScreenModeIntern() { - ScreenMode res; + protected final MonitorMode queryCurrentMonitorModeIntern(MonitorDevice monitor) { + MonitorMode res; if(DEBUG_TEST_SCREENMODE_DISABLED) { res = null; } else { - res = getCurrentScreenModeImpl(); + res = queryCurrentMonitorModeImpl(monitor); } if(null == res) { - if( 0==getWidth()*getHeight() ) { + if( 0>=getWidth() || 0>=getHeight() ) { updateVirtualScreenOriginAndSize(); } - int[] props = new int[ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL]; - int i = 0; - props[i++] = 0; // set later for verification of iterator - props[i++] = getWidth(); // width - props[i++] = getHeight(); // height - props[i++] = default_sm_bpp; - props[i++] = default_sm_widthmm; - props[i++] = default_sm_heightmm; - props[i++] = default_sm_rate; - props[i++] = default_sm_rotation; - props[i - ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL] = i; // count - res = ScreenModeUtil.streamIn(props, 0); + res = getVirtualMonitorMode(null, monitor.getCurrentMode().getId()); } return res; } - /** - * To be implemented by the native specification.<br> - * Is called within a thread safe environment.<br> - */ - protected boolean setCurrentScreenModeImpl(ScreenMode screenMode) { - return false; - } - - private ScreenModeStatus initScreenModeStatus() { + private final ScreenMonitorState initMonitorState() { long t0; if(DEBUG) { t0 = System.nanoTime(); - System.err.println("Screen.initScreenModeStatus() START ("+DisplayImpl.getThreadName()+", "+this+")"); + System.err.println("Screen.initMonitorState() START ("+DisplayImpl.getThreadName()+", "+this+")"); } else { t0 = 0; } - ScreenModeStatus sms; - ScreenModeStatus.lockScreenModeStatus(); + boolean vScrnSizeUpdated = false; + ScreenMonitorState sms; + ScreenMonitorState.lockScreenMonitorState(); try { - sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); - if(null==sms) { - IntIntHashMap screenModesIdx2NativeIdx = new IntIntHashMap(); - final ScreenMode currentSM = getCurrentScreenModeIntern(); - if(null == currentSM) { - throw new InternalError("getCurrentScreenModeImpl() == null"); + sms = ScreenMonitorState.getScreenMonitorState(this.getFQName()); + if(null==sms) { + final MonitorModeProps.Cache cache = new MonitorModeProps.Cache(); + if( 0 >= collectNativeMonitorModes(cache) ) { + updateVirtualScreenOriginAndSize(); + vScrnSizeUpdated = true; + final MonitorMode mode = getVirtualMonitorMode(cache, 0); + cache.monitorModes.getOrAdd(mode); + final MonitorDevice monitor = getVirtualMonitorDevice(cache, 0, mode); + cache.monitorDevices.getOrAdd(monitor); + } + // Sort MonitorModes (all and per device) in descending order - default! + MonitorModeUtil.sort(cache.monitorModes.getData(), false ); // descending order + for(Iterator<MonitorDevice> iMonitor=cache.monitorDevices.iterator(); iMonitor.hasNext(); ) { + MonitorModeUtil.sort(iMonitor.next().getSupportedModes(), false ); // descending order } - - ArrayHashSet<ScreenMode> screenModes = collectNativeScreenModes(screenModesIdx2NativeIdx); - screenModes.getOrAdd(currentSM); if(DEBUG) { int i=0; - for(Iterator<ScreenMode> iter=screenModes.iterator(); iter.hasNext(); i++) { - System.err.println(i+": "+iter.next()); + for(Iterator<MonitorMode> iMode=cache.monitorModes.iterator(); iMode.hasNext(); i++) { + System.err.println("All["+i+"]: "+iMode.next()); + } + i=0; + for(Iterator<MonitorDevice> iMonitor=cache.monitorDevices.iterator(); iMonitor.hasNext(); i++) { + final MonitorDevice crt = iMonitor.next(); + System.err.println("["+i+"]: "+crt); + int j=0; + for(Iterator<MonitorMode> iMode=crt.getSupportedModes().iterator(); iMode.hasNext(); j++) { + System.err.println("["+i+"]["+j+"]: "+iMode.next()); + } } } - - sms = new ScreenModeStatus(screenModes, screenModesIdx2NativeIdx); - ScreenMode originalScreenMode0 = screenModes.get(currentSM); // unify via value hash - if(null == originalScreenMode0) { - throw new RuntimeException(currentSM+" could not be hashed from ScreenMode list"); - } - sms.setOriginalScreenMode(originalScreenMode0); - ScreenModeStatus.mapScreenModeStatus(this.getFQName(), sms); + sms = new ScreenMonitorState(cache.monitorDevices, cache.monitorModes); + ScreenMonitorState.mapScreenMonitorState(this.getFQName(), sms); } } finally { - ScreenModeStatus.unlockScreenModeStatus(); + ScreenMonitorState.unlockScreenMonitorState(); } if(DEBUG) { - System.err.println("Screen.initScreenModeStatus() END dt "+ (System.nanoTime()-t0)/1e6 +"ms"); + System.err.println("Screen.initMonitorState() END dt "+ (System.nanoTime()-t0)/1e6 +"ms"); } + if( !vScrnSizeUpdated ) { + updateVirtualScreenOriginAndSize(); + } + return sms; } - /** ignores bpp < 15 */ - private ArrayHashSet<ScreenMode> collectNativeScreenModes(IntIntHashMap screenModesIdx2NativeId) { - ArrayHashSet<DimensionImmutable> resolutionPool = new ArrayHashSet<DimensionImmutable>(); - ArrayHashSet<SurfaceSize> surfaceSizePool = new ArrayHashSet<SurfaceSize>(); - ArrayHashSet<DimensionImmutable> screenSizeMMPool = new ArrayHashSet<DimensionImmutable>(); - ArrayHashSet<MonitorMode> monitorModePool = new ArrayHashSet<MonitorMode>(); - ArrayHashSet<ScreenMode> screenModePool = new ArrayHashSet<ScreenMode>(); - - int[] smProps = null; - int num = 0; - final int idxBpp = 1 // native mode - + 1 // count - + ScreenModeUtil.NUM_RESOLUTION_PROPERTIES - + ScreenModeUtil.NUM_SURFACE_SIZE_PROPERTIES - - 1 ; // index 0 based - do { - if(DEBUG_TEST_SCREENMODE_DISABLED) { - smProps = null; - } else if(0 == num) { - smProps = getScreenModeFirstImpl(); - } else { - smProps = getScreenModeNextImpl(); - } - if(null != smProps && 0 < smProps.length && smProps[idxBpp] >= 15) { - int nativeId = smProps[0]; - int screenModeIdx = ScreenModeUtil.streamIn(resolutionPool, surfaceSizePool, screenSizeMMPool, - monitorModePool, screenModePool, smProps, 1); - if(DEBUG) { - System.err.println("ScreenImpl.collectNativeScreenModes: #"+num+": idx: "+nativeId+" native -> "+screenModeIdx+" newt"); + /** + * Returns the number of successful collected {@link MonitorDevice}s. + * <p> + * Collects {@link MonitorDevice}s and {@link MonitorMode}s within the given cache. + * </p> + */ + private final int collectNativeMonitorModes(MonitorModeProps.Cache cache) { + if(!DEBUG_TEST_SCREENMODE_DISABLED) { + collectNativeMonitorModesAndDevicesImpl(cache); + } + // filter out insufficient modes + for(int i=cache.monitorModes.size()-1; i>=0; i--) { + final MonitorMode mode = cache.monitorModes.get(i); + if( 16 > mode.getSurfaceSize().getBitsPerPixel() ) { + boolean keep = false; + for(int j=cache.monitorDevices.size()-1; !keep && j>=0; j--) { + final MonitorDevice monitor = cache.monitorDevices.get(j); + keep = monitor.getCurrentMode().equals(mode); } - - if(screenModeIdx >= 0) { - screenModesIdx2NativeId.put(screenModeIdx, nativeId); + if(!keep) { + cache.monitorModes.remove(i); + for(int j=cache.monitorDevices.size()-1; j>=0; j--) { + final MonitorDeviceImpl monitor = (MonitorDeviceImpl) cache.monitorDevices.get(j); + monitor.getSupportedModesImpl().remove(mode); + } } - } else if(DEBUG) { - System.err.println("ScreenImpl.collectNativeScreenModes: #"+num+": smProps: "+(null!=smProps)+ - ", len: "+(null != smProps ? smProps.length : 0)+ - ", bpp: "+(null != smProps && 0 < smProps.length ? smProps[idxBpp] : 0)+ - " - DROPPING"); } - num++; - } while ( null != smProps && 0 < smProps.length ); - - if(DEBUG) { - System.err.println("ScreenImpl.collectNativeScreenModes: ScreenMode number : "+screenModePool.size()); - System.err.println("ScreenImpl.collectNativeScreenModes: MonitorMode number : "+monitorModePool.size()); - System.err.println("ScreenImpl.collectNativeScreenModes: ScreenSizeMM number: "+screenSizeMMPool.size()); - System.err.println("ScreenImpl.collectNativeScreenModes: SurfaceSize number : "+surfaceSizePool.size()); - System.err.println("ScreenImpl.collectNativeScreenModes: Resolution number : "+resolutionPool.size()); } - - return screenModePool; + if( DEBUG ) { + System.err.println("ScreenImpl.collectNativeMonitorModes: MonitorDevice number : "+cache.monitorDevices.size()); + System.err.println("ScreenImpl.collectNativeMonitorModes: MonitorMode number : "+cache.monitorModes.size()); + System.err.println("ScreenImpl.collectNativeMonitorModes: SizeAndRate number : "+cache.sizeAndRates.size()); + System.err.println("ScreenImpl.collectNativeMonitorModes: SurfaceSize number : "+cache.surfaceSizes.size()); + System.err.println("ScreenImpl.collectNativeMonitorModes: Resolution number : "+cache.resolutions.size()); + } + return cache.monitorDevices.size(); } - private void releaseScreenModeStatus() { - ScreenModeStatus sms; - ScreenModeStatus.lockScreenModeStatus(); + private final void releaseMonitorState() { + ScreenMonitorState sms; + ScreenMonitorState.lockScreenMonitorState(); try { - sms = ScreenModeStatus.getScreenModeStatus(getFQName()); + sms = ScreenMonitorState.getScreenMonitorState(getFQName()); if(null != sms) { sms.lock(); try { if(0 == sms.removeListener(this)) { - if(sms.isOriginalModeChangedByOwner()) { - System.err.println("Screen.destroy(): "+sms.getCurrentScreenMode()+" -> "+sms.getOriginalScreenMode()); - try { - setCurrentScreenMode(sms.getOriginalScreenMode()); - } catch (Throwable t) { - // be verbose but continue - t.printStackTrace(); + final ArrayList<MonitorDevice> monitorDevices = sms.getMonitorDevices().getData(); + for(int i=0; i<monitorDevices.size(); i++) { + final MonitorDevice monitor = monitorDevices.get(i); + if( monitor.isModeChangedByUs() ) { + System.err.println("Screen.destroy(): Reset "+monitor); + try { + monitor.setCurrentMode(monitor.getOriginalMode()); + } catch (Throwable t) { + // be verbose but continue + t.printStackTrace(); + } } } - ScreenModeStatus.unmapScreenModeStatus(getFQName()); + ScreenMonitorState.unmapScreenMonitorState(getFQName()); } } finally { sms.unlock(); } - } + } } finally { - ScreenModeStatus.unlockScreenModeStatus(); + ScreenMonitorState.unlockScreenMonitorState(); } } - + private final void shutdown() { - ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatusUnlocked(getFQName()); + ScreenMonitorState sms = ScreenMonitorState.getScreenMonitorStateUnlocked(getFQName()); if(null != sms) { - if(sms.isOriginalModeChangedByOwner()) { - try { - System.err.println("Screen.shutdown(): "+sms.getCurrentScreenMode()+" -> "+sms.getOriginalScreenMode()); - setCurrentScreenModeImpl(sms.getOriginalScreenMode()); - } catch (Throwable t) { - // be quiet .. shutdown + final ArrayList<MonitorDevice> monitorDevices = sms.getMonitorDevices().getData(); + for(int i=0; i<monitorDevices.size(); i++) { + final MonitorDevice monitor = monitorDevices.get(i); + if( monitor.isModeChangedByUs() ) { + System.err.println("Screen.shutdown(): Reset "+monitor); + try { + monitor.setCurrentMode(monitor.getOriginalMode()); + } catch (Throwable t) { + // be quiet .. shutdown + } } } - ScreenModeStatus.unmapScreenModeStatusUnlocked(getFQName()); - } - } - private static final void shutdownAll() { - for(int i=0; i < screenList.size(); i++) { - ((ScreenImpl)screenList.get(i)).shutdown(); + ScreenMonitorState.unmapScreenMonitorStateUnlocked(getFQName()); } } - - private static synchronized void registerShutdownHook() { - final Thread shutdownHook = new Thread(new Runnable() { - public void run() { - ScreenImpl.shutdownAll(); + + /** pp */ static final void shutdownAll() { + final int sCount = screenList.size(); + if(DEBUG) { + System.err.println("Screen.shutdownAll "+sCount+" instances, on thread "+Display.getThreadName()); + } + for(int i=0; i<sCount && screenList.size()>0; i++) { // be safe .. + final ScreenImpl s = (ScreenImpl) screenList.remove(0).get(); + if(DEBUG) { + System.err.println("Screen.shutdownAll["+(i+1)+"/"+sCount+"]: "+s+", GCed "+(null==s)); } - }); - AccessController.doPrivileged(new PrivilegedAction<Object>() { - public Object run() { - Runtime.getRuntime().addShutdownHook(shutdownHook); - return null; + if( null != s ) { + s.shutdown(); } - }); + } } } - diff --git a/src/newt/classes/jogamp/newt/ScreenModeStatus.java b/src/newt/classes/jogamp/newt/ScreenModeStatus.java deleted file mode 100644 index 4075fb131..000000000 --- a/src/newt/classes/jogamp/newt/ScreenModeStatus.java +++ /dev/null @@ -1,231 +0,0 @@ -/** - * Copyright 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: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ - -package jogamp.newt; - -import com.jogamp.common.util.ArrayHashSet; -import com.jogamp.common.util.IntIntHashMap; -import com.jogamp.common.util.locks.LockFactory; -import com.jogamp.common.util.locks.RecursiveLock; -import com.jogamp.newt.Screen; -import com.jogamp.newt.ScreenMode; -import com.jogamp.newt.event.ScreenModeListener; - -import java.util.ArrayList; -import java.util.HashMap; - -public class ScreenModeStatus { - private static boolean DEBUG = Screen.DEBUG; - - private RecursiveLock lock = LockFactory.createRecursiveLock(); - private ArrayHashSet<ScreenMode> screenModes; - private IntIntHashMap screenModesIdx2NativeIdx; - private ScreenMode currentScreenMode; - private ScreenMode originalScreenMode; - private boolean screenModeChangedByOwner; - private ArrayList<ScreenModeListener> listener = new ArrayList<ScreenModeListener>(); - - private static HashMap<String, ScreenModeStatus> screenFQN2ScreenModeStatus = new HashMap<String, ScreenModeStatus>(); - private static RecursiveLock screen2ScreenModeStatusLock = LockFactory.createRecursiveLock(); - - protected static void mapScreenModeStatus(String screenFQN, ScreenModeStatus sms) { - screen2ScreenModeStatusLock.lock(); - try { - ScreenModeStatus _sms = screenFQN2ScreenModeStatus.get(screenFQN); - if( null != _sms ) { - throw new RuntimeException("ScreenModeStatus "+_sms+" already mapped to "+screenFQN); - } - screenFQN2ScreenModeStatus.put(screenFQN, sms); - if(DEBUG) { - System.err.println("ScreenModeStatus.map "+screenFQN+" -> "+sms); - } - } finally { - screen2ScreenModeStatusLock.unlock(); - } - } - - /** - * @param screen the prev user - * @return true if mapping is empty, ie no more usage of the mapped ScreenModeStatus - */ - protected static void unmapScreenModeStatus(String screenFQN) { - screen2ScreenModeStatusLock.lock(); - try { - unmapScreenModeStatusUnlocked(screenFQN); - } finally { - screen2ScreenModeStatusLock.unlock(); - } - } - protected static void unmapScreenModeStatusUnlocked(String screenFQN) { - ScreenModeStatus sms = screenFQN2ScreenModeStatus.remove(screenFQN); - if(DEBUG) { - System.err.println("ScreenModeStatus.unmap "+screenFQN+" -> "+sms); - } - } - - protected static ScreenModeStatus getScreenModeStatus(String screenFQN) { - screen2ScreenModeStatusLock.lock(); - try { - return getScreenModeStatusUnlocked(screenFQN); - } finally { - screen2ScreenModeStatusLock.unlock(); - } - } - protected static ScreenModeStatus getScreenModeStatusUnlocked(String screenFQN) { - return screenFQN2ScreenModeStatus.get(screenFQN); - } - - protected static void lockScreenModeStatus() { - screen2ScreenModeStatusLock.lock(); - } - - protected static void unlockScreenModeStatus() { - screen2ScreenModeStatusLock.unlock(); - } - - public ScreenModeStatus(ArrayHashSet<ScreenMode> screenModes, - IntIntHashMap screenModesIdx2NativeIdx) { - this.screenModes = screenModes; - this.screenModesIdx2NativeIdx = screenModesIdx2NativeIdx; - this.screenModeChangedByOwner = false; - } - - protected final void setOriginalScreenMode(ScreenMode originalScreenMode) { - this.originalScreenMode = originalScreenMode; - this.currentScreenMode = originalScreenMode; - } - - public final ScreenMode getOriginalScreenMode() { - return originalScreenMode; - } - - public final ScreenMode getCurrentScreenMode() { - lock(); - try { - return currentScreenMode; - } finally { - unlock(); - } - } - - /** - * We cannot guarantee that we won't interfere w/ another running - * application's screen mode change. - * <p> - * At least we only return <code>true</true> if the owner, ie. the Screen, - * has changed the screen mode and if the original screen mode - * is not current the current one. - * </p> - * @return - */ - public final boolean isOriginalModeChangedByOwner() { - lock(); - try { - return screenModeChangedByOwner && !isCurrentModeOriginalMode(); - } finally { - unlock(); - } - } - - protected final boolean isCurrentModeOriginalMode() { - if(null != currentScreenMode && null != originalScreenMode) { - return currentScreenMode.hashCode() == originalScreenMode.hashCode(); - } - return true; - } - - protected final ArrayHashSet<ScreenMode> getScreenModes() { - return screenModes; - } - - protected final IntIntHashMap getScreenModesIdx2NativeIdx() { - return screenModesIdx2NativeIdx; - } - - protected final int addListener(ScreenModeListener l) { - lock(); - try { - listener.add(l); - if(DEBUG) { - System.err.println("ScreenModeStatus.addListener (size: "+listener.size()+"): "+l); - } - return listener.size(); - } finally { - unlock(); - } - } - - protected final int removeListener(ScreenModeListener l) { - lock(); - try { - if(!listener.remove(l)) { - throw new RuntimeException("ScreenModeListener "+l+" not contained"); - } - if(DEBUG) { - System.err.println("ScreenModeStatus.removeListener (size: "+listener.size()+"): "+l); - } - return listener.size(); - } finally { - unlock(); - } - } - - protected final void fireScreenModeChangeNotify(ScreenMode desiredScreenMode) { - lock(); - try { - for(int i=0; i<listener.size(); i++) { - listener.get(i).screenModeChangeNotify(desiredScreenMode); - } - } finally { - unlock(); - } - } - - protected void fireScreenModeChanged(ScreenMode currentScreenMode, boolean success) { - lock(); - try { - if(success) { - this.currentScreenMode = currentScreenMode; - this.screenModeChangedByOwner = !isCurrentModeOriginalMode(); - } - for(int i=0; i<listener.size(); i++) { - listener.get(i).screenModeChanged(currentScreenMode, success); - } - } finally { - unlock(); - } - } - - protected final void lock() throws RuntimeException { - lock.lock(); - } - - protected final void unlock() throws RuntimeException { - lock.unlock(); - } -} diff --git a/src/newt/classes/jogamp/newt/ScreenMonitorState.java b/src/newt/classes/jogamp/newt/ScreenMonitorState.java new file mode 100644 index 000000000..01e6cfee9 --- /dev/null +++ b/src/newt/classes/jogamp/newt/ScreenMonitorState.java @@ -0,0 +1,195 @@ +/** + * Copyright 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt; + +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.Screen; +import com.jogamp.newt.MonitorMode; +import com.jogamp.newt.event.MonitorEvent; +import com.jogamp.newt.event.MonitorModeListener; + +import java.util.ArrayList; +import java.util.HashMap; + +public class ScreenMonitorState { + private static boolean DEBUG = Screen.DEBUG; + + private final RecursiveLock lock = LockFactory.createRecursiveLock(); + private final ArrayHashSet<MonitorDevice> allMonitors; + private final ArrayHashSet<MonitorMode> allMonitorModes; + private ArrayList<MonitorModeListener> listener = new ArrayList<MonitorModeListener>(); + + private static HashMap<String, ScreenMonitorState> screenFQN2ScreenMonitorState = new HashMap<String, ScreenMonitorState>(); + private static RecursiveLock screen2ScreenMonitorState = LockFactory.createRecursiveLock(); + + protected static void mapScreenMonitorState(String screenFQN, ScreenMonitorState sms) { + screen2ScreenMonitorState.lock(); + try { + ScreenMonitorState _sms = screenFQN2ScreenMonitorState.get(screenFQN); + if( null != _sms ) { + throw new RuntimeException("ScreenMonitorState "+_sms+" already mapped to "+screenFQN); + } + screenFQN2ScreenMonitorState.put(screenFQN, sms); + if(DEBUG) { + System.err.println("ScreenMonitorState.map "+screenFQN+" -> "+sms); + } + } finally { + screen2ScreenMonitorState.unlock(); + } + } + + /** + * @param screen the prev user + * @return true if mapping is empty, ie no more usage of the mapped ScreenMonitorState + */ + protected static void unmapScreenMonitorState(String screenFQN) { + screen2ScreenMonitorState.lock(); + try { + unmapScreenMonitorStateUnlocked(screenFQN); + } finally { + screen2ScreenMonitorState.unlock(); + } + } + protected static void unmapScreenMonitorStateUnlocked(String screenFQN) { + ScreenMonitorState sms = screenFQN2ScreenMonitorState.remove(screenFQN); + if(DEBUG) { + System.err.println("ScreenMonitorState.unmap "+screenFQN+" -> "+sms); + } + } + + protected static ScreenMonitorState getScreenMonitorState(String screenFQN) { + screen2ScreenMonitorState.lock(); + try { + return getScreenMonitorStateUnlocked(screenFQN); + } finally { + screen2ScreenMonitorState.unlock(); + } + } + protected static ScreenMonitorState getScreenMonitorStateUnlocked(String screenFQN) { + return screenFQN2ScreenMonitorState.get(screenFQN); + } + + protected static void lockScreenMonitorState() { + screen2ScreenMonitorState.lock(); + } + + protected static void unlockScreenMonitorState() { + screen2ScreenMonitorState.unlock(); + } + + public ScreenMonitorState(ArrayHashSet<MonitorDevice> allMonitors, + ArrayHashSet<MonitorMode> allMonitorModes) { + this.allMonitors = allMonitors; + this.allMonitorModes = allMonitorModes; + } + + protected ArrayHashSet<MonitorDevice> getMonitorDevices() { + return allMonitors; + } + + protected ArrayHashSet<MonitorMode> getMonitorModes() { + return allMonitorModes; + } + + protected final int addListener(MonitorModeListener l) { + lock(); + try { + listener.add(l); + if(DEBUG) { + System.err.println("ScreenMonitorState.addListener (size: "+listener.size()+"): "+l); + } + return listener.size(); + } finally { + unlock(); + } + } + + protected final int removeListener(MonitorModeListener l) { + lock(); + try { + if(!listener.remove(l)) { + throw new RuntimeException("MonitorModeListener "+l+" not contained"); + } + if(DEBUG) { + System.err.println("ScreenMonitorState.removeListener (size: "+listener.size()+"): "+l); + } + return listener.size(); + } finally { + unlock(); + } + } + + protected final MonitorDevice getMonitor(MonitorDevice monitor) { + return allMonitors.get(monitor); + } + + protected final void validateMonitor(MonitorDevice monitor) { + final MonitorDevice md = allMonitors.get(monitor); + if( null == md ) { + throw new InternalError("Monitor unknown: "+monitor); + } + } + + protected final void fireMonitorModeChangeNotify(MonitorDevice monitor, MonitorMode desiredMode) { + lock(); + try { + validateMonitor(monitor); + final MonitorEvent me = new MonitorEvent(MonitorEvent.EVENT_MONITOR_MODE_CHANGE_NOTIFY, monitor, System.currentTimeMillis(), desiredMode); + for(int i=0; i<listener.size(); i++) { + listener.get(i).monitorModeChangeNotify(me); + } + } finally { + unlock(); + } + } + + protected void fireMonitorModeChanged(MonitorDevice monitor, MonitorMode currentMode, boolean success) { + lock(); + try { + validateMonitor(monitor); + final MonitorEvent me = new MonitorEvent(MonitorEvent.EVENT_MONITOR_MODE_CHANGED, monitor, System.currentTimeMillis(), currentMode); + for(int i=0; i<listener.size(); i++) { + listener.get(i).monitorModeChanged(me, success); + } + } finally { + unlock(); + } + } + + protected final void lock() throws RuntimeException { + lock.lock(); + } + + protected final void unlock() throws RuntimeException { + lock.unlock(); + } +} diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 074f635e4..7f7cb61a9 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,33 +29,15 @@ * 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 java.util.ArrayList; +import java.lang.ref.WeakReference; import java.lang.reflect.Method; - -import com.jogamp.common.util.ReflectionUtil; -import com.jogamp.newt.NewtFactory; -import com.jogamp.newt.Display; -import com.jogamp.newt.Screen; -import com.jogamp.newt.Window; -import com.jogamp.common.util.locks.LockFactory; -import com.jogamp.common.util.locks.RecursiveLock; -import com.jogamp.newt.ScreenMode; -import com.jogamp.newt.event.InputEvent; -import com.jogamp.newt.event.KeyEvent; -import com.jogamp.newt.event.KeyListener; -import com.jogamp.newt.event.MouseEvent; -import com.jogamp.newt.event.MouseListener; -import com.jogamp.newt.event.NEWTEvent; -import com.jogamp.newt.event.NEWTEventConsumer; -import com.jogamp.newt.event.ScreenModeListener; -import com.jogamp.newt.event.WindowEvent; -import com.jogamp.newt.event.WindowListener; -import com.jogamp.newt.event.WindowUpdateEvent; +import java.util.ArrayList; +import java.util.List; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; @@ -65,28 +47,115 @@ import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.OffscreenLayerSurface; import javax.media.nativewindow.SurfaceUpdatedListener; import javax.media.nativewindow.WindowClosingProtocol; import javax.media.nativewindow.util.DimensionImmutable; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.InsetsImmutable; +import javax.media.nativewindow.util.PixelRectangle; import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.PointImmutable; import javax.media.nativewindow.util.Rectangle; +import javax.media.nativewindow.util.RectangleImmutable; import jogamp.nativewindow.SurfaceUpdatedHelper; +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.common.util.IntBitfield; +import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; +import com.jogamp.newt.Display; +import com.jogamp.newt.Display.PointerIcon; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.Screen; +import com.jogamp.newt.Window; +import com.jogamp.newt.event.DoubleTapScrollGesture; +import com.jogamp.newt.event.GestureHandler; +import com.jogamp.newt.event.InputEvent; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.MonitorEvent; +import com.jogamp.newt.event.MonitorModeListener; +import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.MouseEvent.PointerType; +import com.jogamp.newt.event.MouseListener; +import com.jogamp.newt.event.NEWTEvent; +import com.jogamp.newt.event.NEWTEventConsumer; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.event.WindowListener; +import com.jogamp.newt.event.WindowUpdateEvent; + public abstract class WindowImpl implements Window, NEWTEventConsumer { - public static final boolean DEBUG_TEST_REPARENT_INCOMPATIBLE = Debug.isPropertyDefined("newt.test.Window.reparent.incompatible", true); - + public static final boolean DEBUG_TEST_REPARENT_INCOMPATIBLE; + + static { + Debug.initSingleton(); + DEBUG_TEST_REPARENT_INCOMPATIBLE = Debug.isPropertyDefined("newt.test.Window.reparent.incompatible", true); + + ScreenImpl.initSingleton(); + } + + protected static final ArrayList<WeakReference<WindowImpl>> windowList = new ArrayList<WeakReference<WindowImpl>>(); + + /** Maybe utilized at a shutdown hook, impl. does not block. */ + public static final void shutdownAll() { + final int wCount = windowList.size(); + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.shutdownAll "+wCount+" instances, on thread "+getThreadName()); + } + for(int i=0; i<wCount && windowList.size()>0; i++) { // be safe .. + final WindowImpl w = windowList.remove(0).get(); + if(DEBUG_IMPLEMENTATION) { + final long wh = null != w ? w.getWindowHandle() : 0; + System.err.println("Window.shutdownAll["+(i+1)+"/"+wCount+"]: "+toHexString(wh)+", GCed "+(null==w)); + } + if( null != w ) { + w.shutdown(); + } + } + } + private static void addWindow2List(WindowImpl window) { + synchronized(windowList) { + // GC before add + int i=0, gced=0; + while( i < windowList.size() ) { + if( null == windowList.get(i).get() ) { + gced++; + windowList.remove(i); + } else { + i++; + } + } + windowList.add(new WeakReference<WindowImpl>(window)); + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.addWindow2List: GCed "+gced+", size "+windowList.size()); + } + } + } + /** Timeout of queued events (repaint and resize) */ - static final long QUEUED_EVENT_TO = 1200; // ms - + static final long QUEUED_EVENT_TO = 1200; // ms + + private static final PointerType[] constMousePointerTypes = new PointerType[] { PointerType.Mouse }; + + // + // Volatile: Multithread Mutable Access + // private volatile long windowHandle = 0; // lifecycle critical private volatile boolean visible = false; // lifecycle critical - private RecursiveLock windowLock = LockFactory.createRecursiveLock(); // Window instance wide lock - private RecursiveLock surfaceLock = LockFactory.createRecursiveLock(); // Surface only lock - + private volatile boolean hasFocus = false; + private volatile int width = 128, height = 128; // client-area size w/o insets, default: may be overwritten by user + private volatile int x = 64, y = 64; // client-area pos w/o insets + private volatile Insets insets = new Insets(); // insets of decoration (if top-level && decorated) + private boolean blockInsetsChange = false; // block insets change (from same thread) + + private final RecursiveLock windowLock = LockFactory.createRecursiveLock(); // Window instance wide lock + private int surfaceLockCount = 0; // surface lock recursion count + private ScreenImpl screen; // never null after create - may change reference though (reparent) private boolean screenReferenceAdded = false; private NativeWindow parentWindow = null; @@ -94,56 +163,102 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private AbstractGraphicsConfiguration config = null; // control access due to delegation protected CapabilitiesImmutable capsRequested = null; protected CapabilitiesChooser capabilitiesChooser = null; // default null -> default - private boolean fullscreen = false, hasFocus = false, brokenFocusChange = false; - private int width = 128, height = 128; // client-area size w/o insets, default: may be overwritten by user - private int x = 64, y = 64; // client-area pos w/o insets + private boolean fullscreen = false, brokenFocusChange = false; + private List<MonitorDevice> fullscreenMonitors = null; + private boolean fullscreenUseMainMonitor = true; private boolean autoPosition = true; // default: true (allow WM to choose top-level position, if not set by user) - private Insets insets = new Insets(); // insets of decoration (if top-level && decorated) - + private int nfs_width, nfs_height, nfs_x, nfs_y; // non fullscreen client-area size/pos w/o insets + private boolean nfs_alwaysOnTop; // non fullscreen alwaysOnTop setting private NativeWindow nfs_parent = null; // non fullscreen parent, in case explicit reparenting is performed (offscreen) private String title = "Newt Window"; private boolean undecorated = false; private boolean alwaysOnTop = false; + private PointerIconImpl pointerIcon = null; private boolean pointerVisible = true; private boolean pointerConfined = false; private LifecycleHook lifecycleHook = null; - private boolean handleDestroyNotify = true; + private Runnable windowDestroyNotifyAction = null; private FocusRunnable focusAction = null; private KeyListener keyboardFocusHandler = null; - private SurfaceUpdatedHelper surfaceUpdatedHelper = new SurfaceUpdatedHelper(); - - private Object childWindowsLock = new Object(); - private ArrayList<NativeWindow> childWindows = new ArrayList<NativeWindow>(); + private final SurfaceUpdatedHelper surfaceUpdatedHelper = new SurfaceUpdatedHelper(); + + private final Object childWindowsLock = new Object(); + private final ArrayList<NativeWindow> childWindows = new ArrayList<NativeWindow>(); private ArrayList<MouseListener> mouseListeners = new ArrayList<MouseListener>(); - private int mouseButtonPressed = 0; // current pressed mouse button number - private long lastMousePressed = 0; // last time when a mouse button was pressed - private int lastMouseClickCount = 0; // last mouse button click count - private boolean mouseInWindow = false;// mouse entered window - is inside the window (may be synthetic) - private Point lastMousePosition = new Point(); - private ArrayList<KeyListener> keyListeners = new ArrayList<KeyListener>(); + /** from event passing: {@link WindowImpl#consumePointerEvent(MouseEvent)}. */ + private static class PointerState0 { + /** Pointer entered window - is inside the window (may be synthetic) */ + boolean insideWindow = false; + /** Mouse EXIT has been sent (only for MOUSE type enter/exit)*/ + boolean exitSent = false; - private ArrayList<WindowListener> windowListeners = new ArrayList<WindowListener>(); - private boolean repaintQueued = false; + /** last time when a pointer button was pressed */ + long lastButtonPressTime = 0; - // Workaround for initialization order problems on Mac OS X - // between native Newt and (apparently) Fmod -- if Fmod is - // initialized first then the connection to the window server - // breaks, leading to errors from deep within the AppKit - public static void init(String type) { - if (NativeWindowFactory.TYPE_MACOSX.equals(type)) { - try { - getWindowClass(type); - } catch (Exception e) { - e.printStackTrace(); + /** Pointer in dragging mode */ + boolean dragging = false; + + void clearButton() { + lastButtonPressTime = 0; + } + public String toString() { return "PState0[inside "+insideWindow+", exitSent "+exitSent+", lastPress "+lastButtonPressTime+", dragging "+dragging+"]"; } + } + private final PointerState0 pState0 = new PointerState0(); + + /** from direct input: {@link WindowImpl#doPointerEvent(boolean, boolean, int[], short, int, int, boolean, short[], int[], int[], float[], float, float[], float)}. */ + private static class PointerState1 extends PointerState0 { + /** Current pressed mouse button number */ + short buttonPressed = (short)0; + /** Current pressed mouse button modifier mask */ + int buttonPressedMask = 0; + /** Last mouse button click count */ + short lastButtonClickCount = (short)0; + + @Override + final void clearButton() { + super.clearButton(); + lastButtonClickCount = (short)0; + if( !dragging || 0 == buttonPressedMask ) { + buttonPressed = 0; + buttonPressedMask = 0; + dragging = false; + } + } + + /** Last pointer-move position for 8 touch-down pointers */ + final Point[] movePositions = new Point[] { + new Point(), new Point(), new Point(), new Point(), + new Point(), new Point(), new Point(), new Point() }; + final Point getMovePosition(int id) { + if( 0 <= id && id < movePositions.length ) { + return movePositions[id]; } + return null; } + public final String toString() { return "PState1[inside "+insideWindow+", exitSent "+exitSent+", lastPress "+lastButtonPressTime+ + ", pressed [button "+buttonPressed+", mask "+buttonPressedMask+", dragging "+dragging+", clickCount "+lastButtonClickCount+"]"; } } + private final PointerState1 pState1 = new PointerState1(); + + /** Pointer names -> pointer ID (consecutive index, starting w/ 0) */ + private final ArrayHashSet<Integer> pName2pID = new ArrayHashSet<Integer>(); + + private boolean defaultGestureHandlerEnabled = true; + private DoubleTapScrollGesture gesture2PtrTouchScroll = null; + private ArrayList<GestureHandler> pointerGestureHandler = new ArrayList<GestureHandler>(); + + private ArrayList<GestureHandler.GestureListener> gestureListeners = new ArrayList<GestureHandler.GestureListener>(); + + private ArrayList<KeyListener> keyListeners = new ArrayList<KeyListener>(); + + private ArrayList<WindowListener> windowListeners = new ArrayList<WindowListener>(); + private boolean repaintQueued = false; // // Construction Methods @@ -152,26 +267,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private static Class<?> getWindowClass(String type) throws ClassNotFoundException { - Class<?> windowClass = NewtFactory.getCustomClass(type, "Window"); - if(null==windowClass) { - if (NativeWindowFactory.TYPE_ANDROID == type) { - windowClass = Class.forName("jogamp.newt.driver.android.AndroidWindow"); - } else if (NativeWindowFactory.TYPE_EGL == type) { - windowClass = Class.forName("jogamp.newt.driver.kd.KDWindow"); - } else if (NativeWindowFactory.TYPE_WINDOWS == type) { - windowClass = Class.forName("jogamp.newt.driver.windows.WindowsWindow"); - } else if (NativeWindowFactory.TYPE_MACOSX == type) { - windowClass = Class.forName("jogamp.newt.driver.macosx.MacWindow"); - } else if (NativeWindowFactory.TYPE_X11 == type) { - windowClass = Class.forName("jogamp.newt.driver.x11.X11Window"); - } else if (NativeWindowFactory.TYPE_AWT == type) { - windowClass = Class.forName("jogamp.newt.driver.awt.AWTWindow"); - } else { - throw new NativeWindowException("Unknown window type \"" + type + "\""); - } - } + final Class<?> windowClass = NewtFactory.getCustomClass(type, "WindowDriver"); if(null==windowClass) { - throw new ClassNotFoundException("Failed to find NEWT Window Class <"+type+".Window>"); + throw new ClassNotFoundException("Failed to find NEWT Window Class <"+type+".WindowDriver>"); } return windowClass; } @@ -189,15 +287,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer window.parentWindowHandle = parentWindowHandle; window.screen = (ScreenImpl) screen; window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable(); - window.setUndecorated(0!=parentWindowHandle); window.instantiationFinished(); + addWindow2List(window); return window; } catch (Throwable t) { t.printStackTrace(); throw new NativeWindowException(t); } } - + public static WindowImpl create(Object[] cstrArguments, Screen screen, CapabilitiesImmutable caps) { try { Class<?> windowClass = getWindowClass(screen.getDisplay().getType()); @@ -212,16 +310,32 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer WindowImpl window = (WindowImpl) ReflectionUtil.createInstance( windowClass, cstrArgumentTypes, cstrArguments ) ; window.screen = (ScreenImpl) screen; window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable(); + window.instantiationFinished(); + addWindow2List(window); return window; } catch (Throwable t) { throw new NativeWindowException(t); } } + /** Fast invalidation of instance w/o any blocking function call. */ + private final void shutdown() { + if(null!=lifecycleHook) { + lifecycleHook.shutdownRenderingAction(); + } + setWindowHandle(0); + visible = false; + fullscreen = false; + fullscreenMonitors = null; + fullscreenUseMainMonitor = true; + hasFocus = false; + parentWindowHandle = 0; + } + protected final void setGraphicsConfiguration(AbstractGraphicsConfiguration cfg) { config = cfg; } - + public static interface LifecycleHook { /** * Reset of internal state counter, ie totalFrames, etc. @@ -229,15 +343,22 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer */ public abstract void resetCounter(); - /** - * Invoked after Window setVisible, + /** + * Invoked after Window setVisible, * allows allocating resources depending on the native Window. * Called from EDT while window is locked. */ void setVisibleActionPost(boolean visible, boolean nativeWindowCreated); - /** - * Invoked before Window destroy action, + /** + * Notifies the receiver to preserve resources (GL, ..) + * for the next destroy*() calls (only), if supported and if <code>value</code> is <code>true</code>, otherwise clears preservation flag. + * @param value <code>true</code> to set the one-shot preservation if supported, otherwise clears it. + */ + void preserveGLStateAtDestroy(boolean value); + + /** + * Invoked before Window destroy action, * allows releasing of resources depending on the native Window.<br> * Surface not locked yet.<br> * Called not necessarily from EDT. @@ -253,7 +374,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer void destroyActionInLock(); /** - * Invoked for expensive modifications, ie while reparenting and ScreenMode change.<br> + * Invoked for expensive modifications, ie while reparenting and MonitorMode change.<br> * No lock is hold when invoked.<br> * * @return true is paused, otherwise false. If true {@link #resumeRenderingAction()} shall be issued. @@ -263,12 +384,20 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer boolean pauseRenderingAction(); /** - * Invoked for expensive modifications, ie while reparenting and ScreenMode change. + * Invoked for expensive modifications, ie while reparenting and MonitorMode change. * No lock is hold when invoked.<br> * * @see #pauseRenderingAction() */ void resumeRenderingAction(); + + /** + * Shutdown rendering action (thread) abnormally. + * <p> + * Should be called only at shutdown, if necessary. + * </p> + */ + void shutdownRenderingAction(); } private boolean createNative() { @@ -279,37 +408,59 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } else { tStart = 0; } - - if( null != parentWindow && + + if( null != parentWindow && NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindow.lockSurface() ) { throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); } - + + final boolean hasParent = null != parentWindow || 0 != this.parentWindowHandle; + // child window: position defaults to 0/0, no auto position, no negative position - if( null != parentWindow && ( autoPosition || 0>getX() || 0>getY() ) ) { + if( hasParent && ( autoPosition || 0>getX() || 0>getY() ) ) { definePosition(0, 0); } boolean postParentlockFocus = false; try { if(validateParentWindowHandle()) { - if(screenReferenceAdded) { - throw new InternalError("XXX"); - } - if(canCreateNativeImpl()) { + if( !screenReferenceAdded ) { screen.addReference(); screenReferenceAdded = true; + } + if(canCreateNativeImpl()) { + final int wX, wY; + final boolean usePosition; + if( autoPosition ) { + wX = 0; + wY = 0; + usePosition = false; + } else { + wX = getX(); + wY = getY(); + usePosition = true; + } + final long t0 = System.currentTimeMillis(); createNativeImpl(); - screen.addScreenModeListener(screenModeListenerImpl); + screen.addMonitorModeListener(monitorModeListenerImpl); setTitleImpl(title); - setPointerVisibleImpl(pointerVisible); + setPointerIconIntern(pointerIcon); + setPointerVisibleIntern(pointerVisible); confinePointerImpl(pointerConfined); - if(waitForVisible(true, false)) { + setKeyboardVisible(keyboardVisible); + final long remainingV = waitForVisible(true, false); + if( 0 <= remainingV ) { if(isFullscreen()) { synchronized(fullScreenAction) { fullscreen = false; // trigger a state change fullScreenAction.init(true); fullScreenAction.run(); } + } else if ( !hasParent ) { + // Wait until position is reached within tolerances, either auto-position or custom position. + waitForPosition(usePosition, wX, wY, Window.TIMEOUT_NATIVEWINDOW); + } + if (DEBUG_IMPLEMENTATION) { + System.err.println("Window.createNative(): elapsed "+(System.currentTimeMillis()-t0)+" ms"); } postParentlockFocus = true; } @@ -322,7 +473,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } if(postParentlockFocus) { // harmonize focus behavior for all platforms: focus on creation - requestFocusInt(isFullscreen() /* skipFocusAction */); + requestFocusInt(isFullscreen() /* skipFocusAction if fullscreen */); ((DisplayImpl) screen.getDisplay()).dispatchMessagesNative(); // status up2date } if(DEBUG_IMPLEMENTATION) { @@ -387,16 +538,18 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer //---------------------------------------------------------------------- // WindowClosingProtocol implementation // - private Object closingListenerLock = new Object(); + private final Object closingListenerLock = new Object(); private WindowClosingMode defaultCloseOperation = WindowClosingMode.DISPOSE_ON_CLOSE; - public WindowClosingMode getDefaultCloseOperation() { + @Override + public final WindowClosingMode getDefaultCloseOperation() { synchronized (closingListenerLock) { return defaultCloseOperation; } } - public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { + @Override + public final WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { synchronized (closingListenerLock) { WindowClosingMode _op = defaultCloseOperation; defaultCloseOperation = op; @@ -410,28 +563,28 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer /** * Notifies the driver impl. that the instantiation is finished, - * ie. instance created and all fields set. + * ie. instance created and all fields set. */ protected void instantiationFinished() { // nop } - + protected boolean canCreateNativeImpl() { return true; // default: always able to be created } - - /** + + /** * The native implementation must set the native windowHandle.<br> * * <p> * The implementation shall respect the states {@link #isAlwaysOnTop()}/{@link #FLAG_IS_ALWAYSONTOP} and * {@link #isUndecorated()}/{@link #FLAG_IS_UNDECORATED}, ie. the created window shall reflect those settings. * </p> - * + * * <p> * The implementation should invoke the referenced java state callbacks * to notify this Java object of state changes.</p> - * + * * @see #windowDestroyNotify(boolean) * @see #focusChanged(boolean, boolean) * @see #visibleChanged(boolean, boolean) @@ -443,11 +596,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer protected abstract void closeNativeImpl(); - /** - * The native implementation must invoke {@link #focusChanged(boolean, boolean)} - * to change the focus state, if <code>force == false</code>. - * This may happen asynchronous within {@link #TIMEOUT_NATIVEWINDOW}. - * + /** + * Async request which shall be performed within {@link #TIMEOUT_NATIVEWINDOW}. + * <p> + * If if <code>force == false</code> the native implementation + * may only request focus if not yet owner.</p> + * <p> + * {@link #focusChanged(boolean, boolean)} should be called + * to notify about the focus traversal. + * </p> + * * @param force if true, bypass {@link #focusChanged(boolean, boolean)} and force focus request */ protected abstract void requestFocusImpl(boolean force); @@ -457,22 +615,23 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer public static final int FLAG_CHANGE_FULLSCREEN = 1 << 2; public static final int FLAG_CHANGE_ALWAYSONTOP = 1 << 3; public static final int FLAG_CHANGE_VISIBILITY = 1 << 4; - + public static final int FLAG_HAS_PARENT = 1 << 8; public static final int FLAG_IS_UNDECORATED = 1 << 9; public static final int FLAG_IS_FULLSCREEN = 1 << 10; - public static final int FLAG_IS_ALWAYSONTOP = 1 << 11; - public static final int FLAG_IS_VISIBLE = 1 << 12; + public static final int FLAG_IS_FULLSCREEN_SPAN = 1 << 11; + public static final int FLAG_IS_ALWAYSONTOP = 1 << 12; + public static final int FLAG_IS_VISIBLE = 1 << 13; /** * The native implementation should invoke the referenced java state callbacks * to notify this Java object of state changes. - * + * * <p> * Implementations shall set x/y to 0, in case it's negative. This could happen due * to insets and positioning a decorated window to 0/0, which would place the frame * outside of the screen.</p> - * + * * @param x client-area position, or <0 if unchanged * @param y client-area position, or <0 if unchanged * @param width client-area size, or <=0 if unchanged @@ -484,6 +643,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer */ protected abstract boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags); + /** + * Tests whether a single reconfigure flag is supported by implementation. + * <p> + * Default is all but {@link #FLAG_IS_FULLSCREEN_SPAN} + * </p> + */ + protected boolean isReconfigureFlagSupported(int changeFlags) { + return 0 == ( changeFlags & FLAG_IS_FULLSCREEN_SPAN ); + } + protected int getReconfigureFlags(int changeFlags, boolean visible) { return changeFlags |= ( ( 0 != getParentWindowHandle() ) ? FLAG_HAS_PARENT : 0 ) | ( isUndecorated() ? FLAG_IS_UNDECORATED : 0 ) | @@ -494,60 +663,68 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer protected static String getReconfigureFlagsAsString(StringBuilder sb, int flags) { if(null == sb) { sb = new StringBuilder(); } sb.append("["); - + if( 0 != ( FLAG_CHANGE_PARENTING & flags) ) { sb.append("*"); } - sb.append("PARENT_"); + sb.append("PARENT "); sb.append(0 != ( FLAG_HAS_PARENT & flags)); sb.append(", "); - + if( 0 != ( FLAG_CHANGE_FULLSCREEN & flags) ) { sb.append("*"); } - sb.append("FS_"); + sb.append("FS "); sb.append(0 != ( FLAG_IS_FULLSCREEN & flags)); - sb.append(", "); + sb.append("[span "); + sb.append(0 != ( FLAG_IS_FULLSCREEN_SPAN & flags)); + sb.append("], "); if( 0 != ( FLAG_CHANGE_DECORATION & flags) ) { sb.append("*"); } - sb.append("UNDECOR_"); + sb.append("UNDECOR "); sb.append(0 != ( FLAG_IS_UNDECORATED & flags)); sb.append(", "); - + if( 0 != ( FLAG_CHANGE_ALWAYSONTOP & flags) ) { sb.append("*"); } - sb.append("ALWAYSONTOP_"); + sb.append("ALWAYSONTOP "); sb.append(0 != ( FLAG_IS_ALWAYSONTOP & flags)); sb.append(", "); - + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { sb.append("*"); } - sb.append("VISIBLE_"); + sb.append("VISIBLE "); sb.append(0 != ( FLAG_IS_VISIBLE & flags)); - + sb.append("]"); return sb.toString(); } - + protected void setTitleImpl(String title) {} /** - * Return screen coordinates of the given coordinates - * or null, in which case a NativeWindow traversal shall being used + * Translates the given window client-area coordinates with top-left origin + * to screen coordinates. + * <p> + * Since the position reflects the client area, it does not include the insets. + * </p> + * <p> + * May return <code>null</code>, in which case the caller shall traverse through the NativeWindow tree * as demonstrated in {@link #getLocationOnScreen(javax.media.nativewindow.util.Point)}. + * </p> * * @return if not null, the screen location of the given coordinates */ protected abstract Point getLocationOnScreenImpl(int x, int y); - + /** Triggered by user via {@link #getInsets()}.<br> - * Implementations may implement this hook to update the insets.<br> + * Implementations may implement this hook to update the insets.<br> * However, they may prefer the event driven path via {@link #insetsChanged(boolean, int, int, int, int)}. - * + * * @see #getInsets() * @see #insetsChanged(boolean, int, int, int, int) */ @@ -556,15 +733,18 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer protected boolean setPointerVisibleImpl(boolean pointerVisible) { return false; } protected boolean confinePointerImpl(boolean confine) { return false; } protected void warpPointerImpl(int x, int y) { } - + protected void setPointerIconImpl(final PointerIconImpl pi) { } + //---------------------------------------------------------------------- // NativeSurface // - public final int lockSurface() { - windowLock.lock(); - surfaceLock.lock(); - int res = surfaceLock.getHoldCount() == 1 ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; // new lock ? + @Override + public final int lockSurface() throws NativeWindowException, RuntimeException { + final RecursiveLock _wlock = windowLock; + _wlock.lock(); + surfaceLockCount++; + int res = ( 1 == surfaceLockCount ) ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; // new lock ? if ( LOCK_SURFACE_NOT_READY == res ) { try { @@ -581,19 +761,20 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } finally { if (LOCK_SURFACE_NOT_READY >= res) { - surfaceLock.unlock(); - windowLock.unlock(); + surfaceLockCount--; + _wlock.unlock(); } } } return res; } + @Override public final void unlockSurface() { - surfaceLock.validateLocked(); - windowLock.validateLocked(); + final RecursiveLock _wlock = windowLock; + _wlock.validateLocked(); - if (surfaceLock.getHoldCount() == 1) { + if ( 1 == surfaceLockCount ) { final AbstractGraphicsDevice adevice = getGraphicsConfiguration().getScreen().getDevice(); try { unlockSurfaceImpl(); @@ -601,52 +782,67 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer adevice.unlock(); } } - surfaceLock.unlock(); - windowLock.unlock(); + surfaceLockCount--; + _wlock.unlock(); } - public final boolean isWindowLockedByOtherThread() { + @Override + public final boolean isSurfaceLockedByOtherThread() { return windowLock.isLockedByOtherThread(); } - public final boolean isWindowLocked() { - return windowLock.isLocked(); + @Override + public final Thread getSurfaceLockOwner() { + return windowLock.getOwner(); } - public final Thread getWindowLockOwner() { - return windowLock.getOwner(); + public final RecursiveLock getLock() { + return windowLock; } - public final boolean isSurfaceLockedByOtherThread() { - return surfaceLock.isLockedByOtherThread(); + @Override + public long getSurfaceHandle() { + return windowHandle; // default: return window handle } - public final boolean isSurfaceLocked() { - return surfaceLock.isLocked(); + @Override + public boolean surfaceSwap() { + return false; } - public final Thread getSurfaceLockOwner() { - return surfaceLock.getOwner(); + @Override + public final void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { + surfaceUpdatedHelper.addSurfaceUpdatedListener(l); } - public long getSurfaceHandle() { - return windowHandle; // default: return window handle + @Override + public final void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException { + surfaceUpdatedHelper.addSurfaceUpdatedListener(index, l); } - public boolean surfaceSwap() { - return false; + @Override + public final void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { + surfaceUpdatedHelper.removeSurfaceUpdatedListener(l); + } + + @Override + public final void surfaceUpdated(Object updater, NativeSurface ns, long when) { + surfaceUpdatedHelper.surfaceUpdated(updater, ns, when); } + @Override public final AbstractGraphicsConfiguration getGraphicsConfiguration() { return config.getNativeGraphicsConfiguration(); } + @Override public final long getDisplayHandle() { - return getScreen().getDisplay().getHandle(); + return config.getNativeGraphicsConfiguration().getScreen().getDevice().getHandle(); } + @Override public final int getScreenIndex() { - return getScreen().getIndex(); + return screen.getIndex(); } //---------------------------------------------------------------------- @@ -655,22 +851,26 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // public final void destroy() - see below + @Override public final NativeWindow getParent() { return parentWindow; } + @Override public final long getWindowHandle() { return windowHandle; } + @Override public Point getLocationOnScreen(Point storage) { if(isNativeValid()) { Point d; - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { d = getLocationOnScreenImpl(0, 0); } finally { - windowLock.unlock(); + _lock.unlock(); } if(null!=d) { if(null!=storage) { @@ -698,27 +898,31 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // Window // + @Override public final boolean isNativeValid() { return 0 != windowHandle ; } + @Override public final Screen getScreen() { return screen; } + @Override + public final MonitorDevice getMainMonitor() { + return screen.getMainMonitor(new Rectangle(getX(), getY(), getWidth(), getHeight())); + } + protected final void setVisibleImpl(boolean visible, int x, int y, int width, int height) { - reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_VISIBILITY, visible)); - } + reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_VISIBILITY, visible)); + } final void setVisibleActionImpl(boolean visible) { boolean nativeWindowCreated = false; boolean madeVisible = false; - - windowLock.lock(); - try { - if(null!=lifecycleHook) { - lifecycleHook.resetCounter(); - } + final RecursiveLock _lock = windowLock; + _lock.lock(); + try { if(!visible && null!=childWindows && childWindows.size()>0) { synchronized(childWindowsLock) { for(int i = 0; i < childWindows.size(); i++ ) { @@ -735,12 +939,14 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer madeVisible = nativeWindowCreated; } // always flag visible, allowing a retry .. - WindowImpl.this.visible = true; + WindowImpl.this.visible = true; } else if(WindowImpl.this.visible != visible) { if(isNativeValid()) { setVisibleImpl(visible, getX(), getY(), getWidth(), getHeight()); - WindowImpl.this.waitForVisible(visible, true); + WindowImpl.this.waitForVisible(visible, false); madeVisible = visible; + } else { + WindowImpl.this.visible = true; } } @@ -762,7 +968,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer System.err.println("Window setVisible: END ("+getThreadName()+") "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+WindowImpl.this.visible+", nativeWindowCreated: "+nativeWindowCreated+", madeVisible: "+madeVisible); } } finally { - windowLock.unlock(); + if(null!=lifecycleHook) { + lifecycleHook.resetCounter(); + } + _lock.unlock(); } if( nativeWindowCreated || madeVisible ) { sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener @@ -775,54 +984,58 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer this.visible = visible; } + @Override public final void run() { setVisibleActionImpl(visible); } } - public void setVisible(boolean visible) { + @Override + public final void setVisible(boolean wait, boolean visible) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window setVisible: START ("+getThreadName()+") "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+this.visible+" -> "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+(null!=parentWindow)); } - runOnEDTIfAvail(true, new VisibleAction(visible)); + runOnEDTIfAvail(wait, new VisibleAction(visible)); } - + + @Override + public final void setVisible(boolean visible) { + setVisible(true, visible); + } + private class SetSizeAction implements Runnable { int width, height; + boolean disregardFS; - private SetSizeAction(int w, int h) { + private SetSizeAction(int w, int h, boolean disregardFS) { this.width = w; this.height = h; + this.disregardFS = disregardFS; } + @Override public final void run() { - boolean recreate = false; - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { - if ( !isFullscreen() && ( getWidth() != width || getHeight() != height ) ) { - recreate = isNativeValid() && !getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); + if ( ( disregardFS || !isFullscreen() ) && ( getWidth() != width || getHeight() != height ) ) { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window setSize: START "+getWidth()+"x"+getHeight()+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible+", recreate "+recreate); - } - if(recreate) { - // will trigger visibleAction:=2 -> create if wasVisible - final boolean wasVisible = WindowImpl.this.visible; - screen.addReference(); // retain screen - destroyAction.run(); - WindowImpl.this.visible = wasVisible; + System.err.println("Window setSize: START "+getWidth()+"x"+getHeight()+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible); } int visibleAction; // 0 nop, 1 invisible, 2 visible (create) - if ( isNativeValid() && 0>=width*height && visible ) { + if ( visible && isNativeValid() && ( 0 >= width || 0 >= height ) ) { visibleAction=1; // invisible defineSize(0, 0); - } else if ( !isNativeValid() && 0<width*height && visible ) { + } else if ( visible && !isNativeValid() && 0 < width && 0 < height ) { visibleAction = 2; // visible (create) defineSize(width, height); - } else if ( isNativeValid() ) { + } else if ( visible && isNativeValid() ) { visibleAction = 0; // this width/height will be set by windowChanged, called by the native implementation reconfigureWindowImpl(getX(), getY(), width, height, getReconfigureFlags(0, isVisible())); + WindowImpl.this.waitForSize(width, height, false, TIMEOUT_NATIVEWINDOW); } else { + // invisible or invalid w/ 0 size visibleAction = 0; defineSize(width, height); } @@ -835,22 +1048,25 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } finally { - if(recreate) { - screen.removeReference(); // bring back ref-count - } - windowLock.unlock(); + _lock.unlock(); } } } - public void setSize(int width, int height) { - runOnEDTIfAvail(true, new SetSizeAction(width, height)); - } - public void setTopLevelSize(int width, int height) { + private void setFullscreenSize(int width, int height) { + runOnEDTIfAvail(true, new SetSizeAction(width, height, true)); + } + @Override + public final void setSize(int width, int height) { + runOnEDTIfAvail(true, new SetSizeAction(width, height, false)); + } + @Override + public final void setTopLevelSize(int width, int height) { setSize(width - getInsets().getTotalWidth(), height - getInsets().getTotalHeight()); } private class DestroyAction implements Runnable { + @Override public final void run() { boolean animatorPaused = false; if(null!=lifecycleHook) { @@ -859,11 +1075,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(null!=lifecycleHook) { lifecycleHook.destroyActionPreLock(); } - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window DestroyAction() "+getThreadName()); + System.err.println("Window DestroyAction() hasScreen "+(null != screen)+", isNativeValid "+isNativeValid()+" - "+getThreadName()); } + + // send synced destroy-notify notification + sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); + // Childs first .. synchronized(childWindowsLock) { if(childWindows.size()>0) { @@ -873,8 +1094,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer while( clonedChildWindows.size() > 0 ) { NativeWindow nw = clonedChildWindows.remove(0); if(nw instanceof WindowImpl) { - ((WindowImpl)nw).sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); - ((WindowImpl)nw).destroy(); + ((WindowImpl)nw).windowDestroyNotify(true); } else { nw.destroy(); } @@ -887,16 +1107,19 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer lifecycleHook.destroyActionInLock(); } - if( null != screen ) { - if( isNativeValid() ) { - screen.removeScreenModeListener(screenModeListenerImpl); - closeNativeImpl(); - removeScreenReference(); - } - Display dpy = screen.getDisplay(); - if(null != dpy) { - dpy.validateEDT(); + if( isNativeValid() ) { + screen.removeMonitorModeListener(monitorModeListenerImpl); + closeNativeImpl(); + final AbstractGraphicsDevice cfgADevice = config.getScreen().getDevice(); + if( cfgADevice != screen.getDisplay().getGraphicsDevice() ) { // don't pull display's device + cfgADevice.close(); // ensure a cfg's device is closed } + setGraphicsConfiguration(null); + } + removeScreenReference(); + Display dpy = screen.getDisplay(); + if(null != dpy) { + dpy.validateEDTStopped(); } // send synced destroyed notification @@ -910,64 +1133,84 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer setWindowHandle(0); visible = false; fullscreen = false; + fullscreenMonitors = null; + fullscreenUseMainMonitor = true; hasFocus = false; parentWindowHandle = 0; - windowLock.unlock(); + _lock.unlock(); } if(animatorPaused) { lifecycleHook.resumeRenderingAction(); } - + // these refs shall be kept alive - resurrection via setVisible(true) /** if(null!=parentWindow && parentWindow instanceof Window) { ((Window)parentWindow).removeChild(WindowImpl.this); - } + } childWindows = null; surfaceUpdatedListeners = null; mouseListeners = null; keyListeners = null; capsRequested = null; lifecycleHook = null; - - screen = null; + + screen = null; windowListeners = null; parentWindow = null; - */ + */ } } private final DestroyAction destroyAction = new DestroyAction(); + @Override public void destroy() { - visible = false; // Immediately mark synchronized visibility flag, avoiding possible recreation + visible = false; // Immediately mark synchronized visibility flag, avoiding possible recreation runOnEDTIfAvail(true, destroyAction); } + protected void destroy(boolean preserveResources) { + if( null != lifecycleHook ) { + lifecycleHook.preserveGLStateAtDestroy( preserveResources ); + } + destroy(); + } + /** * @param cWin child window, must not be null * @param pWin parent window, may be null - * @return true if at least one of both window's configurations is offscreen + * @return true if at least one of both window's configurations is offscreen */ protected static boolean isOffscreenInstance(NativeWindow cWin, NativeWindow pWin) { boolean ofs = false; - if( null != cWin.getGraphicsConfiguration() ) { - ofs = !cWin.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); + final AbstractGraphicsConfiguration cWinCfg = cWin.getGraphicsConfiguration(); + if( null != cWinCfg ) { + ofs = !cWinCfg.getChosenCapabilities().isOnscreen(); } - if( !ofs && null != pWin && null != pWin.getGraphicsConfiguration() ) { - ofs |= !pWin.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); + if( !ofs && null != pWin ) { + final AbstractGraphicsConfiguration pWinCfg = pWin.getGraphicsConfiguration(); + if( null != pWinCfg ) { + ofs = !pWinCfg.getChosenCapabilities().isOnscreen(); + } } return ofs; } - + private class ReparentAction implements Runnable { - NativeWindow newParentWindow; - boolean forceDestroyCreate; + final NativeWindow newParentWindow; + final int topLevelX, topLevelY; + final int hints; ReparentOperation operation; - private ReparentAction(NativeWindow newParentWindow, boolean forceDestroyCreate) { + private ReparentAction(NativeWindow newParentWindow, int topLevelX, int topLevelY, int hints) { this.newParentWindow = newParentWindow; - this.forceDestroyCreate = forceDestroyCreate | DEBUG_TEST_REPARENT_INCOMPATIBLE; + this.topLevelX = topLevelX; + this.topLevelY = topLevelY; + if( DEBUG_TEST_REPARENT_INCOMPATIBLE ) { + hints |= REPARENT_HINT_FORCE_RECREATION; + } + this.hints = hints; this.operation = ReparentOperation.ACTION_INVALID; // ensure it's set } @@ -979,8 +1222,17 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer removeScreenReference(); screen = newScreen; } - + + @Override public final void run() { + if( WindowImpl.this.isFullscreen() ) { + // Bug 924: Ignore reparent when in fullscreen - otherwise may confuse WM + if( DEBUG_IMPLEMENTATION) { + System.err.println("Window.reparent: NOP (in fullscreen, "+getThreadName()+") valid "+isNativeValid()+ + ", windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + } + return; + } boolean animatorPaused = false; if(null!=lifecycleHook) { animatorPaused = lifecycleHook.pauseRenderingAction(); @@ -990,23 +1242,35 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer lifecycleHook.resumeRenderingAction(); } } - + private void reparent() { // mirror pos/size so native change notification can get overwritten - int x = getX(); - int y = getY(); - int width = getWidth(); - int height = getHeight(); - boolean wasVisible; - - windowLock.lock(); + final int oldX = getX(); + final int oldY = getY(); + final int oldWidth = getWidth(); + final int oldHeight = getHeight(); + final int x, y; + int width = oldWidth; + int height = oldHeight; + + final boolean wasVisible; + final boolean becomesVisible; + final boolean forceDestroyCreate; + + final RecursiveLock _lock = windowLock; + _lock.lock(); try { - if(isNativeValid()) { - // force recreation if offscreen, since it may become onscreen - forceDestroyCreate |= isOffscreenInstance(WindowImpl.this, newParentWindow); + { + boolean v = 0 != ( REPARENT_HINT_FORCE_RECREATION & hints ); + if(isNativeValid()) { + // force recreation if offscreen, since it may become onscreen + v |= isOffscreenInstance(WindowImpl.this, newParentWindow); + } + forceDestroyCreate = v; } - + wasVisible = isVisible(); + becomesVisible = wasVisible || 0 != ( REPARENT_HINT_BECOMES_VISIBLE & hints ); Window newParentWindowNEWT = null; if(newParentWindow instanceof Window) { @@ -1015,15 +1279,21 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer long newParentWindowHandle = 0 ; - if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.reparent: START ("+getThreadName()+") valid "+isNativeValid()+", windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)+", visible "+wasVisible+", old parentWindow: "+Display.hashCodeNullSafe(parentWindow)+", new parentWindow: "+Display.hashCodeNullSafe(newParentWindow)+", forceDestroyCreate "+forceDestroyCreate+", "+x+"/"+y+" "+width+"x"+height); - } - - if(null!=lifecycleHook) { - lifecycleHook.resetCounter(); + if( DEBUG_IMPLEMENTATION) { + System.err.println("Window.reparent: START ("+getThreadName()+") valid "+isNativeValid()+ + ", windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)+ + ", visible "+wasVisible+", becomesVisible "+becomesVisible+ + ", forceDestroyCreate "+forceDestroyCreate+ + ", DEBUG_TEST_REPARENT_INCOMPATIBLE "+DEBUG_TEST_REPARENT_INCOMPATIBLE+ + ", HINT_FORCE_RECREATION "+( 0 != ( REPARENT_HINT_FORCE_RECREATION & hints ) )+ + ", HINT_BECOMES_VISIBLE "+( 0 != ( REPARENT_HINT_BECOMES_VISIBLE & hints ) ) + + ", old parentWindow: "+Display.hashCodeNullSafe(parentWindow)+ + ", new parentWindow: "+Display.hashCodeNullSafe(newParentWindow) ); } if(null!=newParentWindow) { + // REPARENT TO CHILD WINDOW + // reset position to 0/0 within parent space x = 0; y = 0; @@ -1045,7 +1315,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } // Destroy this window and use parent's Screen. // It may be created properly when the parent is made visible. - destroy(); + destroy( becomesVisible ); setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() ); operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING; } else if(newParentWindow != getParent()) { @@ -1056,25 +1326,24 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(null!=newParentWindowNEWT) { setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() ); } else { - Screen newScreen = NewtFactory.createCompatibleScreen(newParentWindow, getScreen()); - if( getScreen() != newScreen ) { + final Screen newScreen = NewtFactory.createCompatibleScreen(newParentWindow, screen); + if( screen != newScreen ) { // auto destroy on-the-fly created Screen/Display setScreen( (ScreenImpl) newScreen ); } } - if( 0<width*height ) { + if( 0 < width && 0 < height ) { operation = ReparentOperation.ACTION_NATIVE_CREATION; } else { operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING; } - } else if ( forceDestroyCreate || !NewtFactory.isScreenCompatible(newParentWindow, getScreen()) ) { - // Destroy this window, may create a new compatible Screen/Display, - // and mark it for creation. - destroy(); + } else if ( forceDestroyCreate || !NewtFactory.isScreenCompatible(newParentWindow, screen) ) { + // Destroy this window, may create a new compatible Screen/Display, while trying to preserve resources if becoming visible again. + destroy( becomesVisible ); if(null!=newParentWindowNEWT) { setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() ); } else { - setScreen( (ScreenImpl) NewtFactory.createCompatibleScreen(newParentWindow, getScreen()) ); + setScreen( (ScreenImpl) NewtFactory.createCompatibleScreen(newParentWindow, screen) ); } operation = ReparentOperation.ACTION_NATIVE_CREATION; } else { @@ -1086,12 +1355,19 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer operation = ReparentOperation.ACTION_NOP; } } else { - if( null != parentWindow ) { + // REPARENT TO TOP-LEVEL WINDOW + if( 0 <= topLevelX && 0 <= topLevelY ) { + x = topLevelX; + y = topLevelY; + } else if( null != parentWindow ) { // child -> top // put client to current parent+child position - Point p = getLocationOnScreen(null); + final Point p = getLocationOnScreen(null); x = p.getX(); y = p.getY(); + } else { + x = oldX; + y = oldY; } // Case: Top Window @@ -1100,8 +1376,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer operation = ReparentOperation.ACTION_NOP; } else if( !isNativeValid() || forceDestroyCreate ) { // Destroy this window and mark it for [pending] creation. - destroy(); - if( 0<width*height ) { + // If isNativeValid() and becoming visible again - try to preserve resources, i.e. b/c on-/offscreen switch. + destroy( becomesVisible ); + if( 0 < width && 0 < height ) { operation = ReparentOperation.ACTION_NATIVE_CREATION; } else { operation = ReparentOperation.ACTION_NATIVE_CREATION_PENDING; @@ -1117,15 +1394,18 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer throw new NativeWindowException("Internal Error: reparentAction not set"); } + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.reparent: ACTION ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" new parentWindowHandle "+toHexString(newParentWindowHandle)+", reparentAction "+operation+", pos/size "+x+"/"+y+" "+width+"x"+height+", visible "+wasVisible); + } + if( ReparentOperation.ACTION_NOP == operation ) { - if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.reparent: NO CHANGE ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" new parentWindowHandle "+toHexString(newParentWindowHandle)+", visible "+wasVisible); - } return; } - if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.reparent: ACTION ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" new parentWindowHandle "+toHexString(newParentWindowHandle)+", reparentAction "+operation+", visible "+wasVisible); + if( null == newParentWindow ) { + // CLIENT -> TOP: Reset Parent's Pointer State + setOffscreenPointerIcon(null); + setOffscreenPointerVisible(true, null); } // rearrange window tree @@ -1137,21 +1417,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer ((Window)parentWindow).addChild(WindowImpl.this); } - if( ReparentOperation.ACTION_NATIVE_CREATION_PENDING == operation ) { - // make size and position persistent for proper recreation - definePosition(x, y); - defineSize(width, height); - return; - } - if( ReparentOperation.ACTION_NATIVE_REPARENTING == operation ) { - DisplayImpl display = (DisplayImpl) screen.getDisplay(); + final DisplayImpl display = (DisplayImpl) screen.getDisplay(); display.dispatchMessagesNative(); // status up2date - if(wasVisible) { - setVisibleImpl(false, x, y, width, height); - WindowImpl.this.waitForVisible(false, true); - // some composite WM behave slacky .. give 'em chance to change state -> invisible, + // TOP -> CLIENT: !visible first (fixes X11 unsuccessful return to parent window) + if( null != parentWindow && wasVisible && NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true) ) { + setVisibleImpl(false, oldX, oldY, oldWidth, oldHeight); + WindowImpl.this.waitForVisible(false, false); + // FIXME: Some composite WM behave slacky .. give 'em chance to change state -> invisible, // even though we do exactly that (KDE+Composite) try { Thread.sleep(100); } catch (InterruptedException e) { } display.dispatchMessagesNative(); // status up2date @@ -1177,46 +1451,72 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer parentWindowLocked.unlockSurface(); } } + definePosition(x, y); // position might not get updated by WM events (SWT parent apparently) // set visible again if(ok) { display.dispatchMessagesNative(); // status up2date if(wasVisible) { setVisibleImpl(true, x, y, width, height); - ok = WindowImpl.this.waitForVisible(true, false); - display.dispatchMessagesNative(); // status up2date + ok = 0 <= WindowImpl.this.waitForVisible(true, false); if(ok) { + if( isAlwaysOnTop() && 0 == parentWindowHandle && NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true) ) { + // Reinforce ALWAYSONTOP when CHILD -> TOP reparenting, since reparenting itself cause X11 WM to loose it's state. + reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible())); + } ok = WindowImpl.this.waitForSize(width, height, false, TIMEOUT_NATIVEWINDOW); } if(ok) { - requestFocusInt(false /* skipFocusAction */); - display.dispatchMessagesNative(); // status up2date + if( 0 == parentWindowHandle ) { + // Position mismatch shall not lead to reparent failure + WindowImpl.this.waitForPosition(true, x, y, TIMEOUT_NATIVEWINDOW); + } + + requestFocusInt( 0 == parentWindowHandle /* skipFocusAction if top-level */); + display.dispatchMessagesNative(); // status up2date } } } if(!ok || !wasVisible) { - // make size and position persistent manual, + // make size and position persistent manual, // since we don't have a WM feedback (invisible or recreation) definePosition(x, y); defineSize(width, height); } - + if(!ok) { - // native reparent failed -> try creation + // native reparent failed -> try creation, while trying to preserve resources if becoming visible again. if(DEBUG_IMPLEMENTATION) { System.err.println("Window.reparent: native reparenting failed ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)+" -> "+toHexString(newParentWindowHandle)+" - Trying recreation"); } - destroy(); + destroy( becomesVisible ); operation = ReparentOperation.ACTION_NATIVE_CREATION ; + } else { + if( null != parentWindow ) { + // TOP -> CLIENT: Setup Parent's Pointer State + setOffscreenPointerIcon(pointerIcon); + setOffscreenPointerVisible(pointerVisible, pointerIcon); + } } + } else { + // Case + // ACTION_NATIVE_CREATION + // ACTION_NATIVE_CREATION_PENDING; + + // make size and position persistent for proper [re]creation + definePosition(x, y); + defineSize(width, height); } - + if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.reparentWindow: END-1 ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+x+"/"+y+" "+width+"x"+height); + System.err.println("Window.reparent: END-1 ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); } } finally { - windowLock.unlock(); + if(null!=lifecycleHook) { + lifecycleHook.resetCounter(); + } + _lock.unlock(); } if(wasVisible) { switch (operation) { @@ -1229,50 +1529,64 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // This may run on the new Display/Screen connection, hence a new EDT task runOnEDTIfAvail(true, reparentActionRecreate); break; + + default: } } if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.reparentWindow: END-X ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+x+"/"+y+" "+width+"x"+height); + System.err.println("Window.reparent: END-X ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); } } } private class ReparentActionRecreate implements Runnable { + @Override public final void run() { - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { - visible = true; if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.reparentWindow: ReparentActionRecreate ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+Display.hashCodeNullSafe(parentWindow)); + System.err.println("Window.reparent: ReparentActionRecreate ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+Display.hashCodeNullSafe(parentWindow)); } - setVisible(true); // native creation + setVisibleActionImpl(true); // native creation + requestFocusInt( 0 == parentWindowHandle /* skipFocusAction if top-level */); } finally { - windowLock.unlock(); + _lock.unlock(); } } } private final ReparentActionRecreate reparentActionRecreate = new ReparentActionRecreate(); + @Override public final ReparentOperation reparentWindow(NativeWindow newParent) { - return reparentWindow(newParent, false); + return reparentWindow(newParent, -1, -1, 0); + } + + @Override + public final ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, boolean forceDestroyCreate) { + return reparentWindow(newParent, x, y, forceDestroyCreate ? REPARENT_HINT_FORCE_RECREATION : 0); } - public ReparentOperation reparentWindow(NativeWindow newParent, boolean forceDestroyCreate) { - final ReparentAction reparentAction = new ReparentAction(newParent, forceDestroyCreate); + @Override + public final ReparentOperation reparentWindow(NativeWindow newParent, int x, int y, int hints) { + final ReparentAction reparentAction = new ReparentAction(newParent, x, y, hints); runOnEDTIfAvail(true, reparentAction); return reparentAction.getOp(); } - public CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) { + @Override + public final CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) { CapabilitiesChooser old = this.capabilitiesChooser; this.capabilitiesChooser = chooser; return old; } + @Override public final CapabilitiesImmutable getChosenCapabilities() { return getGraphicsConfiguration().getChosenCapabilities(); } + @Override public final CapabilitiesImmutable getRequestedCapabilities() { return capsRequested; } @@ -1284,8 +1598,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer this.undecorated = undecorated; } + @Override public final void run() { - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { if(WindowImpl.this.undecorated != undecorated) { // set current state @@ -1305,16 +1621,18 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } finally { - windowLock.unlock(); + _lock.unlock(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } } - public void setUndecorated(boolean value) { + @Override + public final void setUndecorated(boolean value) { runOnEDTIfAvail(true, new DecorationAction(value)); } + @Override public final boolean isUndecorated() { return 0 != parentWindowHandle || undecorated || fullscreen ; } @@ -1326,13 +1644,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer this.alwaysOnTop = alwaysOnTop; } + @Override public final void run() { - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { if(WindowImpl.this.alwaysOnTop != alwaysOnTop) { // set current state WindowImpl.this.alwaysOnTop = alwaysOnTop; - + if( isNativeValid() ) { // Mirror pos/size so native change notification can get overwritten final int x = getX(); @@ -1347,24 +1667,32 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } finally { - windowLock.unlock(); + _lock.unlock(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } } + @Override public final void setAlwaysOnTop(boolean value) { - runOnEDTIfAvail(true, new AlwaysOnTopAction(value)); + if( isFullscreen() ) { + nfs_alwaysOnTop = value; + } else { + runOnEDTIfAvail(true, new AlwaysOnTopAction(value)); + } } - + + @Override public final boolean isAlwaysOnTop() { return alwaysOnTop; } - - public String getTitle() { + + @Override + public final String getTitle() { return title; } - public void setTitle(String title) { + @Override + public final void setTitle(String title) { if (title == null) { title = ""; } @@ -1374,25 +1702,122 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } - public boolean isPointerVisible() { + @Override + public final boolean isPointerVisible() { return pointerVisible; } - public void setPointerVisible(boolean pointerVisible) { + @Override + public final void setPointerVisible(final boolean pointerVisible) { if(this.pointerVisible != pointerVisible) { boolean setVal = 0 == getWindowHandle(); if(!setVal) { - setVal = setPointerVisibleImpl(pointerVisible); + setVal = setPointerVisibleIntern(pointerVisible); } if(setVal) { - this.pointerVisible = pointerVisible; + this.pointerVisible = pointerVisible; + } + } + } + private boolean setPointerVisibleIntern(final boolean pointerVisible) { + boolean res = setOffscreenPointerVisible(pointerVisible, pointerIcon); + return setPointerVisibleImpl(pointerVisible) || res; // accept onscreen or offscreen positive result! + } + /** + * Helper method to delegate {@link #setPointerVisibleImpl(boolean)} to + * {@link OffscreenLayerSurface#hideCursor()} or {@link OffscreenLayerSurface#setCursor(PixelRectangle, PointImmutable)}. + * <p> + * Note: JAWTWindow is an OffscreenLayerSurface. + * </p> + * <p> + * Performing OffscreenLayerSurface's setCursor(..)/hideCursor(), if available, + * gives same behavior on all platforms. + * </p> + * <p> + * If visible, implementation invokes {@link #setOffscreenPointerIcon(OffscreenLayerSurface, PointerIconImpl)} using the + * given <code>defaultPointerIcon</code>, otherwise {@link OffscreenLayerSurface#hideCursor()} is invoked. + * </p> + * @param pointerVisible true for visible, otherwise invisible. + * @param defaultPointerIcon default PointerIcon for visibility + * @param ols the {@link OffscreenLayerSurface} instance, if null method does nothing. + */ + private boolean setOffscreenPointerVisible(final boolean pointerVisible, final PointerIconImpl defaultPointerIcon) { + if( pointerVisible ) { + return setOffscreenPointerIcon(defaultPointerIcon); + } else { + final NativeWindow parent = getParent(); + if( parent instanceof OffscreenLayerSurface ) { + final OffscreenLayerSurface ols = (OffscreenLayerSurface) parent; + try { + return ols.hideCursor(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return false; + } + + @Override + public final PointerIcon getPointerIcon() { return pointerIcon; } + + @Override + public final void setPointerIcon(final PointerIcon pi) { + final PointerIconImpl piImpl = (PointerIconImpl)pi; + if( this.pointerIcon != piImpl ) { + if( isNativeValid() ) { + runOnEDTIfAvail(true, new Runnable() { + public void run() { + setPointerIconIntern(piImpl); + } } ); + } + this.pointerIcon = piImpl; + } + } + private void setPointerIconIntern(final PointerIconImpl pi) { + setOffscreenPointerIcon(pi); + setPointerIconImpl(pi); + } + /** + * Helper method to delegate {@link #setPointerIconIntern(PointerIconImpl)} to + * {@link OffscreenLayerSurface#setCursor(PixelRectangle, PointImmutable)} + * <p> + * Note: JAWTWindow is an OffscreenLayerSurface. + * </p> + * <p> + * Performing OffscreenLayerSurface's setCursor(..), if available, + * gives same behavior on all platforms. + * </p> + * <p> + * Workaround for AWT/Windows bug within browser, + * where the PointerIcon gets periodically overridden + * by the AWT Component's icon. + * </p> + * @param ols the {@link OffscreenLayerSurface} instance, if null method does nothing. + * @param pi the {@link PointerIconImpl} instance, if null PointerIcon gets reset. + */ + private boolean setOffscreenPointerIcon(final PointerIconImpl pi) { + final NativeWindow parent = getParent(); + if( parent instanceof OffscreenLayerSurface ) { + final OffscreenLayerSurface ols = (OffscreenLayerSurface) parent; + try { + if( null != pi ) { + return ols.setCursor(pi, pi.getHotspot()); + } else { + return ols.setCursor(null, null); // default + } + } catch (Exception e) { + e.printStackTrace(); } } + return false; } - public boolean isPointerConfined() { + + @Override + public final boolean isPointerConfined() { return pointerConfined; } - - public void confinePointer(boolean confine) { + @Override + public final void confinePointer(boolean confine) { if(this.pointerConfined != confine) { boolean setVal = 0 == getWindowHandle(); if(!setVal) { @@ -1410,17 +1835,19 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } if(setVal) { - this.pointerConfined = confine; + this.pointerConfined = confine; } - } + } } - - public void warpPointer(int x, int y) { + + @Override + public final void warpPointer(int x, int y) { if(0 != getWindowHandle()) { warpPointerImpl(x, y); } } - + + @Override public final InsetsImmutable getInsets() { if(isUndecorated()) { return Insets.getZero(); @@ -1428,26 +1855,30 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer updateInsetsImpl(insets); return insets; } - + + @Override public final int getWidth() { return width; } + @Override public final int getHeight() { return height; } + @Override public final int getX() { return x; } + @Override public final int getY() { return y; } protected final boolean autoPosition() { return autoPosition; } - - /** Sets the position fields {@link #x} and {@link #y} to the given values and {@link #autoPosition} to false. */ + + /** Sets the position fields {@link #x} and {@link #y} to the given values and {@link #autoPosition} to false. */ protected final void definePosition(int x, int y) { if(DEBUG_IMPLEMENTATION) { System.err.println("definePosition: "+this.x+"/"+this.y+" -> "+x+"/"+y); @@ -1457,7 +1888,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer this.x = x; this.y = y; } - /** Sets the size fields {@link #width} and {@link #height} to the given values. */ + /** Sets the size fields {@link #width} and {@link #height} to the given values. */ protected final void defineSize(int width, int height) { if(DEBUG_IMPLEMENTATION) { System.err.println("defineSize: "+this.width+"x"+this.height+" -> "+width+"x"+height); @@ -1465,11 +1896,13 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } this.width = width; this.height = height; } - + + @Override public final boolean isVisible() { return visible; } + @Override public final boolean isFullscreen() { return fullscreen; } @@ -1478,6 +1911,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // Window // + @Override + public final Window getDelegatedWindow() { + return this; + } + + //---------------------------------------------------------------------- + // WindowImpl + // + /** * If the implementation is capable of detecting a device change * return true and clear the status/reason of the change. @@ -1486,52 +1928,35 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return false; } - public LifecycleHook getLifecycleHook() { + public final LifecycleHook getLifecycleHook() { return lifecycleHook; } - public LifecycleHook setLifecycleHook(LifecycleHook hook) { + public final LifecycleHook setLifecycleHook(LifecycleHook hook) { LifecycleHook old = lifecycleHook; lifecycleHook = hook; return old; } - /** If this Window actually wraps one from another toolkit such as - the AWT, this will return a non-null value. */ - public Object getWrappedWindow() { - return null; - } - - public final Window getDelegatedWindow() { - return this; - } - /** - * If set to true, the default value, this NEWT Window implementation will - * handle the destruction (ie {@link #destroy()} call) within {@link #windowDestroyNotify(boolean)} implementation.<br> - * If set to false, it's up to the caller/owner to handle destruction within {@link #windowDestroyNotify(boolean)}. + * If this Window actually wraps a {@link NativeSurface} from another instance or toolkit, + * it will return such reference. Otherwise returns null. */ - public void setHandleDestroyNotify(boolean b) { - handleDestroyNotify = b; + public NativeSurface getWrappedSurface() { + return null; } - //---------------------------------------------------------------------- - // WindowImpl - // - - /** - * Returns the non delegated {@link AbstractGraphicsConfiguration}, - * see {@link #getGraphicsConfiguration()}. */ - public final AbstractGraphicsConfiguration getPrivateGraphicsConfiguration() { - return config; + @Override + public final void setWindowDestroyNotifyAction(Runnable r) { + windowDestroyNotifyAction = r; } - + protected final long getParentWindowHandle() { return isFullscreen() ? 0 : parentWindowHandle; } @Override - public String toString() { + public final String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getName()+"[Config "+config+ @@ -1539,12 +1964,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer "\n, ParentWindow "+parentWindow+ "\n, ParentWindowHandle "+toHexString(parentWindowHandle)+" ("+(0!=getParentWindowHandle())+")"+ "\n, WindowHandle "+toHexString(getWindowHandle())+ - "\n, SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+isWindowLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+ + "\n, SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+windowLock.isLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+ "\n, Pos "+getX()+"/"+getY()+" (auto "+autoPosition()+"), size "+getWidth()+"x"+getHeight()+ "\n, Visible "+isVisible()+", focus "+hasFocus()+ "\n, Undecorated "+undecorated+" ("+isUndecorated()+")"+ "\n, AlwaysOnTop "+alwaysOnTop+", Fullscreen "+fullscreen+ - "\n, WrappedWindow "+getWrappedWindow()+ + "\n, WrappedSurface "+getWrappedSurface()+ "\n, ChildWindows "+childWindows.size()); sb.append(", SurfaceUpdatedListeners num "+surfaceUpdatedHelper.size()+" ["); @@ -1559,12 +1984,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer for (int i = 0; i < mouseListeners.size(); i++ ) { sb.append(mouseListeners.get(i)+", "); } + sb.append("], PointerGestures default "+defaultGestureHandlerEnabled+", custom "+pointerGestureHandler.size()+" ["); + for (int i = 0; i < pointerGestureHandler.size(); i++ ) { + sb.append(pointerGestureHandler.get(i)+", "); + } sb.append("], KeyListeners num "+keyListeners.size()+" ["); for (int i = 0; i < keyListeners.size(); i++ ) { sb.append(keyListeners.get(i)+", "); } - sb.append("], surfaceLock "+surfaceLock); - sb.append(", windowLock "+windowLock+"]"); + sb.append("], windowLock "+windowLock+", surfaceLockCount "+surfaceLockCount+"]"); return sb.toString(); } @@ -1572,16 +2000,17 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer windowHandle = handle; } - public void runOnEDTIfAvail(boolean wait, final Runnable task) { - Screen scrn = getScreen(); - if(null==scrn) { - throw new RuntimeException("Null screen of inner class: "+this); + @Override + public final void runOnEDTIfAvail(boolean wait, final Runnable task) { + if( windowLock.isOwner( Thread.currentThread() ) ) { + task.run(); + } else { + ( (DisplayImpl) screen.getDisplay() ).runOnEDTIfAvail(wait, task); } - DisplayImpl d = (DisplayImpl) scrn.getDisplay(); - d.runOnEDTIfAvail(wait, task); } private final Runnable requestFocusAction = new Runnable() { + @Override public final void run() { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.RequestFocusAction: force 0 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); @@ -1590,6 +2019,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } }; private final Runnable requestFocusActionForced = new Runnable() { + @Override public final void run() { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.RequestFocusAction: force 1 - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); @@ -1598,18 +2028,21 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } }; + @Override public final boolean hasFocus() { return hasFocus; } - public void requestFocus() { + @Override + public final void requestFocus() { requestFocus(true); } - public void requestFocus(boolean wait) { + @Override + public final void requestFocus(boolean wait) { requestFocus(wait /* wait */, false /* skipFocusAction */, brokenFocusChange /* force */); } - + private void requestFocus(boolean wait, boolean skipFocusAction, boolean force) { if( isNativeValid() && ( force || !hasFocus() ) && @@ -1617,21 +2050,22 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer runOnEDTIfAvail(wait, force ? requestFocusActionForced : requestFocusAction); } } - + /** Internally forcing request focus on current thread */ private void requestFocusInt(boolean skipFocusAction) { if( skipFocusAction || !focusAction() ) { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.RequestFocusInt: forcing - ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + System.err.println("Window.RequestFocusInt: forcing - ("+getThreadName()+"): skipFocusAction "+skipFocusAction+", focus "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); } requestFocusImpl(true); - } + } } - - public void setFocusAction(FocusRunnable focusAction) { + + @Override + public final void setFocusAction(FocusRunnable focusAction) { this.focusAction = focusAction; } - + private boolean focusAction() { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.focusAction() START - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle())); @@ -1647,15 +2081,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } return res; } - - protected void setBrokenFocusChange(boolean v) { + + protected final void setBrokenFocusChange(boolean v) { brokenFocusChange = v; } - - public void setKeyboardFocusHandler(KeyListener l) { + + @Override + public final void setKeyboardFocusHandler(KeyListener l) { keyboardFocusHandler = l; } - + private class SetPositionAction implements Runnable { int x, y; @@ -1664,8 +2099,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer this.y = y; } + @Override public final void run() { - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); try { if(DEBUG_IMPLEMENTATION) { System.err.println("Window setPosition: "+getX()+"/"+getY()+" -> "+x+"/"+y+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)); @@ -1674,67 +2111,106 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(isNativeValid()) { // this.x/this.y will be set by sizeChanged, triggered by windowing event system reconfigureWindowImpl(x, y, getWidth(), getHeight(), getReconfigureFlags(0, isVisible())); + if( null == parentWindow ) { + // Wait until custom position is reached within tolerances + waitForPosition(true, x, y, Window.TIMEOUT_NATIVEWINDOW); + } } else { definePosition(x, y); // set pos for createNative(..) } } } finally { - windowLock.unlock(); + _lock.unlock(); } } } + @Override public void setPosition(int x, int y) { autoPosition = false; runOnEDTIfAvail(true, new SetPositionAction(x, y)); } - - public void setTopLevelPosition(int x, int y) { + + @Override + public final void setTopLevelPosition(int x, int y) { setPosition(x + getInsets().getLeftWidth(), y + getInsets().getTopHeight()); } - + private class FullScreenAction implements Runnable { - boolean fullscreen; + boolean _fullscreen; - private boolean init(boolean fullscreen) { + private boolean init(boolean fullscreen) { if(isNativeValid()) { - this.fullscreen = fullscreen; + this._fullscreen = fullscreen; return isFullscreen() != fullscreen; } else { WindowImpl.this.fullscreen = fullscreen; // set current state for createNative(..) return false; } - } - public boolean fsOn() { return fullscreen; } + } + public boolean fsOn() { return _fullscreen; } + @Override public final void run() { - windowLock.lock(); + final RecursiveLock _lock = windowLock; + _lock.lock(); + blockInsetsChange = true; try { - // set current state - WindowImpl.this.fullscreen = fullscreen; + final int oldX = getX(); + final int oldY = getY(); + final int oldWidth = getWidth(); + final int oldHeight = getHeight(); int x,y,w,h; - - if(fullscreen) { - nfs_x = getX(); - nfs_y = getY(); - nfs_width = getWidth(); - nfs_height = getHeight(); - x = screen.getX(); - y = screen.getY(); - w = screen.getWidth(); - h = screen.getHeight(); + + final RectangleImmutable sviewport = screen.getViewport(); + final RectangleImmutable viewport; + final int fs_span_flag; + final boolean alwaysOnTopChange; + if(_fullscreen) { + if( null == fullscreenMonitors ) { + if( fullscreenUseMainMonitor ) { + fullscreenMonitors = new ArrayList<MonitorDevice>(); + fullscreenMonitors.add( getMainMonitor() ); + } else { + fullscreenMonitors = getScreen().getMonitorDevices(); + } + } + viewport = MonitorDevice.unionOfViewports(new Rectangle(), fullscreenMonitors); + if( isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN) && + ( fullscreenMonitors.size() > 1 || sviewport.compareTo(viewport) > 0 ) ) { + fs_span_flag = FLAG_IS_FULLSCREEN_SPAN; + } else { + fs_span_flag = 0; + } + nfs_x = oldX; + nfs_y = oldY; + nfs_width = oldWidth; + nfs_height = oldHeight; + nfs_alwaysOnTop = alwaysOnTop; + x = viewport.getX(); + y = viewport.getY(); + w = viewport.getWidth(); + h = viewport.getHeight(); + alwaysOnTop = false; + alwaysOnTopChange = nfs_alwaysOnTop != alwaysOnTop; } else { + fullscreenUseMainMonitor = true; + fullscreenMonitors = null; + fs_span_flag = 0; + viewport = null; x = nfs_x; y = nfs_y; w = nfs_width; h = nfs_height; - + alwaysOnTopChange = nfs_alwaysOnTop != alwaysOnTop; + alwaysOnTop = nfs_alwaysOnTop; + if(null!=parentWindow) { // reset position to 0/0 within parent space x = 0; y = 0; - + // refit if size is bigger than parent if( w > parentWindow.getWidth() ) { w = parentWindow.getWidth(); @@ -1744,14 +2220,30 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } + + final DisplayImpl display = (DisplayImpl) screen.getDisplay(); + display.dispatchMessagesNative(); // status up2date + final boolean wasVisible = isVisible(); + final boolean tempInvisible = !_fullscreen && wasVisible && NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true); + if(DEBUG_IMPLEMENTATION) { - System.err.println("Window fs: "+fullscreen+" "+x+"/"+y+" "+w+"x"+h+", "+isUndecorated()+", "+screen); + System.err.println("Window fs: "+_fullscreen+" "+x+"/"+y+" "+w+"x"+h+", "+isUndecorated()+ + ", virtl-screenSize: "+sviewport+", monitorsViewport "+viewport+ + ", spanning "+(0!=fs_span_flag)+ + ", alwaysOnTop "+alwaysOnTop+(alwaysOnTopChange?"*":"")+ + ", wasVisible "+wasVisible+", tempInvisible "+tempInvisible+ + ", hasParent "+(null!=parentWindow)+ + " @ "+Thread.currentThread().getName()); + } + + // fullscreen off: !visible first (fixes X11 unsuccessful return to parent window _and_ wrong window size propagation) + if( tempInvisible ) { + setVisibleImpl(false, oldX, oldY, oldWidth, oldHeight); + WindowImpl.this.waitForVisible(false, false); + try { Thread.sleep(100); } catch (InterruptedException e) { } + display.dispatchMessagesNative(); // status up2date } - DisplayImpl display = (DisplayImpl) screen.getDisplay(); - display.dispatchMessagesNative(); // status up2date - boolean wasVisible = isVisible(); - // Lock parentWindow only during reparenting (attempt) final NativeWindow parentWindowLocked; if( null != parentWindow ) { @@ -1763,81 +2255,127 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer parentWindowLocked = null; } try { - reconfigureWindowImpl(x, y, w, h, - getReconfigureFlags( ( ( null != parentWindowLocked ) ? FLAG_CHANGE_PARENTING : 0 ) | - FLAG_CHANGE_FULLSCREEN | FLAG_CHANGE_DECORATION, wasVisible) ); + if(alwaysOnTopChange && _fullscreen) { + // Enter fullscreen - Disable alwaysOnTop + reconfigureWindowImpl(oldX, oldY, oldWidth, oldHeight, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible())); + } + + WindowImpl.this.fullscreen = _fullscreen; + reconfigureWindowImpl(x, y, w, h, + getReconfigureFlags( ( ( null != parentWindowLocked ) ? FLAG_CHANGE_PARENTING : 0 ) | + fs_span_flag | FLAG_CHANGE_FULLSCREEN | FLAG_CHANGE_DECORATION, isVisible()) ); + if(alwaysOnTopChange && !_fullscreen) { + // Leave fullscreen - Restore alwaysOnTop + reconfigureWindowImpl(x, y, w, h, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible())); + } } finally { if(null!=parentWindowLocked) { parentWindowLocked.unlockSurface(); } } display.dispatchMessagesNative(); // status up2date - + if(wasVisible) { + if( NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true) ) { + // Give sluggy WM's (e.g. Unity) a chance to properly restore window .. + try { Thread.sleep(100); } catch (InterruptedException e) { } + display.dispatchMessagesNative(); // status up2date + } setVisibleImpl(true, x, y, w, h); - WindowImpl.this.waitForVisible(true, false); - display.dispatchMessagesNative(); // status up2date - WindowImpl.this.waitForSize(w, h, false, TIMEOUT_NATIVEWINDOW); - display.dispatchMessagesNative(); // status up2date - + boolean ok = 0 <= WindowImpl.this.waitForVisible(true, false); + if(ok) { + ok = WindowImpl.this.waitForSize(w, h, false, TIMEOUT_NATIVEWINDOW); + } + if(ok && !_fullscreen && null == parentWindow) { + // Position mismatch shall not lead to fullscreen failure + WindowImpl.this.waitForPosition(true, x, y, TIMEOUT_NATIVEWINDOW); + } + if(ok) { + requestFocusInt(_fullscreen /* skipFocusAction if fullscreen */); + display.dispatchMessagesNative(); // status up2date + } if(DEBUG_IMPLEMENTATION) { - System.err.println("Window fs done: " + WindowImpl.this); + System.err.println("Window fs done: ok " + ok + ", " + WindowImpl.this); } } } finally { - windowLock.unlock(); + blockInsetsChange = false; + _lock.unlock(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener } } private final FullScreenAction fullScreenAction = new FullScreenAction(); + @Override public boolean setFullscreen(boolean fullscreen) { + return setFullscreenImpl(fullscreen, true, null); + } + + @Override + public boolean setFullscreen(List<MonitorDevice> monitors) { + return setFullscreenImpl(true, false, monitors); + } + + private boolean setFullscreenImpl(boolean fullscreen, boolean useMainMonitor, List<MonitorDevice> monitors) { synchronized(fullScreenAction) { - if( fullScreenAction.init(fullscreen) ) { - if(fullScreenAction.fsOn() && - isOffscreenInstance(WindowImpl.this, parentWindow)) { + fullscreenMonitors = monitors; + fullscreenUseMainMonitor = useMainMonitor; + if( fullScreenAction.init(fullscreen) ) { + if( fullScreenAction.fsOn() && isOffscreenInstance(WindowImpl.this, parentWindow) ) { // enable fullscreen on offscreen instance if(null != parentWindow) { nfs_parent = parentWindow; - reparentWindow(null, true); + reparentWindow(null, -1, -1, REPARENT_HINT_FORCE_RECREATION | REPARENT_HINT_BECOMES_VISIBLE); } else { throw new InternalError("Offscreen instance w/o parent unhandled"); } } - + runOnEDTIfAvail(true, fullScreenAction); - + if(!fullScreenAction.fsOn() && null != nfs_parent) { // disable fullscreen on offscreen instance - reparentWindow(nfs_parent, true); + reparentWindow(nfs_parent, -1, -1, REPARENT_HINT_FORCE_RECREATION | REPARENT_HINT_BECOMES_VISIBLE); nfs_parent = null; } - - if(isVisible()) { - requestFocus(true /* wait */, this.fullscreen /* skipFocusAction */, true /* force */); - } } - return this.fullscreen; + return this.fullscreen; } } - private class ScreenModeListenerImpl implements ScreenModeListener { + private class MonitorModeListenerImpl implements MonitorModeListener { boolean animatorPaused = false; - - public void screenModeChangeNotify(ScreenMode sm) { + boolean hadFocus = false; + boolean fullscreenPaused = false; + List<MonitorDevice> _fullscreenMonitors = null; + boolean _fullscreenUseMainMonitor = true; + + @Override + public void monitorModeChangeNotify(MonitorEvent me) { + hadFocus = hasFocus(); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.screenModeChangeNotify: "+sm); + System.err.println("Window.monitorModeChangeNotify: hadFocus "+hadFocus+", "+me+" @ "+Thread.currentThread().getName()); } if(null!=lifecycleHook) { animatorPaused = lifecycleHook.pauseRenderingAction(); } + if( fullscreen && isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN) ) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.monitorModeChangeNotify: FS Pause"); + } + fullscreenPaused = true; + _fullscreenMonitors = fullscreenMonitors; + _fullscreenUseMainMonitor = fullscreenUseMainMonitor; + setFullscreenImpl(false, true, null); + } } - public void screenModeChanged(ScreenMode sm, boolean success) { + @Override + public void monitorModeChanged(MonitorEvent me, boolean success) { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.screenModeChanged: "+sm+", success: "+success); + System.err.println("Window.monitorModeChanged: hadFocus "+hadFocus+", "+me+", success: "+success+" @ "+Thread.currentThread().getName()); } if(success) { @@ -1845,33 +2383,68 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // Didn't pass above notify method. probably detected screen change after it happened. animatorPaused = lifecycleHook.pauseRenderingAction(); } - DimensionImmutable screenSize = sm.getMonitorMode().getSurfaceSize().getResolution(); - if ( getHeight() > screenSize.getHeight() || - getWidth() > screenSize.getWidth() ) { - setSize(screenSize.getWidth(), screenSize.getHeight()); + if( !fullscreen && !fullscreenPaused ) { + // Simply move/resize window to fit in virtual screen if required + final RectangleImmutable viewport = screen.getViewport(); + if( viewport.getWidth() > 0 && viewport.getHeight() > 0 ) { // failsafe + final RectangleImmutable rect = new Rectangle(getX(), getY(), getWidth(), getHeight()); + final RectangleImmutable isect = viewport.intersection(rect); + if ( getHeight() > isect.getHeight() || + getWidth() > isect.getWidth() ) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.monitorModeChanged: fit window "+rect+" into screen viewport "+viewport+ + ", due to minimal intersection "+isect); + } + definePosition(viewport.getX(), viewport.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip + setSize(viewport.getWidth(), viewport.getHeight()); + } + } + } else if( fullscreenPaused ){ + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.monitorModeChanged: FS Restore"); + } + setFullscreenImpl(true, _fullscreenUseMainMonitor, _fullscreenMonitors); + fullscreenPaused = false; + _fullscreenMonitors = null; + _fullscreenUseMainMonitor = true; + } else { + // If changed monitor is part of this fullscreen mode, reset size! (Bug 771) + final MonitorDevice md = me.getMonitor(); + if( fullscreenMonitors.contains(md) ) { + final RectangleImmutable viewport = MonitorDevice.unionOfViewports(new Rectangle(), fullscreenMonitors); + if(DEBUG_IMPLEMENTATION) { + final RectangleImmutable rect = new Rectangle(getX(), getY(), getWidth(), getHeight()); + System.err.println("Window.monitorModeChanged: FS Monitor Match: Fit window "+rect+" into new viewport union "+viewport+", provoked by "+md); + } + definePosition(viewport.getX(), viewport.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip + setFullscreenSize(viewport.getWidth(), viewport.getHeight()); + } } } - if(animatorPaused) { lifecycleHook.resumeRenderingAction(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener + if( hadFocus ) { + requestFocus(true); + } } } - private final ScreenModeListenerImpl screenModeListenerImpl = new ScreenModeListenerImpl(); - + private final MonitorModeListenerImpl monitorModeListenerImpl = new MonitorModeListenerImpl(); //---------------------------------------------------------------------- // Child Window Management - // + // + @Override public final boolean removeChild(NativeWindow win) { synchronized(childWindowsLock) { return childWindows.remove(win); } } + @Override public final boolean addChild(NativeWindow win) { if (win == null) { return false; @@ -1897,26 +2470,28 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } - public void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { + @Override + public final void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { if(isNativeValid()) { - ((DisplayImpl)getScreen().getDisplay()).enqueueEvent(wait, event); + ((DisplayImpl)screen.getDisplay()).enqueueEvent(wait, event); } } - public boolean consumeEvent(NEWTEvent e) { + @Override + public final boolean consumeEvent(NEWTEvent e) { switch(e.getEventType()) { // special repaint treatment case WindowEvent.EVENT_WINDOW_REPAINT: // queue repaint event in case window is locked, ie in operation - if( isWindowLocked() ) { + if( null != windowLock.getOwner() ) { // make sure only one repaint event is queued if(!repaintQueued) { repaintQueued=true; final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen(); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.consumeEvent: "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO); + System.err.println("Window.consumeEvent: REPAINT "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO); // Thread.dumpStack(); - } + } return discardTO; // discardTO:=true -> consumed } return true; @@ -1927,10 +2502,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // common treatment case WindowEvent.EVENT_WINDOW_RESIZED: // queue event in case window is locked, ie in operation - if( isWindowLocked() ) { + if( null != windowLock.getOwner() ) { final boolean discardTO = QUEUED_EVENT_TO <= System.currentTimeMillis()-e.getWhen(); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.consumeEvent: "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO); + System.err.println("Window.consumeEvent: RESIZED "+Thread.currentThread().getName()+" - queued "+e+", discard-to "+discardTO); // Thread.dumpStack(); } return discardTO; // discardTO:=true -> consumed @@ -1944,7 +2519,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } else if(e instanceof KeyEvent) { consumeKeyEvent((KeyEvent)e); } else if(e instanceof MouseEvent) { - consumeMouseEvent((MouseEvent)e); + consumePointerEvent((MouseEvent)e); } else { throw new NativeWindowException("Unexpected NEWTEvent type " + e); } @@ -1952,160 +2527,638 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } // - // SurfaceUpdatedListener Support + // MouseListener/Event Support // - public void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { - surfaceUpdatedHelper.addSurfaceUpdatedListener(l); - } - public void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException { - surfaceUpdatedHelper.addSurfaceUpdatedListener(index, l); + // + // Native MouseEvents pre-processed to be enqueued or consumed directly + // + + public void sendMouseEvent(final short eventType, final int modifiers, + final int x, final int y, final short button, final float rotation) { + doMouseEvent(false, false, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f); + } + public final void enqueueMouseEvent(final boolean wait, final short eventType, final int modifiers, + final int x, final int y, final short button, final float rotation) { + doMouseEvent(true, wait, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f); + } + protected final void doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers, + final int x, final int y, final short button, final float rotation) { + doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f); + } + /** + public final void sendMouseEvent(final short eventType, final int modifiers, + final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) { + doMouseEvent(false, false, eventType, modifiers, x, y, button, rotationXYZ, rotationScale); } + public final void enqueueMouseEvent(final boolean wait, final short eventType, final int modifiers, + final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) { + doMouseEvent(true, wait, eventType, modifiers, x, y, button, rotationXYZ, rotationScale); + } */ - public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { - surfaceUpdatedHelper.removeSurfaceUpdatedListener(l); + /** + * Send mouse event (one-pointer) either to be directly consumed or to be enqueued + * + * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued}, + * otherwise {@link #consumeEvent(NEWTEvent) consumed} directly. + * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}. + */ + protected void doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers, + final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) { + if( 0 > button || button > MouseEvent.BUTTON_COUNT ) { + throw new NativeWindowException("Invalid mouse button number" + button); + } + doPointerEvent(enqueue, wait, constMousePointerTypes, eventType, modifiers, + 0 /*actionIdx*/, new short[] { (short)(button-1) }, + new int[]{x}, new int[]{y}, new float[]{0f} /*pressure*/, + 1f /*maxPressure*/, rotationXYZ, rotationScale); } - public void surfaceUpdated(Object updater, NativeSurface ns, long when) { - surfaceUpdatedHelper.surfaceUpdated(updater, ns, when); + /** + * Send multiple-pointer event either to be directly consumed or to be enqueued + * <p> + * The index for the element of multiple-pointer arrays represents the pointer which triggered the event + * is passed via <i>actionIdx</i>. + * </p> + * <p> + * The given pointer names, <code>pNames</code>, are mapped to consecutive pointer IDs starting w/ 0 + * using a hash-map if <code>normalPNames</code> is <code>false</code>. + * Otherwise a simple <code>int</code> to <code>short</code> type cast is performed. + * </p> + * <p> + * See {@link #doPointerEvent(boolean, boolean, PointerType[], short, int, int, short[], int[], int[], float[], float, float[], float)} + * for details! + * </p> + * + * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued}, + * otherwise {@link #consumeEvent(NEWTEvent) consumed} directly. + * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}. + * @param pTypes {@link MouseEvent.PointerType} for each pointer (multiple pointer) + * @param eventType + * @param modifiers + * @param actionIdx index of multiple-pointer arrays representing the pointer which triggered the event + * @param normalPNames see pName below. + * @param pNames Pointer name for each pointer (multiple pointer). + * We assume consecutive pointer names starting w/ 0 if <code>normalPIDs</code> is <code>true</code>. + * Otherwise we hash-map the values during state pressed to retrieve the normal ID. + * @param pX X-axis for each pointer (multiple pointer) + * @param pY Y-axis for each pointer (multiple pointer) + * @param pPressure Pressure for each pointer (multiple pointer) + * @param maxPressure Maximum pointer pressure for all pointer + */ + public final void doPointerEvent(final boolean enqueue, final boolean wait, + final PointerType[] pTypes, final short eventType, final int modifiers, + final int actionIdx, final boolean normalPNames, final int[] pNames, + final int[] pX, final int[] pY, final float[] pPressure, + float maxPressure, final float[] rotationXYZ, final float rotationScale) { + final int pCount = pNames.length; + final short[] pIDs = new short[pCount]; + for(int i=0; i<pCount; i++) { + if( !normalPNames ) { + // hash map int name -> short idx + final int sz0 = pName2pID.size(); + final Integer pNameI1 = pName2pID.getOrAdd(Integer.valueOf(pNames[i])); + final short pID = (short)pName2pID.indexOf(pNameI1); + pIDs[i] = pID; + if(DEBUG_MOUSE_EVENT) { + final int sz1 = pName2pID.size(); + if( sz0 != sz1 ) { + System.err.println("PointerName2ID[sz "+sz1+"]: Map "+pNameI1+" == "+pID); + } + } + if( MouseEvent.EVENT_MOUSE_RELEASED == eventType ) { + pName2pID.remove(pNameI1); + if(DEBUG_MOUSE_EVENT) { + System.err.println("PointerName2ID[sz "+pName2pID.size()+"]: Unmap "+pNameI1+" == "+pID); + } + } + } else { + // simple type cast + pIDs[i] = (short)pNames[i]; + } + } + doPointerEvent(enqueue, wait, pTypes, eventType, modifiers, actionIdx, pIDs, + pX, pY, pPressure, maxPressure, rotationXYZ, rotationScale); } - // - // MouseListener/Event Support - // - public void sendMouseEvent(int eventType, int modifiers, - int x, int y, int button, int rotation) { - doMouseEvent(false, false, eventType, modifiers, x, y, button, rotation); - } - public void enqueueMouseEvent(boolean wait, int eventType, int modifiers, - int x, int y, int button, int rotation) { - doMouseEvent(true, wait, eventType, modifiers, x, y, button, rotation); - } - - protected void doMouseEvent(boolean enqueue, boolean wait, int eventType, int modifiers, - int x, int y, int button, int rotation) { - if(eventType == MouseEvent.EVENT_MOUSE_ENTERED || - eventType == MouseEvent.EVENT_MOUSE_EXITED) { - if(eventType == MouseEvent.EVENT_MOUSE_EXITED && x==-1 && y==-1) { - x = lastMousePosition.getX(); - y = lastMousePosition.getY(); - } - // clip coordinates to window dimension - x = Math.min(Math.max(x, 0), getWidth()-1); - y = Math.min(Math.max(y, 0), getHeight()-1); - mouseInWindow = eventType == MouseEvent.EVENT_MOUSE_ENTERED; - lastMousePressed=0; // clear state - mouseButtonPressed=0; // clear state - } - if(x<0||y<0||x>=getWidth()||y>=getHeight()) { + /** + * Send multiple-pointer event either to be directly consumed or to be enqueued. + * <p> + * Pointer/Mouse Processing Pass 1 (Pass 2 is performed in {@link #consumePointerEvent(MouseEvent)}. + * </p> + * <p> + * Usually directly called by event source to enqueue and process event. + * </p> + * <p> + * The index for the element of multiple-pointer arrays represents the pointer which triggered the event + * is passed via <i>actionIdx</i>. + * </p> + * <p> + * <ul> + * <li>Determine ENTERED/EXITED state</li> + * <li>Remove redundant move/drag events</li> + * <li>Reset states if applicable</li> + * <li>Drop exterior events</li> + * <li>Determine CLICK COUNT</li> + * <li>Ignore sent CLICKED</li> + * <li>Track buttonPressed incl. buttonPressedMask</li> + * <li>Synthesize DRAGGED event (from MOVED if pointer is pressed)</li> + * </ul> + * </p> + * + * @param enqueue if true, event will be {@link #enqueueEvent(boolean, NEWTEvent) enqueued}, + * otherwise {@link #consumeEvent(NEWTEvent) consumed} directly. + * @param wait if true wait until {@link #consumeEvent(NEWTEvent) consumed}. + * @param pTypes {@link MouseEvent.PointerType} for each pointer (multiple pointer) + * @param eventType + * @param modifiers + * @param pActionIdx index of multiple-pointer arrays representing the pointer which triggered the event + * @param pID Pointer ID for each pointer (multiple pointer). We assume consecutive pointerIDs starting w/ 0. + * A pointer-ID of -1 may also denote no pointer/button activity, i.e. {@link PointerType#Mouse} move. + * @param pX X-axis for each pointer (multiple pointer) + * @param pY Y-axis for each pointer (multiple pointer) + * @param pPressure Pressure for each pointer (multiple pointer) + * @param maxPressure Maximum pointer pressure for all pointer + */ + public final void doPointerEvent(final boolean enqueue, final boolean wait, + final PointerType[] pTypes, final short eventType, int modifiers, + final int pActionIdx, final short[] pID, final int[] pX, final int[] pY, final float[] pPressure, + final float maxPressure, final float[] rotationXYZ, final float rotationScale) { + final long when = System.currentTimeMillis(); + final int pCount = pTypes.length; + + if( 0 > pActionIdx || pActionIdx >= pCount) { + throw new IllegalArgumentException("actionIdx out of bounds [0.."+(pCount-1)+"]"); + } + if( 0 < pActionIdx ) { + // swap values to make idx 0 the triggering pointer + { + final PointerType aType = pTypes[pActionIdx]; + pTypes[pActionIdx] = pTypes[0]; + pTypes[0] = aType; + } + { + final short s = pID[pActionIdx]; + pID[pActionIdx] = pID[0]; + pID[0] = s; + } + { + int s = pX[pActionIdx]; + pX[pActionIdx] = pX[0]; + pX[0] = s; + s = pY[pActionIdx]; + pY[pActionIdx] = pY[0]; + pY[0] = s; + } + { + final float aPress = pPressure[pActionIdx]; + pPressure[pActionIdx] = pPressure[0]; + pPressure[0] = aPress; + } + } + final short id = pID[0]; + final short button; + { + final int b = id + 1; + if( 0 <= b && b <= com.jogamp.newt.event.MouseEvent.BUTTON_COUNT ) { // we allow id==-1 -> button==0 for no button, i.e. mouse move + button = (short)b; + } else { + button = com.jogamp.newt.event.MouseEvent.BUTTON1; + } + } + + // + // - Determine ENTERED/EXITED state + // - Remove redundant move/drag events + // - Reset states if applicable + // + int x = pX[0]; + int y = pY[0]; + final boolean insideWindow = x >= 0 && y >= 0 && x < getWidth() && y < getHeight(); + final Point movePositionP0 = pState1.getMovePosition(id); + switch( eventType ) { + case MouseEvent.EVENT_MOUSE_EXITED: + if( pState1.dragging ) { + // Drop mouse EXIT if dragging, i.e. due to exterior dragging outside of window. + // NOTE-1: X11 produces the 'premature' EXIT, however it also produces 'EXIT' after exterior dragging! + // NOTE-2: consumePointerEvent(MouseEvent) will synthesize a missing EXIT event! + if(DEBUG_MOUSE_EVENT) { + System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" due to dragging: "+pState1); + } + return; + } + if( null != movePositionP0 ) { + if( x==-1 && y==-1 ) { + x = movePositionP0.getX(); + y = movePositionP0.getY(); + } + movePositionP0.set(0, 0); + } + // Fall through intended! + + case MouseEvent.EVENT_MOUSE_ENTERED: + if( eventType == MouseEvent.EVENT_MOUSE_ENTERED ) { + pState1.insideWindow = true; + pState1.exitSent = false; + } else { + pState1.insideWindow = false; + pState1.exitSent = true; + } + pState1.clearButton(); + if( pTypes[0] != PointerType.Mouse ) { + // Drop !MOUSE ENTER/EXIT Events - Safeguard for non compliant implementations only. + if(DEBUG_MOUSE_EVENT) { + System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" due to !Mouse but "+pTypes[0]+": "+pState1); + } + return; + } + // clip coordinates to window dimension + x = Math.min(Math.max(x, 0), getWidth()-1); + y = Math.min(Math.max(y, 0), getHeight()-1); + break; + + case MouseEvent.EVENT_MOUSE_MOVED: + case MouseEvent.EVENT_MOUSE_DRAGGED: + if( null != movePositionP0 ) { + if( movePositionP0.getX() == x && movePositionP0.getY() == y ) { + // Drop same position + if(DEBUG_MOUSE_EVENT) { + System.err.println("doPointerEvent: drop "+MouseEvent.getEventTypeString(eventType)+" w/ same position: "+movePositionP0+", "+pState1); + } + return; + } + movePositionP0.set(x, y); + } + + // Fall through intended ! + + default: + if( pState1.insideWindow != insideWindow ) { + // ENTER/EXIT! + pState1.insideWindow = insideWindow; + if( insideWindow ) { + pState1.exitSent = false; + } + pState1.clearButton(); + } + } + + // + // Drop exterior events if not dragging pointer and not EXIT event + // Safeguard for non compliant implementations! + // + if( !pState1.dragging && !insideWindow && MouseEvent.EVENT_MOUSE_EXITED != eventType ) { + if(DEBUG_MOUSE_EVENT) { + System.err.println("doPointerEvent: drop: "+MouseEvent.getEventTypeString(eventType)+ + ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+movePositionP0+", insideWindow "+insideWindow+", "+pState1); + } return; // .. invalid .. } if(DEBUG_MOUSE_EVENT) { - System.err.println("doMouseEvent: enqueue "+enqueue+", wait "+wait+", "+MouseEvent.getEventTypeString(eventType)+ - ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+lastMousePosition); - } - long when = System.currentTimeMillis(); - MouseEvent eEntered = null; - if(eventType == MouseEvent.EVENT_MOUSE_MOVED) { - if(!mouseInWindow) { - mouseInWindow = true; - eEntered = new MouseEvent(MouseEvent.EVENT_MOUSE_ENTERED, this, when, - modifiers, x, y, lastMouseClickCount, button, 0); - lastMousePressed=0; // clear state - mouseButtonPressed=0; // clear state - } else if(lastMousePosition.getX() == x && lastMousePosition.getY()==y) { + System.err.println("doPointerEvent: enqueue "+enqueue+", wait "+wait+", "+MouseEvent.getEventTypeString(eventType)+ + ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+movePositionP0+", "+pState1); + } + + final int buttonMask = InputEvent.getButtonMask(button); + modifiers |= buttonMask; // Always add current button to modifier mask (Bug 571) + modifiers |= pState1.buttonPressedMask; // Always add currently pressed mouse buttons to modifier mask + + if( isPointerConfined() ) { + modifiers |= InputEvent.CONFINED_MASK; + } + if( !isPointerVisible() ) { + modifiers |= InputEvent.INVISIBLE_MASK; + } + + pX[0] = x; + pY[0] = y; + + // + // - Determine CLICK COUNT + // - Ignore sent CLICKED + // - Track buttonPressed incl. buttonPressedMask + // - Synthesize DRAGGED event (from MOVED if pointer is pressed) + // + final MouseEvent e; + switch( eventType ) { + case MouseEvent.EVENT_MOUSE_CLICKED: + e = null; + break; + + case MouseEvent.EVENT_MOUSE_PRESSED: + if( 0 >= pPressure[0] ) { + pPressure[0] = maxPressure; + } + pState1.buttonPressedMask |= buttonMask; + if( 1 == pCount ) { + if( when - pState1.lastButtonPressTime < MouseEvent.getClickTimeout() ) { + pState1.lastButtonClickCount++; + } else { + pState1.lastButtonClickCount=(short)1; + } + pState1.lastButtonPressTime = when; + pState1.buttonPressed = button; + e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID, + pX, pY, pPressure, maxPressure, button, pState1.lastButtonClickCount, rotationXYZ, rotationScale); + } else { + e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID, + pX, pY, pPressure, maxPressure, button, (short)1, rotationXYZ, rotationScale); + } + break; + case MouseEvent.EVENT_MOUSE_RELEASED: + pState1.buttonPressedMask &= ~buttonMask; + if( 1 == pCount ) { + e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID, + pX, pY, pPressure, maxPressure, button, pState1.lastButtonClickCount, rotationXYZ, rotationScale); + if( when - pState1.lastButtonPressTime >= MouseEvent.getClickTimeout() ) { + pState1.lastButtonClickCount = (short)0; + pState1.lastButtonPressTime = 0; + } + pState1.buttonPressed = 0; + pState1.dragging = false; + } else { + e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID, + pX, pY, pPressure, maxPressure, button, (short)1, rotationXYZ, rotationScale); + if( 0 == pState1.buttonPressedMask ) { + pState1.clearButton(); + } + } + if( null != movePositionP0 ) { + movePositionP0.set(0, 0); + } + break; + case MouseEvent.EVENT_MOUSE_MOVED: + if ( 0 != pState1.buttonPressedMask ) { // any button or pointer move -> drag + e = new MouseEvent(MouseEvent.EVENT_MOUSE_DRAGGED, this, when, modifiers, pTypes, pID, + pX, pY, pPressure, maxPressure, pState1.buttonPressed, (short)1, rotationXYZ, rotationScale); + pState1.dragging = true; + } else { + e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID, + pX, pY, pPressure, maxPressure, button, (short)0, rotationXYZ, rotationScale); + } + break; + case MouseEvent.EVENT_MOUSE_DRAGGED: + if( 0 >= pPressure[0] ) { + pPressure[0] = maxPressure; + } + pState1.dragging = true; + // Fall through intended! + default: + e = new MouseEvent(eventType, this, when, modifiers, pTypes, pID, + pX, pY, pPressure, maxPressure, button, (short)0, rotationXYZ, rotationScale); + } + doEvent(enqueue, wait, e); // actual mouse event + } + + private static int step(int lower, int edge, int value) { + return value < edge ? lower : value; + } + + /** + * Consume the {@link MouseEvent}. + * <p> + * Pointer/Mouse Processing Pass 2 (Pass 1 is performed in {@link #doPointerEvent(boolean, boolean, PointerType[], short, int, int, short[], int[], int[], float[], float, float[], float)}). + * </p> + * <p> + * Invoked before dispatching the dequeued event. + * </p> + * <p> + * <ul> + * <li>Validate</li> + * <li>Handle gestures</li> + * <li>Synthesize events [ENTERED, EXIT, CLICK] and gestures.</li> + * <li>Drop exterior events</li> + * <li>Dispatch event to listener</li> + * </ul> + * </p> + */ + protected void consumePointerEvent(MouseEvent pe) { + int x = pe.getX(); + int y = pe.getY(); + + if(DEBUG_MOUSE_EVENT) { + System.err.println("consumePointerEvent.in: "+pe+", "+pState0+", pos "+x+"/"+y+" clientSize["+getWidth()+"x"+getHeight()+"]"); + } + + // + // - Determine ENTERED/EXITED state + // - Synthesize ENTERED and EXIT event + // - Reset states if applicable + // + final long when = pe.getWhen(); + final int eventType = pe.getEventType(); + final boolean insideWindow; + boolean eExitAllowed = false; + MouseEvent eEntered = null, eExited = null; + switch( eventType ) { + case MouseEvent.EVENT_MOUSE_EXITED: + if( pState0.exitSent || pState0.dragging ) { + if(DEBUG_MOUSE_EVENT) { + System.err.println("consumePointerEvent: drop "+(pState0.exitSent?"already sent":"due to dragging")+": "+pe+", "+pState0); + } + return; + } + // Fall through intended ! + case MouseEvent.EVENT_MOUSE_ENTERED: + // clip coordinates to window dimension + x = Math.min(Math.max(x, 0), getWidth()-1); + y = Math.min(Math.max(y, 0), getHeight()-1); + pState0.clearButton(); + if( eventType == MouseEvent.EVENT_MOUSE_ENTERED ) { + insideWindow = true; + pState0.insideWindow = true; + pState0.exitSent = false; + pState0.dragging = false; + } else { + insideWindow = false; + pState0.insideWindow = false; + pState0.exitSent = true; + } + break; + + case MouseEvent.EVENT_MOUSE_MOVED: + case MouseEvent.EVENT_MOUSE_RELEASED: + if( 1 >= pe.getButtonDownCount() ) { // MOVE or RELEASE last button + eExitAllowed = !pState0.exitSent; + pState0.dragging = false; + } + // Fall through intended ! + + default: + insideWindow = x >= 0 && y >= 0 && x < getWidth() && y < getHeight(); + if( pe.getPointerType(0) == PointerType.Mouse ) { + if( !pState0.insideWindow && insideWindow ) { + // ENTER .. use clipped coordinates + eEntered = new MouseEvent(MouseEvent.EVENT_MOUSE_ENTERED, pe.getSource(), pe.getWhen(), pe.getModifiers(), + Math.min(Math.max(x, 0), getWidth()-1), + Math.min(Math.max(y, 0), getHeight()-1), + (short)0, (short)0, pe.getRotation(), pe.getRotationScale()); + pState0.exitSent = false; + } else if( !insideWindow && eExitAllowed ) { + // EXIT .. use clipped coordinates + eExited = new MouseEvent(MouseEvent.EVENT_MOUSE_EXITED, pe.getSource(), pe.getWhen(), pe.getModifiers(), + Math.min(Math.max(x, 0), getWidth()-1), + Math.min(Math.max(y, 0), getHeight()-1), + (short)0, (short)0, pe.getRotation(), pe.getRotationScale()); + pState0.exitSent = true; + } + } + if( pState0.insideWindow != insideWindow || null != eEntered || null != eExited) { + pState0.clearButton(); + } + pState0.insideWindow = insideWindow; + } + if( null != eEntered ) { + if(DEBUG_MOUSE_EVENT) { + System.err.println("consumePointerEvent.send.0: "+eEntered+", "+pState0); + } + dispatchMouseEvent(eEntered); + } else if( DEBUG_MOUSE_EVENT && !insideWindow ) { + System.err.println("INFO consumePointerEvent.exterior: "+pState0+", "+pe); + } + + // + // Handle Default Gestures + // + if( defaultGestureHandlerEnabled && + pe.getPointerType(0).getPointerClass() == MouseEvent.PointerClass.Onscreen ) + { + if( null == gesture2PtrTouchScroll ) { + final int scaledScrollSlop; + final int scaledDoubleTapSlop; + final MonitorDevice monitor = getMainMonitor(); + if ( null != monitor ) { + final DimensionImmutable mm = monitor.getSizeMM(); + final float pixWPerMM = (float)monitor.getCurrentMode().getRotatedWidth() / (float)mm.getWidth(); + final float pixHPerMM = (float)monitor.getCurrentMode().getRotatedHeight() / (float)mm.getHeight(); + final float pixPerMM = Math.min(pixHPerMM, pixWPerMM); + scaledScrollSlop = Math.round(DoubleTapScrollGesture.SCROLL_SLOP_MM * pixPerMM); + scaledDoubleTapSlop = Math.round(DoubleTapScrollGesture.DOUBLE_TAP_SLOP_MM * pixPerMM); + if(DEBUG_MOUSE_EVENT) { + System.err.println("consumePointerEvent.gscroll: scrollSlop "+scaledScrollSlop+", doubleTapSlop "+scaledDoubleTapSlop+", pixPerMM "+pixPerMM+", "+monitor+", "+pState0); + } + } else { + scaledScrollSlop = DoubleTapScrollGesture.SCROLL_SLOP_PIXEL; + scaledDoubleTapSlop = DoubleTapScrollGesture.DOUBLE_TAP_SLOP_PIXEL; + } + gesture2PtrTouchScroll = new DoubleTapScrollGesture(step(DoubleTapScrollGesture.SCROLL_SLOP_PIXEL, DoubleTapScrollGesture.SCROLL_SLOP_PIXEL/2, scaledScrollSlop), + step(DoubleTapScrollGesture.DOUBLE_TAP_SLOP_PIXEL, DoubleTapScrollGesture.DOUBLE_TAP_SLOP_PIXEL/2, scaledDoubleTapSlop)); + } + if( gesture2PtrTouchScroll.process(pe) ) { + pe = (MouseEvent) gesture2PtrTouchScroll.getGestureEvent(); + gesture2PtrTouchScroll.clear(false); if(DEBUG_MOUSE_EVENT) { - System.err.println("doMouseEvent: skip EVENT_MOUSE_MOVED w/ same position: "+lastMousePosition); + System.err.println("consumePointerEvent.gscroll: "+pe+", "+pState0); + } + dispatchMouseEvent(pe); + return; + } + if( gesture2PtrTouchScroll.isWithinGesture() ) { + return; // within gesture .. need more input .. + } + } + // + // Handle Custom Gestures + // + { + final int pointerGestureHandlerCount = pointerGestureHandler.size(); + if( pointerGestureHandlerCount > 0 ) { + boolean withinGesture = false; + for(int i = 0; !pe.isConsumed() && i < pointerGestureHandlerCount; i++ ) { + final GestureHandler gh = pointerGestureHandler.get(i); + if( gh.process(pe) ) { + final InputEvent ieG = gh.getGestureEvent(); + gh.clear(false); + if( ieG instanceof MouseEvent ) { + dispatchMouseEvent((MouseEvent)ieG); + } else if( ieG instanceof GestureHandler.GestureEvent) { + final GestureHandler.GestureEvent ge = (GestureHandler.GestureEvent) ieG; + for(int j = 0; !ge.isConsumed() && j < gestureListeners.size(); j++ ) { + gestureListeners.get(j).gestureDetected(ge); + } + } + return; + } + withinGesture |= gh.isWithinGesture(); + } + if( withinGesture ) { + return; } - return; // skip same position } - lastMousePosition.setX(x); - lastMousePosition.setY(y); - } - if( 0 > button || button > MouseEvent.BUTTON_NUMBER ) { - throw new NativeWindowException("Invalid mouse button number" + button); } - // Fixes Bug 571: X11 behavior, where the PRESSED button is not included in the native mask. - modifiers |= InputEvent.getButtonMask(button); + // + // - Synthesize mouse CLICKED + // - Ignore sent CLICKED + // MouseEvent eClicked = null; - MouseEvent e = null; + switch( eventType ) { + case MouseEvent.EVENT_MOUSE_PRESSED: + if( 1 == pe.getPointerCount() ) { + pState0.lastButtonPressTime = when; + } + break; + case MouseEvent.EVENT_MOUSE_RELEASED: + if( 1 == pe.getPointerCount() && when - pState0.lastButtonPressTime < MouseEvent.getClickTimeout() ) { + eClicked = pe.createVariant(MouseEvent.EVENT_MOUSE_CLICKED); + } else { + pState0.lastButtonPressTime = 0; + } + break; + case MouseEvent.EVENT_MOUSE_CLICKED: + // ignore - synthesized here .. + if(DEBUG_MOUSE_EVENT) { + System.err.println("consumePointerEvent: drop recv'ed (synth here) "+pe+", "+pState0); + } + pe = null; + break; - if(isPointerConfined()) { - modifiers |= InputEvent.CONFINED_MASK; - } - if(!isPointerVisible()) { - modifiers |= InputEvent.INVISIBLE_MASK; + case MouseEvent.EVENT_MOUSE_DRAGGED: + pState0.dragging = true; + break; } - - if(MouseEvent.EVENT_MOUSE_PRESSED==eventType) { - if(when-lastMousePressed<MouseEvent.getClickTimeout()) { - lastMouseClickCount++; - } else { - lastMouseClickCount=1; - } - lastMousePressed=when; - mouseButtonPressed=button; - e = new MouseEvent(eventType, this, when, - modifiers, x, y, lastMouseClickCount, button, 0); - } else if(MouseEvent.EVENT_MOUSE_RELEASED==eventType) { - e = new MouseEvent(eventType, this, when, - modifiers, x, y, lastMouseClickCount, button, 0); - if(when-lastMousePressed<MouseEvent.getClickTimeout()) { - eClicked = new MouseEvent(MouseEvent.EVENT_MOUSE_CLICKED, this, when, - modifiers, x, y, lastMouseClickCount, button, 0); - } else { - lastMouseClickCount=0; - lastMousePressed=0; - } - mouseButtonPressed=0; - } else if(MouseEvent.EVENT_MOUSE_MOVED==eventType) { - if (mouseButtonPressed>0) { - e = new MouseEvent(MouseEvent.EVENT_MOUSE_DRAGGED, this, when, - modifiers, x, y, 1, mouseButtonPressed, 0); - } else { - e = new MouseEvent(eventType, this, when, - modifiers, x, y, 0, button, 0); + + if( null != pe ) { + if(DEBUG_MOUSE_EVENT) { + System.err.println("consumePointerEvent.send.1: "+pe+", "+pState0); } - } else if(MouseEvent.EVENT_MOUSE_WHEEL_MOVED==eventType) { - e = new MouseEvent(eventType, this, when, modifiers, x, y, 0, button, rotation); - } else { - e = new MouseEvent(eventType, this, when, modifiers, x, y, 0, button, 0); + dispatchMouseEvent(pe); // actual mouse event } - if(null!=eEntered) { + if( null != eClicked ) { if(DEBUG_MOUSE_EVENT) { - System.err.println("doMouseEvent: synthesized MOUSE_ENTERED event: "+eEntered); + System.err.println("consumePointerEvent.send.2: "+eClicked+", "+pState0); } - doEvent(enqueue, wait, eEntered); + dispatchMouseEvent(eClicked); } - doEvent(enqueue, wait, e); // actual mouse event - if(null!=eClicked) { + if( null != eExited ) { if(DEBUG_MOUSE_EVENT) { - System.err.println("doMouseEvent: synthesized MOUSE_CLICKED event: "+eClicked); + System.err.println("consumePointerEvent.send.3: "+eExited+", "+pState0); } - doEvent(enqueue, wait, eClicked); + dispatchMouseEvent(eExited); } } - - public void addMouseListener(MouseListener l) { + @Override + public final void addMouseListener(MouseListener l) { addMouseListener(-1, l); } - public void addMouseListener(int index, MouseListener l) { + @Override + public final void addMouseListener(int index, MouseListener l) { if(l == null) { return; } @SuppressWarnings("unchecked") ArrayList<MouseListener> clonedListeners = (ArrayList<MouseListener>) mouseListeners.clone(); - if(0>index) { - index = clonedListeners.size(); + if(0>index) { + index = clonedListeners.size(); } clonedListeners.add(index, l); mouseListeners = clonedListeners; } - public void removeMouseListener(MouseListener l) { + @Override + public final void removeMouseListener(MouseListener l) { if (l == null) { return; } @@ -2115,25 +3168,87 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer mouseListeners = clonedListeners; } - public MouseListener getMouseListener(int index) { + @Override + public final MouseListener getMouseListener(int index) { @SuppressWarnings("unchecked") ArrayList<MouseListener> clonedListeners = (ArrayList<MouseListener>) mouseListeners.clone(); - if(0>index) { - index = clonedListeners.size()-1; + if(0>index) { + index = clonedListeners.size()-1; } return clonedListeners.get(index); } - public MouseListener[] getMouseListeners() { + @Override + public final MouseListener[] getMouseListeners() { return mouseListeners.toArray(new MouseListener[mouseListeners.size()]); } - protected void consumeMouseEvent(MouseEvent e) { - if(DEBUG_MOUSE_EVENT) { - System.err.println("consumeMouseEvent: event: "+e); + @Override + public final void setDefaultGesturesEnabled(boolean enable) { + defaultGestureHandlerEnabled = enable; + } + @Override + public final boolean areDefaultGesturesEnabled() { + return defaultGestureHandlerEnabled; + } + + @Override + public final void addGestureHandler(GestureHandler gh) { + addGestureHandler(-1, gh); + } + @Override + public final void addGestureHandler(int index, GestureHandler gh) { + if(gh == null) { + return; + } + @SuppressWarnings("unchecked") + ArrayList<GestureHandler> cloned = (ArrayList<GestureHandler>) pointerGestureHandler.clone(); + if(0>index) { + index = cloned.size(); + } + cloned.add(index, gh); + pointerGestureHandler = cloned; + } + @Override + public final void removeGestureHandler(GestureHandler gh) { + if (gh == null) { + return; + } + @SuppressWarnings("unchecked") + ArrayList<GestureHandler> cloned = (ArrayList<GestureHandler>) pointerGestureHandler.clone(); + cloned.remove(gh); + pointerGestureHandler = cloned; + } + @Override + public final void addGestureListener(GestureHandler.GestureListener gl) { + addGestureListener(-1, gl); + } + @Override + public final void addGestureListener(int index, GestureHandler.GestureListener gl) { + if(gl == null) { + return; + } + @SuppressWarnings("unchecked") + ArrayList<GestureHandler.GestureListener> cloned = (ArrayList<GestureHandler.GestureListener>) gestureListeners.clone(); + if(0>index) { + index = cloned.size(); + } + cloned.add(index, gl); + gestureListeners = cloned; + } + @Override + public final void removeGestureListener(GestureHandler.GestureListener gl) { + if (gl == null) { + return; } - boolean consumed = false; - for(int i = 0; !consumed && i < mouseListeners.size(); i++ ) { + @SuppressWarnings("unchecked") + ArrayList<GestureHandler.GestureListener> cloned = (ArrayList<GestureHandler.GestureListener>) gestureListeners.clone(); + cloned.remove(gl); + gestureListeners= cloned; + } + + private final void dispatchMouseEvent(MouseEvent e) { + for(int i = 0; !e.isConsumed() && i < mouseListeners.size(); i++ ) { MouseListener l = mouseListeners.get(i); switch(e.getEventType()) { case MouseEvent.EVENT_MOUSE_CLICKED: @@ -2163,39 +3278,113 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer default: throw new NativeWindowException("Unexpected mouse event type " + e.getEventType()); } - consumed = InputEvent.consumedTag == e.getAttachment(); } } // // KeyListener/Event Support // - public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { - consumeKeyEvent(new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers, keyCode, keyChar) ); + private static final int keyTrackingRange = 255; + private final IntBitfield keyPressedState = new IntBitfield( keyTrackingRange + 1 ); + + protected final boolean isKeyCodeTracked(final short keyCode) { + return ( 0xFFFF & keyCode ) <= keyTrackingRange; } - public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - enqueueEvent(wait, new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers, keyCode, keyChar) ); + /** + * @param keyCode the keyCode to set pressed state + * @param pressed true if pressed, otherwise false + * @return the previus pressed value + */ + protected final boolean setKeyPressed(short keyCode, boolean pressed) { + final int v = 0xFFFF & keyCode; + if( v <= keyTrackingRange ) { + return keyPressedState.put(v, pressed); + } + return false; + } + /** + * @param keyCode the keyCode to test pressed state + * @return true if pressed, otherwise false + */ + protected final boolean isKeyPressed(short keyCode) { + final int v = 0xFFFF & keyCode; + if( v <= keyTrackingRange ) { + return keyPressedState.get(v); + } + return false; } - public void addKeyListener(KeyListener l) { + public void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + // Always add currently pressed mouse buttons to modifier mask + consumeKeyEvent( KeyEvent.create(eventType, this, System.currentTimeMillis(), modifiers | pState1.buttonPressedMask, keyCode, keySym, keyChar) ); + } + + public void enqueueKeyEvent(boolean wait, short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + // Always add currently pressed mouse buttons to modifier mask + enqueueEvent(wait, KeyEvent.create(eventType, this, System.currentTimeMillis(), modifiers | pState1.buttonPressedMask, keyCode, keySym, keyChar) ); + } + + @Override + public final void setKeyboardVisible(boolean visible) { + if(isNativeValid()) { + // We don't skip the impl. if it seems that there is no state change, + // since we cannot assume the impl. reliably gives us it's current state. + final boolean ok = setKeyboardVisibleImpl(visible); + if(DEBUG_IMPLEMENTATION || DEBUG_KEY_EVENT) { + System.err.println("setKeyboardVisible(native): visible "+keyboardVisible+" -- op[visible:"+visible +", ok "+ok+"] -> "+(visible && ok)); + } + keyboardVisibilityChanged( visible && ok ); + } else { + keyboardVisibilityChanged( visible ); // earmark for creation + } + } + @Override + public final boolean isKeyboardVisible() { + return keyboardVisible; + } + /** + * Returns <code>true</code> if operation was successful, otherwise <code>false</code>. + * <p> + * We assume that a failed invisible operation is due to an already invisible keyboard, + * hence even if an invisible operation failed, the keyboard is considered invisible! + * </p> + */ + protected boolean setKeyboardVisibleImpl(boolean visible) { + return false; // nop + } + /** Triggered by implementation's WM events to update the virtual on-screen keyboard's visibility state. */ + protected void keyboardVisibilityChanged(boolean visible) { + if(keyboardVisible != visible) { + if(DEBUG_IMPLEMENTATION || DEBUG_KEY_EVENT) { + System.err.println("keyboardVisibilityChanged: "+keyboardVisible+" -> "+visible); + } + keyboardVisible = visible; + } + } + protected boolean keyboardVisible = false; + + @Override + public final void addKeyListener(KeyListener l) { addKeyListener(-1, l); } - public void addKeyListener(int index, KeyListener l) { + @Override + public final void addKeyListener(int index, KeyListener l) { if(l == null) { return; } @SuppressWarnings("unchecked") ArrayList<KeyListener> clonedListeners = (ArrayList<KeyListener>) keyListeners.clone(); - if(0>index) { + if(0>index) { index = clonedListeners.size(); } clonedListeners.add(index, l); keyListeners = clonedListeners; } - public void removeKeyListener(KeyListener l) { + @Override + public final void removeKeyListener(KeyListener l) { if (l == null) { return; } @@ -2205,16 +3394,18 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer keyListeners = clonedListeners; } - public KeyListener getKeyListener(int index) { + @Override + public final KeyListener getKeyListener(int index) { @SuppressWarnings("unchecked") ArrayList<KeyListener> clonedListeners = (ArrayList<KeyListener>) keyListeners.clone(); - if(0>index) { + if(0>index) { index = clonedListeners.size()-1; } return clonedListeners.get(index); } - public KeyListener[] getKeyListeners() { + @Override + public final KeyListener[] getKeyListeners() { return keyListeners.toArray(new KeyListener[keyListeners.size()]); } @@ -2226,49 +3417,51 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer case KeyEvent.EVENT_KEY_RELEASED: l.keyReleased(e); break; - case KeyEvent.EVENT_KEY_TYPED: - l.keyTyped(e); - break; default: throw new NativeWindowException("Unexpected key event type " + e.getEventType()); } - return InputEvent.consumedTag == e.getAttachment(); + return e.isConsumed(); } - + protected void consumeKeyEvent(KeyEvent e) { - boolean consumed; - if(null != keyboardFocusHandler) { - consumed = propagateKeyEvent(e, keyboardFocusHandler); + boolean consumedE = false; + if( null != keyboardFocusHandler && !e.isAutoRepeat() ) { + consumedE = propagateKeyEvent(e, keyboardFocusHandler); if(DEBUG_KEY_EVENT) { - System.err.println("consumeKeyEvent: "+e+", keyboardFocusHandler consumed: "+consumed); + if( consumedE ) { + System.err.println("consumeKeyEvent(kfh): "+e+", consumed: "+consumedE); + } + } + } + if( !consumedE ) { + for(int i = 0; !consumedE && i < keyListeners.size(); i++ ) { + consumedE = propagateKeyEvent(e, keyListeners.get(i)); } - } else { - consumed = false; if(DEBUG_KEY_EVENT) { - System.err.println("consumeKeyEvent: "+e); + System.err.println("consumeKeyEvent(usr): "+e+", consumed: "+consumedE); } } - for(int i = 0; !consumed && i < keyListeners.size(); i++ ) { - consumed = propagateKeyEvent(e, keyListeners.get(i)); - } } // // WindowListener/Event Support // - public void sendWindowEvent(int eventType) { - consumeWindowEvent( new WindowEvent(eventType, this, System.currentTimeMillis()) ); + @Override + public final void sendWindowEvent(int eventType) { + consumeWindowEvent( new WindowEvent((short)eventType, this, System.currentTimeMillis()) ); } - public void enqueueWindowEvent(boolean wait, int eventType) { - enqueueEvent( wait, new WindowEvent(eventType, this, System.currentTimeMillis()) ); + public final void enqueueWindowEvent(boolean wait, int eventType) { + enqueueEvent( wait, new WindowEvent((short)eventType, this, System.currentTimeMillis()) ); } - public void addWindowListener(WindowListener l) { + @Override + public final void addWindowListener(WindowListener l) { addWindowListener(-1, l); } - public void addWindowListener(int index, WindowListener l) + @Override + public final void addWindowListener(int index, WindowListener l) throws IndexOutOfBoundsException { if(l == null) { @@ -2276,13 +3469,14 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } @SuppressWarnings("unchecked") ArrayList<WindowListener> clonedListeners = (ArrayList<WindowListener>) windowListeners.clone(); - if(0>index) { - index = clonedListeners.size(); + if(0>index) { + index = clonedListeners.size(); } clonedListeners.add(index, l); windowListeners = clonedListeners; } + @Override public final void removeWindowListener(WindowListener l) { if (l == null) { return; @@ -2293,16 +3487,18 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer windowListeners = clonedListeners; } - public WindowListener getWindowListener(int index) { + @Override + public final WindowListener getWindowListener(int index) { @SuppressWarnings("unchecked") ArrayList<WindowListener> clonedListeners = (ArrayList<WindowListener>) windowListeners.clone(); - if(0>index) { - index = clonedListeners.size()-1; + if(0>index) { + index = clonedListeners.size()-1; } return clonedListeners.get(index); } - public WindowListener[] getWindowListeners() { + @Override + public final WindowListener[] getWindowListeners() { return windowListeners.toArray(new WindowListener[windowListeners.size()]); } @@ -2310,7 +3506,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(DEBUG_IMPLEMENTATION) { System.err.println("consumeWindowEvent: "+e+", visible "+isVisible()+" "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); } - for(int i = 0; i < windowListeners.size(); i++ ) { + for(int i = 0; !e.isConsumed() && i < windowListeners.size(); i++ ) { WindowListener l = windowListeners.get(i); switch(e.getEventType()) { case WindowEvent.EVENT_WINDOW_RESIZED: @@ -2335,7 +3531,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer l.windowRepaint((WindowUpdateEvent)e); break; default: - throw + throw new NativeWindowException("Unexpected window event type " + e.getEventType()); } @@ -2349,17 +3545,17 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer System.err.println("Window.focusChanged: ("+getThreadName()+"): (defer: "+defer+") "+this.hasFocus+" -> "+focusGained+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); } hasFocus = focusGained; - final int evt = focusGained ? WindowEvent.EVENT_WINDOW_GAINED_FOCUS : WindowEvent.EVENT_WINDOW_LOST_FOCUS ; + final int evt = focusGained ? WindowEvent.EVENT_WINDOW_GAINED_FOCUS : WindowEvent.EVENT_WINDOW_LOST_FOCUS ; if(!defer) { sendWindowEvent(evt); } else { enqueueWindowEvent(false, evt); } } - } + } /** Triggered by implementation's WM events to update the visibility state. */ - protected void visibleChanged(boolean defer, boolean visible) { + protected final void visibleChanged(boolean defer, boolean visible) { if(this.visible != visible) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.visibleChanged ("+getThreadName()+"): (defer: "+defer+") "+this.visible+" -> "+visible+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); @@ -2368,28 +3564,37 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } - private boolean waitForVisible(boolean visible, boolean failFast) { + /** Returns -1 if failed, otherwise remaining time until {@link #TIMEOUT_NATIVEWINDOW}, maybe zero. */ + private long waitForVisible(boolean visible, boolean failFast) { return waitForVisible(visible, failFast, TIMEOUT_NATIVEWINDOW); } - private boolean waitForVisible(boolean visible, boolean failFast, long timeOut) { - DisplayImpl display = (DisplayImpl) screen.getDisplay(); - for(long sleep = timeOut; 0<sleep && this.visible != visible; sleep-=10 ) { - display.dispatchMessagesNative(); // status up2date + /** Returns -1 if failed, otherwise remaining time until <code>timeOut</code>, maybe zero. */ + private long waitForVisible(boolean visible, boolean failFast, long timeOut) { + final DisplayImpl display = (DisplayImpl) screen.getDisplay(); + display.dispatchMessagesNative(); // status up2date + long remaining; + for(remaining = timeOut; 0<remaining && this.visible != visible; remaining-=10 ) { try { Thread.sleep(10); } catch (InterruptedException ie) {} + display.dispatchMessagesNative(); // status up2date } if(this.visible != visible) { - final String msg = "Visibility not reached as requested within "+timeOut+"ms : requested "+visible+", is "+this.visible; + final String msg = "Visibility not reached as requested within "+timeOut+"ms : requested "+visible+", is "+this.visible; if(failFast) { throw new NativeWindowException(msg); } else if (DEBUG_IMPLEMENTATION) { System.err.println(msg); + Thread.dumpStack(); } + return -1; + } else if( 0 < remaining ){ + return remaining; + } else { + return 0; } - return this.visible == visible; } - /** Triggered by implementation's WM events to update the client-area size w/o insets/decorations. */ + /** Triggered by implementation's WM events to update the client-area size w/o insets/decorations. */ protected void sizeChanged(boolean defer, int newWidth, int newHeight, boolean force) { if(force || getWidth() != newWidth || getHeight() != newHeight) { if(DEBUG_IMPLEMENTATION) { @@ -2408,20 +3613,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } - + private boolean waitForSize(int w, int h, boolean failFast, long timeOut) { - DisplayImpl display = (DisplayImpl) screen.getDisplay(); - boolean reached = false; - for(long sleep = timeOut; !reached && 0<sleep; sleep-=10 ) { - if( w==getWidth() && h==getHeight() ) { - // reached pos/size - reached = true; - } else { - display.dispatchMessagesNative(); // status up2date - try { Thread.sleep(10); } catch (InterruptedException ie) {} - } + final DisplayImpl display = (DisplayImpl) screen.getDisplay(); + display.dispatchMessagesNative(); // status up2date + long sleep; + for(sleep = timeOut; 0<sleep && w!=getWidth() && h!=getHeight(); sleep-=10 ) { + try { Thread.sleep(10); } catch (InterruptedException ie) {} + display.dispatchMessagesNative(); // status up2date } - if(!reached) { + if(0 >= sleep) { final String msg = "Size/Pos not reached as requested within "+timeOut+"ms : requested "+w+"x"+h+", is "+getWidth()+"x"+getHeight(); if(failFast) { throw new NativeWindowException(msg); @@ -2429,12 +3630,14 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer System.err.println(msg); Thread.dumpStack(); } + return false; + } else { + return true; } - return reached; } - - /** Triggered by implementation's WM events to update the position. */ - protected void positionChanged(boolean defer, int newX, int newY) { + + /** Triggered by implementation's WM events to update the position. */ + protected final void positionChanged(boolean defer, int newX, int newY) { if ( getX() != newX || getY() != newY ) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.positionChanged: ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); @@ -2446,74 +3649,133 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer enqueueWindowEvent(false, WindowEvent.EVENT_WINDOW_MOVED); } } else { - autoPosition = false; // ensure it's off even w/ same position + autoPosition = false; // ensure it's off even w/ same position } } /** - * Triggered by implementation's WM events to update the insets. - * + * Wait until position is reached within tolerances, either auto-position or custom position. + * <p> + * Since WM may not obey our positional request exactly, we allow a tolerance of 2 times insets[left/top], or 64 pixels, whatever is greater. + * </p> + */ + private boolean waitForPosition(boolean useCustomPosition, int x, int y, long timeOut) { + final DisplayImpl display = (DisplayImpl) screen.getDisplay(); + final int maxDX, maxDY; + { + final InsetsImmutable insets = getInsets(); + maxDX = Math.max(64, insets.getLeftWidth() * 2); + maxDY = Math.max(64, insets.getTopHeight() * 2); + } + long remaining = timeOut; + boolean ok; + do { + if( useCustomPosition ) { + ok = Math.abs(x - getX()) <= maxDX && Math.abs(y - getY()) <= maxDY ; + } else { + ok = !autoPosition; + } + if( !ok ) { + try { Thread.sleep(10); } catch (InterruptedException ie) {} + display.dispatchMessagesNative(); // status up2date + remaining-=10; + } + } while ( 0<remaining && !ok ); + if (DEBUG_IMPLEMENTATION) { + if( !ok ) { + if( useCustomPosition ) { + System.err.println("Custom position "+x+"/"+y+" not reached within timeout, has "+getX()+"/"+getY()+", remaining "+remaining); + } else { + System.err.println("Auto position not reached within timeout, has "+getX()+"/"+getY()+", autoPosition "+autoPosition+", remaining "+remaining); + } + Thread.dumpStack(); + } + } + return ok; + } + + /** + * Triggered by implementation's WM events to update the insets. + * * @see #getInsets() * @see #updateInsetsImpl(Insets) */ protected void insetsChanged(boolean defer, int left, int right, int top, int bottom) { if ( left >= 0 && right >= 0 && top >= 0 && bottom >= 0 ) { - if(isUndecorated()) { + if( blockInsetsChange || isUndecorated() ) { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.insetsChanged: skip insets change for undecoration mode"); + System.err.println("Window.insetsChanged (defer: "+defer+"): Skip insets change "+insets+" -> "+new Insets(left, right, top, bottom)+" (blocked "+blockInsetsChange+", undecoration "+isUndecorated()+")"); } - } else if ( (left != insets.getLeftWidth() || right != insets.getRightWidth() || + } else if ( (left != insets.getLeftWidth() || right != insets.getRightWidth() || top != insets.getTopHeight() || bottom != insets.getBottomHeight() ) ) { - insets.setLeftWidth(left); - insets.setRightWidth(right); - insets.setTopHeight(top); - insets.setBottomHeight(bottom); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.insetsChanged: (defer: "+defer+") "+insets); + System.err.println("Window.insetsChanged (defer: "+defer+"): Changed "+insets+" -> "+new Insets(left, right, top, bottom)); } + insets.set(left, right, top, bottom); } } } - + /** - * Triggered by implementation's WM events or programmatically - * + * Triggered by implementation's WM events or programmatic while respecting {@link #getDefaultCloseOperation()}. + * * @param force if true, overrides {@link #setDefaultCloseOperation(WindowClosingMode)} with {@link WindowClosingProtocol#DISPOSE_ON_CLOSE} * and hence force destruction. Otherwise is follows the user settings. * @return true if this window is no more valid and hence has been destroyed, otherwise false. */ - protected boolean windowDestroyNotify(boolean force) { + public final boolean windowDestroyNotify(boolean force) { + final WindowClosingMode defMode = getDefaultCloseOperation(); + final WindowClosingMode mode = force ? WindowClosingMode.DISPOSE_ON_CLOSE : defMode; if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.windowDestroyNotify(force: "+force+") START "+getThreadName()+": "+this); - } - if(force) { - setDefaultCloseOperation(WindowClosingMode.DISPOSE_ON_CLOSE); + System.err.println("Window.windowDestroyNotify(isNativeValid: "+isNativeValid()+", force: "+force+", mode "+defMode+" -> "+mode+") "+getThreadName()+": "+this); + // Thread.dumpStack(); } - // send synced destroy notifications - enqueueWindowEvent(true, WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); + final boolean destroyed; - if(handleDestroyNotify && WindowClosingMode.DISPOSE_ON_CLOSE == getDefaultCloseOperation()) { - destroy(); + if( isNativeValid() ) { + if( WindowClosingMode.DISPOSE_ON_CLOSE == mode ) { + if(force) { + setDefaultCloseOperation(mode); + } + try { + if( null == windowDestroyNotifyAction ) { + destroy(); + } else { + windowDestroyNotifyAction.run(); + } + } finally { + if(force) { + setDefaultCloseOperation(defMode); + } + } + } else { + // send synced destroy notifications + sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); + } + + destroyed = !isNativeValid(); + } else { + destroyed = true; } - - final boolean destroyed = !isNativeValid(); if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.windowDestroyNotify(force: "+force+") END "+getThreadName()+": destroyed "+destroyed+", "+this); + System.err.println("Window.windowDestroyNotify(isNativeValid: "+isNativeValid()+", force: "+force+", mode "+mode+") END "+getThreadName()+": destroyed "+destroyed+", "+this); } + return destroyed; } - public void windowRepaint(int x, int y, int width, int height) { - windowRepaint(false, x, y, width, height); + @Override + public final void windowRepaint(int x, int y, int width, int height) { + windowRepaint(false, x, y, width, height); } - + /** * Triggered by implementation's WM events to update the content - */ - protected void windowRepaint(boolean defer, int x, int y, int width, int height) { + */ + protected final void windowRepaint(boolean defer, int x, int y, int width, int height) { width = ( 0 >= width ) ? getWidth() : width; height = ( 0 >= height ) ? getHeight() : height; if(DEBUG_IMPLEMENTATION) { @@ -2574,10 +3836,6 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return sb.toString(); } - protected final void shouldNotCallThis() { - throw new NativeWindowException("Should not call this"); - } - public static String getThreadName() { return Display.getThreadName(); } diff --git a/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java b/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java index 861a6d6be..2ba5b3460 100644 --- a/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java +++ b/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package jogamp.newt.awt; import javax.media.nativewindow.AbstractGraphicsConfiguration; @@ -76,12 +76,12 @@ public class NewtFactoryAWT extends NewtFactory { } return (JAWTWindow)nw; } - + public static void destroyNativeWindow(JAWTWindow jawtWindow) { final AbstractGraphicsConfiguration config = jawtWindow.getGraphicsConfiguration(); jawtWindow.destroy(); - config.getScreen().getDevice().close(); + config.getScreen().getDevice().close(); } - + } diff --git a/src/newt/classes/jogamp/newt/awt/event/AWTNewtEventFactory.java b/src/newt/classes/jogamp/newt/awt/event/AWTNewtEventFactory.java index cb70da13f..0ee3cc0cd 100644 --- a/src/newt/classes/jogamp/newt/awt/event/AWTNewtEventFactory.java +++ b/src/newt/classes/jogamp/newt/awt/event/AWTNewtEventFactory.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,138 +20,641 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package jogamp.newt.awt.event; -import com.jogamp.common.util.IntIntHashMap; -import com.jogamp.newt.event.InputEvent; +import com.jogamp.newt.event.MouseEvent; +/** + * + * <a name="AWTEventModifierMapping"><h5>AWT Event Modifier Mapping</h5></a> + * <pre> + Modifier AWT Constant AWT Bit AWT Ex NEWT Constant NEWT Bit + ------------- ------------------------------- ------- ------ ------------------------- -------- + Shift Event.SHIFT_MASK 0 + Ctrl Event.CTRL_MASK 1 + Meta Event.META_MASK 2 + Alt Event.ALT_MASK 3 + Button1 InputEvent.BUTTON1_MASK 4 + Button2 InputEvent.BUTTON2_MASK 3 + Button3 InputEvent.BUTTON3_MASK 2 + Shift Down InputEvent.SHIFT_DOWN_MASK 6 * InputEvent.SHIFT_MASK 0 + Ctrl Down InputEvent.CTRL_DOWN_MASK 7 * InputEvent.CTRL_MASK 1 + Meta Down InputEvent.META_DOWN_MASK 8 * InputEvent.META_MASK 2 + Alt Down InputEvent.ALT_DOWN_MASK 9 * InputEvent.ALT_MASK 3 + Button1 Down InputEvent.BUTTON1_DOWN_MASK 10 * InputEvent.BUTTON1_MASK 5 + Button2 Down InputEvent.BUTTON2_DOWN_MASK 11 * InputEvent.BUTTON2_MASK 6 + Button3 Down InputEvent.BUTTON3_DOWN_MASK 12 * InputEvent.BUTTON3_MASK 7 + AltGraph Down InputEvent.ALT_GRAPH_DOWN_MASK 13 * InputEvent.ALT_GRAPH_MASK 4 + Button4 Down -- 14 * InputEvent.BUTTON4_MASK 8 + Button5 Down -- 15 * InputEvent.BUTTON5_MASK 9 + Button6 Down -- 16 * InputEvent.BUTTON6_MASK 10 + Button7 Down -- 17 * InputEvent.BUTTON7_MASK 11 + Button8 Down -- 18 * InputEvent.BUTTON8_MASK 12 + Button9 Down -- 19 * InputEvent.BUTTON9_MASK 13 + Button10 Down -- 20 * 14 + Button11 Down -- 21 * 15 + Button12 Down -- 22 * 16 + Button13 Down -- 23 * 17 + Button14 Down -- 24 * 18 + Button15 Down -- 25 * 19 + Button16 Down -- 26 * InputEvent.BUTTONLAST_MASK 20 + Button17 Down -- 27 * + Button18 Down -- 28 * + Button19 Down -- 29 * + Button20 Down -- 30 * + Autorepeat -- - InputEvent.AUTOREPEAT_MASK 29 + Confined -- - InputEvent.CONFINED_MASK 30 + Invisible -- - InputEvent.INVISIBLE_MASK 31 + * </pre> + * + */ public class AWTNewtEventFactory { - protected static final IntIntHashMap eventTypeAWT2NEWT; + /** zero-based AWT button mask array filled by {@link #getAWTButtonDownMask(int)}, allowing fast lookup. */ + private static int awtButtonDownMasks[] ; static { - IntIntHashMap map = new IntIntHashMap(); - map.setKeyNotFoundValue(0xFFFFFFFF); - // n/a map.put(java.awt.event.WindowEvent.WINDOW_OPENED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_OPENED); - map.put(java.awt.event.WindowEvent.WINDOW_CLOSING, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); - map.put(java.awt.event.WindowEvent.WINDOW_CLOSED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DESTROYED); - // n/a map.put(java.awt.event.WindowEvent.WINDOW_ICONIFIED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_ICONIFIED); - // n/a map.put(java.awt.event.WindowEvent.WINDOW_DEICONIFIED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DEICONIFIED); - map.put(java.awt.event.WindowEvent.WINDOW_ACTIVATED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS); - map.put(java.awt.event.WindowEvent.WINDOW_GAINED_FOCUS, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS); - map.put(java.awt.event.FocusEvent.FOCUS_GAINED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS); - map.put(java.awt.event.WindowEvent.WINDOW_DEACTIVATED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS); - map.put(java.awt.event.WindowEvent.WINDOW_LOST_FOCUS, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS); - map.put(java.awt.event.FocusEvent.FOCUS_LOST, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS); - // n/a map.put(java.awt.event.WindowEvent.WINDOW_STATE_CHANGED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_STATE_CHANGED); - - map.put(java.awt.event.ComponentEvent.COMPONENT_MOVED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_MOVED); - map.put(java.awt.event.ComponentEvent.COMPONENT_RESIZED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_RESIZED); - // n/a map.put(java.awt.event.ComponentEvent.COMPONENT_SHOWN, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_SHOWN); - // n/a map.put(java.awt.event.ComponentEvent.COMPONENT_HIDDEN, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_HIDDEN); - - map.put(java.awt.event.MouseEvent.MOUSE_CLICKED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED); - map.put(java.awt.event.MouseEvent.MOUSE_PRESSED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED); - map.put(java.awt.event.MouseEvent.MOUSE_RELEASED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED); - map.put(java.awt.event.MouseEvent.MOUSE_MOVED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED); - map.put(java.awt.event.MouseEvent.MOUSE_ENTERED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_ENTERED); - map.put(java.awt.event.MouseEvent.MOUSE_EXITED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_EXITED); - map.put(java.awt.event.MouseEvent.MOUSE_DRAGGED, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED); - map.put(java.awt.event.MouseEvent.MOUSE_WHEEL, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED); - - map.put(java.awt.event.KeyEvent.KEY_PRESSED, com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED); - map.put(java.awt.event.KeyEvent.KEY_RELEASED, com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED); - map.put(java.awt.event.KeyEvent.KEY_TYPED, com.jogamp.newt.event.KeyEvent.EVENT_KEY_TYPED); - - eventTypeAWT2NEWT = map; + // There is an assumption in awtModifiers2Newt(int,int,boolean) + // that the awtButtonMasks and newtButtonMasks are peers, i.e. + // a given index refers to the same button in each array. + + /* { + Method _getMaskForButtonMethod = null; + try { + _getMaskForButtonMethod = ReflectionUtil.getMethod(java.awt.event.InputEvent.class, "getMaskForButton", int.class); + } catch(Throwable t) {} + getMaskForButtonMethod = _getMaskForButtonMethod; + } */ + + awtButtonDownMasks = new int[com.jogamp.newt.event.MouseEvent.BUTTON_COUNT] ; // java.awt.MouseInfo.getNumberOfButtons() ; + for (int n = 0 ; n < awtButtonDownMasks.length ; ++n) { + awtButtonDownMasks[n] = getAWTButtonDownMaskImpl(n+1); + } } - public static final int awtModifiers2Newt(int awtMods, boolean mouseHint) { + public static final short eventTypeAWT2NEWT(int awtType) { + switch( awtType ) { + // n/a case java.awt.event.WindowEvent.WINDOW_OPENED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_OPENED; + case java.awt.event.WindowEvent.WINDOW_CLOSING: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY; + case java.awt.event.WindowEvent.WINDOW_CLOSED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DESTROYED; + // n/a case java.awt.event.WindowEvent.WINDOW_ICONIFIED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_ICONIFIED; + // n/a case java.awt.event.WindowEvent.WINDOW_DEICONIFIED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_DEICONIFIED; + case java.awt.event.WindowEvent.WINDOW_ACTIVATED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS; + case java.awt.event.WindowEvent.WINDOW_GAINED_FOCUS: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS; + case java.awt.event.FocusEvent.FOCUS_GAINED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS; + case java.awt.event.WindowEvent.WINDOW_DEACTIVATED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS; + case java.awt.event.WindowEvent.WINDOW_LOST_FOCUS: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS; + case java.awt.event.FocusEvent.FOCUS_LOST: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_LOST_FOCUS; + // n/a case java.awt.event.WindowEvent.WINDOW_STATE_CHANGED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_STATE_CHANGED; + + case java.awt.event.ComponentEvent.COMPONENT_MOVED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_MOVED; + case java.awt.event.ComponentEvent.COMPONENT_RESIZED: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_RESIZED; + // n/a case java.awt.event.ComponentEvent.COMPONENT_SHOWN: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_SHOWN; + // n/a case java.awt.event.ComponentEvent.COMPONENT_HIDDEN: return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_HIDDEN; + + case java.awt.event.MouseEvent.MOUSE_CLICKED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED; + case java.awt.event.MouseEvent.MOUSE_PRESSED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED; + case java.awt.event.MouseEvent.MOUSE_RELEASED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED; + case java.awt.event.MouseEvent.MOUSE_MOVED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED; + case java.awt.event.MouseEvent.MOUSE_ENTERED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_ENTERED; + case java.awt.event.MouseEvent.MOUSE_EXITED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_EXITED; + case java.awt.event.MouseEvent.MOUSE_DRAGGED: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED; + case java.awt.event.MouseEvent.MOUSE_WHEEL: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED; + + case java.awt.event.KeyEvent.KEY_PRESSED: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED; + case java.awt.event.KeyEvent.KEY_RELEASED: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED; + } + return (short)0; + } + + private static int getAWTButtonDownMaskImpl(int button) { + /** + * java.awt.event.InputEvent.getMaskForButton(button); + * + if(null != getMaskForButtonMethod) { + Object r=null; + try { + r = getMaskForButtonMethod.invoke(null, new Integer(button)); + } catch (Throwable t) { } + if(null != r) { + return ((Integer)r).intValue(); + } + } */ + final int m; + switch(button) { + case 0 : m = 0; break; + case 1 : m = java.awt.event.InputEvent.BUTTON1_DOWN_MASK; break; // 1<<10 + case 2 : m = java.awt.event.InputEvent.BUTTON2_DOWN_MASK; break; // 1<<11 + case 3 : m = java.awt.event.InputEvent.BUTTON3_DOWN_MASK; break; // 1<<12 + default: + if( button <= com.jogamp.newt.event.MouseEvent.BUTTON_COUNT ) { + m = 1 << ( 10 + button ) ; // b4 = 1<<14, b5 = 1<<15, etc + } else { + m = 0; + } + } + return m; + } + + /** + * <p> + * See <a href="#AWTEventModifierMapping"> AWT event modifier mapping details</a>. + * </p> + * + * @param button + * @return + */ + public static int getAWTButtonDownMask(int button) { + if( 0 < button && button <= awtButtonDownMasks.length ) { + return awtButtonDownMasks[button-1]; + } else { + return 0; + } + } + + public static final short awtButton2Newt(int awtButton) { + if( 0 < awtButton && awtButton <= com.jogamp.newt.event.MouseEvent.BUTTON_COUNT ) { + return (short)awtButton; + } else { + return (short)0; + } + } + + /** + * Converts the specified set of AWT event modifiers and extended event + * modifiers to the equivalent NEWT event modifiers. + * + * <p> + * See <a href="#AWTEventModifierMapping"> AWT event modifier mapping details</a>. + * </p> + * + * @param awtMods + * The AWT event modifiers. + * + * @param awtModsEx + * The AWT extended event modifiers. + * AWT passes mouse button specific bits here and are the preferred way check the mouse button state. + */ + public static final int awtModifiers2Newt(final int awtMods, final int awtModsEx) { int newtMods = 0; + + /** Redundant old modifiers .. if ((awtMods & java.awt.event.InputEvent.SHIFT_MASK) != 0) newtMods |= com.jogamp.newt.event.InputEvent.SHIFT_MASK; if ((awtMods & java.awt.event.InputEvent.CTRL_MASK) != 0) newtMods |= com.jogamp.newt.event.InputEvent.CTRL_MASK; if ((awtMods & java.awt.event.InputEvent.META_MASK) != 0) newtMods |= com.jogamp.newt.event.InputEvent.META_MASK; if ((awtMods & java.awt.event.InputEvent.ALT_MASK) != 0) newtMods |= com.jogamp.newt.event.InputEvent.ALT_MASK; - if ((awtMods & java.awt.event.InputEvent.ALT_GRAPH_MASK) != 0) newtMods |= com.jogamp.newt.event.InputEvent.ALT_GRAPH_MASK; + if ((awtMods & java.awt.event.InputEvent.ALT_GRAPH_MASK) != 0) newtMods |= com.jogamp.newt.event.InputEvent.ALT_GRAPH_MASK; */ + + if ((awtModsEx & java.awt.event.InputEvent.SHIFT_DOWN_MASK) != 0) newtMods |= com.jogamp.newt.event.InputEvent.SHIFT_MASK; + if ((awtModsEx & java.awt.event.InputEvent.CTRL_DOWN_MASK) != 0) newtMods |= com.jogamp.newt.event.InputEvent.CTRL_MASK; + if ((awtModsEx & java.awt.event.InputEvent.META_DOWN_MASK) != 0) newtMods |= com.jogamp.newt.event.InputEvent.META_MASK; + if ((awtModsEx & java.awt.event.InputEvent.ALT_DOWN_MASK) != 0) newtMods |= com.jogamp.newt.event.InputEvent.ALT_MASK; + if ((awtModsEx & java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK) != 0) newtMods |= com.jogamp.newt.event.InputEvent.ALT_GRAPH_MASK; + + // The BUTTON1_MASK, BUTTON2_MASK, and BUTTON3_MASK bits are + // being ignored intentionally. The AWT docs say that the + // BUTTON1_DOWN_MASK etc bits in the extended modifiers are + // the preferred place to check current button state. + + if( 0 != awtModsEx ) { + for (int n = 0 ; n < awtButtonDownMasks.length ; ++n) { + if ( (awtModsEx & awtButtonDownMasks[n]) != 0 ) { + newtMods |= com.jogamp.newt.event.InputEvent.getButtonMask(n+1); + } + } + } + return newtMods; } - public static final int awtButton2Newt(int awtButton) { - switch (awtButton) { - case java.awt.event.MouseEvent.BUTTON1: return com.jogamp.newt.event.MouseEvent.BUTTON1; - case java.awt.event.MouseEvent.BUTTON2: return com.jogamp.newt.event.MouseEvent.BUTTON2; - case java.awt.event.MouseEvent.BUTTON3: return com.jogamp.newt.event.MouseEvent.BUTTON3; + public static short awtKeyCode2NewtKeyCode(final int awtKeyCode) { + final short defNEWTKeyCode = (short)awtKeyCode; + switch (awtKeyCode) { + case java.awt.event.KeyEvent.VK_HOME : return com.jogamp.newt.event.KeyEvent.VK_HOME; + case java.awt.event.KeyEvent.VK_END : return com.jogamp.newt.event.KeyEvent.VK_END; + case java.awt.event.KeyEvent.VK_FINAL : return com.jogamp.newt.event.KeyEvent.VK_FINAL; + case java.awt.event.KeyEvent.VK_PRINTSCREEN : return com.jogamp.newt.event.KeyEvent.VK_PRINTSCREEN; + case java.awt.event.KeyEvent.VK_BACK_SPACE : return com.jogamp.newt.event.KeyEvent.VK_BACK_SPACE; + case java.awt.event.KeyEvent.VK_TAB : return com.jogamp.newt.event.KeyEvent.VK_TAB; + case java.awt.event.KeyEvent.VK_ENTER : return com.jogamp.newt.event.KeyEvent.VK_ENTER; + case java.awt.event.KeyEvent.VK_PAGE_DOWN : return com.jogamp.newt.event.KeyEvent.VK_PAGE_DOWN; + case java.awt.event.KeyEvent.VK_CLEAR : return com.jogamp.newt.event.KeyEvent.VK_CLEAR; + case java.awt.event.KeyEvent.VK_SHIFT : return com.jogamp.newt.event.KeyEvent.VK_SHIFT; + case java.awt.event.KeyEvent.VK_PAGE_UP : return com.jogamp.newt.event.KeyEvent.VK_PAGE_UP; + case java.awt.event.KeyEvent.VK_CONTROL : return com.jogamp.newt.event.KeyEvent.VK_CONTROL; + case java.awt.event.KeyEvent.VK_ALT : return com.jogamp.newt.event.KeyEvent.VK_ALT; + case java.awt.event.KeyEvent.VK_ALT_GRAPH : return com.jogamp.newt.event.KeyEvent.VK_ALT_GRAPH; + case java.awt.event.KeyEvent.VK_CAPS_LOCK : return com.jogamp.newt.event.KeyEvent.VK_CAPS_LOCK; + case java.awt.event.KeyEvent.VK_PAUSE : return com.jogamp.newt.event.KeyEvent.VK_PAUSE; + case java.awt.event.KeyEvent.VK_SCROLL_LOCK : return com.jogamp.newt.event.KeyEvent.VK_SCROLL_LOCK; + case java.awt.event.KeyEvent.VK_CANCEL : return com.jogamp.newt.event.KeyEvent.VK_CANCEL; + case java.awt.event.KeyEvent.VK_INSERT : return com.jogamp.newt.event.KeyEvent.VK_INSERT; + case java.awt.event.KeyEvent.VK_ESCAPE : return com.jogamp.newt.event.KeyEvent.VK_ESCAPE; + case java.awt.event.KeyEvent.VK_CONVERT : return com.jogamp.newt.event.KeyEvent.VK_CONVERT; + case java.awt.event.KeyEvent.VK_NONCONVERT : return com.jogamp.newt.event.KeyEvent.VK_NONCONVERT; + case java.awt.event.KeyEvent.VK_ACCEPT : return com.jogamp.newt.event.KeyEvent.VK_ACCEPT; + case java.awt.event.KeyEvent.VK_MODECHANGE : return com.jogamp.newt.event.KeyEvent.VK_MODECHANGE; + case java.awt.event.KeyEvent.VK_SPACE : return com.jogamp.newt.event.KeyEvent.VK_SPACE; + case java.awt.event.KeyEvent.VK_EXCLAMATION_MARK: return com.jogamp.newt.event.KeyEvent.VK_EXCLAMATION_MARK; + case java.awt.event.KeyEvent.VK_QUOTEDBL : return com.jogamp.newt.event.KeyEvent.VK_QUOTEDBL; + case java.awt.event.KeyEvent.VK_NUMBER_SIGN : return com.jogamp.newt.event.KeyEvent.VK_NUMBER_SIGN; + case java.awt.event.KeyEvent.VK_DOLLAR : return com.jogamp.newt.event.KeyEvent.VK_DOLLAR; + // case 0x25 : return com.jogamp.newt.event.KeyEvent.VK_PERCENT; + case java.awt.event.KeyEvent.VK_AMPERSAND : return com.jogamp.newt.event.KeyEvent.VK_AMPERSAND; + case java.awt.event.KeyEvent.VK_QUOTE : return com.jogamp.newt.event.KeyEvent.VK_QUOTE; + case java.awt.event.KeyEvent.VK_LEFT_PARENTHESIS : return com.jogamp.newt.event.KeyEvent.VK_LEFT_PARENTHESIS; + case java.awt.event.KeyEvent.VK_RIGHT_PARENTHESIS: return com.jogamp.newt.event.KeyEvent.VK_RIGHT_PARENTHESIS; + case java.awt.event.KeyEvent.VK_ASTERISK : return com.jogamp.newt.event.KeyEvent.VK_ASTERISK; + case java.awt.event.KeyEvent.VK_PLUS : return com.jogamp.newt.event.KeyEvent.VK_PLUS; + case java.awt.event.KeyEvent.VK_COMMA : return com.jogamp.newt.event.KeyEvent.VK_COMMA; + case java.awt.event.KeyEvent.VK_MINUS : return com.jogamp.newt.event.KeyEvent.VK_MINUS; + case java.awt.event.KeyEvent.VK_PERIOD : return com.jogamp.newt.event.KeyEvent.VK_PERIOD; + case java.awt.event.KeyEvent.VK_SLASH : return com.jogamp.newt.event.KeyEvent.VK_SLASH; + case java.awt.event.KeyEvent.VK_0 : return com.jogamp.newt.event.KeyEvent.VK_0; + case java.awt.event.KeyEvent.VK_1 : return com.jogamp.newt.event.KeyEvent.VK_1; + case java.awt.event.KeyEvent.VK_2 : return com.jogamp.newt.event.KeyEvent.VK_2; + case java.awt.event.KeyEvent.VK_3 : return com.jogamp.newt.event.KeyEvent.VK_3; + case java.awt.event.KeyEvent.VK_4 : return com.jogamp.newt.event.KeyEvent.VK_4; + case java.awt.event.KeyEvent.VK_5 : return com.jogamp.newt.event.KeyEvent.VK_5; + case java.awt.event.KeyEvent.VK_6 : return com.jogamp.newt.event.KeyEvent.VK_6; + case java.awt.event.KeyEvent.VK_7 : return com.jogamp.newt.event.KeyEvent.VK_7; + case java.awt.event.KeyEvent.VK_8 : return com.jogamp.newt.event.KeyEvent.VK_8; + case java.awt.event.KeyEvent.VK_9 : return com.jogamp.newt.event.KeyEvent.VK_9; + case java.awt.event.KeyEvent.VK_COLON : return com.jogamp.newt.event.KeyEvent.VK_COLON; + case java.awt.event.KeyEvent.VK_SEMICOLON : return com.jogamp.newt.event.KeyEvent.VK_SEMICOLON; + case java.awt.event.KeyEvent.VK_LESS : return com.jogamp.newt.event.KeyEvent.VK_LESS; + case java.awt.event.KeyEvent.VK_EQUALS : return com.jogamp.newt.event.KeyEvent.VK_EQUALS; + case java.awt.event.KeyEvent.VK_GREATER : return com.jogamp.newt.event.KeyEvent.VK_GREATER; + case 0x3f : return com.jogamp.newt.event.KeyEvent.VK_QUESTIONMARK; + case java.awt.event.KeyEvent.VK_AT : return com.jogamp.newt.event.KeyEvent.VK_AT; + case java.awt.event.KeyEvent.VK_A : return com.jogamp.newt.event.KeyEvent.VK_A; + case java.awt.event.KeyEvent.VK_B : return com.jogamp.newt.event.KeyEvent.VK_B; + case java.awt.event.KeyEvent.VK_C : return com.jogamp.newt.event.KeyEvent.VK_C; + case java.awt.event.KeyEvent.VK_D : return com.jogamp.newt.event.KeyEvent.VK_D; + case java.awt.event.KeyEvent.VK_E : return com.jogamp.newt.event.KeyEvent.VK_E; + case java.awt.event.KeyEvent.VK_F : return com.jogamp.newt.event.KeyEvent.VK_F; + case java.awt.event.KeyEvent.VK_G : return com.jogamp.newt.event.KeyEvent.VK_G; + case java.awt.event.KeyEvent.VK_H : return com.jogamp.newt.event.KeyEvent.VK_H; + case java.awt.event.KeyEvent.VK_I : return com.jogamp.newt.event.KeyEvent.VK_I; + case java.awt.event.KeyEvent.VK_J : return com.jogamp.newt.event.KeyEvent.VK_J; + case java.awt.event.KeyEvent.VK_K : return com.jogamp.newt.event.KeyEvent.VK_K; + case java.awt.event.KeyEvent.VK_L : return com.jogamp.newt.event.KeyEvent.VK_L; + case java.awt.event.KeyEvent.VK_M : return com.jogamp.newt.event.KeyEvent.VK_M; + case java.awt.event.KeyEvent.VK_N : return com.jogamp.newt.event.KeyEvent.VK_N; + case java.awt.event.KeyEvent.VK_O : return com.jogamp.newt.event.KeyEvent.VK_O; + case java.awt.event.KeyEvent.VK_P : return com.jogamp.newt.event.KeyEvent.VK_P; + case java.awt.event.KeyEvent.VK_Q : return com.jogamp.newt.event.KeyEvent.VK_Q; + case java.awt.event.KeyEvent.VK_R : return com.jogamp.newt.event.KeyEvent.VK_R; + case java.awt.event.KeyEvent.VK_S : return com.jogamp.newt.event.KeyEvent.VK_S; + case java.awt.event.KeyEvent.VK_T : return com.jogamp.newt.event.KeyEvent.VK_T; + case java.awt.event.KeyEvent.VK_U : return com.jogamp.newt.event.KeyEvent.VK_U; + case java.awt.event.KeyEvent.VK_V : return com.jogamp.newt.event.KeyEvent.VK_V; + case java.awt.event.KeyEvent.VK_W : return com.jogamp.newt.event.KeyEvent.VK_W; + case java.awt.event.KeyEvent.VK_X : return com.jogamp.newt.event.KeyEvent.VK_X; + case java.awt.event.KeyEvent.VK_Y : return com.jogamp.newt.event.KeyEvent.VK_Y; + case java.awt.event.KeyEvent.VK_Z : return com.jogamp.newt.event.KeyEvent.VK_Z; + case java.awt.event.KeyEvent.VK_OPEN_BRACKET : return com.jogamp.newt.event.KeyEvent.VK_OPEN_BRACKET; + case java.awt.event.KeyEvent.VK_BACK_SLASH : return com.jogamp.newt.event.KeyEvent.VK_BACK_SLASH; + case java.awt.event.KeyEvent.VK_CLOSE_BRACKET : return com.jogamp.newt.event.KeyEvent.VK_CLOSE_BRACKET; + case java.awt.event.KeyEvent.VK_CIRCUMFLEX : return com.jogamp.newt.event.KeyEvent.VK_CIRCUMFLEX; + case java.awt.event.KeyEvent.VK_UNDERSCORE : return com.jogamp.newt.event.KeyEvent.VK_UNDERSCORE; + case java.awt.event.KeyEvent.VK_BACK_QUOTE : return com.jogamp.newt.event.KeyEvent.VK_BACK_QUOTE; + case java.awt.event.KeyEvent.VK_F1 : return com.jogamp.newt.event.KeyEvent.VK_F1; + case java.awt.event.KeyEvent.VK_F2 : return com.jogamp.newt.event.KeyEvent.VK_F2; + case java.awt.event.KeyEvent.VK_F3 : return com.jogamp.newt.event.KeyEvent.VK_F3; + case java.awt.event.KeyEvent.VK_F4 : return com.jogamp.newt.event.KeyEvent.VK_F4; + case java.awt.event.KeyEvent.VK_F5 : return com.jogamp.newt.event.KeyEvent.VK_F5; + case java.awt.event.KeyEvent.VK_F6 : return com.jogamp.newt.event.KeyEvent.VK_F6; + case java.awt.event.KeyEvent.VK_F7 : return com.jogamp.newt.event.KeyEvent.VK_F7; + case java.awt.event.KeyEvent.VK_F8 : return com.jogamp.newt.event.KeyEvent.VK_F8; + case java.awt.event.KeyEvent.VK_F9 : return com.jogamp.newt.event.KeyEvent.VK_F9; + case java.awt.event.KeyEvent.VK_F10 : return com.jogamp.newt.event.KeyEvent.VK_F10; + case java.awt.event.KeyEvent.VK_F11 : return com.jogamp.newt.event.KeyEvent.VK_F11; + case java.awt.event.KeyEvent.VK_F12 : return com.jogamp.newt.event.KeyEvent.VK_F12; + case java.awt.event.KeyEvent.VK_F13 : return com.jogamp.newt.event.KeyEvent.VK_F13; + case java.awt.event.KeyEvent.VK_F14 : return com.jogamp.newt.event.KeyEvent.VK_F14; + case java.awt.event.KeyEvent.VK_F15 : return com.jogamp.newt.event.KeyEvent.VK_F15; + case java.awt.event.KeyEvent.VK_F16 : return com.jogamp.newt.event.KeyEvent.VK_F16; + case java.awt.event.KeyEvent.VK_F17 : return com.jogamp.newt.event.KeyEvent.VK_F17; + case java.awt.event.KeyEvent.VK_F18 : return com.jogamp.newt.event.KeyEvent.VK_F18; + case java.awt.event.KeyEvent.VK_F19 : return com.jogamp.newt.event.KeyEvent.VK_F19; + case java.awt.event.KeyEvent.VK_F20 : return com.jogamp.newt.event.KeyEvent.VK_F20; + case java.awt.event.KeyEvent.VK_F21 : return com.jogamp.newt.event.KeyEvent.VK_F21; + case java.awt.event.KeyEvent.VK_F22 : return com.jogamp.newt.event.KeyEvent.VK_F22; + case java.awt.event.KeyEvent.VK_F23 : return com.jogamp.newt.event.KeyEvent.VK_F23; + case java.awt.event.KeyEvent.VK_F24 : return com.jogamp.newt.event.KeyEvent.VK_F24; + case java.awt.event.KeyEvent.VK_BRACELEFT : return com.jogamp.newt.event.KeyEvent.VK_LEFT_BRACE; + case 0x7c : return com.jogamp.newt.event.KeyEvent.VK_PIPE; + case java.awt.event.KeyEvent.VK_BRACERIGHT : return com.jogamp.newt.event.KeyEvent.VK_RIGHT_BRACE; + case java.awt.event.KeyEvent.VK_DEAD_TILDE : return com.jogamp.newt.event.KeyEvent.VK_TILDE; + case java.awt.event.KeyEvent.VK_DELETE : return com.jogamp.newt.event.KeyEvent.VK_DELETE; + case java.awt.event.KeyEvent.VK_NUMPAD0 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD0; + case java.awt.event.KeyEvent.VK_NUMPAD1 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD1; + case java.awt.event.KeyEvent.VK_NUMPAD2 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD2; + case java.awt.event.KeyEvent.VK_NUMPAD3 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD3; + case java.awt.event.KeyEvent.VK_NUMPAD4 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD4; + case java.awt.event.KeyEvent.VK_NUMPAD5 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD5; + case java.awt.event.KeyEvent.VK_NUMPAD6 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD6; + case java.awt.event.KeyEvent.VK_NUMPAD7 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD7; + case java.awt.event.KeyEvent.VK_NUMPAD8 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD8; + case java.awt.event.KeyEvent.VK_NUMPAD9 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD9; + case java.awt.event.KeyEvent.VK_DECIMAL : return com.jogamp.newt.event.KeyEvent.VK_DECIMAL; + case java.awt.event.KeyEvent.VK_SEPARATOR : return com.jogamp.newt.event.KeyEvent.VK_SEPARATOR; + case java.awt.event.KeyEvent.VK_ADD : return com.jogamp.newt.event.KeyEvent.VK_ADD; + case java.awt.event.KeyEvent.VK_SUBTRACT : return com.jogamp.newt.event.KeyEvent.VK_SUBTRACT; + case java.awt.event.KeyEvent.VK_MULTIPLY : return com.jogamp.newt.event.KeyEvent.VK_MULTIPLY; + case java.awt.event.KeyEvent.VK_DIVIDE : return com.jogamp.newt.event.KeyEvent.VK_DIVIDE; + case java.awt.event.KeyEvent.VK_NUM_LOCK : return com.jogamp.newt.event.KeyEvent.VK_NUM_LOCK; + case java.awt.event.KeyEvent.VK_KP_LEFT : /** Fall through intended .. */ + case java.awt.event.KeyEvent.VK_LEFT : return com.jogamp.newt.event.KeyEvent.VK_LEFT; + case java.awt.event.KeyEvent.VK_KP_UP : /** Fall through intended .. */ + case java.awt.event.KeyEvent.VK_UP : return com.jogamp.newt.event.KeyEvent.VK_UP; + case java.awt.event.KeyEvent.VK_KP_RIGHT : /** Fall through intended .. */ + case java.awt.event.KeyEvent.VK_RIGHT : return com.jogamp.newt.event.KeyEvent.VK_RIGHT; + case java.awt.event.KeyEvent.VK_KP_DOWN : /** Fall through intended .. */ + case java.awt.event.KeyEvent.VK_DOWN : return com.jogamp.newt.event.KeyEvent.VK_DOWN; + case java.awt.event.KeyEvent.VK_CONTEXT_MENU : return com.jogamp.newt.event.KeyEvent.VK_CONTEXT_MENU; + case java.awt.event.KeyEvent.VK_WINDOWS : return com.jogamp.newt.event.KeyEvent.VK_WINDOWS; + case java.awt.event.KeyEvent.VK_META : return com.jogamp.newt.event.KeyEvent.VK_META; + case java.awt.event.KeyEvent.VK_HELP : return com.jogamp.newt.event.KeyEvent.VK_HELP; + case java.awt.event.KeyEvent.VK_COMPOSE : return com.jogamp.newt.event.KeyEvent.VK_COMPOSE; + case java.awt.event.KeyEvent.VK_BEGIN : return com.jogamp.newt.event.KeyEvent.VK_BEGIN; + case java.awt.event.KeyEvent.VK_STOP : return com.jogamp.newt.event.KeyEvent.VK_STOP; + case java.awt.event.KeyEvent.VK_INVERTED_EXCLAMATION_MARK: return com.jogamp.newt.event.KeyEvent.VK_INVERTED_EXCLAMATION_MARK; + case java.awt.event.KeyEvent.VK_EURO_SIGN : return com.jogamp.newt.event.KeyEvent.VK_EURO_SIGN; + case java.awt.event.KeyEvent.VK_CUT : return com.jogamp.newt.event.KeyEvent.VK_CUT; + case java.awt.event.KeyEvent.VK_COPY : return com.jogamp.newt.event.KeyEvent.VK_COPY; + case java.awt.event.KeyEvent.VK_PASTE : return com.jogamp.newt.event.KeyEvent.VK_PASTE; + case java.awt.event.KeyEvent.VK_UNDO : return com.jogamp.newt.event.KeyEvent.VK_UNDO; + case java.awt.event.KeyEvent.VK_AGAIN : return com.jogamp.newt.event.KeyEvent.VK_AGAIN; + case java.awt.event.KeyEvent.VK_FIND : return com.jogamp.newt.event.KeyEvent.VK_FIND; + case java.awt.event.KeyEvent.VK_PROPS : return com.jogamp.newt.event.KeyEvent.VK_PROPS; + case java.awt.event.KeyEvent.VK_INPUT_METHOD_ON_OFF: return com.jogamp.newt.event.KeyEvent.VK_INPUT_METHOD_ON_OFF; + case java.awt.event.KeyEvent.VK_CODE_INPUT : return com.jogamp.newt.event.KeyEvent.VK_CODE_INPUT; + case java.awt.event.KeyEvent.VK_ROMAN_CHARACTERS: return com.jogamp.newt.event.KeyEvent.VK_ROMAN_CHARACTERS; + case java.awt.event.KeyEvent.VK_ALL_CANDIDATES: return com.jogamp.newt.event.KeyEvent.VK_ALL_CANDIDATES; + case java.awt.event.KeyEvent.VK_PREVIOUS_CANDIDATE: return com.jogamp.newt.event.KeyEvent.VK_PREVIOUS_CANDIDATE; + case java.awt.event.KeyEvent.VK_ALPHANUMERIC : return com.jogamp.newt.event.KeyEvent.VK_ALPHANUMERIC; + case java.awt.event.KeyEvent.VK_KATAKANA : return com.jogamp.newt.event.KeyEvent.VK_KATAKANA; + case java.awt.event.KeyEvent.VK_HIRAGANA : return com.jogamp.newt.event.KeyEvent.VK_HIRAGANA; + case java.awt.event.KeyEvent.VK_FULL_WIDTH : return com.jogamp.newt.event.KeyEvent.VK_FULL_WIDTH; + case java.awt.event.KeyEvent.VK_HALF_WIDTH : return com.jogamp.newt.event.KeyEvent.VK_HALF_WIDTH; + case java.awt.event.KeyEvent.VK_JAPANESE_KATAKANA: return com.jogamp.newt.event.KeyEvent.VK_JAPANESE_KATAKANA; + case java.awt.event.KeyEvent.VK_JAPANESE_HIRAGANA: return com.jogamp.newt.event.KeyEvent.VK_JAPANESE_HIRAGANA; + case java.awt.event.KeyEvent.VK_JAPANESE_ROMAN: return com.jogamp.newt.event.KeyEvent.VK_JAPANESE_ROMAN; + case java.awt.event.KeyEvent.VK_KANA_LOCK : return com.jogamp.newt.event.KeyEvent.VK_KANA_LOCK; + } + return defNEWTKeyCode; + } + + public static int newtKeyCode2AWTKeyCode(final short newtKeyCode) { + final int defAwtKeyCode = 0xFFFF & (int)newtKeyCode; + switch (newtKeyCode) { + case com.jogamp.newt.event.KeyEvent.VK_HOME : return java.awt.event.KeyEvent.VK_HOME; + case com.jogamp.newt.event.KeyEvent.VK_END : return java.awt.event.KeyEvent.VK_END; + case com.jogamp.newt.event.KeyEvent.VK_FINAL : return java.awt.event.KeyEvent.VK_FINAL; + case com.jogamp.newt.event.KeyEvent.VK_PRINTSCREEN : return java.awt.event.KeyEvent.VK_PRINTSCREEN; + case com.jogamp.newt.event.KeyEvent.VK_BACK_SPACE : return java.awt.event.KeyEvent.VK_BACK_SPACE; + case com.jogamp.newt.event.KeyEvent.VK_TAB : return java.awt.event.KeyEvent.VK_TAB; + case com.jogamp.newt.event.KeyEvent.VK_ENTER : return java.awt.event.KeyEvent.VK_ENTER; + case com.jogamp.newt.event.KeyEvent.VK_PAGE_DOWN : return java.awt.event.KeyEvent.VK_PAGE_DOWN; + case com.jogamp.newt.event.KeyEvent.VK_CLEAR : return java.awt.event.KeyEvent.VK_CLEAR; + case com.jogamp.newt.event.KeyEvent.VK_SHIFT : return java.awt.event.KeyEvent.VK_SHIFT; + case com.jogamp.newt.event.KeyEvent.VK_PAGE_UP : return java.awt.event.KeyEvent.VK_PAGE_UP; + case com.jogamp.newt.event.KeyEvent.VK_CONTROL : return java.awt.event.KeyEvent.VK_CONTROL; + case com.jogamp.newt.event.KeyEvent.VK_ALT : return java.awt.event.KeyEvent.VK_ALT; + // FIXME: On X11 it results to 0xff7e w/ AWTRobot, which is wrong. 0xffea Alt_R is expected AFAIK. + case com.jogamp.newt.event.KeyEvent.VK_ALT_GRAPH : return java.awt.event.KeyEvent.VK_ALT_GRAPH; + case com.jogamp.newt.event.KeyEvent.VK_CAPS_LOCK : return java.awt.event.KeyEvent.VK_CAPS_LOCK; + case com.jogamp.newt.event.KeyEvent.VK_PAUSE : return java.awt.event.KeyEvent.VK_PAUSE; + case com.jogamp.newt.event.KeyEvent.VK_SCROLL_LOCK : return java.awt.event.KeyEvent.VK_SCROLL_LOCK; + case com.jogamp.newt.event.KeyEvent.VK_CANCEL : return java.awt.event.KeyEvent.VK_CANCEL; + case com.jogamp.newt.event.KeyEvent.VK_INSERT : return java.awt.event.KeyEvent.VK_INSERT; + case com.jogamp.newt.event.KeyEvent.VK_ESCAPE : return java.awt.event.KeyEvent.VK_ESCAPE; + case com.jogamp.newt.event.KeyEvent.VK_CONVERT : return java.awt.event.KeyEvent.VK_CONVERT; + case com.jogamp.newt.event.KeyEvent.VK_NONCONVERT : return java.awt.event.KeyEvent.VK_NONCONVERT; + case com.jogamp.newt.event.KeyEvent.VK_ACCEPT : return java.awt.event.KeyEvent.VK_ACCEPT; + case com.jogamp.newt.event.KeyEvent.VK_MODECHANGE : return java.awt.event.KeyEvent.VK_MODECHANGE; + case com.jogamp.newt.event.KeyEvent.VK_SPACE : return java.awt.event.KeyEvent.VK_SPACE; + case com.jogamp.newt.event.KeyEvent.VK_EXCLAMATION_MARK: return java.awt.event.KeyEvent.VK_EXCLAMATION_MARK; + case com.jogamp.newt.event.KeyEvent.VK_QUOTEDBL : return java.awt.event.KeyEvent.VK_QUOTEDBL; + case com.jogamp.newt.event.KeyEvent.VK_NUMBER_SIGN : return java.awt.event.KeyEvent.VK_NUMBER_SIGN; + case com.jogamp.newt.event.KeyEvent.VK_DOLLAR : return java.awt.event.KeyEvent.VK_DOLLAR; + case com.jogamp.newt.event.KeyEvent.VK_PERCENT : return defAwtKeyCode; + case com.jogamp.newt.event.KeyEvent.VK_AMPERSAND : return java.awt.event.KeyEvent.VK_AMPERSAND; + case com.jogamp.newt.event.KeyEvent.VK_QUOTE : return java.awt.event.KeyEvent.VK_QUOTE; + case com.jogamp.newt.event.KeyEvent.VK_LEFT_PARENTHESIS : return java.awt.event.KeyEvent.VK_LEFT_PARENTHESIS; + case com.jogamp.newt.event.KeyEvent.VK_RIGHT_PARENTHESIS: return java.awt.event.KeyEvent.VK_RIGHT_PARENTHESIS; + case com.jogamp.newt.event.KeyEvent.VK_ASTERISK : return java.awt.event.KeyEvent.VK_ASTERISK; + case com.jogamp.newt.event.KeyEvent.VK_PLUS : return java.awt.event.KeyEvent.VK_PLUS; + case com.jogamp.newt.event.KeyEvent.VK_COMMA : return java.awt.event.KeyEvent.VK_COMMA; + case com.jogamp.newt.event.KeyEvent.VK_MINUS : return java.awt.event.KeyEvent.VK_MINUS; + case com.jogamp.newt.event.KeyEvent.VK_PERIOD : return java.awt.event.KeyEvent.VK_PERIOD; + case com.jogamp.newt.event.KeyEvent.VK_SLASH : return java.awt.event.KeyEvent.VK_SLASH; + case com.jogamp.newt.event.KeyEvent.VK_0 : return java.awt.event.KeyEvent.VK_0; + case com.jogamp.newt.event.KeyEvent.VK_1 : return java.awt.event.KeyEvent.VK_1; + case com.jogamp.newt.event.KeyEvent.VK_2 : return java.awt.event.KeyEvent.VK_2; + case com.jogamp.newt.event.KeyEvent.VK_3 : return java.awt.event.KeyEvent.VK_3; + case com.jogamp.newt.event.KeyEvent.VK_4 : return java.awt.event.KeyEvent.VK_4; + case com.jogamp.newt.event.KeyEvent.VK_5 : return java.awt.event.KeyEvent.VK_5; + case com.jogamp.newt.event.KeyEvent.VK_6 : return java.awt.event.KeyEvent.VK_6; + case com.jogamp.newt.event.KeyEvent.VK_7 : return java.awt.event.KeyEvent.VK_7; + case com.jogamp.newt.event.KeyEvent.VK_8 : return java.awt.event.KeyEvent.VK_8; + case com.jogamp.newt.event.KeyEvent.VK_9 : return java.awt.event.KeyEvent.VK_9; + case com.jogamp.newt.event.KeyEvent.VK_COLON : return java.awt.event.KeyEvent.VK_COLON; + case com.jogamp.newt.event.KeyEvent.VK_SEMICOLON : return java.awt.event.KeyEvent.VK_SEMICOLON; + case com.jogamp.newt.event.KeyEvent.VK_LESS : return java.awt.event.KeyEvent.VK_LESS; + case com.jogamp.newt.event.KeyEvent.VK_EQUALS : return java.awt.event.KeyEvent.VK_EQUALS; + case com.jogamp.newt.event.KeyEvent.VK_GREATER : return java.awt.event.KeyEvent.VK_GREATER; + case com.jogamp.newt.event.KeyEvent.VK_QUESTIONMARK : return defAwtKeyCode; + case com.jogamp.newt.event.KeyEvent.VK_AT : return java.awt.event.KeyEvent.VK_AT; + case com.jogamp.newt.event.KeyEvent.VK_A : return java.awt.event.KeyEvent.VK_A; + case com.jogamp.newt.event.KeyEvent.VK_B : return java.awt.event.KeyEvent.VK_B; + case com.jogamp.newt.event.KeyEvent.VK_C : return java.awt.event.KeyEvent.VK_C; + case com.jogamp.newt.event.KeyEvent.VK_D : return java.awt.event.KeyEvent.VK_D; + case com.jogamp.newt.event.KeyEvent.VK_E : return java.awt.event.KeyEvent.VK_E; + case com.jogamp.newt.event.KeyEvent.VK_F : return java.awt.event.KeyEvent.VK_F; + case com.jogamp.newt.event.KeyEvent.VK_G : return java.awt.event.KeyEvent.VK_G; + case com.jogamp.newt.event.KeyEvent.VK_H : return java.awt.event.KeyEvent.VK_H; + case com.jogamp.newt.event.KeyEvent.VK_I : return java.awt.event.KeyEvent.VK_I; + case com.jogamp.newt.event.KeyEvent.VK_J : return java.awt.event.KeyEvent.VK_J; + case com.jogamp.newt.event.KeyEvent.VK_K : return java.awt.event.KeyEvent.VK_K; + case com.jogamp.newt.event.KeyEvent.VK_L : return java.awt.event.KeyEvent.VK_L; + case com.jogamp.newt.event.KeyEvent.VK_M : return java.awt.event.KeyEvent.VK_M; + case com.jogamp.newt.event.KeyEvent.VK_N : return java.awt.event.KeyEvent.VK_N; + case com.jogamp.newt.event.KeyEvent.VK_O : return java.awt.event.KeyEvent.VK_O; + case com.jogamp.newt.event.KeyEvent.VK_P : return java.awt.event.KeyEvent.VK_P; + case com.jogamp.newt.event.KeyEvent.VK_Q : return java.awt.event.KeyEvent.VK_Q; + case com.jogamp.newt.event.KeyEvent.VK_R : return java.awt.event.KeyEvent.VK_R; + case com.jogamp.newt.event.KeyEvent.VK_S : return java.awt.event.KeyEvent.VK_S; + case com.jogamp.newt.event.KeyEvent.VK_T : return java.awt.event.KeyEvent.VK_T; + case com.jogamp.newt.event.KeyEvent.VK_U : return java.awt.event.KeyEvent.VK_U; + case com.jogamp.newt.event.KeyEvent.VK_V : return java.awt.event.KeyEvent.VK_V; + case com.jogamp.newt.event.KeyEvent.VK_W : return java.awt.event.KeyEvent.VK_W; + case com.jogamp.newt.event.KeyEvent.VK_X : return java.awt.event.KeyEvent.VK_X; + case com.jogamp.newt.event.KeyEvent.VK_Y : return java.awt.event.KeyEvent.VK_Y; + case com.jogamp.newt.event.KeyEvent.VK_Z : return java.awt.event.KeyEvent.VK_Z; + case com.jogamp.newt.event.KeyEvent.VK_OPEN_BRACKET : return java.awt.event.KeyEvent.VK_OPEN_BRACKET; + case com.jogamp.newt.event.KeyEvent.VK_BACK_SLASH : return java.awt.event.KeyEvent.VK_BACK_SLASH; + case com.jogamp.newt.event.KeyEvent.VK_CLOSE_BRACKET : return java.awt.event.KeyEvent.VK_CLOSE_BRACKET; + case com.jogamp.newt.event.KeyEvent.VK_CIRCUMFLEX : return java.awt.event.KeyEvent.VK_CIRCUMFLEX; + case com.jogamp.newt.event.KeyEvent.VK_UNDERSCORE : return java.awt.event.KeyEvent.VK_UNDERSCORE; + case com.jogamp.newt.event.KeyEvent.VK_BACK_QUOTE : return java.awt.event.KeyEvent.VK_BACK_QUOTE; + case com.jogamp.newt.event.KeyEvent.VK_F1 : return java.awt.event.KeyEvent.VK_F1; + case com.jogamp.newt.event.KeyEvent.VK_F2 : return java.awt.event.KeyEvent.VK_F2; + case com.jogamp.newt.event.KeyEvent.VK_F3 : return java.awt.event.KeyEvent.VK_F3; + case com.jogamp.newt.event.KeyEvent.VK_F4 : return java.awt.event.KeyEvent.VK_F4; + case com.jogamp.newt.event.KeyEvent.VK_F5 : return java.awt.event.KeyEvent.VK_F5; + case com.jogamp.newt.event.KeyEvent.VK_F6 : return java.awt.event.KeyEvent.VK_F6; + case com.jogamp.newt.event.KeyEvent.VK_F7 : return java.awt.event.KeyEvent.VK_F7; + case com.jogamp.newt.event.KeyEvent.VK_F8 : return java.awt.event.KeyEvent.VK_F8; + case com.jogamp.newt.event.KeyEvent.VK_F9 : return java.awt.event.KeyEvent.VK_F9; + case com.jogamp.newt.event.KeyEvent.VK_F10 : return java.awt.event.KeyEvent.VK_F10; + case com.jogamp.newt.event.KeyEvent.VK_F11 : return java.awt.event.KeyEvent.VK_F11; + case com.jogamp.newt.event.KeyEvent.VK_F12 : return java.awt.event.KeyEvent.VK_F12; + case com.jogamp.newt.event.KeyEvent.VK_F13 : return java.awt.event.KeyEvent.VK_F13; + case com.jogamp.newt.event.KeyEvent.VK_F14 : return java.awt.event.KeyEvent.VK_F14; + case com.jogamp.newt.event.KeyEvent.VK_F15 : return java.awt.event.KeyEvent.VK_F15; + case com.jogamp.newt.event.KeyEvent.VK_F16 : return java.awt.event.KeyEvent.VK_F16; + case com.jogamp.newt.event.KeyEvent.VK_F17 : return java.awt.event.KeyEvent.VK_F17; + case com.jogamp.newt.event.KeyEvent.VK_F18 : return java.awt.event.KeyEvent.VK_F18; + case com.jogamp.newt.event.KeyEvent.VK_F19 : return java.awt.event.KeyEvent.VK_F19; + case com.jogamp.newt.event.KeyEvent.VK_F20 : return java.awt.event.KeyEvent.VK_F20; + case com.jogamp.newt.event.KeyEvent.VK_F21 : return java.awt.event.KeyEvent.VK_F21; + case com.jogamp.newt.event.KeyEvent.VK_F22 : return java.awt.event.KeyEvent.VK_F22; + case com.jogamp.newt.event.KeyEvent.VK_F23 : return java.awt.event.KeyEvent.VK_F23; + case com.jogamp.newt.event.KeyEvent.VK_F24 : return java.awt.event.KeyEvent.VK_F24; + case com.jogamp.newt.event.KeyEvent.VK_LEFT_BRACE : return java.awt.event.KeyEvent.VK_BRACELEFT; + case com.jogamp.newt.event.KeyEvent.VK_PIPE : return defAwtKeyCode; + case com.jogamp.newt.event.KeyEvent.VK_RIGHT_BRACE : return java.awt.event.KeyEvent.VK_BRACERIGHT; + case com.jogamp.newt.event.KeyEvent.VK_TILDE : return java.awt.event.KeyEvent.VK_DEAD_TILDE; + case com.jogamp.newt.event.KeyEvent.VK_DELETE : return java.awt.event.KeyEvent.VK_DELETE; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD0 : return java.awt.event.KeyEvent.VK_NUMPAD0; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD1 : return java.awt.event.KeyEvent.VK_NUMPAD1; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD2 : return java.awt.event.KeyEvent.VK_NUMPAD2; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD3 : return java.awt.event.KeyEvent.VK_NUMPAD3; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD4 : return java.awt.event.KeyEvent.VK_NUMPAD4; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD5 : return java.awt.event.KeyEvent.VK_NUMPAD5; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD6 : return java.awt.event.KeyEvent.VK_NUMPAD6; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD7 : return java.awt.event.KeyEvent.VK_NUMPAD7; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD8 : return java.awt.event.KeyEvent.VK_NUMPAD8; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD9 : return java.awt.event.KeyEvent.VK_NUMPAD9; + case com.jogamp.newt.event.KeyEvent.VK_DECIMAL : return java.awt.event.KeyEvent.VK_DECIMAL; + case com.jogamp.newt.event.KeyEvent.VK_SEPARATOR : return java.awt.event.KeyEvent.VK_SEPARATOR; + case com.jogamp.newt.event.KeyEvent.VK_ADD : return java.awt.event.KeyEvent.VK_ADD; + case com.jogamp.newt.event.KeyEvent.VK_SUBTRACT : return java.awt.event.KeyEvent.VK_SUBTRACT; + case com.jogamp.newt.event.KeyEvent.VK_MULTIPLY : return java.awt.event.KeyEvent.VK_MULTIPLY; + case com.jogamp.newt.event.KeyEvent.VK_DIVIDE : return java.awt.event.KeyEvent.VK_DIVIDE; + case com.jogamp.newt.event.KeyEvent.VK_NUM_LOCK : return java.awt.event.KeyEvent.VK_NUM_LOCK; + case com.jogamp.newt.event.KeyEvent.VK_LEFT : return java.awt.event.KeyEvent.VK_LEFT; + case com.jogamp.newt.event.KeyEvent.VK_UP : return java.awt.event.KeyEvent.VK_UP; + case com.jogamp.newt.event.KeyEvent.VK_RIGHT : return java.awt.event.KeyEvent.VK_RIGHT; + case com.jogamp.newt.event.KeyEvent.VK_DOWN : return java.awt.event.KeyEvent.VK_DOWN; + case com.jogamp.newt.event.KeyEvent.VK_CONTEXT_MENU : return java.awt.event.KeyEvent.VK_CONTEXT_MENU; + case com.jogamp.newt.event.KeyEvent.VK_WINDOWS : return java.awt.event.KeyEvent.VK_WINDOWS; + case com.jogamp.newt.event.KeyEvent.VK_META : return java.awt.event.KeyEvent.VK_META; + case com.jogamp.newt.event.KeyEvent.VK_HELP : return java.awt.event.KeyEvent.VK_HELP; + case com.jogamp.newt.event.KeyEvent.VK_COMPOSE : return java.awt.event.KeyEvent.VK_COMPOSE; + case com.jogamp.newt.event.KeyEvent.VK_BEGIN : return java.awt.event.KeyEvent.VK_BEGIN; + case com.jogamp.newt.event.KeyEvent.VK_STOP : return java.awt.event.KeyEvent.VK_STOP; + case com.jogamp.newt.event.KeyEvent.VK_INVERTED_EXCLAMATION_MARK: return java.awt.event.KeyEvent.VK_INVERTED_EXCLAMATION_MARK; + case com.jogamp.newt.event.KeyEvent.VK_EURO_SIGN : return java.awt.event.KeyEvent.VK_EURO_SIGN; + case com.jogamp.newt.event.KeyEvent.VK_CUT : return java.awt.event.KeyEvent.VK_CUT; + case com.jogamp.newt.event.KeyEvent.VK_COPY : return java.awt.event.KeyEvent.VK_COPY; + case com.jogamp.newt.event.KeyEvent.VK_PASTE : return java.awt.event.KeyEvent.VK_PASTE; + case com.jogamp.newt.event.KeyEvent.VK_UNDO : return java.awt.event.KeyEvent.VK_UNDO; + case com.jogamp.newt.event.KeyEvent.VK_AGAIN : return java.awt.event.KeyEvent.VK_AGAIN; + case com.jogamp.newt.event.KeyEvent.VK_FIND : return java.awt.event.KeyEvent.VK_FIND; + case com.jogamp.newt.event.KeyEvent.VK_PROPS : return java.awt.event.KeyEvent.VK_PROPS; + case com.jogamp.newt.event.KeyEvent.VK_INPUT_METHOD_ON_OFF: return java.awt.event.KeyEvent.VK_INPUT_METHOD_ON_OFF; + case com.jogamp.newt.event.KeyEvent.VK_CODE_INPUT : return java.awt.event.KeyEvent.VK_CODE_INPUT; + case com.jogamp.newt.event.KeyEvent.VK_ROMAN_CHARACTERS: return java.awt.event.KeyEvent.VK_ROMAN_CHARACTERS; + case com.jogamp.newt.event.KeyEvent.VK_ALL_CANDIDATES: return java.awt.event.KeyEvent.VK_ALL_CANDIDATES; + case com.jogamp.newt.event.KeyEvent.VK_PREVIOUS_CANDIDATE: return java.awt.event.KeyEvent.VK_PREVIOUS_CANDIDATE; + case com.jogamp.newt.event.KeyEvent.VK_ALPHANUMERIC : return java.awt.event.KeyEvent.VK_ALPHANUMERIC; + case com.jogamp.newt.event.KeyEvent.VK_KATAKANA : return java.awt.event.KeyEvent.VK_KATAKANA; + case com.jogamp.newt.event.KeyEvent.VK_HIRAGANA : return java.awt.event.KeyEvent.VK_HIRAGANA; + case com.jogamp.newt.event.KeyEvent.VK_FULL_WIDTH : return java.awt.event.KeyEvent.VK_FULL_WIDTH; + case com.jogamp.newt.event.KeyEvent.VK_HALF_WIDTH : return java.awt.event.KeyEvent.VK_HALF_WIDTH; + case com.jogamp.newt.event.KeyEvent.VK_JAPANESE_KATAKANA: return java.awt.event.KeyEvent.VK_JAPANESE_KATAKANA; + case com.jogamp.newt.event.KeyEvent.VK_JAPANESE_HIRAGANA: return java.awt.event.KeyEvent.VK_JAPANESE_HIRAGANA; + case com.jogamp.newt.event.KeyEvent.VK_JAPANESE_ROMAN: return java.awt.event.KeyEvent.VK_JAPANESE_ROMAN; + case com.jogamp.newt.event.KeyEvent.VK_KANA_LOCK : return java.awt.event.KeyEvent.VK_KANA_LOCK; } - return 0; + return defAwtKeyCode; } public static final com.jogamp.newt.event.WindowEvent createWindowEvent(java.awt.event.WindowEvent event, com.jogamp.newt.Window newtSource) { - int type = eventTypeAWT2NEWT.get(event.getID()); - if(0xFFFFFFFF != type) { - return new com.jogamp.newt.event.WindowEvent(type, ((null==newtSource)?(Object)event.getComponent():(Object)newtSource), System.currentTimeMillis()); + final short newtType = eventTypeAWT2NEWT(event.getID()); + if( (short)0 != newtType ) { + return new com.jogamp.newt.event.WindowEvent(newtType, ((null==newtSource)?(Object)event.getComponent():(Object)newtSource), System.currentTimeMillis()); } return null; // no mapping .. } public static final com.jogamp.newt.event.WindowEvent createWindowEvent(java.awt.event.ComponentEvent event, com.jogamp.newt.Window newtSource) { - int type = eventTypeAWT2NEWT.get(event.getID()); - if(0xFFFFFFFF != type) { - return new com.jogamp.newt.event.WindowEvent(type, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, System.currentTimeMillis()); + final short newtType = eventTypeAWT2NEWT(event.getID()); + if( (short)0 != newtType ) { + return new com.jogamp.newt.event.WindowEvent(newtType, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, System.currentTimeMillis()); } return null; // no mapping .. } public static final com.jogamp.newt.event.WindowEvent createWindowEvent(java.awt.event.FocusEvent event, com.jogamp.newt.Window newtSource) { - int type = eventTypeAWT2NEWT.get(event.getID()); - if(0xFFFFFFFF != type) { - return new com.jogamp.newt.event.WindowEvent(type, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, System.currentTimeMillis()); + final short newtType = eventTypeAWT2NEWT(event.getID()); + if( (short)0 != newtType ) { + return new com.jogamp.newt.event.WindowEvent(newtType, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, System.currentTimeMillis()); } return null; // no mapping .. } public static final com.jogamp.newt.event.MouseEvent createMouseEvent(java.awt.event.MouseEvent event, com.jogamp.newt.Window newtSource) { - int type = eventTypeAWT2NEWT.get(event.getID()); - if(0xFFFFFFFF != type) { - int rotation = 0; + final short newtType = eventTypeAWT2NEWT(event.getID()); + if( (short)0 != newtType ) { + float rotation = 0; if (event instanceof java.awt.event.MouseWheelEvent) { // AWT/NEWT rotation is reversed - AWT +1 is down, NEWT +1 is up. - rotation = -1 * ((java.awt.event.MouseWheelEvent)event).getWheelRotation(); + rotation = -1f * ((java.awt.event.MouseWheelEvent)event).getWheelRotation(); } - int mods = awtModifiers2Newt(event.getModifiers(), true); + final short newtButton = awtButton2Newt(event.getButton()); + int mods = awtModifiers2Newt(event.getModifiers(), event.getModifiersEx()); + mods |= com.jogamp.newt.event.InputEvent.getButtonMask(newtButton); // always include NEWT BUTTON_MASK if(null!=newtSource) { if(newtSource.isPointerConfined()) { - mods |= InputEvent.CONFINED_MASK; + mods |= com.jogamp.newt.event.InputEvent.CONFINED_MASK; } if(!newtSource.isPointerVisible()) { - mods |= InputEvent.INVISIBLE_MASK; + mods |= com.jogamp.newt.event.InputEvent.INVISIBLE_MASK; } } - return new com.jogamp.newt.event.MouseEvent( - type, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, event.getWhen(), - mods, event.getX(), event.getY(), event.getClickCount(), - awtButton2Newt(event.getButton()), rotation); + newtType, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, event.getWhen(), + mods, event.getX(), event.getY(), (short)event.getClickCount(), + newtButton, MouseEvent.getRotationXYZ(rotation, mods), 1f); } return null; // no mapping .. } public static final com.jogamp.newt.event.KeyEvent createKeyEvent(java.awt.event.KeyEvent event, com.jogamp.newt.Window newtSource) { - int type = eventTypeAWT2NEWT.get(event.getID()); - if(0xFFFFFFFF != type) { - return new com.jogamp.newt.event.KeyEvent( - type, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, event.getWhen(), - awtModifiers2Newt(event.getModifiers(), false), - event.getKeyCode(), event.getKeyChar()); + return createKeyEvent(eventTypeAWT2NEWT(event.getID()), event, newtSource); + } + + public static final com.jogamp.newt.event.KeyEvent createKeyEvent(short newtType, java.awt.event.KeyEvent event, com.jogamp.newt.Window newtSource) { + if( (short)0 != newtType ) { + final short newtKeyCode = awtKeyCode2NewtKeyCode( event.getKeyCode() ); + return com.jogamp.newt.event.KeyEvent.create( + newtType, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, event.getWhen(), + awtModifiers2Newt(event.getModifiers(), event.getModifiersEx()), + newtKeyCode, newtKeyCode, event.getKeyChar()); } return null; // no mapping .. } } - diff --git a/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java b/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java index ce8ed7c49..770502326 100644 --- a/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java +++ b/src/newt/classes/jogamp/newt/awt/event/AWTParentWindowAdapter.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package jogamp.newt.awt.event; import java.awt.KeyboardFocusManager; @@ -34,6 +34,7 @@ import javax.media.nativewindow.NativeWindow; import jogamp.newt.driver.DriverUpdatePosition; +import com.jogamp.newt.Window; import com.jogamp.newt.event.awt.AWTAdapter; import com.jogamp.newt.event.awt.AWTWindowAdapter; @@ -41,111 +42,147 @@ import com.jogamp.newt.event.awt.AWTWindowAdapter; * Specialized parent/client adapter, * where the NEWT child window really gets resized, * and the parent move window event gets discarded. */ -public class AWTParentWindowAdapter - extends AWTWindowAdapter - implements java.awt.event.HierarchyListener +public class AWTParentWindowAdapter extends AWTWindowAdapter implements java.awt.event.HierarchyListener { NativeWindow downstreamParent; - + public AWTParentWindowAdapter(NativeWindow downstreamParent, com.jogamp.newt.Window downstream) { super(downstream); this.downstreamParent = downstreamParent; } + public AWTParentWindowAdapter() { + super(); + } + public AWTParentWindowAdapter setDownstream(NativeWindow downstreamParent, com.jogamp.newt.Window downstream) { + setDownstream(downstream); + this.downstreamParent = downstreamParent; + return this; + } + + @Override + public synchronized AWTAdapter clear() { + super.clear(); + this.downstreamParent = null; + return this; + } - public AWTAdapter addTo(java.awt.Component awtComponent) { + @Override + public synchronized AWTAdapter addTo(java.awt.Component awtComponent) { awtComponent.addHierarchyListener(this); return super.addTo(awtComponent); } - public AWTAdapter removeFrom(java.awt.Component awtComponent) { + @Override + public synchronized AWTAdapter removeFrom(java.awt.Component awtComponent) { awtComponent.removeHierarchyListener(this); return super.removeFrom(awtComponent); } - public void focusGained(java.awt.event.FocusEvent e) { + @Override + public synchronized void focusGained(java.awt.event.FocusEvent e) { + if( !isSetup ) { return; } // forward focus to NEWT child final com.jogamp.newt.Window newtChild = getNewtWindow(); - final boolean isOnscreen = newtChild.isNativeValid() && newtChild.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); - final boolean isParent = downstreamParent == newtChild.getParent(); - final boolean isFullscreen = newtChild.isFullscreen(); - if(DEBUG_IMPLEMENTATION) { - System.err.println("AWT: focusGained: onscreen "+ isOnscreen+", "+e+", isParent: "+isParent+", isFS "+isFullscreen); - } - if(isParent) { - if(isOnscreen && !isFullscreen) { - KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); + if( null != newtChild ) { + final boolean isOnscreen = newtChild.isNativeValid() && newtChild.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); + final boolean isParent = downstreamParent == newtChild.getParent(); + final boolean isFullscreen = newtChild.isFullscreen(); + if(DEBUG_IMPLEMENTATION) { + System.err.println("AWT: focusGained: onscreen "+ isOnscreen+", "+e+", isParent: "+isParent+", isFS "+isFullscreen); + } + if(isParent) { + if(isOnscreen && !isFullscreen) { + KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); + } + newtChild.requestFocus(false); } - newtChild.requestFocus(false); } } - public void focusLost(java.awt.event.FocusEvent e) { + @Override + public synchronized void focusLost(java.awt.event.FocusEvent e) { + if( !isSetup ) { return; } if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: focusLost: "+ e); } } - public void componentResized(java.awt.event.ComponentEvent e) { + @Override + public synchronized void componentResized(java.awt.event.ComponentEvent e) { + if( !isSetup ) { return; } // Need to resize the NEWT child window // the resized event will be send via the native window feedback. final java.awt.Component comp = e.getComponent(); if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: componentResized: "+comp); } - getNewtWindow().runOnEDTIfAvail(false, new Runnable() { - public void run() { - int cw = comp.getWidth(); - int ch = comp.getHeight(); - if( 0 < cw * ch ) { - if( getNewtWindow().getWidth() != cw || getNewtWindow().getHeight() != ch ) { - getNewtWindow().setSize(cw, ch); - if(comp.isVisible() != getNewtWindow().isVisible()) { - getNewtWindow().setVisible(comp.isVisible()); + final Window newtChild = getNewtWindow(); + if( null != newtChild ) { + newtChild.runOnEDTIfAvail(false, new Runnable() { + @Override + public void run() { + final int cw = comp.getWidth(); + final int ch = comp.getHeight(); + if( 0 < cw && 0 < ch ) { + if( newtChild.getWidth() != cw || newtChild.getHeight() != ch ) { + newtChild.setSize(cw, ch); + final boolean v = comp.isShowing(); // compute showing-state throughout hierarchy + if(v != newtChild.isVisible()) { + newtChild.setVisible(v); + } } + } else if(newtChild.isVisible()) { + newtChild.setVisible(false); } - } else if(getNewtWindow().isVisible()) { - getNewtWindow().setVisible(false); - } - }}); + }}); + } } - public void componentMoved(java.awt.event.ComponentEvent e) { + @Override + public synchronized void componentMoved(java.awt.event.ComponentEvent e) { + if( !isSetup ) { return; } if(DEBUG_IMPLEMENTATION) { - System.err.println("AWT: componentMoved: "+e); + System.err.println("AWT: componentMoved: "+e); + } + final Window newtChild = getNewtWindow(); + if( null != newtChild && ( newtChild.getDelegatedWindow() instanceof DriverUpdatePosition ) ) { + ((DriverUpdatePosition)newtChild.getDelegatedWindow()).updatePosition(0, 0); } - if(getNewtWindow().getDelegatedWindow() instanceof DriverUpdatePosition) { - ((DriverUpdatePosition)getNewtWindow().getDelegatedWindow()).updatePosition(); - } } - public void windowActivated(java.awt.event.WindowEvent e) { + @Override + public synchronized void windowActivated(java.awt.event.WindowEvent e) { // no propagation to NEWT child window } - public void windowDeactivated(java.awt.event.WindowEvent e) { + @Override + public synchronized void windowDeactivated(java.awt.event.WindowEvent e) { // no propagation to NEWT child window } - public void hierarchyChanged(java.awt.event.HierarchyEvent e) { - if( null == getNewtEventListener() ) { - long bits = e.getChangeFlags(); - final java.awt.Component changed = e.getChanged(); + @Override + public synchronized void hierarchyChanged(java.awt.event.HierarchyEvent e) { + if( !isSetup ) { return; } + final Window newtChild = getNewtWindow(); + if( null != newtChild && null == getNewtEventListener() ) { + final long bits = e.getChangeFlags(); + final java.awt.Component comp = e.getComponent(); if( 0 != ( java.awt.event.HierarchyEvent.SHOWING_CHANGED & bits ) ) { - final boolean showing = changed.isShowing(); + final boolean showing = comp.isShowing(); // compute showing-state throughout hierarchy if(DEBUG_IMPLEMENTATION) { - System.err.println("AWT: hierarchyChanged SHOWING_CHANGED: showing "+showing+", "+changed+", source "+e.getComponent()); + System.err.println("AWT: hierarchyChanged SHOWING_CHANGED: showing "+showing+", comp "+comp+", changed "+e.getChanged()); } - getNewtWindow().runOnEDTIfAvail(false, new Runnable() { + newtChild.runOnEDTIfAvail(false, new Runnable() { + @Override public void run() { - if(getNewtWindow().isVisible() != showing) { - getNewtWindow().setVisible(showing); + if(newtChild.isVisible() != showing) { + newtChild.setVisible(showing); } }}); - } + } if(DEBUG_IMPLEMENTATION) { if( 0 != ( java.awt.event.HierarchyEvent.DISPLAYABILITY_CHANGED & bits ) ) { - final boolean displayability = changed.isDisplayable(); - System.err.println("AWT: hierarchyChanged DISPLAYABILITY_CHANGED: displayability "+displayability+", "+changed); + System.err.println("AWT: hierarchyChanged DISPLAYABILITY_CHANGED: "+e.getChanged()); } } } diff --git a/src/newt/classes/jogamp/newt/driver/DriverClearFocus.java b/src/newt/classes/jogamp/newt/driver/DriverClearFocus.java index 0a824e83b..0ff86fe93 100644 --- a/src/newt/classes/jogamp/newt/driver/DriverClearFocus.java +++ b/src/newt/classes/jogamp/newt/driver/DriverClearFocus.java @@ -1,10 +1,10 @@ package jogamp.newt.driver; -/** +/** * Interface tagging driver requirement of clearing the focus. * <p> - * Some drivers require a programmatic {@link #clearFocus()} when traversing the focus. - * </p> + * Some drivers require a programmatic {@link #clearFocus()} when traversing the focus. + * </p> */ public interface DriverClearFocus { /** Programmatic clear the focus */ diff --git a/src/newt/classes/jogamp/newt/driver/DriverUpdatePosition.java b/src/newt/classes/jogamp/newt/driver/DriverUpdatePosition.java index bb846c081..e5f9b6a68 100644 --- a/src/newt/classes/jogamp/newt/driver/DriverUpdatePosition.java +++ b/src/newt/classes/jogamp/newt/driver/DriverUpdatePosition.java @@ -1,9 +1,15 @@ package jogamp.newt.driver; -/** +/** * Interface tagging driver requirement of absolute positioning, ie. depend on parent position. */ public interface DriverUpdatePosition { - /** Programmatic update the position */ - void updatePosition(); + /** + * Programmatic update the top-left corner + * of the client area relative to it's parent. + * + * @param x x-component + * @param y y-component + **/ + void updatePosition(int x, int y); } diff --git a/src/newt/classes/jogamp/newt/driver/PNGIcon.java b/src/newt/classes/jogamp/newt/driver/PNGIcon.java new file mode 100644 index 000000000..967acd413 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/PNGIcon.java @@ -0,0 +1,80 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.newt.driver; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.nio.ByteBuffer; + +import jogamp.newt.Debug; +import jogamp.newt.DisplayImpl; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.ReflectionUtil; + +public class PNGIcon { + private static final String err0 = "PNG decoder not implemented."; + + private static final boolean avail; + + static { + Debug.initSingleton(); + + final ClassLoader cl = PNGIcon.class.getClassLoader(); + avail = DisplayImpl.isPNGUtilAvailable() && ReflectionUtil.isClassAvailable("jogamp.newt.driver.opengl.JoglUtilPNGIcon", cl); + } + + /** Returns true if PNG decoder is available. */ + public static boolean isAvailable() { + return avail; + } + + /** + * Special implementation for X11 Window Icons + * <p> + * The returned byte buffer is a direct buffer! + * </p> + * + * @param resources + * @param data_size + * @param elem_bytesize + * + * @return BGRA8888 bytes with origin at upper-left corner where component B is located on the lowest 8-bit and component A is located on the highest 8-bit. + * + * @throws UnsupportedOperationException if not implemented + * @throws InterruptedException + * @throws IOException + * @throws MalformedURLException + */ + public static ByteBuffer arrayToX11BGRAImages(IOUtil.ClassResources resources, int[] data_size, int[] elem_bytesize) throws UnsupportedOperationException, InterruptedException, IOException, MalformedURLException { + if( avail ) { + return jogamp.newt.driver.opengl.JoglUtilPNGIcon.arrayToX11BGRAImages(resources, data_size, elem_bytesize); + } + throw new UnsupportedOperationException(err0); + } +} diff --git a/src/newt/classes/jogamp/newt/driver/android/AndroidScreen.java b/src/newt/classes/jogamp/newt/driver/android/AndroidScreen.java deleted file mode 100644 index e108ed0bb..000000000 --- a/src/newt/classes/jogamp/newt/driver/android/AndroidScreen.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright 2011 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: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ - -package jogamp.newt.driver.android; - -import javax.media.nativewindow.*; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; - -import com.jogamp.newt.ScreenMode; -import com.jogamp.newt.util.ScreenModeUtil; - -import android.content.Context; -import android.graphics.PixelFormat; -import android.util.DisplayMetrics; -import android.view.Surface; -import android.view.WindowManager; - -public class AndroidScreen extends jogamp.newt.ScreenImpl { - - static { - AndroidDisplay.initSingleton(); - } - - public AndroidScreen() { - } - - protected void createNativeImpl() { - aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); - } - - protected void closeNativeImpl() { } - - protected ScreenMode getCurrentScreenModeImpl() { - final Context ctx = jogamp.common.os.android.StaticContext.getContext(); - final WindowManager wmgr = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE); - final DisplayMetrics outMetrics = new DisplayMetrics(); - final android.view.Display aDisplay = wmgr.getDefaultDisplay(); - aDisplay.getMetrics(outMetrics); - - final int arot = aDisplay.getRotation(); - final int nrot = androidRotation2NewtRotation(arot); - int[] props = new int[ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL]; - int offset = 1; // set later for verification of iterator - offset = getScreenSize(outMetrics, nrot, props, offset); - offset = getBpp(aDisplay, props, offset); - offset = getScreenSizeMM(outMetrics, props, offset); - props[offset++] = (int) aDisplay.getRefreshRate(); - props[offset++] = nrot; - props[offset - ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL] = offset; // count - return ScreenModeUtil.streamIn(props, 0); - } - - protected int validateScreenIndex(int idx) { - return 0; // FIXME: only one screen available ? - } - - protected void getVirtualScreenOriginAndSize(Point virtualOrigin, Dimension virtualSize) { - virtualOrigin.setX(0); - virtualOrigin.setY(0); - final ScreenMode sm = getCurrentScreenMode(); - virtualSize.setWidth(sm.getRotatedWidth()); - virtualSize.setHeight(sm.getRotatedHeight()); - } - - //---------------------------------------------------------------------- - // Internals only - // - static int androidRotation2NewtRotation(int arot) { - switch(arot) { - case Surface.ROTATION_270: return ScreenMode.ROTATE_270; - case Surface.ROTATION_180: return ScreenMode.ROTATE_180; - case Surface.ROTATION_90: return ScreenMode.ROTATE_90; - case Surface.ROTATION_0: - } - return ScreenMode.ROTATE_0; - } - static int getScreenSize(DisplayMetrics outMetrics, int nrot, int[] props, int offset) { - // swap width and height, since Android reflects rotated dimension, we don't - if (ScreenMode.ROTATE_90 == nrot || ScreenMode.ROTATE_270 == nrot) { - props[offset++] = outMetrics.heightPixels; - props[offset++] = outMetrics.widthPixels; - } else { - props[offset++] = outMetrics.widthPixels; - props[offset++] = outMetrics.heightPixels; - } - return offset; - } - static int getBpp(android.view.Display aDisplay, int[] props, int offset) { - int bpp; - switch(aDisplay.getPixelFormat()) { - case PixelFormat.RGBA_8888: bpp=32; break; - case PixelFormat.RGBX_8888: bpp=32; break; - case PixelFormat.RGB_888: bpp=24; break; - case PixelFormat.RGB_565: bpp=16; break; - case PixelFormat.RGBA_5551: bpp=16; break; - case PixelFormat.RGBA_4444: bpp=16; break; - case PixelFormat.RGB_332: bpp= 8; break; - default: bpp=32; - } - props[offset++] = bpp; - return offset; - } - static int getScreenSizeMM(DisplayMetrics outMetrics, int[] props, int offset) { - final float iw = (float) outMetrics.widthPixels / outMetrics.xdpi; - final float ih = (float) outMetrics.heightPixels / outMetrics.xdpi; - final float mmpi = 25.4f; - props[offset++] = (int) ((iw * mmpi)+0.5); - props[offset++] = (int) ((ih * mmpi)+0.5); - return offset; - } -} - diff --git a/src/newt/classes/jogamp/newt/driver/android/AndroidWindow.java b/src/newt/classes/jogamp/newt/driver/android/AndroidWindow.java deleted file mode 100644 index 63d5f7003..000000000 --- a/src/newt/classes/jogamp/newt/driver/android/AndroidWindow.java +++ /dev/null @@ -1,408 +0,0 @@ -/** - * Copyright 2011 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: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ - -package jogamp.newt.driver.android; - -import jogamp.common.os.android.StaticContext; -import jogamp.newt.driver.android.event.AndroidNewtEventFactory; - -import javax.media.nativewindow.Capabilities; -import javax.media.nativewindow.CapabilitiesImmutable; -import javax.media.nativewindow.NativeWindowException; -import javax.media.nativewindow.VisualIDHolder; -import javax.media.nativewindow.util.Insets; -import javax.media.nativewindow.util.Point; -import javax.media.opengl.GLCapabilitiesChooser; -import javax.media.opengl.GLCapabilitiesImmutable; - -import com.jogamp.nativewindow.egl.EGLGraphicsDevice; - -import jogamp.opengl.egl.EGL; -import jogamp.opengl.egl.EGLGraphicsConfiguration; -import jogamp.opengl.egl.EGLGraphicsConfigurationFactory; - -import android.content.Context; -import android.graphics.PixelFormat; -import android.util.Log; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceHolder.Callback2; -import android.view.SurfaceView; -import android.view.View; - -public class AndroidWindow extends jogamp.newt.WindowImpl implements Callback2 { - static { - AndroidDisplay.initSingleton(); - } - - public static CapabilitiesImmutable fixCaps(boolean matchFormatPrecise, int format, CapabilitiesImmutable rCaps) { - PixelFormat pf = new PixelFormat(); - PixelFormat.getPixelFormatInfo(format, pf); - final CapabilitiesImmutable res; - int r, g, b, a; - - switch(format) { - case PixelFormat.RGBA_8888: r=8; g=8; b=8; a=8; break; - case PixelFormat.RGBX_8888: r=8; g=8; b=8; a=0; break; - case PixelFormat.RGB_888: r=8; g=8; b=8; a=0; break; - case PixelFormat.RGB_565: r=5; g=6; b=5; a=0; break; - case PixelFormat.RGBA_5551: r=5; g=5; b=5; a=1; break; - case PixelFormat.RGBA_4444: r=4; g=4; b=4; a=4; break; - case PixelFormat.RGB_332: r=3; g=3; b=2; a=0; break; - default: throw new InternalError("Unhandled pixelformat: "+format); - } - final boolean change = matchFormatPrecise || - rCaps.getRedBits() > r && - rCaps.getGreenBits() > g && - rCaps.getBlueBits() > b && - rCaps.getAlphaBits() > a ; - - if(change) { - Capabilities nCaps = (Capabilities) rCaps.cloneMutable(); - nCaps.setRedBits(r); - nCaps.setGreenBits(g); - nCaps.setBlueBits(b); - nCaps.setAlphaBits(a); - res = nCaps; - } else { - res = rCaps; - } - Log.d(MD.TAG, "fixCaps: format: "+format); - Log.d(MD.TAG, "fixCaps: requested: "+rCaps); - Log.d(MD.TAG, "fixCaps: chosen: "+res); - - return res; - } - - public static int getFormat(CapabilitiesImmutable rCaps) { - int fmt = PixelFormat.UNKNOWN; - - if(!rCaps.isBackgroundOpaque()) { - fmt = PixelFormat.TRANSLUCENT; - } else if(rCaps.getRedBits()<=5 && - rCaps.getGreenBits()<=6 && - rCaps.getBlueBits()<=5 && - rCaps.getAlphaBits()==0) { - fmt = PixelFormat.RGB_565; - } - /* else if(rCaps.getRedBits()<=5 && - rCaps.getGreenBits()<=5 && - rCaps.getBlueBits()<=5 && - rCaps.getAlphaBits()==1) { - fmt = PixelFormat.RGBA_5551; // FIXME: Supported ? - } */ - else { - fmt = PixelFormat.RGBA_8888; - } - Log.d(MD.TAG, "getFormat: requested: "+rCaps); - Log.d(MD.TAG, "getFormat: returned: "+fmt); - - return fmt; - } - - public static boolean isAndroidFormatTransparent(int aFormat) { - switch (aFormat) { - case PixelFormat.TRANSLUCENT: - case PixelFormat.TRANSPARENT: - return true; - } - return false; - } - - class AndroidEvents implements View.OnKeyListener, View.OnTouchListener, View.OnFocusChangeListener { - - @Override - public boolean onTouch(View v, android.view.MotionEvent event) { - final com.jogamp.newt.event.MouseEvent[] newtEvents = AndroidNewtEventFactory.createMouseEvents(event, AndroidWindow.this); - if(null != newtEvents) { - focusChanged(false, true); - for(int i=0; i<newtEvents.length; i++) { - AndroidWindow.this.enqueueEvent(false, newtEvents[i]); - } - try { Thread.sleep((long) (1000.0F/30.0F)); } - catch(InterruptedException e) { } - return true; // consumed/handled, further interest in events - } - return false; // no mapping, no further interest in the event! - } - - @Override - public boolean onKey(View v, int keyCode, android.view.KeyEvent event) { - final com.jogamp.newt.event.KeyEvent[] newtEvents = AndroidNewtEventFactory.createKeyEvents(keyCode, event, AndroidWindow.this); - if(null != newtEvents) { - for(int i=0; i<newtEvents.length; i++) { - AndroidWindow.this.enqueueEvent(false, newtEvents[i]); - } - return true; - } - return false; - } - - @Override - public void onFocusChange(View v, boolean hasFocus) { - AndroidWindow.this.focusChanged(false, hasFocus); - } - - } - - public static Class<?>[] getCustomConstructorArgumentTypes() { - return new Class<?>[] { Context.class } ; - } - - public AndroidWindow() { - reset(); - } - - private void reset() { - ownAndroidWindow = false; - androidView = null; - nativeFormat = VisualIDHolder.VID_UNDEFINED; - androidFormat = VisualIDHolder.VID_UNDEFINED; - capsByFormat = null; - surface = null; - surfaceHandle = 0; - eglSurface = 0; - definePosition(0, 0); // default to 0/0 - setBrokenFocusChange(true); - } - - @Override - protected void instantiationFinished() { - final Context ctx = StaticContext.getContext(); - if(null == ctx) { - throw new NativeWindowException("No static [Application] Context has been set. Call StaticContext.setContext(Context) first."); - } - androidView = new MSurfaceView(ctx); - - final AndroidEvents ae = new AndroidEvents(); - androidView.setOnTouchListener(ae); - androidView.setClickable(false); - androidView.setOnKeyListener(ae); - androidView.setOnFocusChangeListener(ae); - androidView.setFocusable(true); - androidView.setFocusableInTouchMode(true); - - final SurfaceHolder sh = androidView.getHolder(); - sh.addCallback(AndroidWindow.this); - sh.setFormat(getFormat(getRequestedCapabilities())); - - // default size -> TBD ! - defineSize(0, 0); - } - - public SurfaceView getAndroidView() { return androidView; } - - @Override - protected boolean canCreateNativeImpl() { - final boolean b = 0 != surfaceHandle; - Log.d(MD.TAG, "canCreateNativeImpl: "+b); - return b; - } - - @Override - protected void createNativeImpl() { - Log.d(MD.TAG, "createNativeImpl 0 - surfaceHandle 0x"+Long.toHexString(surfaceHandle)+ - ", format [a "+androidFormat+", n "+nativeFormat+"], "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+" - "+Thread.currentThread().getName()); - - if(0!=getParentWindowHandle()) { - throw new NativeWindowException("Window parenting not supported (yet)"); - } - if(0==surfaceHandle) { - throw new InternalError("XXX"); - } - - final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) getScreen().getDisplay().getGraphicsDevice(); - final EGLGraphicsConfiguration eglConfig = EGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic( - capsByFormat, (GLCapabilitiesImmutable) getRequestedCapabilities(), - (GLCapabilitiesChooser)capabilitiesChooser, getScreen().getGraphicsScreen(), nativeFormat, - isAndroidFormatTransparent(androidFormat)); - if (eglConfig == null) { - throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); - } - final int nativeVisualID = eglConfig.getVisualID(VisualIDHolder.VIDType.NATIVE); - Log.d(MD.TAG, "nativeVisualID 0x"+Integer.toHexString(nativeVisualID)); - if(VisualIDHolder.VID_UNDEFINED != nativeVisualID) { - setSurfaceVisualID0(surfaceHandle, nativeVisualID); - } - - eglSurface = EGL.eglCreateWindowSurface(eglDevice.getHandle(), eglConfig.getNativeConfig(), surfaceHandle, null); - if (EGL.EGL_NO_SURFACE==eglSurface) { - throw new NativeWindowException("Creation of window surface failed: "+eglConfig+", surfaceHandle 0x"+Long.toHexString(surfaceHandle)+", error "+toHexString(EGL.eglGetError())); - } - - // propagate data .. - setGraphicsConfiguration(eglConfig); - setWindowHandle(surfaceHandle); - focusChanged(false, true); - Log.d(MD.TAG, "createNativeImpl X"); - } - - @Override - protected void closeNativeImpl() { - release0(surfaceHandle); - surface = null; - surfaceHandle = 0; - eglSurface = 0; - } - - @Override - public final long getSurfaceHandle() { - return eglSurface; - } - - protected void requestFocusImpl(boolean reparented) { - if(null != androidView) { - Log.d(MD.TAG, "requestFocusImpl: reparented "+reparented); - androidView.post(new Runnable() { - public void run() { - androidView.requestFocus(); - androidView.bringToFront(); - } - }); - } - } - - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { - if( 0 != ( FLAG_CHANGE_FULLSCREEN & flags) ) { - Log.d(MD.TAG, "reconfigureWindowImpl.setFullscreen post creation (setContentView()) n/a"); - return false; - } - if(width>0 || height>0) { - if(0!=getWindowHandle()) { - Log.d(MD.TAG, "reconfigureWindowImpl.setSize n/a"); - return false; - } - } - if(x>=0 || y>=0) { - Log.d(MD.TAG, "reconfigureWindowImpl.setPos n/a"); - return false; - } - if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { - visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); - } - return true; - } - - protected Point getLocationOnScreenImpl(int x, int y) { - return new Point(x,y); - } - - protected void updateInsetsImpl(Insets insets) { - // nop .. - } - - //---------------------------------------------------------------------- - // Surface Callbacks - // - - public void surfaceCreated(SurfaceHolder holder) { - Log.d(MD.TAG, "surfaceCreated: "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); - } - - public void surfaceChanged(SurfaceHolder aHolder, int aFormat, int aWidth, int aHeight) { - Log.d(MD.TAG, "surfaceChanged: f "+nativeFormat+" -> "+aFormat+", "+aWidth+"x"+aHeight+", current surfaceHandle: 0x"+Long.toHexString(surfaceHandle)); - if(0!=surfaceHandle && androidFormat != aFormat ) { - // re-create - Log.d(MD.TAG, "surfaceChanged (destroy old)"); - if(!windowDestroyNotify(true)) { - destroy(); - } - surfaceHandle = 0; - surface=null; - } - if(getScreen().isNativeValid()) { - getScreen().getCurrentScreenMode(); // if ScreenMode changed .. trigger ScreenMode event - } - - if(0>getX() || 0>getY()) { - positionChanged(false, 0, 0); - } - - if(0 == surfaceHandle) { - androidFormat = aFormat; - surface = aHolder.getSurface(); - surfaceHandle = getSurfaceHandle0(surface); - acquire0(surfaceHandle); - nativeFormat = getSurfaceVisualID0(surfaceHandle); - final int nWidth = getWidth0(surfaceHandle); - final int nHeight = getHeight0(surfaceHandle); - capsByFormat = (GLCapabilitiesImmutable) fixCaps(true /* matchFormatPrecise */, nativeFormat, getRequestedCapabilities()); - sizeChanged(false, nWidth, nHeight, false); - - Log.d(MD.TAG, "surfaceRealized: isValid: "+surface.isValid()+ - ", new surfaceHandle 0x"+Long.toHexString(surfaceHandle)+ - ", format [a "+androidFormat+"/n "+nativeFormat+"], "+ - getX()+"/"+getY()+" "+nWidth+"x"+nHeight+", visible: "+isVisible()); - - if(isVisible()) { - setVisible(true); - } - } - sizeChanged(false, aWidth, aHeight, false); - windowRepaint(0, 0, aWidth, aHeight); - Log.d(MD.TAG, "surfaceChanged: X"); - } - - public void surfaceDestroyed(SurfaceHolder holder) { - Log.d(MD.TAG, "surfaceDestroyed"); - windowDestroyNotify(true); // actually too late .. however .. - } - - public void surfaceRedrawNeeded(SurfaceHolder holder) { - Log.d(MD.TAG, "surfaceRedrawNeeded"); - windowRepaint(0, 0, getWidth(), getHeight()); - } - - private boolean ownAndroidWindow; - private MSurfaceView androidView; - private int nativeFormat; // chosen current native PixelFormat (suitable for EGL) - private int androidFormat; // chosen current android PixelFormat (-1, -2 ..) - private GLCapabilitiesImmutable capsByFormat; // fixed requestedCaps by PixelFormat - private Surface surface; - private volatile long surfaceHandle; - private long eglSurface; - - class MSurfaceView extends SurfaceView { - public MSurfaceView (Context ctx) { - super(ctx); - setBackgroundDrawable(null); - // setBackgroundColor(Color.TRANSPARENT); - } - } - //---------------------------------------------------------------------- - // Internals only - // - protected static native boolean initIDs0(); - protected static native long getSurfaceHandle0(Surface surface); - protected static native int getSurfaceVisualID0(long surfaceHandle); - protected static native void setSurfaceVisualID0(long surfaceHandle, int nativeVisualID); - protected static native int getWidth0(long surfaceHandle); - protected static native int getHeight0(long surfaceHandle); - protected static native void acquire0(long surfaceHandle); - protected static native void release0(long surfaceHandle); -} diff --git a/src/newt/classes/jogamp/newt/driver/android/AndroidDisplay.java b/src/newt/classes/jogamp/newt/driver/android/DisplayDriver.java index 3f360f20f..32bd970a1 100644 --- a/src/newt/classes/jogamp/newt/driver/android/AndroidDisplay.java +++ b/src/newt/classes/jogamp/newt/driver/android/DisplayDriver.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -32,15 +32,12 @@ import jogamp.newt.*; import jogamp.opengl.egl.*; import javax.media.nativewindow.*; -import javax.media.opengl.GLException; - -import com.jogamp.nativewindow.egl.*; -public class AndroidDisplay extends jogamp.newt.DisplayImpl { +public class DisplayDriver extends jogamp.newt.DisplayImpl { static { NEWTJNILibLoader.loadNEWT(); - if (!AndroidWindow.initIDs0()) { + if (!WindowDriver.initIDs0()) { throw new NativeWindowException("Failed to initialize Android NEWT Windowing library"); } } @@ -50,29 +47,21 @@ public class AndroidDisplay extends jogamp.newt.DisplayImpl { } - public AndroidDisplay() { + public DisplayDriver() { } protected void createNativeImpl() { // EGL Device - final long eglDisplay = EGLDisplayUtil.eglGetDisplay(EGL.EGL_DEFAULT_DISPLAY); - if (eglDisplay == EGL.EGL_NO_DISPLAY) { - throw new GLException("Failed to created EGL default display: error 0x"+Integer.toHexString(EGL.eglGetError())); - } - if (!EGLDisplayUtil.eglInitialize(eglDisplay, null, null)) { - throw new GLException("eglInitialize failed eglDisplay 0x"+Long.toHexString(eglDisplay)+", error 0x"+Integer.toHexString(EGL.eglGetError())); - } - aDevice = new EGLGraphicsDevice(eglDisplay, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); + aDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); + aDevice.open(); } - protected void closeNativeImpl() { - if (aDevice.getHandle() != EGL.EGL_NO_DISPLAY) { - EGLDisplayUtil.eglTerminate(aDevice.getHandle()); - } + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { + aDevice.close(); } protected void dispatchMessagesNative() { // n/a .. DispatchMessages(); - } + } } diff --git a/src/newt/classes/jogamp/newt/driver/android/MD.java b/src/newt/classes/jogamp/newt/driver/android/MD.java index 150dea9c7..f2f30937b 100644 --- a/src/newt/classes/jogamp/newt/driver/android/MD.java +++ b/src/newt/classes/jogamp/newt/driver/android/MD.java @@ -43,7 +43,7 @@ public class MD { .append(JoglVersion.getInstance()).append(Platform.NEWLINE) .append(Platform.NEWLINE); - JoglVersion.getDefaultOpenGLInfo(sb); + JoglVersion.getDefaultOpenGLInfo(null, sb, true); return sb.toString(); } diff --git a/src/newt/classes/jogamp/newt/driver/android/NewtBaseActivity.java b/src/newt/classes/jogamp/newt/driver/android/NewtBaseActivity.java index 4537fa963..76eb890e2 100644 --- a/src/newt/classes/jogamp/newt/driver/android/NewtBaseActivity.java +++ b/src/newt/classes/jogamp/newt/driver/android/NewtBaseActivity.java @@ -32,11 +32,14 @@ import java.util.List; import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.opengl.FPSCounter; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; import com.jogamp.newt.Window; -import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.GLEventListenerState; +import com.jogamp.opengl.GLStateKeeper; -import jogamp.newt.driver.android.AndroidWindow; +import jogamp.newt.driver.android.WindowDriver; import android.app.Activity; import android.content.Context; @@ -46,12 +49,46 @@ import android.view.WindowManager; public class NewtBaseActivity extends Activity { List<Window> newtWindows = new ArrayList<Window>(); - Animator animator = null; + List<GLAutoDrawable> glAutoDrawables = new ArrayList<GLAutoDrawable>(); + + GLAnimatorControl animator = null; boolean isDelegatedActivity; Activity rootActivity; boolean setThemeCalled = false; + protected void startAnimation(boolean start) { + if(null != animator) { + final boolean res; + if( start ) { + if( animator.isPaused() ) { + res = animator.resume(); + } else { + res = animator.start(); + } + } else { + res = animator.stop(); + } + Log.d(MD.TAG, "Animator global: start "+start+", result "+res); + } + for(int i=0; i<glAutoDrawables.size(); i++) { + final GLAnimatorControl anim = glAutoDrawables.get(i).getAnimator(); + if(null != anim) { + final boolean res; + if( start ) { + if( anim.isPaused() ) { + res = anim.resume(); + } else { + res = anim.start(); + } + } else { + res = anim.stop(); + } + Log.d(MD.TAG, "Animator glad["+i+"]: start "+start+", result "+res); + } + } + } + public NewtBaseActivity() { super(); isDelegatedActivity = false; @@ -59,8 +96,8 @@ public class NewtBaseActivity extends Activity { } public void setRootActivity(Activity rootActivity) { - this.isDelegatedActivity = true; this.rootActivity = rootActivity; + this.isDelegatedActivity = this != rootActivity; } public final boolean isDelegatedActivity() { @@ -79,19 +116,21 @@ public class NewtBaseActivity extends Activity { * </p> * @param androidWindow * @param newtWindow + * @throws IllegalArgumentException if the <code>newtWindow</code>'s {@link Window#getDelegatedWindow() delegate} is not an AndroidDriver. + * @see #registerNEWTWindow(Window) * @see #addContentView(android.view.Window, Window, android.view.ViewGroup.LayoutParams) */ - public void setContentView(android.view.Window androidWindow, Window newtWindow) { - newtWindow = newtWindow.getDelegatedWindow(); - if(newtWindow instanceof AndroidWindow) { - adaptTheme4Transparency(newtWindow.getRequestedCapabilities()); - layoutForNEWTWindow(androidWindow, newtWindow); - AndroidWindow newtAWindow = (AndroidWindow)newtWindow; + public void setContentView(final android.view.Window androidWindow, final Window newtWindow) throws IllegalArgumentException { + final Window delegateWindow = newtWindow.getDelegatedWindow(); + if(delegateWindow instanceof WindowDriver) { + adaptTheme4Transparency(delegateWindow.getRequestedCapabilities()); + layoutForNEWTWindow(androidWindow, delegateWindow); + final WindowDriver newtAWindow = (WindowDriver)delegateWindow; androidWindow.setContentView(newtAWindow.getAndroidView()); - registerNEWTWindow(newtAWindow); } else { - throw new IllegalArgumentException("Given NEWT Window is not an Android Window: "+newtWindow.getClass()); + throw new IllegalArgumentException("Given NEWT Window is not an Android Window: "+newtWindow.getClass().getName()); } + registerNEWTWindow(newtWindow); } /** * This is one of the three registration methods (see below). @@ -102,32 +141,68 @@ public class NewtBaseActivity extends Activity { * @param androidWindow * @param newtWindow * @param params - * @see #setContentView(android.view.Window, Window) + * @throws IllegalArgumentException if the <code>newtWindow</code>'s {@link Window#getDelegatedWindow() delegate} is not an AndroidDriver. * @see #registerNEWTWindow(Window) + * @see #setContentView(android.view.Window, Window) */ - public void addContentView(android.view.Window androidWindow, Window newtWindow, android.view.ViewGroup.LayoutParams params) { - newtWindow = newtWindow.getDelegatedWindow(); - if(newtWindow instanceof AndroidWindow) { - AndroidWindow newtAWindow = (AndroidWindow)newtWindow; + public void addContentView(final android.view.Window androidWindow, final Window newtWindow, final android.view.ViewGroup.LayoutParams params) throws IllegalArgumentException { + final Window delegateWindow = newtWindow.getDelegatedWindow(); + if(delegateWindow instanceof WindowDriver) { + final WindowDriver newtAWindow = (WindowDriver)delegateWindow; androidWindow.addContentView(newtAWindow.getAndroidView(), params); - registerNEWTWindow(newtAWindow); } else { - throw new IllegalArgumentException("Given NEWT Window is not an Android Window: "+newtWindow.getClass()); + throw new IllegalArgumentException("Given NEWT Window's Delegate is not an Android Window: "+delegateWindow.getClass().getName()); } + registerNEWTWindow(newtWindow); } /** * This is one of the three registration methods (see below). * <p> - * This methods simply registers the given NEWT window to ensure it's destruction at {@link #onDestroy()}. - * </p> + * This methods registers the given NEWT window to ensure it's destruction at {@link #onDestroy()}. + * </p> + * <p> + * If adding a {@link GLAutoDrawable} implementation, the {@link GLAnimatorControl} retrieved by {@link GLAutoDrawable#getAnimator()} + * will be used for {@link #onPause()} and {@link #onResume()}. + * </p> + * <p> + * If adding a {@link GLAutoDrawable} implementation, the {@link GLEventListenerState} will preserve it's state + * when {@link #onPause()} is being called while not {@link #isFinishing()}. A later {@link #onResume()} will + * reinstate the {@link GLEventListenerState}. + * </p> * * @param newtWindow + * @throws IllegalArgumentException if the <code>newtWindow</code>'s {@link Window#getDelegatedWindow() delegate} is not an AndroidDriver. * @see #setContentView(android.view.Window, Window) * @see #addContentView(android.view.Window, Window, android.view.ViewGroup.LayoutParams) */ - public void registerNEWTWindow(Window newtWindow) { + public void registerNEWTWindow(final Window newtWindow) throws IllegalArgumentException { + final Window delegateWindow = newtWindow.getDelegatedWindow(); + Log.d(MD.TAG, "registerNEWTWindow: Type "+newtWindow.getClass().getName()+", delegate "+delegateWindow.getClass().getName()); + if(delegateWindow instanceof WindowDriver) { + final WindowDriver newtAWindow = (WindowDriver)delegateWindow; + newtAWindow.registerActivity(getActivity()); + } else { + throw new IllegalArgumentException("Given NEWT Window's Delegate is not an Android Window: "+delegateWindow.getClass().getName()); + } newtWindows.add(newtWindow); + if(newtWindow instanceof GLAutoDrawable) { + glAutoDrawables.add((GLAutoDrawable)newtWindow); + } + if(newtWindow instanceof GLStateKeeper) { + ((GLStateKeeper)newtWindow).setGLStateKeeperListener(glStateKeeperListener); + } } + private final GLStateKeeper.Listener glStateKeeperListener = new GLStateKeeper.Listener() { + @Override + public void glStatePreserveNotify(GLStateKeeper glsk) { + Log.d(MD.TAG, "GLStateKeeper Preserving: 0x"+Integer.toHexString(glsk.hashCode())); + } + @Override + public void glStateRestored(GLStateKeeper glsk) { + Log.d(MD.TAG, "GLStateKeeper Restored: 0x"+Integer.toHexString(glsk.hashCode())); + startAnimation(true); + } + }; /** * Convenient method to set the Android window's flags to fullscreen or size-layout depending on the given NEWT window. @@ -221,7 +296,19 @@ public class NewtBaseActivity extends Activity { } } - public void setAnimator(Animator animator) { + /** + * Setting up a global {@Link GLAnimatorControl} for {@link #onPause()} and {@link #onResume()}. + * <p> + * Note that if adding a {@link GLAutoDrawable} implementation via {@link #registerNEWTWindow(Window)}, + * {@link #setContentView(android.view.Window, Window)} or {@link #addContentView(android.view.Window, Window, android.view.ViewGroup.LayoutParams)} + * their {@link GLAnimatorControl} retrieved by {@link GLAutoDrawable#getAnimator()} will be used as well. + * In this case, using this global {@Link GLAnimatorControl} is redundant. + * </p> + * @see #registerNEWTWindow(Window) + * @see #setContentView(android.view.Window, Window) + * @see #addContentView(android.view.Window, Window, android.view.ViewGroup.LayoutParams) + */ + public void setAnimator(GLAnimatorControl animator) { this.animator = animator; if(!animator.isStarted()) { animator.start(); @@ -231,85 +318,146 @@ public class NewtBaseActivity extends Activity { @Override public android.view.Window getWindow() { - return getActivity().getWindow(); + if( isDelegatedActivity() ) { + return getActivity().getWindow(); + } else { + return super.getWindow(); + } } @Override public void onCreate(Bundle savedInstanceState) { - Log.d(MD.TAG, "onCreate"); + Log.d(MD.TAG, "onCreate.0"); if(!isDelegatedActivity()) { super.onCreate(savedInstanceState); } + // Extraordinary cleanup, for cases of 'onCreate()' calls w/ valid states, + // i.e. w/o having onDestroy() being called. + // Could happened due to spec when App process is killed for memory exhaustion or other reasons. + cleanup(); + jogamp.common.os.android.StaticContext.init(rootActivity.getApplicationContext()); + Log.d(MD.TAG, "onCreate.X"); } @Override public void onStart() { - Log.d(MD.TAG, "onStart"); + Log.d(MD.TAG, "onStart.0"); if(!isDelegatedActivity()) { super.onStart(); } + Log.d(MD.TAG, "onStart.X"); } @Override public void onRestart() { - Log.d(MD.TAG, "onRestart"); + Log.d(MD.TAG, "onRestart.0"); if(!isDelegatedActivity()) { super.onRestart(); } + Log.d(MD.TAG, "onRestart.X"); } @Override public void onResume() { - Log.d(MD.TAG, "onResume"); + Log.d(MD.TAG, "onResume.0"); if(!isDelegatedActivity()) { super.onResume(); } - if(null != animator) { - animator.resume(); - animator.resetFPSCounter(); - } - for(int i=newtWindows.size()-1; i>=0; i--) { + for(int i=0; i<newtWindows.size(); i++) { final Window win = newtWindows.get(i); + win.setVisible(true); if(win instanceof FPSCounter) { ((FPSCounter)win).resetFPSCounter(); } } + startAnimation(true); + Log.d(MD.TAG, "onResume.X"); } @Override public void onPause() { - Log.d(MD.TAG, "onPause"); - if(null != animator) { - animator.pause(); + Log.d(MD.TAG, "onPause.0"); + if( !getActivity().isFinishing() ) { + int ok=0, fail=0; + for(int i=0; i<glAutoDrawables.size(); i++) { + final GLAutoDrawable glad = glAutoDrawables.get(i); + if(glad instanceof GLStateKeeper) { + if( ((GLStateKeeper)glad).preserveGLStateAtDestroy(true) ) { + ok++; + } else { + fail++; + } + } + } + Log.d(MD.TAG, "GLStateKeeper.Mark2Preserve: Total "+glAutoDrawables.size()+", OK "+ok+", Fail "+fail); } - if(!isDelegatedActivity()) { + startAnimation(false); + if( !isDelegatedActivity() ) { super.onPause(); } + Log.d(MD.TAG, "onPause.X"); } @Override public void onStop() { - Log.d(MD.TAG, "onStop"); - if(!isDelegatedActivity()) { + Log.d(MD.TAG, "onStop.0"); + for(int i=0; i<newtWindows.size(); i++) { + final Window win = newtWindows.get(i); + win.setVisible(false); + } + if( !isDelegatedActivity() ) { super.onStop(); } + Log.d(MD.TAG, "onStop.X"); } - - @Override - public void onDestroy() { - Log.d(MD.TAG, "onDestroy"); - if(null != animator) { - animator.stop(); - animator = null; + + /** + * Performs cleaning up all references, + * <p> + * Cleaning and destroying up all preserved GLEventListenerState + * and clearing the preserve-flag of all GLStateKeeper. + * </p> + * <p> + * Destroying all GLWindow. + * </p> + */ + private void cleanup() { + Log.d(MD.TAG, "cleanup.0"); + int glelsKilled = 0, glelsClean = 0; + for(int i=0; i<glAutoDrawables.size(); i++) { + final GLAutoDrawable glad = glAutoDrawables.get(i); + if(glad instanceof GLStateKeeper) { + final GLStateKeeper glsk = (GLStateKeeper)glad; + glsk.preserveGLStateAtDestroy(false); + final GLEventListenerState glels = glsk.clearPreservedGLState(); + if( null != glels) { + glels.destroy(); + glelsKilled++; + } else { + glelsClean++; + } + } } - while(newtWindows.size()>0) { - final Window win = newtWindows.remove(newtWindows.size()-1); + Log.d(MD.TAG, "cleanup.1: GLStateKeeper.ForceDestroy: Total "+glAutoDrawables.size()+", destroyed "+glelsKilled+", clean "+glelsClean); + for(int i=0; i<newtWindows.size(); i++) { + final Window win = newtWindows.get(i); win.destroy(); } + newtWindows.clear(); + glAutoDrawables.clear(); + Log.d(MD.TAG, "cleanup.1: StaticContext.getContext: "+jogamp.common.os.android.StaticContext.getContext()); jogamp.common.os.android.StaticContext.clear(); + Log.d(MD.TAG, "cleanup.X"); + } + + @Override + public void onDestroy() { + Log.d(MD.TAG, "onDestroy.0"); + cleanup(); // normal cleanup if(!isDelegatedActivity()) { super.onDestroy(); } + Log.d(MD.TAG, "onDestroy.X"); } } diff --git a/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivity.java b/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivity.java index 36b83337a..259acb8f3 100644 --- a/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivity.java +++ b/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivity.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -47,58 +47,75 @@ import android.widget.ScrollView; import android.widget.TextView; public class NewtVersionActivity extends NewtBaseActivity { + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - + setFullscreenFeature(getWindow(), true); final android.view.ViewGroup viewGroup = new android.widget.FrameLayout(getActivity().getApplicationContext()); getWindow().setContentView(viewGroup); - + final TextView tv = new TextView(getActivity()); final ScrollView scroller = new ScrollView(getActivity()); scroller.addView(tv); viewGroup.addView(scroller, new android.widget.FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.TOP|Gravity.LEFT)); - - tv.setText(VersionUtil.getPlatformInfo()+Platform.NEWLINE+GlueGenVersion.getInstance()+Platform.NEWLINE+JoglVersion.getInstance()+Platform.NEWLINE); - - // create GLWindow (-> incl. underlying NEWT Display, Screen & Window) - GLCapabilities caps = new GLCapabilities(GLProfile.get(GLProfile.GLES2)); - GLWindow glWindow = GLWindow.create(caps); - glWindow.setUndecorated(true); - glWindow.setSize(32, 32); - glWindow.setPosition(0, 0); - final android.view.View androidGLView = ((AndroidWindow)glWindow.getDelegatedWindow()).getAndroidView(); - viewGroup.addView(androidGLView, new android.widget.FrameLayout.LayoutParams(glWindow.getWidth(), glWindow.getHeight(), Gravity.BOTTOM|Gravity.RIGHT)); - registerNEWTWindow(glWindow); - - glWindow.addGLEventListener(new GLEventListener() { - public void init(GLAutoDrawable drawable) { - GL gl = drawable.getGL(); - final StringBuffer sb = new StringBuffer(); - sb.append(JoglVersion.getGLInfo(gl, null)).append(Platform.NEWLINE); - sb.append("Requested: ").append(Platform.NEWLINE); - sb.append(drawable.getNativeSurface().getGraphicsConfiguration().getRequestedCapabilities()).append(Platform.NEWLINE).append(Platform.NEWLINE); - sb.append("Chosen: ").append(Platform.NEWLINE); - sb.append(drawable.getChosenGLCapabilities()).append(Platform.NEWLINE).append(Platform.NEWLINE); - viewGroup.post(new Runnable() { - public void run() { - tv.append(sb.toString()); - viewGroup.removeView(androidGLView); - } } ); - } - public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { - } + final String info1 = "JOGL Version Info"+Platform.NEWLINE+VersionUtil.getPlatformInfo()+Platform.NEWLINE+GlueGenVersion.getInstance()+Platform.NEWLINE+JoglVersion.getInstance()+Platform.NEWLINE; + Log.d(MD.TAG, info1); + tv.setText(info1); + + final GLProfile glp; + if( GLProfile.isAvailable(GLProfile.GL2ES2) ) { + glp = GLProfile.get(GLProfile.GL2ES2); + } else if( GLProfile.isAvailable(GLProfile.GL2ES1) ) { + glp = GLProfile.get(GLProfile.GL2ES1); + } else { + glp = null; + tv.append("No GLProfile GL2ES2 nor GL2ES1 available!"); + } + if( null != glp ) { + // create GLWindow (-> incl. underlying NEWT Display, Screen & Window) + GLCapabilities caps = new GLCapabilities(glp); + GLWindow glWindow = GLWindow.create(caps); + glWindow.setUndecorated(true); + glWindow.setSize(32, 32); + glWindow.setPosition(0, 0); + final android.view.View androidGLView = ((WindowDriver)glWindow.getDelegatedWindow()).getAndroidView(); + viewGroup.addView(androidGLView, new android.widget.FrameLayout.LayoutParams(glWindow.getWidth(), glWindow.getHeight(), Gravity.BOTTOM|Gravity.RIGHT)); + registerNEWTWindow(glWindow); + + glWindow.addGLEventListener(new GLEventListener() { + public void init(GLAutoDrawable drawable) { + GL gl = drawable.getGL(); + final StringBuilder sb = new StringBuilder(); + sb.append(JoglVersion.getGLInfo(gl, null, true)).append(Platform.NEWLINE); + sb.append("Requested: ").append(Platform.NEWLINE); + sb.append(drawable.getNativeSurface().getGraphicsConfiguration().getRequestedCapabilities()).append(Platform.NEWLINE).append(Platform.NEWLINE); + sb.append("Chosen: ").append(Platform.NEWLINE); + sb.append(drawable.getChosenGLCapabilities()).append(Platform.NEWLINE).append(Platform.NEWLINE); + final String info2 = sb.toString(); + // Log.d(MD.TAG, info2); // too big! + System.err.println(info2); + viewGroup.post(new Runnable() { + public void run() { + tv.append(info2); + viewGroup.removeView(androidGLView); + } } ); + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + } - public void display(GLAutoDrawable drawable) { - } + public void display(GLAutoDrawable drawable) { + } - public void dispose(GLAutoDrawable drawable) { - } - }); - glWindow.setVisible(true); + public void dispose(GLAutoDrawable drawable) { + } + }); + glWindow.setVisible(true); + } Log.d(MD.TAG, "onCreate - X"); - } + } } diff --git a/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivityLauncher.java b/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivityLauncher.java index cb8799b19..553900f6a 100644 --- a/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivityLauncher.java +++ b/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivityLauncher.java @@ -10,12 +10,12 @@ public class NewtVersionActivityLauncher extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - final Uri uri = Uri.parse("launch://jogamp.org/jogamp.newt.driver.android.NewtVersionActivity"); - final Intent intent = new Intent("org.jogamp.launcher.action.LAUNCH_ACTIVITY_NORMAL", uri); + + final Uri uri = Uri.parse("launch://jogamp.org/jogamp.newt.driver.android.NewtVersionActivity?sys=com.jogamp.common&sys=javax.media.opengl&pkg=com.jogamp.opengl.test&jogamp.debug=all&nativewindow.debug=all&jogl.debug=all&newt.debug=all"); + final Intent intent = new Intent("org.jogamp.launcher.action.LAUNCH_ACTIVITY_NORMAL", uri); Log.d(getClass().getSimpleName(), "Launching Activity: "+intent); startActivity (intent); - + finish(); // done - } + } } diff --git a/src/newt/classes/jogamp/newt/driver/android/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/android/ScreenDriver.java new file mode 100644 index 000000000..66237204c --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/android/ScreenDriver.java @@ -0,0 +1,189 @@ +/** + * Copyright 2011 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.android; + +import javax.media.nativewindow.DefaultGraphicsScreen; + +import jogamp.newt.MonitorModeProps; +import jogamp.newt.MonitorModeProps.Cache; + +import android.content.Context; +import android.graphics.PixelFormat; +import android.util.DisplayMetrics; +import android.view.Surface; +import android.view.WindowManager; + +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +public class ScreenDriver extends jogamp.newt.ScreenImpl { + + static { + DisplayDriver.initSingleton(); + } + + public ScreenDriver() { + } + + @Override + protected void createNativeImpl() { + aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); + } + + @Override + protected void closeNativeImpl() { } + + @Override + protected int validateScreenIndex(int idx) { + return 0; // FIXME: only one screen available ? + } + + private final MonitorMode getModeImpl(final Cache cache, final android.view.Display aDisplay, DisplayMetrics outMetrics, int modeIdx, int screenSizeNRot, int nrot) { + final int[] props = new int[MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL]; + int i = 0; + props[i++] = MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL; + i = getScreenSize(outMetrics, screenSizeNRot, props, i); // width, height + i = getBpp(aDisplay, props, i); // bpp + props[i++] = (int) ( aDisplay.getRefreshRate() * 100.0f ); // Hz * 100 + props[i++] = 0; // flags + props[i++] = modeIdx; // modeId; + props[i++] = nrot; + return MonitorModeProps.streamInMonitorMode(null, cache, props, 0); + } + + @Override + protected void collectNativeMonitorModesAndDevicesImpl(Cache cache) { + // FIXME: Multi Monitor Implementation missing [for newer Android version ?] + + final Context ctx = jogamp.common.os.android.StaticContext.getContext(); + final WindowManager wmgr = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE); + final DisplayMetrics outMetrics = new DisplayMetrics(); + final android.view.Display aDisplay = wmgr.getDefaultDisplay(); + aDisplay.getMetrics(outMetrics); + + final int arot = aDisplay.getRotation(); + final int nrot = androidRotation2NewtRotation(arot); + + final int modeIdx=0; // no native modeId in use - use 0 + MonitorMode currentMode = null; + for(int r=0; r<4; r++) { // for all rotations + final int nrot_i = r*MonitorMode.ROTATE_90; + MonitorMode mode = getModeImpl(cache, aDisplay, outMetrics, modeIdx, 0, nrot_i); + if( nrot == nrot_i ) { + currentMode = mode; + } + } + + final int[] props = new int[MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES - 1 - MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES]; + int i = 0; + props[i++] = props.length; + props[i++] = 0; // crt_idx + i = getScreenSizeMM(outMetrics, props, i); // sizeMM + props[i++] = 0; // rotated viewport x + props[i++] = 0; // rotated viewport y + props[i++] = outMetrics.widthPixels; // rotated viewport width + props[i++] = outMetrics.heightPixels; // rotated viewport height + MonitorModeProps.streamInMonitorDevice(null, cache, this, cache.monitorModes, currentMode, props, 0); + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(MonitorDevice monitor) { + final Context ctx = jogamp.common.os.android.StaticContext.getContext(); + final WindowManager wmgr = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE); + final DisplayMetrics outMetrics = new DisplayMetrics(); + final android.view.Display aDisplay = wmgr.getDefaultDisplay(); + aDisplay.getMetrics(outMetrics); + + final int currNRot = androidRotation2NewtRotation(aDisplay.getRotation()); + return getModeImpl(null, aDisplay, outMetrics, 0, currNRot, currNRot); + } + + @Override + protected boolean setCurrentMonitorModeImpl(MonitorDevice monitor, MonitorMode mode) { + return false; + } + + //---------------------------------------------------------------------- + // Internals only + // + static int androidRotation2NewtRotation(int arot) { + switch(arot) { + case Surface.ROTATION_270: return MonitorMode.ROTATE_270; + case Surface.ROTATION_180: return MonitorMode.ROTATE_180; + case Surface.ROTATION_90: return MonitorMode.ROTATE_90; + case Surface.ROTATION_0: + } + return MonitorMode.ROTATE_0; + } + static int getScreenSize(DisplayMetrics outMetrics, int nrot, int[] props, int offset) { + // swap width and height, since Android reflects rotated dimension, we don't + if (MonitorMode.ROTATE_90 == nrot || MonitorMode.ROTATE_270 == nrot) { + props[offset++] = outMetrics.heightPixels; + props[offset++] = outMetrics.widthPixels; + } else { + props[offset++] = outMetrics.widthPixels; + props[offset++] = outMetrics.heightPixels; + } + return offset; + } + static int getBpp(android.view.Display aDisplay, int[] props, int offset) { + int bpp; + switch(aDisplay.getPixelFormat()) { + case PixelFormat.RGBA_8888: bpp=32; break; + case PixelFormat.RGBX_8888: bpp=32; break; + case PixelFormat.RGB_888: bpp=24; break; + case PixelFormat.RGB_565: bpp=16; break; + case PixelFormat.RGBA_5551: bpp=16; break; + case PixelFormat.RGBA_4444: bpp=16; break; + case PixelFormat.RGB_332: bpp= 8; break; + default: bpp=32; + } + props[offset++] = bpp; + return offset; + } + static int getScreenSizeMM(DisplayMetrics outMetrics, int[] props, int offset) { + final float inW = outMetrics.widthPixels / outMetrics.xdpi; + final float inH = outMetrics.heightPixels / outMetrics.ydpi; + final float mmpi = 25.4f; + final float mmW = inW * mmpi; + final float mmH = inH * mmpi; + if( DEBUG ) { + System.err.println("Screen A screen "+outMetrics.widthPixels+" x "+outMetrics.heightPixels); + System.err.println("Screen A xy dpi "+outMetrics.xdpi+" x "+outMetrics.ydpi); + System.err.println("Screen A densityDPI "+outMetrics.densityDpi); + System.err.println("Screen A density "+outMetrics.density); + System.err.println("Screen N xy inch "+inW+" x "+inH); + System.err.println("Screen N xy mm "+mmW+" x "+mmH); + } + props[offset++] = Math.round(mmW); + props[offset++] = Math.round(mmH); + return offset; + } +} + diff --git a/src/newt/classes/jogamp/newt/driver/android/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/android/WindowDriver.java new file mode 100644 index 000000000..9af455445 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/android/WindowDriver.java @@ -0,0 +1,715 @@ +/** + * Copyright 2011 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.android; + +import jogamp.common.os.android.StaticContext; +import jogamp.newt.WindowImpl; +import jogamp.newt.driver.android.event.AndroidNewtEventFactory; +import jogamp.newt.driver.android.event.AndroidNewtEventTranslator; + +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.Capabilities; +import javax.media.nativewindow.CapabilitiesImmutable; +import javax.media.nativewindow.DefaultGraphicsScreen; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.VisualIDHolder; +import javax.media.nativewindow.util.Insets; +import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.RectangleImmutable; +import javax.media.opengl.GLCapabilitiesChooser; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLException; + +import com.jogamp.common.os.AndroidVersion; +import com.jogamp.nativewindow.egl.EGLGraphicsDevice; +import com.jogamp.newt.MonitorDevice; + +import jogamp.opengl.egl.EGL; +import jogamp.opengl.egl.EGLDisplayUtil; +import jogamp.opengl.egl.EGLGraphicsConfiguration; +import jogamp.opengl.egl.EGLGraphicsConfigurationFactory; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.PixelFormat; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Looper; +import android.os.ResultReceiver; +import android.util.Log; +import android.view.Gravity; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceHolder.Callback2; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.view.SurfaceView; +import android.view.KeyEvent; + + +public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { + static { + DisplayDriver.initSingleton(); + } + + /** + * First stage of selecting an Android PixelFormat, + * at construction via {@link SurfaceHolder#setFormat(int)} + * before native realization! + * + * @param rCaps requested Capabilities + * @return An Android PixelFormat number suitable for {@link SurfaceHolder#setFormat(int)}. + */ + public static final int getSurfaceHolderFormat(CapabilitiesImmutable rCaps) { + int fmt = PixelFormat.UNKNOWN; + + if( !rCaps.isBackgroundOpaque() ) { + fmt = PixelFormat.TRANSLUCENT; + } else if( rCaps.getRedBits()<=5 && + rCaps.getGreenBits()<=6 && + rCaps.getBlueBits()<=5 && + rCaps.getAlphaBits()==0 ) { + fmt = PixelFormat.RGB_565; + } else if( rCaps.getAlphaBits()==0 ) { + fmt = PixelFormat.RGB_888; + } else { + fmt = PixelFormat.RGBA_8888; + } + Log.d(MD.TAG, "getSurfaceHolderFormat: requested: "+rCaps); + Log.d(MD.TAG, "getSurfaceHolderFormat: returned: "+fmt); + + return fmt; + } + + + public static final int NATIVE_WINDOW_FORMAT_RGBA_8888 = 1; + public static final int NATIVE_WINDOW_FORMAT_RGBX_8888 = 2; + public static final int NATIVE_WINDOW_FORMAT_RGB_565 = 4; + + /** + * Second stage of selecting an Android PixelFormat, + * at right after native (surface) realization at {@link Callback2#surfaceChanged(SurfaceHolder, int, int, int)}. + * Selection happens via {@link #setSurfaceVisualID0(long, int)} before native EGL creation. + * + * @param androidPixelFormat An Android PixelFormat delivered via {@link Callback2#surfaceChanged(SurfaceHolder, int, int, int)} params. + * @return A native Android PixelFormat number suitable for {@link #setSurfaceVisualID0(long, int)}. + */ + public static final int getANativeWindowFormat(int androidPixelFormat) { + final int nativePixelFormat; + switch(androidPixelFormat) { + case PixelFormat.RGBA_8888: + case PixelFormat.RGBA_5551: + case PixelFormat.RGBA_4444: + nativePixelFormat = NATIVE_WINDOW_FORMAT_RGBA_8888; + break; + + case PixelFormat.RGBX_8888: + case PixelFormat.RGB_888: + nativePixelFormat = NATIVE_WINDOW_FORMAT_RGBX_8888; + break; + + case PixelFormat.RGB_565: + case PixelFormat.RGB_332: + nativePixelFormat = NATIVE_WINDOW_FORMAT_RGB_565; + break; + default: nativePixelFormat = NATIVE_WINDOW_FORMAT_RGBA_8888; + } + Log.d(MD.TAG, "getANativeWindowFormat: android: "+androidPixelFormat+" -> native "+nativePixelFormat); + return nativePixelFormat; + } + + /** + * Final stage of Android PixelFormat operation, + * match the requested Capabilities w/ Android PixelFormat number. + * This is done at native realization @ {@link Callback2#surfaceChanged(SurfaceHolder, int, int, int)}. + * + * @param matchFormatPrecise + * @param format + * @param rCaps requested Capabilities + * @return The fixed Capabilities + */ + public static final CapabilitiesImmutable fixCaps(boolean matchFormatPrecise, int format, CapabilitiesImmutable rCaps) { + PixelFormat pf = new PixelFormat(); + PixelFormat.getPixelFormatInfo(format, pf); + final CapabilitiesImmutable res; + int r, g, b, a; + + switch(format) { + case PixelFormat.RGBA_8888: r=8; g=8; b=8; a=8; break; // NATIVE_WINDOW_FORMAT_RGBA_8888 + case PixelFormat.RGBX_8888: r=8; g=8; b=8; a=0; break; // NATIVE_WINDOW_FORMAT_RGBX_8888 + case PixelFormat.RGB_888: r=8; g=8; b=8; a=0; break; + case PixelFormat.RGB_565: r=5; g=6; b=5; a=0; break; // NATIVE_WINDOW_FORMAT_RGB_565 + case PixelFormat.RGBA_5551: r=5; g=5; b=5; a=1; break; + case PixelFormat.RGBA_4444: r=4; g=4; b=4; a=4; break; + case PixelFormat.RGB_332: r=3; g=3; b=2; a=0; break; + default: throw new InternalError("Unhandled pixelformat: "+format); + } + final boolean change = matchFormatPrecise || + rCaps.getRedBits() > r && + rCaps.getGreenBits() > g && + rCaps.getBlueBits() > b && + rCaps.getAlphaBits() > a ; + + if(change) { + Capabilities nCaps = (Capabilities) rCaps.cloneMutable(); + nCaps.setRedBits(r); + nCaps.setGreenBits(g); + nCaps.setBlueBits(b); + nCaps.setAlphaBits(a); + res = nCaps; + } else { + res = rCaps; + } + Log.d(MD.TAG, "fixCaps: format: "+format); + Log.d(MD.TAG, "fixCaps: requested: "+rCaps); + Log.d(MD.TAG, "fixCaps: chosen: "+res); + + return res; + } + + public static final boolean isAndroidFormatTransparent(int aFormat) { + switch (aFormat) { + case PixelFormat.TRANSLUCENT: + case PixelFormat.TRANSPARENT: + return true; + } + return false; + } + + public static Class<?>[] getCustomConstructorArgumentTypes() { + return new Class<?>[] { Context.class } ; + } + + public WindowDriver() { + reset(); + } + + public void registerActivity(Activity activity) { + this.activity = activity; + } + protected Activity activity = null; + + private final void reset() { + added2StaticViewGroup = false; + androidView = null; + nativeFormat = VisualIDHolder.VID_UNDEFINED; + androidFormat = VisualIDHolder.VID_UNDEFINED; + capsByFormat = null; + surface = null; + surfaceHandle = 0; + eglSurface = 0; + definePosition(0, 0); // default to 0/0 + defineSize(0, 0); // default size -> TBD ! + + setBrokenFocusChange(true); + } + + private final void setupInputListener(final boolean enable) { + Log.d(MD.TAG, "setupInputListener(enable "+enable+") - "+Thread.currentThread().getName()); + + final AndroidNewtEventTranslator eventTranslator = + enable ? new AndroidNewtEventTranslator(this, androidView.getContext(), androidView.getHandler()) : null; + androidView.setOnTouchListener(eventTranslator); + androidView.setOnKeyListener(eventTranslator); + androidView.setOnFocusChangeListener(eventTranslator); + if(AndroidVersion.SDK_INT >= 12) { // API Level 12 + Log.d(MD.TAG, "setupInputListener - enable GenericMotionListener - "+Thread.currentThread().getName()); + androidView.setOnGenericMotionListener(eventTranslator); + } + if( enable ) { + androidView.post(new Runnable() { + public void run() { + androidView.setClickable(false); + androidView.setFocusable(true); + androidView.setFocusableInTouchMode(true); + } } ); + } + } + + private final void setupAndroidView(Context ctx) { + androidView = new MSurfaceView(ctx); + + final SurfaceHolder sh = androidView.getHolder(); + sh.addCallback(WindowDriver.this); + sh.setFormat(getSurfaceHolderFormat(getRequestedCapabilities())); + } + + public final SurfaceView getAndroidView() { return androidView; } + + @Override + protected final void instantiationFinished() { + Log.d(MD.TAG, "instantiationFinished() - "+Thread.currentThread().getName()); + + final Context ctx = StaticContext.getContext(); + if(null == ctx) { + throw new NativeWindowException("No static [Application] Context has been set. Call StaticContext.setContext(Context) first."); + } + + if( null != Looper.myLooper() ) { + setupAndroidView(ctx); + } + } + + @Override + protected final boolean canCreateNativeImpl() { + Log.d(MD.TAG, "canCreateNativeImpl.0: surfaceHandle ready "+(0!=surfaceHandle)+" - on thread "+Thread.currentThread().getName()); + if(WindowImpl.DEBUG_IMPLEMENTATION) { + Thread.dumpStack(); + } + + if( isFullscreen() ) { + final MonitorDevice mainMonitor = getMainMonitor(); + final RectangleImmutable viewport = mainMonitor.getViewport(); + definePosition(viewport.getX(), viewport.getY()); + defineSize(viewport.getWidth(), viewport.getHeight()); + } + + final boolean b; + + if( 0 == surfaceHandle ) { + // Static ViewGroup, i.e. self contained main code + final ViewGroup viewGroup = StaticContext.getContentViewGroup(); + Log.d(MD.TAG, "canCreateNativeImpl: viewGroup "+viewGroup); + if( null != viewGroup && !added2StaticViewGroup ) { + added2StaticViewGroup = true; + viewGroup.post(new Runnable() { + public void run() { + if(null == androidView) { + setupAndroidView( StaticContext.getContext() ); + } + viewGroup.addView(androidView, new android.widget.FrameLayout.LayoutParams(getWidth(), getHeight(), Gravity.BOTTOM|Gravity.RIGHT)); + Log.d(MD.TAG, "canCreateNativeImpl: added to static ViewGroup - on thread "+Thread.currentThread().getName()); + } }); + for(long sleep = TIMEOUT_NATIVEWINDOW; 0<sleep && 0 == surfaceHandle; sleep-=10 ) { + try { Thread.sleep(10); } catch (InterruptedException ie) {} + } + b = 0 != surfaceHandle; + Log.d(MD.TAG, "canCreateNativeImpl: surfaceHandle ready(2) "+b+" - on thread "+Thread.currentThread().getName()); + } else { + // No surfaceHandle defined, No static ViewGroup to add ourselves + b = false; + } + } else { + // surfaceHandle already defined + b = true; + } + return b; + } + + @Override + protected final void createNativeImpl() { + // Create own screen/device resource instance allowing independent ownership, + // while still utilizing shared EGL resources. + final AbstractGraphicsScreen aScreen = getScreen().getGraphicsScreen(); + final EGLGraphicsDevice aDevice = (EGLGraphicsDevice) aScreen.getDevice(); + final EGLGraphicsDevice eglDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(aDevice.getNativeDisplayID(), aDevice.getConnection(), aDevice.getUnitID()); + eglDevice.open(); + final DefaultGraphicsScreen eglScreen = new DefaultGraphicsScreen(eglDevice, aScreen.getIndex()); + + Log.d(MD.TAG, "createNativeImpl 0 - eglDevice 0x"+Integer.toHexString(eglDevice.hashCode())+", "+eglDevice+", surfaceHandle 0x"+Long.toHexString(surfaceHandle)+ + ", format [a "+androidFormat+", n "+nativeFormat+"], "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+" - on thread "+Thread.currentThread().getName()); + + if(0!=getParentWindowHandle()) { + throw new NativeWindowException("Window parenting not supported (yet)"); + } + if(0==surfaceHandle) { + throw new InternalError("surfaceHandle null"); + } + + final EGLGraphicsConfiguration eglConfig = EGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic( + capsByFormat, (GLCapabilitiesImmutable) getRequestedCapabilities(), + (GLCapabilitiesChooser)capabilitiesChooser, eglScreen, nativeFormat, isAndroidFormatTransparent(androidFormat)); + if (eglConfig == null) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + final int nativeVisualID = eglConfig.getVisualID(VisualIDHolder.VIDType.NATIVE); + Log.d(MD.TAG, "nativeVisualID 0x"+Integer.toHexString(nativeVisualID)); + Log.d(MD.TAG, "requestedCaps: "+eglConfig.getRequestedCapabilities()); + Log.d(MD.TAG, "chosenCaps : "+eglConfig.getChosenCapabilities()); + if(VisualIDHolder.VID_UNDEFINED != nativeVisualID) { + setSurfaceVisualID0(surfaceHandle, nativeVisualID); + } + + eglSurface = EGL.eglCreateWindowSurface(eglDevice.getHandle(), eglConfig.getNativeConfig(), surfaceHandle, null); + if (EGL.EGL_NO_SURFACE==eglSurface) { + throw new NativeWindowException("Creation of window surface failed: "+eglConfig+", surfaceHandle 0x"+Long.toHexString(surfaceHandle)+", error "+toHexString(EGL.eglGetError())); + } + + // propagate data .. + setGraphicsConfiguration(eglConfig); + setWindowHandle(surfaceHandle); + visibleChanged(false, true); + focusChanged(false, true); + + setupInputListener(true); + + Log.d(MD.TAG, "createNativeImpl X: eglDevice 0x"+Integer.toHexString(eglDevice.hashCode())+", "+eglDevice+", eglSurfaceHandle 0x"+Long.toHexString(eglSurface)); + } + + @Override + protected final void closeNativeImpl() { + final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) getGraphicsConfiguration().getScreen().getDevice(); + + Log.d(MD.TAG, "closeNativeImpl 0 - eglDevice 0x"+Integer.toHexString(eglDevice.hashCode())+", "+eglDevice+", surfaceHandle 0x"+Long.toHexString(surfaceHandle)+ + ", eglSurfaceHandle 0x"+Long.toHexString(eglSurface)+ + ", format [a "+androidFormat+", n "+nativeFormat+"], "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+" - on thread "+Thread.currentThread().getName()); + if(WindowImpl.DEBUG_IMPLEMENTATION) { + Thread.dumpStack(); + } + + setupInputListener(false); + + if(0 != eglSurface) { + try { + if (!EGL.eglDestroySurface(eglDevice.getHandle(), eglSurface)) { + throw new GLException("Error destroying window surface (eglDestroySurface)"); + } + } catch (Throwable t) { + Log.d(MD.TAG, "closeNativeImpl: Catch exception "+t.getMessage()); + t.printStackTrace(); + } finally { + eglSurface = 0; + } + } + release0(surfaceHandle); + + eglDevice.close(); + + if( null != androidView ) { + if( added2StaticViewGroup ) { + added2StaticViewGroup = false; + final ViewGroup viewGroup = StaticContext.getContentViewGroup(); + if( null != viewGroup) { + viewGroup.post(new Runnable() { + public void run() { + viewGroup.removeView(androidView); + Log.d(MD.TAG, "closeNativeImpl: removed from static ViewGroup - on thread "+Thread.currentThread().getName()); + } }); + } + } + } + + surface = null; + surfaceHandle = 0; + } + + @Override + public final long getSurfaceHandle() { + return eglSurface; + } + + /** + * <p> + * Accessible protected method! + * </p> + * + * {@inheritDoc} + */ + @Override + public final void focusChanged(boolean defer, boolean focusGained) { + super.focusChanged(defer, focusGained); + } + + @Override + protected final void requestFocusImpl(boolean reparented) { + if(null != androidView) { + Log.d(MD.TAG, "requestFocusImpl: reparented "+reparented); + androidView.post(new Runnable() { + public void run() { + androidView.requestFocus(); + androidView.bringToFront(); + } + }); + } + } + + @Override + protected final boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { + boolean res = true; + + if( 0 != ( FLAG_CHANGE_FULLSCREEN & flags) ) { + Log.d(MD.TAG, "reconfigureWindowImpl.setFullscreen post creation (setContentView()) n/a"); + return false; + } + if(getWidth() != width || getHeight() != height) { + if(0!=getWindowHandle()) { + Log.d(MD.TAG, "reconfigureWindowImpl.setSize n/a"); + res = false; + } else { + defineSize(width, height); + } + } + if(getX() != x || getY() != y) { + if(0!=getWindowHandle()) { + Log.d(MD.TAG, "reconfigureWindowImpl.setPos n/a"); + res = false; + } else { + definePosition(x, y); + } + } + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { + visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); + } + return res; + } + + @Override + protected final Point getLocationOnScreenImpl(int x, int y) { + return new Point(x,y); + } + + @Override + protected final void updateInsetsImpl(Insets insets) { + // nop .. + } + + //---------------------------------------------------------------------- + // Virtual On-Screen Keyboard / SoftInput + // + + private class KeyboardVisibleReceiver extends ResultReceiver { + public KeyboardVisibleReceiver() { + super(null); + } + + @Override + public void onReceiveResult(int r, Bundle data) { + boolean v = false; + + switch(r) { + case InputMethodManager.RESULT_UNCHANGED_SHOWN: + case InputMethodManager.RESULT_SHOWN: + v = true; + break; + case InputMethodManager.RESULT_HIDDEN: + case InputMethodManager.RESULT_UNCHANGED_HIDDEN: + v = false; + break; + } + Log.d(MD.TAG, "keyboardVisible: "+v); + keyboardVisibilityChanged(v); + } + } + private final KeyboardVisibleReceiver keyboardVisibleReceiver = new KeyboardVisibleReceiver(); + + @Override + protected final boolean setKeyboardVisibleImpl(boolean visible) { + if(null != androidView) { + final InputMethodManager imm = (InputMethodManager) getAndroidView().getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + final IBinder winid = getAndroidView().getWindowToken(); + final boolean result; + if(visible) { + // Show soft-keyboard: + result = imm.showSoftInput(androidView, 0, keyboardVisibleReceiver); + } else { + // hide keyboard : + result = imm.hideSoftInputFromWindow(winid, 0, keyboardVisibleReceiver); + } + return result; + } else { + return false; // nop + } + } + + //---------------------------------------------------------------------- + // Surface Callbacks + // + + @Override + public final void surfaceCreated(SurfaceHolder holder) { + Log.d(MD.TAG, "surfaceCreated: "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+" - on thread "+Thread.currentThread().getName()); + } + + @Override + public final void surfaceChanged(SurfaceHolder aHolder, int aFormat, int aWidth, int aHeight) { + Log.d(MD.TAG, "surfaceChanged: f "+nativeFormat+" -> "+aFormat+", "+aWidth+"x"+aHeight+", current surfaceHandle: 0x"+Long.toHexString(surfaceHandle)+" - on thread "+Thread.currentThread().getName()); + if(WindowImpl.DEBUG_IMPLEMENTATION) { + Thread.dumpStack(); + } + if(0!=surfaceHandle && androidFormat != aFormat ) { + // re-create + Log.d(MD.TAG, "surfaceChanged (destroy old)"); + if(!windowDestroyNotify(true)) { + destroy(); + } + surfaceHandle = 0; + surface=null; + } + if(getScreen().isNativeValid()) { + // if MonitorMode changed .. trigger MonitorMode event + final MonitorDevice mainMonitor = getMainMonitor(); + mainMonitor.queryCurrentMode(); + } + + if(0>getX() || 0>getY()) { + positionChanged(false, 0, 0); + } + + if(0 == surfaceHandle) { + androidFormat = aFormat; + surface = aHolder.getSurface(); + surfaceHandle = getSurfaceHandle0(surface); + acquire0(surfaceHandle); + final int aNativeWindowFormat = getANativeWindowFormat(androidFormat); + setSurfaceVisualID0(surfaceHandle, aNativeWindowFormat); + nativeFormat = getSurfaceVisualID0(surfaceHandle); + Log.d(MD.TAG, "surfaceChanged: androidFormat "+androidFormat+" -- (set-native "+aNativeWindowFormat+") --> nativeFormat "+nativeFormat); + + final int nWidth = getWidth0(surfaceHandle); + final int nHeight = getHeight0(surfaceHandle); + capsByFormat = (GLCapabilitiesImmutable) fixCaps(true /* matchFormatPrecise */, nativeFormat, getRequestedCapabilities()); + sizeChanged(false, nWidth, nHeight, false); + + Log.d(MD.TAG, "surfaceRealized: isValid: "+surface.isValid()+ + ", new surfaceHandle 0x"+Long.toHexString(surfaceHandle)+ + ", format [a "+androidFormat+"/n "+nativeFormat+"], "+ + getX()+"/"+getY()+" "+nWidth+"x"+nHeight+", visible: "+isVisible()); + + if(isVisible()) { + setVisible(false, true); + } + } + sizeChanged(false, aWidth, aHeight, false); + windowRepaint(0, 0, aWidth, aHeight); + Log.d(MD.TAG, "surfaceChanged: X"); + } + + @Override + public final void surfaceDestroyed(SurfaceHolder holder) { + Log.d(MD.TAG, "surfaceDestroyed - on thread "+Thread.currentThread().getName()); + windowDestroyNotify(true); // actually too late .. however .. + Thread.dumpStack(); + } + + @Override + public final void surfaceRedrawNeeded(SurfaceHolder holder) { + Log.d(MD.TAG, "surfaceRedrawNeeded - on thread "+Thread.currentThread().getName()); + windowRepaint(0, 0, getWidth(), getHeight()); + } + + protected boolean handleKeyCodeBack(KeyEvent.DispatcherState state, android.view.KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { + Log.d(MD.TAG, "handleKeyCodeBack.0 : "+event); + state.startTracking(event, this); + } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled() && state.isTracking(event)) { + // Since we cannot trust the visibility state 'completly', + // assume an already invisible state if the invisible operation fails. + final boolean wasVisible = setKeyboardVisibleImpl(false); + Log.d(MD.TAG, "handleKeyCodeBack.1 : wasVisible "+wasVisible+": "+event); + keyboardVisibilityChanged(false); + if( wasVisible ) { + // event processed, just send invisible event, no activity.finished() + enqueueAKey2NKeyUpDown(event, com.jogamp.newt.event.KeyEvent.VK_KEYBOARD_INVISIBLE); + return true; + } else if( null != activity ) { + // process event on our own, since we have an activity to call finish() + // and decide in overriden consumeKeyEvent(..) whether we suppress or proceed w/ activity.finish(). + enqueueAKey2NKeyUpDown(event, com.jogamp.newt.event.KeyEvent.VK_ESCAPE); + return true; + } else { + Log.d(MD.TAG, "handleKeyCodeBack.X1 : "+event); + windowDestroyNotify(true); + // -> default BACK action, usually activity.finish() + } + } + return false; // continue w/ further processing + } + private void enqueueAKey2NKeyUpDown(android.view.KeyEvent aEvent, short newtKeyCode) { + final com.jogamp.newt.event.KeyEvent eDown = AndroidNewtEventFactory.createKeyEvent(aEvent, newtKeyCode, com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED, this); + final com.jogamp.newt.event.KeyEvent eUp = AndroidNewtEventFactory.createKeyEvent(aEvent, newtKeyCode, com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED, this); + enqueueEvent(false, eDown); + enqueueEvent(false, eUp); + } + + @Override + protected void consumeKeyEvent(com.jogamp.newt.event.KeyEvent e) { + super.consumeKeyEvent(e); // consume event, i.e. call all KeyListener + if( com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED == e.getEventType() && !e.isConsumed() ) { + if( com.jogamp.newt.event.KeyEvent.VK_ESCAPE == e.getKeyCode() ) { + Log.d(MD.TAG, "handleKeyCodeBack.X2 : "+e); + activity.finish(); + } else if( com.jogamp.newt.event.KeyEvent.VK_HOME == e.getKeyCode() ) { + Log.d(MD.TAG, "handleKeyCodeHome.X2 : "+e); + triggerHome(); + } + } + } + private void triggerHome() { + Context ctx = StaticContext.getContext(); + if(null == ctx) { + throw new NativeWindowException("No static [Application] Context has been set. Call StaticContext.setContext(Context) first."); + } + Intent showOptions = new Intent(Intent.ACTION_MAIN); + showOptions.addCategory(Intent.CATEGORY_HOME); + ctx.startActivity(showOptions); + } + + private boolean added2StaticViewGroup; + private MSurfaceView androidView; + private int nativeFormat; // chosen current native PixelFormat (suitable for EGL) + private int androidFormat; // chosen current android PixelFormat (-1, -2 ..) + private GLCapabilitiesImmutable capsByFormat; // fixed requestedCaps by PixelFormat + private Surface surface; + private volatile long surfaceHandle; + private long eglSurface; + + class MSurfaceView extends SurfaceView { + public MSurfaceView (Context ctx) { + super(ctx); + setBackgroundDrawable(null); + // setBackgroundColor(Color.TRANSPARENT); + } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + Log.d(MD.TAG, "onKeyPreIme : "+event); + if ( event.getKeyCode() == KeyEvent.KEYCODE_BACK ) { + final KeyEvent.DispatcherState state = getKeyDispatcherState(); + if (state != null) { + return handleKeyCodeBack(state, event); + } + } + return false; // cont. processing + } + } + //---------------------------------------------------------------------- + // Internals only + // + protected static native boolean initIDs0(); + protected static native long getSurfaceHandle0(Surface surface); + /** Return the native window format via <code>ANativeWindow_getFormat(..)</code>. */ + protected static native int getSurfaceVisualID0(long surfaceHandle); + /** Set the native window format via <code>ANativeWindow_setBuffersGeometry(..)</code>. */ + protected static native void setSurfaceVisualID0(long surfaceHandle, int nativeVisualID); + protected static native int getWidth0(long surfaceHandle); + protected static native int getHeight0(long surfaceHandle); + protected static native void acquire0(long surfaceHandle); + protected static native void release0(long surfaceHandle); +} diff --git a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java index d23b5f576..e5d667f3e 100644 --- a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java +++ b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java @@ -28,49 +28,88 @@ package jogamp.newt.driver.android.event; -import java.awt.event.MouseEvent; +import jogamp.newt.Debug; +import android.view.MotionEvent; -import com.jogamp.common.util.IntIntHashMap; -import com.jogamp.newt.Window; +import com.jogamp.common.os.AndroidVersion; import com.jogamp.newt.event.InputEvent; +import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.NEWTEvent; public class AndroidNewtEventFactory { - - protected static final IntIntHashMap eventTypeANDROID2NEWT; - - private static final String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" , - "POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" }; + private static final boolean DEBUG_MOUSE_EVENT = Debug.debug("Android.MouseEvent"); + private static final boolean DEBUG_KEY_EVENT = Debug.debug("Android.KeyEvent"); - static { - IntIntHashMap map = new IntIntHashMap(); - map.setKeyNotFoundValue(0xFFFFFFFF); - - map.put(android.view.MotionEvent.ACTION_DOWN, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED); - map.put(android.view.MotionEvent.ACTION_UP, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED); - map.put(android.view.MotionEvent.ACTION_CANCEL, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED); - map.put(android.view.MotionEvent.ACTION_MOVE, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED); - map.put(android.view.MotionEvent.ACTION_OUTSIDE, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED); - - map.put(android.view.MotionEvent.ACTION_POINTER_DOWN, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED); - map.put(android.view.MotionEvent.ACTION_POINTER_UP, com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED); - - map.put(android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED, com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS); + /** API Level 12: {@link android.view.MotionEvent#ACTION_SCROLL} = {@value} */ + private static final int ACTION_SCROLL = 8; - eventTypeANDROID2NEWT = map; + private static final com.jogamp.newt.event.MouseEvent.PointerType aToolType2PointerType(int aToolType) { + switch( aToolType ) { + case MotionEvent.TOOL_TYPE_FINGER: + return com.jogamp.newt.event.MouseEvent.PointerType.TouchScreen; + case MotionEvent.TOOL_TYPE_MOUSE: + return com.jogamp.newt.event.MouseEvent.PointerType.Mouse; + case MotionEvent.TOOL_TYPE_STYLUS: + case MotionEvent.TOOL_TYPE_ERASER: + return com.jogamp.newt.event.MouseEvent.PointerType.Pen; + default: + return com.jogamp.newt.event.MouseEvent.PointerType.Undefined; + } + } + + private static final short aMotionEventType2Newt(int aType) { + switch( aType ) { + case android.view.MotionEvent.ACTION_DOWN: + case android.view.MotionEvent.ACTION_POINTER_DOWN: + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED; + case android.view.MotionEvent.ACTION_UP: + case android.view.MotionEvent.ACTION_POINTER_UP: + case android.view.MotionEvent.ACTION_CANCEL: + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED; + case android.view.MotionEvent.ACTION_MOVE: + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED; + case android.view.MotionEvent.ACTION_OUTSIDE: + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED; + // case ACTION_HOVER_MOVE + case ACTION_SCROLL: // API Level 12 ! + return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED; + // case ACTION_HOVER_ENTER + // case ACTION_HOVER_EXIT + } + return (short)0; + } + + private static final short aAccessibilityEventType2Newt(int aType) { + switch( aType ) { + case android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED: + return com.jogamp.newt.event.WindowEvent.EVENT_WINDOW_GAINED_FOCUS; + } + return (short)0; } - static final int androidKeyCode2Newt(int androidKeyCode) { + private static final short aKeyEventType2NewtEventType(int androidKeyAction) { + switch(androidKeyAction) { + case android.view.KeyEvent.ACTION_DOWN: + case android.view.KeyEvent.ACTION_MULTIPLE: + return com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED; + case android.view.KeyEvent.ACTION_UP: + return com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED; + } + return (short)0; + } + + private static final short aKeyCode2NewtKeyCode(int androidKeyCode, boolean inclSysKeys) { if(android.view.KeyEvent.KEYCODE_0 <= androidKeyCode && androidKeyCode <= android.view.KeyEvent.KEYCODE_9) { - return com.jogamp.newt.event.KeyEvent.VK_0 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_0 ) ; + return (short) ( com.jogamp.newt.event.KeyEvent.VK_0 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_0 ) ); } if(android.view.KeyEvent.KEYCODE_A <= androidKeyCode && androidKeyCode <= android.view.KeyEvent.KEYCODE_Z) { - return com.jogamp.newt.event.KeyEvent.VK_A + ( androidKeyCode - android.view.KeyEvent.KEYCODE_A ) ; + return (short) ( com.jogamp.newt.event.KeyEvent.VK_A + ( androidKeyCode - android.view.KeyEvent.KEYCODE_A ) ); } if(android.view.KeyEvent.KEYCODE_F1 <= androidKeyCode && androidKeyCode <= android.view.KeyEvent.KEYCODE_F12) { - return com.jogamp.newt.event.KeyEvent.VK_F1 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_F1 ) ; + return (short) ( com.jogamp.newt.event.KeyEvent.VK_F1 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_F1 ) ); } if(android.view.KeyEvent.KEYCODE_NUMPAD_0 <= androidKeyCode && androidKeyCode <= android.view.KeyEvent.KEYCODE_NUMPAD_9) { - return com.jogamp.newt.event.KeyEvent.VK_NUMPAD0 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_NUMPAD_0 ) ; + return (short) ( com.jogamp.newt.event.KeyEvent.VK_NUMPAD0 + ( androidKeyCode - android.view.KeyEvent.KEYCODE_NUMPAD_0 ) ); } switch(androidKeyCode) { case android.view.KeyEvent.KEYCODE_COMMA: return com.jogamp.newt.event.KeyEvent.VK_COMMA; @@ -82,7 +121,7 @@ public class AndroidNewtEventFactory { case android.view.KeyEvent.KEYCODE_TAB: return com.jogamp.newt.event.KeyEvent.VK_TAB; case android.view.KeyEvent.KEYCODE_SPACE: return com.jogamp.newt.event.KeyEvent.VK_SPACE; case android.view.KeyEvent.KEYCODE_ENTER: return com.jogamp.newt.event.KeyEvent.VK_ENTER; - case android.view.KeyEvent.KEYCODE_DEL: return com.jogamp.newt.event.KeyEvent.VK_DELETE; + case android.view.KeyEvent.KEYCODE_DEL: return com.jogamp.newt.event.KeyEvent.VK_BACK_SPACE; case android.view.KeyEvent.KEYCODE_MINUS: return com.jogamp.newt.event.KeyEvent.VK_MINUS; case android.view.KeyEvent.KEYCODE_EQUALS: return com.jogamp.newt.event.KeyEvent.VK_EQUALS; case android.view.KeyEvent.KEYCODE_LEFT_BRACKET: return com.jogamp.newt.event.KeyEvent.VK_LEFT_PARENTHESIS; @@ -95,24 +134,29 @@ public class AndroidNewtEventFactory { // case android.view.KeyEvent.KEYCODE_MUTE: ?? case android.view.KeyEvent.KEYCODE_PAGE_UP: return com.jogamp.newt.event.KeyEvent.VK_PAGE_UP; case android.view.KeyEvent.KEYCODE_PAGE_DOWN: return com.jogamp.newt.event.KeyEvent.VK_PAGE_DOWN; - // case android.view.KeyEvent.KEYCODE_HOME: return com.jogamp.newt.event.KeyEvent.VK_HOME; - // case android.view.KeyEvent.KEYCODE_BACK: return com.jogamp.newt.event.KeyEvent.VK_BACK_SPACE; case android.view.KeyEvent.KEYCODE_ESCAPE: return com.jogamp.newt.event.KeyEvent.VK_ESCAPE; case android.view.KeyEvent.KEYCODE_CTRL_LEFT: return com.jogamp.newt.event.KeyEvent.VK_CONTROL; case android.view.KeyEvent.KEYCODE_CTRL_RIGHT: return com.jogamp.newt.event.KeyEvent.VK_CONTROL; // ?? + case android.view.KeyEvent.KEYCODE_BACK: + if( inclSysKeys ) { + // Note that manual mapping is performed, based on the keyboard state. + // I.e. we map to VK_KEYBOARD_INVISIBLE if keyboard was visible and now becomes invisible! + // Otherwise we map to VK_ESCAPE, and if not consumed by user, the application will be terminated. + return com.jogamp.newt.event.KeyEvent.VK_ESCAPE; + } + break; + case android.view.KeyEvent.KEYCODE_HOME: + if( inclSysKeys ) { + // If not consumed by user, the application will be 'paused', + // i.e. resources (GLEventListener) pulled before surface gets destroyed! + return com.jogamp.newt.event.KeyEvent.VK_HOME; + } + break; } - return 0; + return com.jogamp.newt.event.KeyEvent.VK_UNDEFINED; } - public static final com.jogamp.newt.event.WindowEvent createWindowEvent(android.view.accessibility.AccessibilityEvent event, com.jogamp.newt.Window newtSource) { - int type = eventTypeANDROID2NEWT.get(event.getEventType()); - if(0xFFFFFFFF != type) { - return new com.jogamp.newt.event.WindowEvent(type, ((null==newtSource)?null:(Object)newtSource), event.getEventTime()); - } - return null; // no mapping .. - } - - static final int androidKeyModifiers2Newt(int androidMods) { + private static final int aKeyModifiers2Newt(int androidMods) { int newtMods = 0; if ((androidMods & android.view.KeyEvent.META_SYM_ON) != 0) newtMods |= com.jogamp.newt.event.InputEvent.META_MASK; if ((androidMods & android.view.KeyEvent.META_SHIFT_ON) != 0) newtMods |= com.jogamp.newt.event.InputEvent.SHIFT_MASK; @@ -121,123 +165,200 @@ public class AndroidNewtEventFactory { return newtMods; } - private static final int androidKeyAction2NewtEventType(int androidKeyAction) { - switch(androidKeyAction) { - case android.view.KeyEvent.ACTION_DOWN: - case android.view.KeyEvent.ACTION_MULTIPLE: - return com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED; - case android.view.KeyEvent.ACTION_UP: - return com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED; - default: - return 0; + public static com.jogamp.newt.event.WindowEvent createWindowEvent(android.view.accessibility.AccessibilityEvent event, com.jogamp.newt.Window newtSource) { + final int aType = event.getEventType(); + final short nType = aAccessibilityEventType2Newt(aType); + + if( (short)0 != nType) { + return new com.jogamp.newt.event.WindowEvent(nType, ((null==newtSource)?null:(Object)newtSource), event.getEventTime()); } + return null; // no mapping .. } + - public static final com.jogamp.newt.event.KeyEvent[] createKeyEvents(int keyCode, android.view.KeyEvent event, com.jogamp.newt.Window newtSource) { - final int type = androidKeyAction2NewtEventType(event.getAction()); - if(Window.DEBUG_MOUSE_EVENT) { - System.err.println("createKeyEvent: type 0x"+Integer.toHexString(type)+", keyCode 0x"+Integer.toHexString(keyCode)+", "+event); - } - if(0xFFFFFFFF != type) { - final int newtKeyCode = androidKeyCode2Newt(keyCode); - if(0 != newtKeyCode) { - final Object src = (null==newtSource)?null:(Object)newtSource; - final long unixTime = System.currentTimeMillis() + ( event.getEventTime() - android.os.SystemClock.uptimeMillis() ); - final int newtMods = androidKeyModifiers2Newt(event.getMetaState()); - - final com.jogamp.newt.event.KeyEvent ke1 = new com.jogamp.newt.event.KeyEvent( - type, src, unixTime, newtMods, newtKeyCode, event.getDisplayLabel()); - - if( com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED == type ) { - return new com.jogamp.newt.event.KeyEvent[] { ke1, - new com.jogamp.newt.event.KeyEvent( - com.jogamp.newt.event.KeyEvent.EVENT_KEY_TYPED, - src, unixTime, newtMods, newtKeyCode, event.getDisplayLabel()) }; - } else { - return new com.jogamp.newt.event.KeyEvent[] { ke1 }; - } - } + public static com.jogamp.newt.event.KeyEvent createKeyEvent(android.view.KeyEvent aEvent, com.jogamp.newt.Window newtSource, boolean inclSysKeys) { + final com.jogamp.newt.event.KeyEvent res; + final short newtType = aKeyEventType2NewtEventType(aEvent.getAction()); + if( (short)0 != newtType) { + final short newtKeyCode = aKeyCode2NewtKeyCode(aEvent.getKeyCode(), inclSysKeys); + res = createKeyEventImpl(aEvent, newtType, newtKeyCode, newtSource); + } else { + res = null; + } + if(DEBUG_KEY_EVENT) { + System.err.println("createKeyEvent0: "+aEvent+" -> "+res); + } + return res; + } + + public static com.jogamp.newt.event.KeyEvent createKeyEvent(android.view.KeyEvent aEvent, short newtType, com.jogamp.newt.Window newtSource, boolean inclSysKeys) { + final short newtKeyCode = aKeyCode2NewtKeyCode(aEvent.getKeyCode(), inclSysKeys); + final com.jogamp.newt.event.KeyEvent res = createKeyEventImpl(aEvent, newtType, newtKeyCode, newtSource); + if(DEBUG_KEY_EVENT) { + System.err.println("createKeyEvent1: newtType "+NEWTEvent.toHexString(newtType)+", "+aEvent+" -> "+res); + } + return res; + } + + public static com.jogamp.newt.event.KeyEvent createKeyEvent(android.view.KeyEvent aEvent, short newtKeyCode, short newtType, com.jogamp.newt.Window newtSource) { + final com.jogamp.newt.event.KeyEvent res = createKeyEventImpl(aEvent, newtType, newtKeyCode, newtSource); + if(DEBUG_KEY_EVENT) { + System.err.println("createKeyEvent2: newtType "+NEWTEvent.toHexString(newtType)+", "+aEvent+" -> "+res); + } + return res; + } + + private static com.jogamp.newt.event.KeyEvent createKeyEventImpl(android.view.KeyEvent aEvent, short newtType, short newtKeyCode, com.jogamp.newt.Window newtSource) { + if( (short)0 != newtType && com.jogamp.newt.event.KeyEvent.VK_UNDEFINED != newtKeyCode ) { + final Object src = null==newtSource ? null : newtSource; + final long unixTime = System.currentTimeMillis() + ( aEvent.getEventTime() - android.os.SystemClock.uptimeMillis() ); + final int newtMods = aKeyModifiers2Newt(aEvent.getMetaState()); + + return com.jogamp.newt.event.KeyEvent.create( + newtType, src, unixTime, newtMods, newtKeyCode, newtKeyCode, (char) aEvent.getUnicodeChar()); } return null; } + + private static float maxPressure = 0.7f; // experienced maximum value (Amazon HD = 0.8f) + + /** + * Dynamic calibration of maximum MotionEvent pressure, starting from 0.7f + * <p> + * Specification says no pressure is 0.0f and + * normal pressure is 1.0f, where > 1.0f denominates very high pressure. + * </p> + * <p> + * Some devices exceed this spec, or better, most devices do. + * <ul> + * <li>Asus TF2*: Pressure always > 1.0f</li> + * <li>Amazon HD: Pressure always ≤ 0.8f</li> + * </ul> + * </p> + * + * @return + */ + public static float getMaxPressure() { + return maxPressure; + } + + private final int touchSlop; + public AndroidNewtEventFactory(android.content.Context context, android.os.Handler handler) { + final android.view.ViewConfiguration configuration = android.view.ViewConfiguration.get(context); + touchSlop = configuration.getScaledTouchSlop(); + final int doubleTapSlop = configuration.getScaledDoubleTapSlop(); + if(DEBUG_MOUSE_EVENT) { + System.err.println("AndroidNewtEventFactory scrollSlop (scaled) "+touchSlop); + System.err.println("AndroidNewtEventFactory doubleTapSlop (scaled) "+doubleTapSlop); + } + } + + private static void collectPointerData(MotionEvent e, int idx, final int[] x, final int[] y, final float[] pressure, + final short[] pointerIds, final MouseEvent.PointerType[] pointerTypes) { + x[idx] = (int)e.getX(idx); + y[idx] = (int)e.getY(idx); + pressure[idx] = e.getPressure(idx); + pointerIds[idx] = (short)e.getPointerId(idx); + if( pressure[idx] > maxPressure ) { + maxPressure = pressure[idx]; + } + pointerTypes[idx] = aToolType2PointerType( e.getToolType(idx) ); + if(DEBUG_MOUSE_EVENT) { + System.err.println("createMouseEvent: ptr-data["+idx+"] "+x[idx]+"/"+y[idx]+", pressure "+pressure[idx]+", id "+pointerIds[idx]+", type "+pointerTypes[idx]); + } + } + + public boolean sendPointerEvent(boolean enqueue, boolean wait, boolean setFocusOnDown, boolean isOnTouchEvent, + android.view.MotionEvent event, jogamp.newt.driver.android.WindowDriver newtSource) { + if(DEBUG_MOUSE_EVENT) { + System.err.println("createMouseEvent: isOnTouchEvent "+isOnTouchEvent+", "+event); + } - public static final com.jogamp.newt.event.MouseEvent[] createMouseEvents(android.view.MotionEvent event, com.jogamp.newt.Window newtSource) { - if(Window.DEBUG_MOUSE_EVENT) { - System.err.println("createMouseEvent: "+toString(event)); - } - int type = eventTypeANDROID2NEWT.get(event.getAction()); - if(0xFFFFFFFF != type) { - int rotation = 0; - int clickCount = 1; + if( event.getPressure() > maxPressure ) { + maxPressure = event.getPressure(); + } + + // + // Prefilter Android Event (Gesture, ..) and determine final type + // + final int aType = event.getActionMasked(); + final short nType = aMotionEventType2Newt(aType); + final float rotationScale = touchSlop; + final float[] rotationXYZ = new float[] { 0f, 0f, 0f }; + + if( (short)0 != nType ) { int modifiers = 0; - int[] x = new int[event.getPointerCount()]; - int[] y = new int[event.getPointerCount()]; - float[] pressure = new float[event.getPointerCount()]; - int[] pointers = new int[event.getPointerCount()]; - int index = 0; - while(index < event.getPointerCount()) { - x[index] = (int)event.getX(index); - y[index] = (int)event.getY(index); - pressure[index] = event.getPressure(index); - pointers[index] = event.getPointerId(index); - index++; + // + // Determine SDK 12 SCROLL, newt-button and whether dedicated pointer is pressed + // + final int pIndex; + final short button; + switch( aType ) { + case android.view.MotionEvent.ACTION_POINTER_DOWN: + case android.view.MotionEvent.ACTION_POINTER_UP: { + pIndex = event.getActionIndex(); + final int b = event.getPointerId(pIndex) + 1; // FIXME: Assumption that Pointer-ID starts w/ 0 ! + if( com.jogamp.newt.event.MouseEvent.BUTTON1 <= b && b <= com.jogamp.newt.event.MouseEvent.BUTTON_COUNT ) { + button = (short)b; + } else { + button = com.jogamp.newt.event.MouseEvent.BUTTON1; + } + } + break; + + case ACTION_SCROLL: + if( AndroidVersion.SDK_INT >= 12 ) { // API Level 12 + rotationXYZ[0] = event.getAxisValue(android.view.MotionEvent.AXIS_X) / rotationScale; + rotationXYZ[1] = event.getAxisValue(android.view.MotionEvent.AXIS_Y) / rotationScale; + + if( rotationXYZ[0]*rotationXYZ[0] > rotationXYZ[1]*rotationXYZ[1] ) { + // Horizontal + modifiers |= com.jogamp.newt.event.InputEvent.SHIFT_MASK; + } + if(DEBUG_MOUSE_EVENT) { + System.err.println("createMouseEvent: SDK-12 Scroll "+rotationXYZ[0]+"/"+rotationXYZ[1]+", "+rotationScale+", mods "+modifiers); + } + } + // Fall through intended! + + default: { + pIndex = 0; + button = com.jogamp.newt.event.MouseEvent.BUTTON1; + } + } + final int pCount = event.getPointerCount(); // all + + switch( aType ) { + case android.view.MotionEvent.ACTION_DOWN: + case android.view.MotionEvent.ACTION_POINTER_DOWN: + modifiers |= InputEvent.getButtonMask(button); + if( setFocusOnDown ) { + newtSource.focusChanged(false, true); + } } - if(null!=newtSource) { - if(newtSource.isPointerConfined()) { - modifiers |= InputEvent.CONFINED_MASK; + // + // Collect common data + // + final int[] x = new int[pCount]; + final int[] y = new int[pCount]; + final float[] pressure = new float[pCount]; + final short[] pointerIds = new short[pCount]; + final MouseEvent.PointerType[] pointerTypes = new MouseEvent.PointerType[pCount]; + if( 0 < pCount ) { + if(DEBUG_MOUSE_EVENT) { + System.err.println("createMouseEvent: collect ptr-data [0.."+(pCount-1)+", count "+pCount+", action "+pIndex+"], aType "+aType+", button "+button); } - if(!newtSource.isPointerVisible()) { - modifiers |= InputEvent.INVISIBLE_MASK; + for(int i=0; i < pCount; i++) { + collectPointerData(event, i, x, y, pressure, pointerIds, pointerTypes); } } - - final Object src = (null==newtSource)?null:(Object)newtSource; - final long unixTime = System.currentTimeMillis() + ( event.getEventTime() - android.os.SystemClock.uptimeMillis() ); - final int button = pointers.length==1 ? MouseEvent.BUTTON1 : 0; - - final com.jogamp.newt.event.MouseEvent me1 = new com.jogamp.newt.event.MouseEvent( - type, src, unixTime, - modifiers, x, y, pressure, pointers, clickCount, - button, rotation); - - if(type == com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED) { - return new com.jogamp.newt.event.MouseEvent[] { me1, - new com.jogamp.newt.event.MouseEvent( - com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED, - src, unixTime, modifiers, x, y, pressure, pointers, clickCount, - button, rotation) }; - } else { - return new com.jogamp.newt.event.MouseEvent[] { me1 }; - } - } - return null; // no mapping .. + newtSource.doPointerEvent(enqueue, wait, pointerTypes, nType, modifiers, + pIndex, pointerIds, x, y, pressure, maxPressure, rotationXYZ, rotationScale); + return true; + } + return false; // no mapping .. } - - - public static String toString(android.view.MotionEvent event) { - StringBuilder sb = new StringBuilder(); - int action = event.getAction(); - int actionCode = action & android.view.MotionEvent.ACTION_MASK; - sb.append("ACTION_" ).append(names[actionCode]); - if (actionCode == android.view.MotionEvent.ACTION_POINTER_DOWN - || actionCode == android.view.MotionEvent.ACTION_POINTER_UP) { - sb.append("(pid " ).append( - action >> android.view.MotionEvent.ACTION_POINTER_ID_SHIFT); - sb.append(")" ); - } - sb.append("[" ); - for (int i = 0; i < event.getPointerCount(); i++) { - sb.append("#" ).append(i); - sb.append("(pid " ).append(event.getPointerId(i)); - sb.append(")=" ).append((int) event.getX(i)); - sb.append("," ).append((int) event.getY(i)); - if (i + 1 < event.getPointerCount()) - sb.append(";" ); - } - sb.append("]" ); - return sb.toString(); - } } diff --git a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java new file mode 100644 index 000000000..5a4743f73 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java @@ -0,0 +1,50 @@ +package jogamp.newt.driver.android.event; + +import jogamp.newt.driver.android.WindowDriver; +import android.view.View; + +public class AndroidNewtEventTranslator implements View.OnKeyListener, View.OnTouchListener, View.OnFocusChangeListener, View.OnGenericMotionListener { + private final WindowDriver newtWindow; + private final AndroidNewtEventFactory factory; + + public AndroidNewtEventTranslator(WindowDriver newtWindow, android.content.Context context, android.os.Handler handler) { + this.newtWindow = newtWindow; + this.factory = new AndroidNewtEventFactory(context, handler); + } + + private final boolean processTouchMotionEvents(View v, android.view.MotionEvent event, boolean isOnTouchEvent) { + final boolean eventSent = factory.sendPointerEvent(true /*enqueue*/, false /*wait*/, true /*setFocusOnDown*/, + isOnTouchEvent, event, newtWindow); + if( eventSent ) { + try { Thread.sleep((long) (100.0F/3.0F)); } // 33 ms - FIXME ?? + catch(InterruptedException e) { } + return true; // consumed/handled, further interest in events + } + return false; // no mapping, no further interest in the event! + } + + @Override + public boolean onTouch(View v, android.view.MotionEvent event) { + return processTouchMotionEvents(v, event, true); + } + + @Override + public boolean onGenericMotion(View v, android.view.MotionEvent event) { + return processTouchMotionEvents(v, event, false); + } + + @Override + public boolean onKey(View v, int keyCode, android.view.KeyEvent event) { + final com.jogamp.newt.event.KeyEvent newtEvent = AndroidNewtEventFactory.createKeyEvent(event, newtWindow, false /* no system keys */); + if(null != newtEvent) { + newtWindow.enqueueEvent(false, newtEvent); + return true; + } + return false; + } + + @Override + public void onFocusChange(View v, boolean hasFocus) { + newtWindow.focusChanged(false, hasFocus); + } +} diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java b/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java index 5a49dd57c..f7722c91c 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java @@ -1,21 +1,22 @@ /* * 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 @@ -28,12 +29,13 @@ * 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.driver.awt; import java.awt.Canvas; +import java.awt.Graphics; import java.awt.GraphicsDevice; import java.awt.GraphicsConfiguration; import java.lang.reflect.Method; @@ -45,19 +47,23 @@ import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.CapabilitiesChooser; import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.GraphicsConfigurationFactory; +import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowException; - +import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.VisualIDHolder; import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; import com.jogamp.nativewindow.awt.AWTGraphicsDevice; import com.jogamp.nativewindow.awt.AWTGraphicsScreen; +import com.jogamp.nativewindow.awt.JAWTWindow; import com.jogamp.newt.Window; +@SuppressWarnings("serial") public class AWTCanvas extends Canvas { private GraphicsDevice device; private GraphicsConfiguration chosen; private AWTGraphicsConfiguration awtConfig; - + private volatile JAWTWindow jawtWindow=null; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle private CapabilitiesChooser chooser=null; private CapabilitiesImmutable capabilities; @@ -77,12 +83,31 @@ public class AWTCanvas extends Canvas { return awtConfig; } + /** + * Overridden from Canvas to prevent the AWT's clearing of the + * canvas from interfering with the OpenGL rendering. + */ + @Override + public void update(Graphics g) { + // paint(g); + } + + /** Overridden to cause OpenGL rendering to be performed during + repaint cycles. Subclasses which override this method must call + super.paint() in their paint() method in order to function + properly. + */ + @Override + public void paint(Graphics g) { + } + public boolean hasDeviceChanged() { boolean res = displayConfigChanged; displayConfigChanged=false; return res; } + @Override public void addNotify() { /** @@ -95,8 +120,7 @@ public class AWTCanvas extends Canvas { */ awtConfig = chooseGraphicsConfiguration(capabilities, capabilities, chooser, device); if(Window.DEBUG_IMPLEMENTATION) { - Exception e = new Exception("Info: Created Config: "+awtConfig); - e.printStackTrace(); + System.err.println(getThreadName()+": AWTCanvas.addNotify.0: Created Config: "+awtConfig); } if(null==awtConfig) { throw new NativeWindowException("Error: NULL AWTGraphicsConfiguration"); @@ -112,12 +136,32 @@ public class AWTCanvas extends Canvas { // after native peer is valid: Windows disableBackgroundErase(); + { + jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); + // trigger initialization cycle + jawtWindow.lockSurface(); + jawtWindow.unlockSurface(); + } + GraphicsConfiguration gc = super.getGraphicsConfiguration(); if(null!=gc) { device = gc.getDevice(); } + if(Window.DEBUG_IMPLEMENTATION) { + System.err.println(getThreadName()+": AWTCanvas.addNotify.X"); + } + } + + public NativeWindow getNativeWindow() { + final JAWTWindow _jawtWindow = jawtWindow; + return (null != _jawtWindow) ? _jawtWindow : null; + } + + public boolean isOffscreenLayerSurfaceEnabled() { + return null != jawtWindow ? jawtWindow.isOffscreenLayerSurfaceEnabled() : false; } + @Override public void removeNotify() { try { dispose(); @@ -127,6 +171,13 @@ public class AWTCanvas extends Canvas { } private void dispose() { + if( null != jawtWindow ) { + jawtWindow.destroy(); + if(Window.DEBUG_IMPLEMENTATION) { + System.err.println(getThreadName()+": AWTCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post JAWTWindow: "+jawtWindow); + } + jawtWindow=null; + } if(null != awtConfig) { AbstractGraphicsDevice adevice = awtConfig.getNativeGraphicsConfiguration().getScreen().getDevice(); String adeviceMsg=null; @@ -135,21 +186,24 @@ public class AWTCanvas extends Canvas { } boolean closed = adevice.close(); if(Window.DEBUG_IMPLEMENTATION) { - System.err.println("AWTCanvas.dispose(): closed GraphicsDevice: "+adeviceMsg+", result: "+closed); + System.err.println(getThreadName()+": AWTCanvas.dispose(): closed GraphicsDevice: "+adeviceMsg+", result: "+closed); } } } + private String getThreadName() { return Thread.currentThread().getName(); } + /** * Overridden to choose a GraphicsConfiguration on a parent container's * GraphicsDevice because both devices */ + @Override public GraphicsConfiguration getGraphicsConfiguration() { /* * Workaround for problems with Xinerama and java.awt.Component.checkGD * when adding to a container on a different graphics device than the * one that this Canvas is associated with. - * + * * GC will be null unless: * - A native peer has assigned it. This means we have a native * peer, and are already comitted to a graphics configuration. @@ -163,7 +217,7 @@ public class AWTCanvas extends Canvas { * chosen is only non-null on platforms where the GLDrawableFactory * returns a non-null GraphicsConfiguration (in the GLCanvas * constructor). - * + * * if gc is from this Canvas' native peer then it should equal chosen, * otherwise it is from an ancestor component that this Canvas is being * added to, and we go into this block. @@ -173,21 +227,21 @@ public class AWTCanvas extends Canvas { * Check for compatibility with gc. If they differ by only the * device then return a new GCconfig with the super-class' GDevice * (and presumably the same visual ID in Xinerama). - * + * */ if (!chosen.getDevice().getIDstring().equals(gc.getDevice().getIDstring())) { /* * Here we select a GraphicsConfiguration on the alternate * device that is presumably identical to the chosen * configuration, but on the other device. - * + * * Should really check to ensure that we select a configuration * with the same X visual ID for Xinerama screens, otherwise the * GLDrawable may have the wrong visual ID (I don't think this * ever gets updated). May need to add a method to * X11GLDrawableFactory to do this in a platform specific * manner. - * + * * However, on platforms where we can actually get into this * block, both devices should have the same visual list, and the * same configuration should be selected here. @@ -214,7 +268,7 @@ public class AWTCanvas extends Canvas { chosen = compatible; if( !config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities())) { displayConfigChanged=true; - } + } awtConfig = config; } } @@ -224,7 +278,7 @@ public class AWTCanvas extends Canvas { * return the GC that was selected in the constructor (and might * cause an exception in Component.checkGD when adding to a * container, but in this case that would be the desired behavior). - * + * */ return chosen; } else if (gc == null) { @@ -248,13 +302,13 @@ public class AWTCanvas extends Canvas { CapabilitiesImmutable capsRequested, CapabilitiesChooser chooser, GraphicsDevice device) { - final AbstractGraphicsScreen aScreen = null != device ? + final AbstractGraphicsScreen aScreen = null != device ? AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT): AWTGraphicsScreen.createDefault(); AWTGraphicsConfiguration config = (AWTGraphicsConfiguration) - GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class).chooseGraphicsConfiguration(capsChosen, + GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, capsChosen.getClass()).chooseGraphicsConfiguration(capsChosen, capsRequested, - chooser, aScreen); + chooser, aScreen, VisualIDHolder.VID_UNDEFINED); if (config == null) { throw new NativeWindowException("Error: Couldn't fetch AWTGraphicsConfiguration"); } @@ -272,10 +326,11 @@ public class AWTCanvas extends Canvas { private void disableBackgroundErase() { if (!disableBackgroundEraseInitialized) { try { - AccessController.doPrivileged(new PrivilegedAction() { + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override public Object run() { try { - Class clazz = getToolkit().getClass(); + Class<?> clazz = getToolkit().getClass(); while (clazz != null && disableBackgroundEraseMethod == null) { try { disableBackgroundEraseMethod = diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java index 942651187..4a7193306 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java @@ -28,72 +28,297 @@ package jogamp.newt.driver.awt; -import javax.media.opengl.Threading; +import java.awt.EventQueue; +import javax.media.nativewindow.NativeWindowException; + +import com.jogamp.common.util.RunnableTask; +import com.jogamp.common.util.awt.AWTEDTExecutor; import com.jogamp.newt.util.EDTUtil; + import jogamp.newt.Debug; public class AWTEDTUtil implements EDTUtil { public static final boolean DEBUG = Debug.debug("EDT"); - private static AWTEDTUtil singletonMainThread = new AWTEDTUtil(); // one singleton MainThread - - public static AWTEDTUtil getSingleton() { - return singletonMainThread; - } + private final Object edtLock = new Object(); // locking the EDT start/stop state + private final ThreadGroup threadGroup; + private final String name; + private final Runnable dispatchMessages; + private NEDT nedt = null; + private int start_iter=0; + private static long pollPeriod = EDTUtil.defaultEDTPollPeriod; - AWTEDTUtil() { - // package private access .. + public AWTEDTUtil(ThreadGroup tg, String name, Runnable dispatchMessages) { + this.threadGroup = tg; + this.name=Thread.currentThread().getName()+"-"+name+"-EDT-"; + this.dispatchMessages=dispatchMessages; + this.nedt = new NEDT(threadGroup, name); + this.nedt.setDaemon(true); // don't stop JVM from shutdown .. } + @Override final public long getPollPeriod() { - return 0; + return pollPeriod; } + @Override final public void setPollPeriod(long ms) { - // nop + pollPeriod = ms; } - - final public void reset() { - // nop AWT is always running + + @Override + public final boolean start() throws IllegalStateException { + synchronized(edtLock) { + if( nedt.isRunning() ) { + throw new IllegalStateException("EDT still running and not subject to stop. Curr "+Thread.currentThread().getName()+", NEDT "+nedt.getName()+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", on AWT-EDT "+EventQueue.isDispatchThread()); + } + if(DEBUG) { + System.err.println(Thread.currentThread()+": AWT-EDT reset - edt: "+nedt); + } + if( nedt.getState() != Thread.State.NEW ) { + nedt = new NEDT(threadGroup, name); + nedt.setDaemon(true); // don't stop JVM from shutdown .. + } + startImpl(); + } + return invoke(true, nullTask); } - final public void start() { - // nop AWT is always running + private final void startImpl() { + if(nedt.isAlive()) { + throw new RuntimeException("AWT-EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", edt: "+nedt); + } + start_iter++; + nedt.setName(name+start_iter); + if(DEBUG) { + System.err.println(Thread.currentThread()+": AWT-EDT START - edt: "+nedt); + // Thread.dumpStack(); + } + nedt.start(); } + @Override final public boolean isCurrentThreadEDT() { - return Threading.isToolkitThread(); + return EventQueue.isDispatchThread(); } + @Override + public final boolean isCurrentThreadNEDT() { + return nedt == Thread.currentThread(); + } + + @Override + public final boolean isCurrentThreadEDTorNEDT() { + return EventQueue.isDispatchThread() || nedt == Thread.currentThread(); + } + + @Override final public boolean isRunning() { - return true; // AWT is always running + return nedt.isRunning() ; } - final public void invokeStop(Runnable r) { - invoke(true, r); // AWT is always running + @Override + public final boolean invokeStop(boolean wait, Runnable task) { + return invokeImpl(wait, task, true); } - final public void invoke(boolean wait, Runnable r) { - if(r == null) { - return; + @Override + public final boolean invoke(boolean wait, Runnable task) { + return invokeImpl(wait, task, false); + } + + private static Runnable nullTask = new Runnable() { + @Override + public void run() { } + }; + + private final boolean invokeImpl(boolean wait, Runnable task, boolean stop) { + Throwable throwable = null; + RunnableTask rTask = null; + final Object rTaskLock = new Object(); + synchronized(rTaskLock) { // lock the optional task execution + synchronized(edtLock) { // lock the EDT status + if( nedt.shouldStop ) { + // drop task .. + System.err.println(Thread.currentThread()+": Warning: AWT-EDT about (1) to stop, won't enqueue new task: "+nedt); + if(DEBUG) { + Thread.dumpStack(); + } + return false; + } + if( isCurrentThreadEDT() ) { + if(null != task) { + task.run(); + } + wait = false; // running in same thread (EDT) -> no wait + if(stop) { + nedt.shouldStop = true; + } + } else { + if( !nedt.isRunning ) { + if( null != task ) { + if( stop ) { + System.err.println(Thread.currentThread()+": Warning: AWT-EDT is about (3) to stop and stopped already, dropping task. NEDT "+nedt); + } else { + System.err.println(Thread.currentThread()+": Warning: AWT-EDT is not running, dropping task. NEDT "+nedt); + } + if(DEBUG) { + Thread.dumpStack(); + } + } + return false; + } else if( stop ) { + if(DEBUG) { + System.err.println(Thread.currentThread()+": AWT-EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop); + } + synchronized(nedt.sync) { + nedt.shouldStop = true; + nedt.sync.notifyAll(); // stop immediate if waiting (poll freq) + } + } + + if(null != task) { + rTask = new RunnableTask(task, + wait ? rTaskLock : null, + true /* always catch and report Exceptions, don't disturb EDT */, + wait ? null : System.err); + AWTEDTExecutor.singleton.invoke(false, rTask); + } + } + } + if( wait ) { + try { + rTaskLock.wait(); // free lock, allow execution of rTask + } catch (InterruptedException ie) { + throwable = ie; + } + if(null==throwable) { + throwable = rTask.getThrowable(); + } + if(null!=throwable) { + if(throwable instanceof NativeWindowException) { + throw (NativeWindowException)throwable; + } + throw new RuntimeException(throwable); + } + } + return true; } - - Threading.invoke(wait, r, null); } - final public void waitUntilIdle() { - // wait until previous events are processed, at least .. + @Override + final public boolean waitUntilIdle() { + final NEDT _edt; + synchronized(edtLock) { + _edt = nedt; + } + if(!_edt.isRunning || _edt == Thread.currentThread() || EventQueue.isDispatchThread()) { + return false; + } try { - Threading.invoke(true, new Runnable() { + AWTEDTExecutor.singleton.invoke(true, new Runnable() { + @Override public void run() { } - }, null); + }); } catch (Exception e) { } + return true; } - final public void waitUntilStopped() { - // nop: AWT is always running + @Override + final public boolean waitUntilStopped() { + synchronized(edtLock) { + if( nedt.isRunning && nedt != Thread.currentThread() && !EventQueue.isDispatchThread() ) { + while( nedt.isRunning ) { + try { + edtLock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return true; + } else { + return false; + } + } } + + class NEDT extends Thread { + volatile boolean shouldStop = false; + volatile boolean isRunning = false; + Object sync = new Object(); + + public NEDT(ThreadGroup tg, String name) { + super(tg, name); + } + + final public boolean isRunning() { + return isRunning && !shouldStop; + } + + @Override + final public void start() throws IllegalThreadStateException { + isRunning = true; + super.start(); + } + + /** + * Utilizing locking only on tasks and its execution, + * not for event dispatching. + */ + @Override + final public void run() { + if(DEBUG) { + System.err.println(getName()+": AWT-EDT run() START "+ getName()); + } + RuntimeException error = null; + try { + do { + // event dispatch + if(!shouldStop) { + // EDT invoke thread is AWT-EDT, + // hence dispatching is required to run on AWT-EDT as well. + // Otherwise a deadlock may happen due to dispatched event's + // triggering a locking action. + AWTEDTExecutor.singleton.invoke(true, dispatchMessages); + } + // wait + synchronized(sync) { + if(!shouldStop) { + try { + sync.wait(pollPeriod); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } while(!shouldStop) ; + } catch (Throwable t) { + // handle errors .. + shouldStop = true; + if(t instanceof RuntimeException) { + error = (RuntimeException) t; + } else { + error = new RuntimeException("Within AWT-EDT", t); + } + } finally { + if(DEBUG) { + System.err.println(getName()+": AWT-EDT run() END "+ getName()+", "+error); + } + synchronized(edtLock) { + isRunning = false; + edtLock.notifyAll(); + } + if(DEBUG) { + System.err.println(getName()+": AWT-EDT run() EXIT "+ getName()+", exception: "+error); + } + if(null!=error) { + throw error; + } + } // finally + } // run() + } // EventDispatchThread + } diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTScreen.java b/src/newt/classes/jogamp/newt/driver/awt/AWTScreen.java deleted file mode 100644 index a3c0281b1..000000000 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTScreen.java +++ /dev/null @@ -1,81 +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 jogamp.newt.driver.awt; - -import java.awt.DisplayMode; - -import jogamp.newt.ScreenImpl; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; - -import com.jogamp.nativewindow.awt.AWTGraphicsDevice; -import com.jogamp.nativewindow.awt.AWTGraphicsScreen; - -public class AWTScreen extends ScreenImpl { - public AWTScreen() { - } - - protected void createNativeImpl() { - aScreen = new AWTGraphicsScreen((AWTGraphicsDevice)display.getGraphicsDevice()); - } - - protected void setAWTGraphicsScreen(AWTGraphicsScreen s) { - aScreen = s; - } - - /** - * Used by AWTWindow .. - */ - @Override - protected void updateVirtualScreenOriginAndSize() { - super.updateVirtualScreenOriginAndSize(); - } - - protected void closeNativeImpl() { } - - protected int validateScreenIndex(int idx) { - return idx; // pass through ... - } - - protected void getVirtualScreenOriginAndSize(Point virtualOrigin, Dimension virtualSize) { - final DisplayMode mode = ((AWTGraphicsDevice)getDisplay().getGraphicsDevice()).getGraphicsDevice().getDisplayMode(); - if(null != mode) { - virtualOrigin.setX(0); - virtualOrigin.setY(0); - virtualSize.setWidth(mode.getWidth()); - virtualSize.setHeight(mode.getHeight()); - } - } - -} diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTWindow.java b/src/newt/classes/jogamp/newt/driver/awt/AWTWindow.java deleted file mode 100644 index 2b2fed545..000000000 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTWindow.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * 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.driver.awt; - -import java.awt.BorderLayout; -import java.awt.Container; -import java.awt.Frame; -import java.awt.Insets; - -import javax.media.nativewindow.NativeWindowException; -import javax.media.nativewindow.util.Point; - -import jogamp.newt.WindowImpl; - -import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; -import com.jogamp.nativewindow.awt.AWTGraphicsDevice; -import com.jogamp.nativewindow.awt.AWTGraphicsScreen; -import com.jogamp.newt.event.awt.AWTKeyAdapter; -import com.jogamp.newt.event.awt.AWTMouseAdapter; -import com.jogamp.newt.event.awt.AWTWindowAdapter; - -/** An implementation of the Newt Window class built using the - AWT. This is provided for convenience of porting to platforms - supporting Java SE. */ - -public class AWTWindow extends WindowImpl { - - public AWTWindow() { - this(null); - } - - public static Class<?>[] getCustomConstructorArgumentTypes() { - return new Class<?>[] { Container.class } ; - } - - public AWTWindow(Container container) { - super(); - this.container = container; - if(container instanceof Frame) { - frame = (Frame) container; - } - } - - private boolean owningFrame; - private Container container = null; - private Frame frame = null; // same instance as container, just for impl. convenience - private AWTCanvas canvas; - - protected void requestFocusImpl(boolean reparented) { - container.requestFocus(); - } - - @Override - protected void setTitleImpl(final String title) { - if (frame != null) { - frame.setTitle(title); - } - } - - protected void createNativeImpl() { - if(0!=getParentWindowHandle()) { - throw new RuntimeException("Window parenting not supported in AWT, use AWTWindow(Frame) cstr for wrapping instead"); - } - - if(null==container) { - frame = new Frame(); - container = frame; - owningFrame=true; - } else { - owningFrame=false; - defineSize(container.getWidth(), container.getHeight()); - definePosition(container.getX(), container.getY()); - } - if(null!=frame) { - frame.setTitle(getTitle()); - } - container.setLayout(new BorderLayout()); - canvas = new AWTCanvas(capsRequested, AWTWindow.this.capabilitiesChooser); - - addWindowListener(new LocalWindowListener()); - - new AWTMouseAdapter(this).addTo(canvas); // fwd all AWT Mouse events to here - new AWTKeyAdapter(this).addTo(canvas); // fwd all AWT Key events to here - - // canvas.addComponentListener(listener); - container.add(canvas, BorderLayout.CENTER); - container.setSize(getWidth(), getHeight()); - container.setLocation(getX(), getY()); - new AWTWindowAdapter(this).addTo(container); // fwd all AWT Window events to here - - reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureFlags(FLAG_CHANGE_VISIBILITY | FLAG_CHANGE_DECORATION, true)); - // throws exception if failed .. - - setWindowHandle(1); // just a marker .. - } - - protected void closeNativeImpl() { - setWindowHandle(0); // just a marker .. - if(null!=container) { - container.setVisible(false); - container.remove(canvas); - container.setEnabled(false); - canvas.setEnabled(false); - } - if(owningFrame && null!=frame) { - frame.dispose(); - owningFrame=false; - frame = null; - } - } - - @Override - public boolean hasDeviceChanged() { - boolean res = canvas.hasDeviceChanged(); - if(res) { - final AWTGraphicsConfiguration cfg = canvas.getAWTGraphicsConfiguration(); - if (null == cfg) { - throw new NativeWindowException("Error Device change null GraphicsConfiguration: "+this); - } - setGraphicsConfiguration(cfg); - - // propagate new info .. - ((AWTScreen)getScreen()).setAWTGraphicsScreen((AWTGraphicsScreen)cfg.getScreen()); - ((AWTDisplay)getScreen().getDisplay()).setAWTGraphicsDevice((AWTGraphicsDevice)cfg.getScreen().getDevice()); - - ((AWTScreen)getScreen()).updateVirtualScreenOriginAndSize(); - } - return res; - } - - protected void updateInsetsImpl(javax.media.nativewindow.util.Insets insets) { - Insets contInsets = container.getInsets(); - insets.setLeftWidth(contInsets.left); - insets.setRightWidth(contInsets.right); - insets.setTopHeight(contInsets.top); - insets.setBottomHeight(contInsets.bottom); - } - - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { - if(0 != ( FLAG_CHANGE_DECORATION & flags) && null!=frame) { - if(!container.isDisplayable()) { - frame.setUndecorated(isUndecorated()); - } else { - if(DEBUG_IMPLEMENTATION) { - System.err.println("AWTWindow can't undecorate already created frame"); - } - } - } - - if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { - container.setVisible(0 != ( FLAG_IS_VISIBLE & flags)); - } - - container.setLocation(x, y); - Insets insets = container.getInsets(); - container.setSize(width + insets.left + insets.right, - height + insets.top + insets.bottom); - - if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { - if( 0 != ( FLAG_IS_VISIBLE & flags ) ) { - if( !hasDeviceChanged() ) { - // oops ?? - final AWTGraphicsConfiguration cfg = canvas.getAWTGraphicsConfiguration(); - if(null == cfg) { - throw new NativeWindowException("Error: !hasDeviceChanged && null == GraphicsConfiguration: "+this); - } - setGraphicsConfiguration(cfg); - } - } - visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); - } - - return true; - } - - protected Point getLocationOnScreenImpl(int x, int y) { - java.awt.Point ap = canvas.getLocationOnScreen(); - ap.translate(x, y); - return new Point((int)(ap.getX()+0.5),(int)(ap.getY()+0.5)); - } - - @Override - public Object getWrappedWindow() { - return canvas; - } - - class LocalWindowListener extends com.jogamp.newt.event.WindowAdapter { - @Override - public void windowMoved(com.jogamp.newt.event.WindowEvent e) { - if(null!=container) { - definePosition(container.getX(), container.getY()); - } - } - @Override - public void windowResized(com.jogamp.newt.event.WindowEvent e) { - if(null!=canvas) { - defineSize(canvas.getWidth(), canvas.getHeight()); - } - } - } -} diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTDisplay.java b/src/newt/classes/jogamp/newt/driver/awt/DisplayDriver.java index 166da5c1c..d9a4a48e5 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTDisplay.java +++ b/src/newt/classes/jogamp/newt/driver/awt/DisplayDriver.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,20 +29,24 @@ * 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.driver.awt; +import javax.media.nativewindow.AbstractGraphicsDevice; + import com.jogamp.nativewindow.awt.AWTGraphicsDevice; import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.util.EDTUtil; import jogamp.newt.DisplayImpl; -public class AWTDisplay extends DisplayImpl { - public AWTDisplay() { +public class DisplayDriver extends DisplayImpl { + public DisplayDriver() { } + @Override protected void createNativeImpl() { aDevice = AWTGraphicsDevice.createDefault(); } @@ -51,18 +55,26 @@ public class AWTDisplay extends DisplayImpl { aDevice = d; } - protected void closeNativeImpl() { } - @Override - protected void createEDTUtil() { + protected EDTUtil createEDTUtil() { + final EDTUtil def; if(NewtFactory.useEDT()) { - edtUtil = AWTEDTUtil.getSingleton(); + def = new AWTEDTUtil(Thread.currentThread().getThreadGroup(), "AWTDisplay-"+getFQName(), dispatchMessagesRunnable); if(DEBUG) { - System.err.println("AWTDisplay.createNative("+getFQName()+") Create EDTUtil: "+edtUtil.getClass().getName()); + System.err.println("Display.createNative("+getFQName()+") Create EDTUtil: "+def.getClass().getName()); } + } else { + def = null; } + return def; } + @Override + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { + aDevice.close(); + } + + @Override protected void dispatchMessagesNative() { /* nop */ } } diff --git a/src/newt/classes/jogamp/newt/driver/awt/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/awt/ScreenDriver.java new file mode 100644 index 000000000..a4356707e --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/awt/ScreenDriver.java @@ -0,0 +1,131 @@ +/* + * 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 jogamp.newt.driver.awt; + +import java.awt.DisplayMode; +import java.awt.GraphicsDevice; + +import jogamp.newt.MonitorModeProps.Cache; +import jogamp.newt.MonitorModeProps; +import jogamp.newt.ScreenImpl; + +import com.jogamp.nativewindow.awt.AWTGraphicsDevice; +import com.jogamp.nativewindow.awt.AWTGraphicsScreen; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +public class ScreenDriver extends ScreenImpl { + public ScreenDriver() { + } + + @Override + protected void createNativeImpl() { + aScreen = new AWTGraphicsScreen((AWTGraphicsDevice)display.getGraphicsDevice()); + } + + protected void setAWTGraphicsScreen(AWTGraphicsScreen s) { + aScreen = s; + } + + /** + * Used by AWTWindow .. + */ + @Override + protected void updateVirtualScreenOriginAndSize() { + super.updateVirtualScreenOriginAndSize(); + } + + @Override + protected void closeNativeImpl() { } + + @Override + protected int validateScreenIndex(int idx) { + return idx; // pass through ... + } + + private static MonitorMode getModeProps(Cache cache, DisplayMode mode) { + int rate = mode.getRefreshRate(); + if( DisplayMode.REFRESH_RATE_UNKNOWN == rate ) { + rate = ScreenImpl.default_sm_rate; + } + int bpp = mode.getBitDepth(); + if( DisplayMode.BIT_DEPTH_MULTI == bpp ) { + bpp= ScreenImpl.default_sm_bpp; + } + int[] props = new int[ MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL ]; + int i = 0; + props[i++] = MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL; + props[i++] = mode.getWidth(); + props[i++] = mode.getHeight(); + props[i++] = bpp; + props[i++] = rate * 100; + props[i++] = 0; // flags + props[i++] = 0; // mode_idx + props[i++] = 0; // rotation + return MonitorModeProps.streamInMonitorMode(null, cache, props, 0); + } + + @Override + protected void collectNativeMonitorModesAndDevicesImpl(Cache cache) { + final GraphicsDevice awtGD = ((AWTGraphicsDevice)getDisplay().getGraphicsDevice()).getGraphicsDevice(); + final DisplayMode[] awtModes = awtGD.getDisplayModes(); + for(int i=0; i<awtModes.length; i++) { + getModeProps(cache, awtModes[i]); + } + final MonitorMode currentMode = getModeProps(cache, awtGD.getDisplayMode()); + + int[] props = new int[MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES - 1 - MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES]; + int i = 0; + props[i++] = props.length; + props[i++] = 0; // crt_idx + props[i++] = ScreenImpl.default_sm_widthmm; // FIXME + props[i++] = ScreenImpl.default_sm_heightmm; // FIXME + props[i++] = 0; // rotated viewport x + props[i++] = 0; // rotated viewport y + props[i++] = currentMode.getRotatedWidth(); // rotated viewport width + props[i++] = currentMode.getRotatedHeight(); // rotated viewport height + MonitorModeProps.streamInMonitorDevice(null, cache, this, cache.monitorModes, currentMode, props, 0); + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(MonitorDevice monitor) { + return null; + } + + @Override + protected boolean setCurrentMonitorModeImpl(MonitorDevice monitor, MonitorMode mode) { + return false; + } + +} diff --git a/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java new file mode 100644 index 000000000..9854524d9 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java @@ -0,0 +1,329 @@ +/* + * 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.driver.awt; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Insets; + +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.util.Point; + +import jogamp.nativewindow.awt.AWTMisc; +import jogamp.newt.WindowImpl; + +import com.jogamp.common.os.Platform; +import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; +import com.jogamp.nativewindow.awt.AWTGraphicsDevice; +import com.jogamp.nativewindow.awt.AWTGraphicsScreen; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.event.WindowUpdateEvent; +import com.jogamp.newt.event.awt.AWTKeyAdapter; +import com.jogamp.newt.event.awt.AWTMouseAdapter; +import com.jogamp.newt.event.awt.AWTWindowAdapter; + +/** An implementation of the Newt Window class built using the + AWT. This is provided for convenience of porting to platforms + supporting Java SE. */ + +public class WindowDriver extends WindowImpl { + + public WindowDriver() { + this(null); + } + + public static Class<?>[] getCustomConstructorArgumentTypes() { + return new Class<?>[] { Container.class } ; + } + + public WindowDriver(Container container) { + super(); + this.awtContainer = container; + if(container instanceof Frame) { + awtFrame = (Frame) container; + } + } + + private boolean owningFrame; + private Container awtContainer = null; + /** same instance as container, just for impl. convenience */ + private Frame awtFrame = null; + private AWTCanvas awtCanvas; + + @Override + protected void requestFocusImpl(boolean reparented) { + awtContainer.requestFocus(); + } + + @Override + protected void setTitleImpl(final String title) { + if (awtFrame != null) { + awtFrame.setTitle(title); + } + } + + @Override + protected void createNativeImpl() { + if(0!=getParentWindowHandle()) { + throw new RuntimeException("Window parenting not supported in AWT, use AWTWindow(Frame) cstr for wrapping instead"); + } + + if(null==awtContainer) { + awtFrame = new Frame(); + awtContainer = awtFrame; + owningFrame=true; + } else { + owningFrame=false; + defineSize(awtContainer.getWidth(), awtContainer.getHeight()); + definePosition(awtContainer.getX(), awtContainer.getY()); + } + if(null!=awtFrame) { + awtFrame.setTitle(getTitle()); + } + awtContainer.setLayout(new BorderLayout()); + + if( null == awtCanvas ) { + awtCanvas = new AWTCanvas(capsRequested, WindowDriver.this.capabilitiesChooser); + + // canvas.addComponentListener(listener); + awtContainer.add(awtCanvas, BorderLayout.CENTER); + + // via EDT .. + new AWTMouseAdapter(this).addTo(awtCanvas); // fwd all AWT Mouse events to here + new AWTKeyAdapter(this).addTo(awtCanvas); // fwd all AWT Key events to here + + // direct w/o EDT + new AWTWindowAdapter(new LocalWindowListener(), this).addTo(awtCanvas); // fwd all AWT Window events to here + } + + reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureFlags(FLAG_CHANGE_VISIBILITY | FLAG_CHANGE_DECORATION, true)); + // throws exception if failed .. + + final NativeWindow nw = awtCanvas.getNativeWindow(); + if( null != nw ) { + setGraphicsConfiguration( awtCanvas.getAWTGraphicsConfiguration() ); + setWindowHandle( nw.getWindowHandle() ); + } + } + + @Override + protected void closeNativeImpl() { + setWindowHandle(0); + if(null!=awtContainer) { + awtContainer.setVisible(false); + awtContainer.remove(awtCanvas); + awtContainer.setEnabled(false); + awtCanvas.setEnabled(false); + } + if(owningFrame && null!=awtFrame) { + awtFrame.dispose(); + owningFrame=false; + } + awtCanvas = null; + awtFrame = null; + awtContainer = null; + } + + @Override + public boolean hasDeviceChanged() { + boolean res = awtCanvas.hasDeviceChanged(); + if(res) { + final AWTGraphicsConfiguration cfg = awtCanvas.getAWTGraphicsConfiguration(); + if (null == cfg) { + throw new NativeWindowException("Error Device change null GraphicsConfiguration: "+this); + } + setGraphicsConfiguration(cfg); + + // propagate new info .. + ((ScreenDriver)getScreen()).setAWTGraphicsScreen((AWTGraphicsScreen)cfg.getScreen()); + ((DisplayDriver)getScreen().getDisplay()).setAWTGraphicsDevice((AWTGraphicsDevice)cfg.getScreen().getDevice()); + + ((ScreenDriver)getScreen()).updateVirtualScreenOriginAndSize(); + } + return res; + } + + @Override + protected void updateInsetsImpl(javax.media.nativewindow.util.Insets insets) { + final Insets contInsets = awtContainer.getInsets(); + insets.set(contInsets.left, contInsets.right, contInsets.top, contInsets.bottom); + } + + private void setCanvasSizeImpl(int width, int height) { + final Dimension szClient = new Dimension(width, height); + final java.awt.Window awtWindow = AWTMisc.getWindow(awtCanvas); + final Container c= null != awtWindow ? awtWindow : awtContainer; + awtCanvas.setMinimumSize(szClient); + awtCanvas.setPreferredSize(szClient); + if(DEBUG_IMPLEMENTATION) { + final Insets insets = c.getInsets(); + final Dimension szContainer = new Dimension(width + insets.left + insets.right, + height + insets.top + insets.bottom); + System.err.println(getThreadName()+": AWTWindow setCanvasSize: szClient "+szClient+", szCont "+szContainer+", insets "+insets); + } + awtCanvas.setSize(szClient); + awtCanvas.invalidate(); + if(null != awtWindow) { + awtWindow.pack(); + } else { + awtContainer.validate(); + } + } + private void setFrameSizeImpl(int width, int height) { + final Insets insets = awtContainer.getInsets(); + final Dimension szContainer = new Dimension(width + insets.left + insets.right, + height + insets.top + insets.bottom); + if(DEBUG_IMPLEMENTATION) { + final Dimension szClient = new Dimension(width, height); + System.err.println(getThreadName()+": AWTWindow setFrameSize: szClient "+szClient+", szCont "+szContainer+", insets "+insets); + } + awtContainer.setSize(szContainer); + awtCanvas.invalidate(); + awtContainer.validate(); + } + + @Override + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("AWTWindow reconfig: "+x+"/"+y+" "+width+"x"+height+", "+ + getReconfigureFlagsAsString(null, flags)); + } + if(0 != ( FLAG_CHANGE_DECORATION & flags) && null!=awtFrame) { + if(!awtContainer.isDisplayable()) { + awtFrame.setUndecorated(isUndecorated()); + } else { + if(DEBUG_IMPLEMENTATION) { + System.err.println(getThreadName()+": AWTWindow can't undecorate already created frame"); + } + } + } + + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { + if( 0 != ( FLAG_IS_VISIBLE & flags) ) { + setCanvasSizeImpl(width, height); + awtContainer.setVisible( true ); + } else { + awtContainer.setVisible( false ); + } + } else if( awtCanvas.getWidth() != width || awtCanvas.getHeight() != height ) { + if( Platform.OSType.MACOS == Platform.getOSType() && awtCanvas.isOffscreenLayerSurfaceEnabled() ) { + setFrameSizeImpl(width, height); + } else { + setCanvasSizeImpl(width, height); + } + } + defineSize(width, height); // we are on AWT-EDT .. change values immediately + + if( awtContainer.getX() != x || awtContainer.getY() != y ) { + awtContainer.setLocation(x, y); + } + + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { + if( 0 != ( FLAG_IS_VISIBLE & flags ) ) { + if( !hasDeviceChanged() ) { + // oops ?? + final AWTGraphicsConfiguration cfg = awtCanvas.getAWTGraphicsConfiguration(); + if(null == cfg) { + throw new NativeWindowException("Error: !hasDeviceChanged && null == GraphicsConfiguration: "+this); + } + setGraphicsConfiguration(cfg); + } + } + visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); + } + + return true; + } + + @Override + protected Point getLocationOnScreenImpl(int x, int y) { + java.awt.Point ap = awtCanvas.getLocationOnScreen(); + ap.translate(x, y); + return new Point((int)(ap.getX()+0.5),(int)(ap.getY()+0.5)); + } + + @Override + public NativeSurface getWrappedSurface() { + return ( null != awtCanvas ) ? awtCanvas.getNativeWindow() : null; + } + + class LocalWindowListener implements com.jogamp.newt.event.WindowListener { + @Override + public void windowMoved(com.jogamp.newt.event.WindowEvent e) { + if(null!=awtContainer) { + WindowDriver.this.positionChanged(false, awtContainer.getX(), awtContainer.getY()); + } + } + @Override + public void windowResized(com.jogamp.newt.event.WindowEvent e) { + if(null!=awtCanvas) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window Resized: "+awtCanvas); + } + WindowDriver.this.sizeChanged(false, awtCanvas.getWidth(), awtCanvas.getHeight(), true); + WindowDriver.this.windowRepaint(false, 0, 0, getWidth(), getHeight()); + } + } + @Override + public void windowDestroyNotify(WindowEvent e) { + WindowDriver.this.windowDestroyNotify(false); + } + @Override + public void windowDestroyed(WindowEvent e) { + // Not fwd by AWTWindowAdapter, synthesized by NEWT + } + @Override + public void windowGainedFocus(WindowEvent e) { + WindowDriver.this.focusChanged(false, true); + } + @Override + public void windowLostFocus(WindowEvent e) { + WindowDriver.this.focusChanged(false, false); + } + @Override + public void windowRepaint(WindowUpdateEvent e) { + if(null!=awtCanvas) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window Repaint: "+awtCanvas); + } + WindowDriver.this.windowRepaint(false, 0, 0, getWidth(), getHeight()); + } + } + } +} diff --git a/src/newt/classes/jogamp/newt/driver/broadcom/egl/Display.java b/src/newt/classes/jogamp/newt/driver/bcm/egl/DisplayDriver.java index f90c62ff4..d1b30f7cc 100644 --- a/src/newt/classes/jogamp/newt/driver/broadcom/egl/Display.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/egl/DisplayDriver.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2012 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 @@ -29,10 +29,10 @@ * 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.driver.broadcom.egl; +package jogamp.newt.driver.bcm.egl; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; @@ -42,12 +42,12 @@ import jogamp.opengl.egl.EGL; import com.jogamp.nativewindow.egl.EGLGraphicsDevice; -public class Display extends jogamp.newt.DisplayImpl { +public class DisplayDriver extends jogamp.newt.DisplayImpl { static { NEWTJNILibLoader.loadNEWT(); - if (!Window.initIDs()) { + if (!WindowDriver.initIDs()) { throw new NativeWindowException("Failed to initialize BCEGL Window jmethodIDs"); } } @@ -57,23 +57,27 @@ public class Display extends jogamp.newt.DisplayImpl { } - public Display() { + public DisplayDriver() { } + @Override protected void createNativeImpl() { - long handle = CreateDisplay(Screen.fixedWidth, Screen.fixedHeight); + final long handle = CreateDisplay(ScreenDriver.fixedWidth, ScreenDriver.fixedHeight); if (handle == EGL.EGL_NO_DISPLAY) { throw new NativeWindowException("BC EGL CreateDisplay failed"); } - aDevice = new EGLGraphicsDevice(handle, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); + aDevice = new EGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, handle, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT, null); } - protected void closeNativeImpl() { + @Override + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { if (aDevice.getHandle() != EGL.EGL_NO_DISPLAY) { DestroyDisplay(aDevice.getHandle()); } + aDevice.close(); } + @Override protected void dispatchMessagesNative() { // n/a .. DispatchMessages(); } diff --git a/src/newt/classes/jogamp/newt/driver/bcm/egl/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/egl/ScreenDriver.java new file mode 100644 index 000000000..d3231557f --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/bcm/egl/ScreenDriver.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2012 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.driver.bcm.egl; + +import javax.media.nativewindow.DefaultGraphicsScreen; +import javax.media.nativewindow.util.Rectangle; + +import jogamp.newt.MonitorModeProps; +import jogamp.newt.ScreenImpl; + +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +public class ScreenDriver extends jogamp.newt.ScreenImpl { + + static { + DisplayDriver.initSingleton(); + } + + + public ScreenDriver() { + } + + @Override + protected void createNativeImpl() { + aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); + } + + @Override + protected void closeNativeImpl() { } + + @Override + protected int validateScreenIndex(int idx) { + return 0; // only one screen available + } + + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(MonitorModeProps.Cache cache) { + int[] props = new int[ MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL ]; + int i = 0; + props[i++] = MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL; + props[i++] = fixedWidth; // FIXME + props[i++] = fixedHeight; // FIXME + props[i++] = ScreenImpl.default_sm_bpp; // FIXME + props[i++] = ScreenImpl.default_sm_rate * 100; // FIXME + props[i++] = 0; // flags + props[i++] = 0; // mode_idx + props[i++] = 0; // rotation + final MonitorMode currentMode = MonitorModeProps.streamInMonitorMode(null, cache, props, 0); + + props = new int[MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES - 1 - MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES]; + i = 0; + props[i++] = props.length; + props[i++] = 0; // crt_idx + props[i++] = ScreenImpl.default_sm_widthmm; // FIXME + props[i++] = ScreenImpl.default_sm_heightmm; // FIXME + props[i++] = 0; // rotated viewport x + props[i++] = 0; // rotated viewport y + props[i++] = fixedWidth; // FIXME rotated viewport width + props[i++] = fixedHeight; // FIXME rotated viewport height + MonitorModeProps.streamInMonitorDevice(null, cache, this, cache.monitorModes, currentMode, props, 0); + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(final MonitorDevice monitor) { + return monitor.getSupportedModes().get(0); + } + + @Override + protected boolean setCurrentMonitorModeImpl(final MonitorDevice monitor, final MonitorMode mode) { + return false; + } + + @Override + protected void calcVirtualScreenOriginAndSize(Rectangle vOriginSize) { + vOriginSize.set(0, 0, fixedWidth, fixedHeight); // FIXME + } + + //---------------------------------------------------------------------- + // Internals only + // + + static final int fixedWidth = 1920; // FIXME + static final int fixedHeight = 1080; // FIXME +} + diff --git a/src/newt/classes/jogamp/newt/driver/broadcom/egl/Window.java b/src/newt/classes/jogamp/newt/driver/bcm/egl/WindowDriver.java index ed1d0511a..39f168e0f 100644 --- a/src/newt/classes/jogamp/newt/driver/broadcom/egl/Window.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/egl/WindowDriver.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2012 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 @@ -29,36 +29,38 @@ * 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.driver.broadcom.egl; +package jogamp.newt.driver.bcm.egl; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.VisualIDHolder; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; import javax.media.opengl.GLCapabilitiesImmutable; import jogamp.opengl.egl.EGLGraphicsConfiguration; -public class Window extends jogamp.newt.WindowImpl { +public class WindowDriver extends jogamp.newt.WindowImpl { static { - Display.initSingleton(); + DisplayDriver.initSingleton(); } - public Window() { + public WindowDriver() { } + @Override protected void createNativeImpl() { if(0!=getParentWindowHandle()) { throw new RuntimeException("Window parenting not supported (yet)"); } - // query a good configuration, however chose the final one by the native queried egl-cfg-id + // query a good configuration, however chose the final one by the native queried egl-cfg-id // after creation at {@link #windowCreated(int, int, int)}. - final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice()).chooseGraphicsConfiguration( - capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen()); + final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice(), capsRequested).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen(), VisualIDHolder.VID_UNDEFINED); if (null == cfg) { throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); } @@ -71,12 +73,14 @@ public class Window extends jogamp.newt.WindowImpl { } } + @Override protected void closeNativeImpl() { if(0!=windowHandleClose) { CloseWindow(getDisplayHandle(), windowHandleClose); } } + @Override protected void requestFocusImpl(boolean reparented) { } protected void setSizeImpl(int width, int height) { @@ -88,8 +92,9 @@ public class Window extends jogamp.newt.WindowImpl { } } - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { - if(0!=getWindowHandle()) { + @Override + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { + if(0!=getWindowHandle()) { if(0 != ( FLAG_CHANGE_FULLSCREEN & flags)) { if( 0 != ( FLAG_IS_FULLSCREEN & flags) ) { // n/a in BroadcomEGL @@ -109,19 +114,21 @@ public class Window extends jogamp.newt.WindowImpl { if(x>=0 || y>=0) { System.err.println("BCEGL Window.setPositionImpl n/a in BroadcomEGL"); } - + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); } return true; } + @Override protected Point getLocationOnScreenImpl(int x, int y) { return new Point(x,y); } + @Override protected void updateInsetsImpl(Insets insets) { - // nop .. + // nop .. } @Override diff --git a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/DisplayDriver.java new file mode 100644 index 000000000..178bb70f7 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/DisplayDriver.java @@ -0,0 +1,209 @@ +/** + * Copyright 2012 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.bcm.vc.iv; + +import java.net.URLConnection; +import java.nio.Buffer; +import java.nio.ByteBuffer; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.util.PixelFormat; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.util.PNGPixelRect; + +import jogamp.newt.DisplayImpl; +import jogamp.newt.NEWTJNILibLoader; +import jogamp.newt.PointerIconImpl; +import jogamp.newt.driver.linux.LinuxMouseTracker; +import jogamp.opengl.egl.EGL; +import jogamp.opengl.egl.EGLDisplayUtil; + +public class DisplayDriver extends DisplayImpl { + static final PNGPixelRect defaultPointerIconImage; + + static { + NEWTJNILibLoader.loadNEWT(); + + if (!DisplayDriver.initIDs()) { + throw new NativeWindowException("Failed to initialize bcm.vc.iv Display jmethodIDs"); + } + if (!ScreenDriver.initIDs()) { + throw new NativeWindowException("Failed to initialize bcm.vc.iv Screen jmethodIDs"); + } + if (!WindowDriver.initIDs()) { + throw new NativeWindowException("Failed to initialize bcm.vc.iv Window jmethodIDs"); + } + + PNGPixelRect image = null; + if( DisplayImpl.isPNGUtilAvailable() ) { + final IOUtil.ClassResources res = new IOUtil.ClassResources(DisplayDriver.class, new String[] { "newt/data/pointer-grey-alpha-16x24.png" } ); + try { + final URLConnection urlConn = res.resolve(0); + image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } catch (Exception e) { + e.printStackTrace(); + } + } + defaultPointerIconImage = image; + } + + public static void initSingleton() { + // just exist to ensure static init has been run + } + + + public DisplayDriver() { + bcmHandle = 0; + activePointerIcon = 0; + activePointerIconVisible = false; + } + + @Override + protected void createNativeImpl() { + // FIXME: map name to EGL_*_DISPLAY + bcmHandle = OpenBCMDisplay0(); + aDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); + aDevice.open(); + + defaultPointerIcon = (PointerIconImpl) createPointerIcon(defaultPointerIconImage, 0, 0); + if( DEBUG_POINTER_ICON ) { + System.err.println("Display.PointerIcon.createDefault: "+defaultPointerIcon); + } + if( null != defaultPointerIcon ) { + final LinuxMouseTracker lmt = LinuxMouseTracker.getSingleton(); + setPointerIconActive(defaultPointerIcon.getHandle(), lmt.getLastX(), lmt.getLastY()); + } + } + private PointerIconImpl defaultPointerIcon = null; + + @Override + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { + aDevice.close(); + CloseBCMDisplay0(bcmHandle); + bcmHandle = 0; + } + + /* pp */ final long getBCMHandle() { return bcmHandle; } + + @Override + protected void dispatchMessagesNative() { + DispatchMessages0(); + } + + // @Override + // public final PixelFormat getNativePointerIconPixelFormat() { return PixelFormat.BGRA8888; } + + @Override + protected final long createPointerIconImpl(PixelFormat pixelformat, int width, int height, final ByteBuffer pixels, final int hotX, final int hotY) { + return CreatePointerIcon(bcmHandle, pixels, width, height, hotX, hotY); + } + + @Override + protected final void destroyPointerIconImpl(final long displayHandle, long piHandle) { + DestroyPointerIcon0(piHandle); + } + + /* pp */ void setPointerIconActive(long piHandle, final int x, final int y) { + synchronized(pointerIconSync) { + if( DEBUG_POINTER_ICON ) { + System.err.println("Display.PointerIcon.set.0: active ["+toHexString(activePointerIcon)+", visible "+activePointerIconVisible+"] -> "+toHexString(piHandle)); + } + if( 0 != activePointerIcon && activePointerIconVisible ) { + SetPointerIcon0(bcmHandle, activePointerIcon, false, x, y); + } + if( 0 == piHandle && null != defaultPointerIcon ) { + piHandle = defaultPointerIcon.getHandle(); + } + if( 0 != piHandle ) { + SetPointerIcon0(bcmHandle, piHandle, true, x, y); + activePointerIconVisible = true; + } else { + activePointerIconVisible = false; + } + activePointerIcon = piHandle; + if( DEBUG_POINTER_ICON ) { + System.err.println("Display.PointerIcon.set.X: active ["+toHexString(activePointerIcon)+", visible "+activePointerIconVisible+"]"); + } + } + } + /* pp */ void setActivePointerIconVisible(final boolean visible, final int x, final int y) { + synchronized(pointerIconSync) { + if( DEBUG_POINTER_ICON ) { + System.err.println("Display.PointerIcon.visible: active ["+toHexString(activePointerIcon)+", visible "+activePointerIconVisible+"] -> visible "+visible); + } + if( activePointerIconVisible != visible ) { + if( 0 != activePointerIcon ) { + SetPointerIcon0(bcmHandle, activePointerIcon, visible, x, y); + } + activePointerIconVisible = visible; + } + } + } + /* pp */ void moveActivePointerIcon(final int x, final int y) { + synchronized(pointerIconSync) { + if( DEBUG_POINTER_ICON ) { + System.err.println("Display.PointerIcon.move: active ["+toHexString(activePointerIcon)+", visible "+activePointerIconVisible+"], "+x+"/"+y); + } + if( 0 != activePointerIcon && activePointerIconVisible ) { + MovePointerIcon0(activePointerIcon, x, y); + } + } + } + + //---------------------------------------------------------------------- + // Internals only + // + + protected static native boolean initIDs(); + private static native long OpenBCMDisplay0(); + private static native void CloseBCMDisplay0(long handle); + + private static long CreatePointerIcon(long bcmHandle, Buffer pixels, int width, int height, int hotX, int hotY) { + final boolean pixels_is_direct = Buffers.isDirect(pixels); + return CreatePointerIcon0(pixels_is_direct ? pixels : Buffers.getArray(pixels), + pixels_is_direct ? Buffers.getDirectBufferByteOffset(pixels) : Buffers.getIndirectBufferByteOffset(pixels), + pixels_is_direct, + width, height, hotX, hotY); + } + private static native long CreatePointerIcon0(Object pixels, int pixels_byte_offset, boolean pixels_is_direct, int width, int height, int hotX, int hotY); + private static native void DestroyPointerIcon0(long handle); + private static native void SetPointerIcon0(long bcmHandle, long handle, boolean enable, int x, int y); + private static native void MovePointerIcon0(long handle, int x, int y); + + private static native void DispatchMessages0(); + + private long bcmHandle; + private long activePointerIcon; + private boolean activePointerIconVisible; + private final Object pointerIconSync = new Object(); +} + diff --git a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/ScreenDriver.java new file mode 100644 index 000000000..dc2a8459a --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/ScreenDriver.java @@ -0,0 +1,115 @@ +/** + * Copyright 2012 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.bcm.vc.iv; + +import javax.media.nativewindow.DefaultGraphicsScreen; +import javax.media.nativewindow.util.Rectangle; + +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +import jogamp.newt.MonitorModeProps; +import jogamp.newt.ScreenImpl; + +public class ScreenDriver extends ScreenImpl { + static { + DisplayDriver.initSingleton(); + } + + public ScreenDriver() { + } + + @Override + protected void createNativeImpl() { + aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); + initNative(); + } + + @Override + protected void closeNativeImpl() { } + + @Override + protected int validateScreenIndex(int idx) { + return 0; // only one screen available + } + + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(MonitorModeProps.Cache cache) { + int[] props = new int[ MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL ]; + int i = 0; + props[i++] = MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL; + props[i++] = cachedWidth; // width + props[i++] = cachedHeight; // height + props[i++] = ScreenImpl.default_sm_bpp; // FIXME + props[i++] = ScreenImpl.default_sm_rate * 100; // FIXME + props[i++] = 0; // flags + props[i++] = 0; // mode_idx + props[i++] = 0; // rotation + final MonitorMode currentMode = MonitorModeProps.streamInMonitorMode(null, cache, props, 0); + + props = new int[MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES - 1 - MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES]; + i = 0; + props[i++] = props.length; + props[i++] = 0; // crt_idx + props[i++] = ScreenImpl.default_sm_widthmm; // FIXME + props[i++] = ScreenImpl.default_sm_heightmm; // FIXME + props[i++] = 0; // rotated viewport x + props[i++] = 0; // rotated viewport y + props[i++] = cachedWidth; // rotated viewport width + props[i++] = cachedWidth; // rotated viewport height + MonitorModeProps.streamInMonitorDevice(null, cache, this, cache.monitorModes, currentMode, props, 0); + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(final MonitorDevice monitor) { + return monitor.getSupportedModes().get(0); + } + + @Override + protected boolean setCurrentMonitorModeImpl(final MonitorDevice monitor, final MonitorMode mode) { + return false; + } + + @Override + protected void calcVirtualScreenOriginAndSize(Rectangle vOriginSize) { + vOriginSize.set(0, 0, cachedWidth, cachedHeight); + } + + /** Called from {@link #initNative()}. */ + protected void setScreenSize(int width, int height) { + cachedWidth = width; + cachedHeight = height; + } + + private static int cachedWidth = 0; + private static int cachedHeight = 0; + + protected static native boolean initIDs(); + protected native void initNative(); +} diff --git a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java new file mode 100644 index 000000000..c3cb8a84c --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java @@ -0,0 +1,215 @@ +/** + * Copyright 2012 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.bcm.vc.iv; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.Capabilities; +import javax.media.nativewindow.DefaultGraphicsScreen; +import javax.media.nativewindow.GraphicsConfigurationFactory; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.VisualIDHolder; +import javax.media.nativewindow.util.Insets; +import javax.media.nativewindow.util.Point; + +import com.jogamp.common.util.IntBitfield; +import com.jogamp.nativewindow.egl.EGLGraphicsDevice; +import com.jogamp.newt.event.MouseEvent; + +import jogamp.newt.PointerIconImpl; +import jogamp.newt.WindowImpl; +import jogamp.newt.driver.linux.LinuxEventDeviceTracker; +import jogamp.newt.driver.linux.LinuxMouseTracker; +import jogamp.opengl.egl.EGLDisplayUtil; + +public class WindowDriver extends WindowImpl { + private static final String WINDOW_CLASS_NAME = "NewtWindow"; + + static { + DisplayDriver.initSingleton(); + } + + public WindowDriver() { + linuxMouseTracker = LinuxMouseTracker.getSingleton(); + linuxEventDeviceTracker = LinuxEventDeviceTracker.getSingleton(); + layer = -1; + nativeWindowHandle = 0; + windowHandleClose = 0; + } + + @Override + protected void createNativeImpl() { + if(0!=getParentWindowHandle()) { + throw new RuntimeException("Window parenting not supported (yet)"); + } + synchronized( layerSync ) { + if( layerCount >= MAX_LAYERS ) { + throw new RuntimeException("Max windows reached: "+layerCount+" ( "+MAX_LAYERS+" )"); + } + for(int i=0; 0 > layer && i<MAX_LAYERS; i++) { + if( !usedLayers.get(nextLayer) ) { + layer = nextLayer; + usedLayers.put(layer, true); + layerCount++; + } + nextLayer++; + if( MAX_LAYERS == nextLayer ) { + nextLayer=0; + } + } + // System.err.println("XXX.Open capacity "+usedLayers.capacity()+", count "+usedLayers.getBitCount()); + } + if( 0 > layer ) { + throw new InternalError("Could not find a free layer: count "+layerCount+", max "+MAX_LAYERS); + } + final ScreenDriver screen = (ScreenDriver) getScreen(); + final DisplayDriver display = (DisplayDriver) screen.getDisplay(); + + // Create own screen/device resource instance allowing independent ownership, + // while still utilizing shared EGL resources. + final AbstractGraphicsScreen aScreen = screen.getGraphicsScreen(); + final EGLGraphicsDevice aDevice = (EGLGraphicsDevice) aScreen.getDevice(); + final EGLGraphicsDevice eglDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(aDevice.getNativeDisplayID(), aDevice.getConnection(), aDevice.getUnitID()); + eglDevice.open(); + final DefaultGraphicsScreen eglScreen = new DefaultGraphicsScreen(eglDevice, aScreen.getIndex()); + + final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice(), capsRequested).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, eglScreen, VisualIDHolder.VID_UNDEFINED); + if (null == cfg) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + final Capabilities chosenCaps = (Capabilities) cfg.getChosenCapabilities(); + // FIXME: Pass along opaque flag, since EGL doesn't determine it + if(capsRequested.isBackgroundOpaque() != chosenCaps.isBackgroundOpaque()) { + chosenCaps.setBackgroundOpaque(capsRequested.isBackgroundOpaque()); + } + setGraphicsConfiguration(cfg); + nativeWindowHandle = CreateWindow0(display.getBCMHandle(), layer, getX(), getY(), getWidth(), getHeight(), + chosenCaps.isBackgroundOpaque(), chosenCaps.getAlphaBits()); + if (nativeWindowHandle == 0) { + throw new NativeWindowException("Error creating egl window: "+cfg); + } + setWindowHandle(nativeWindowHandle); + if (0 == getWindowHandle()) { + throw new NativeWindowException("Error native Window Handle is null"); + } + windowHandleClose = nativeWindowHandle; + + addWindowListener(linuxEventDeviceTracker); + addWindowListener(linuxMouseTracker); + focusChanged(false, true); + } + + @Override + protected void closeNativeImpl() { + final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); + final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) getGraphicsConfiguration().getScreen().getDevice(); + + removeWindowListener(linuxMouseTracker); + removeWindowListener(linuxEventDeviceTracker); + + if(0!=windowHandleClose) { + CloseWindow0(display.getBCMHandle(), windowHandleClose); + } + + eglDevice.close(); + + synchronized( layerSync ) { + usedLayers.put(layer, false); + layerCount--; + layer = -1; + // System.err.println("XXX.Close capacity "+usedLayers.capacity()+", count "+usedLayers.getBitCount()); + } + } + + @Override + protected void requestFocusImpl(boolean reparented) { + focusChanged(false, true); + } + + @Override + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { + reconfigure0(nativeWindowHandle, x, y, width, height, flags); + return true; + } + + @Override + protected Point getLocationOnScreenImpl(int x, int y) { + return new Point(x,y); + } + + @Override + protected void updateInsetsImpl(Insets insets) { + // nop .. + } + + @Override + public final void sendMouseEvent(final short eventType, final int modifiers, + final int x, final int y, final short button, final float rotation) { + if( MouseEvent.EVENT_MOUSE_MOVED == eventType ) { + final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); + display.moveActivePointerIcon(x, y); + } + super.sendMouseEvent(eventType, modifiers, x, y, button, rotation); + } + + @Override + protected void setPointerIconImpl(final PointerIconImpl pi) { + final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); + display.setPointerIconActive(null != pi ? pi.validatedHandle() : 0, linuxMouseTracker.getLastX(), linuxMouseTracker.getLastY()); + } + + @Override + protected boolean setPointerVisibleImpl(final boolean pointerVisible) { + final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); + display.setActivePointerIconVisible(pointerVisible, linuxMouseTracker.getLastX(), linuxMouseTracker.getLastY()); + return true; + } + + //---------------------------------------------------------------------- + // Internals only + // + private final LinuxMouseTracker linuxMouseTracker; + private final LinuxEventDeviceTracker linuxEventDeviceTracker; + + protected static native boolean initIDs(); + private native long CreateWindow0(long bcmDisplay, int layer, int x, int y, int width, int height, boolean opaque, int alphaBits); + private native void CloseWindow0(long bcmDisplay, long eglWindowHandle); + private native void reconfigure0(long eglWindowHandle, int x, int y, int width, int height, int flags); + + private int layer; + private long nativeWindowHandle; + private long windowHandleClose; + + private static int nextLayer = 0; + private static int layerCount = 0; + private static final int MAX_LAYERS = 32; + private static final IntBitfield usedLayers = new IntBitfield(MAX_LAYERS); + private static final Object layerSync = new Object(); +} diff --git a/src/newt/classes/jogamp/newt/driver/broadcom/egl/Screen.java b/src/newt/classes/jogamp/newt/driver/broadcom/egl/Screen.java deleted file mode 100644 index 0544bc06c..000000000 --- a/src/newt/classes/jogamp/newt/driver/broadcom/egl/Screen.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * Copyright (c) 2012 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.driver.broadcom.egl; - -import javax.media.nativewindow.DefaultGraphicsScreen; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; - -public class Screen extends jogamp.newt.ScreenImpl { - - static { - Display.initSingleton(); - } - - - public Screen() { - } - - protected void createNativeImpl() { - aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); - } - - protected void closeNativeImpl() { } - - protected int validateScreenIndex(int idx) { - return 0; // only one screen available - } - - protected void getVirtualScreenOriginAndSize(Point virtualOrigin, Dimension virtualSize) { - virtualOrigin.setX(0); - virtualOrigin.setY(0); - virtualSize.setWidth(fixedWidth); // FIXME - virtualSize.setHeight(fixedHeight); // FIXME - } - - //---------------------------------------------------------------------- - // Internals only - // - - static final int fixedWidth = 1920; // FIXME - static final int fixedHeight = 1080; // FIXME -} - diff --git a/src/newt/classes/jogamp/newt/driver/intel/gdl/Display.java b/src/newt/classes/jogamp/newt/driver/intel/gdl/DisplayDriver.java index 20e151eb3..cc435540f 100644 --- a/src/newt/classes/jogamp/newt/driver/intel/gdl/Display.java +++ b/src/newt/classes/jogamp/newt/driver/intel/gdl/DisplayDriver.java @@ -1,21 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * + * Copyright (c) 2012 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 @@ -28,7 +29,7 @@ * 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.driver.intel.gdl; @@ -36,16 +37,16 @@ package jogamp.newt.driver.intel.gdl; import jogamp.newt.*; import javax.media.nativewindow.*; -public class Display extends jogamp.newt.DisplayImpl { +public class DisplayDriver extends jogamp.newt.DisplayImpl { static int initCounter = 0; static { NEWTJNILibLoader.loadNEWT(); - if (!Screen.initIDs()) { + if (!ScreenDriver.initIDs()) { throw new NativeWindowException("Failed to initialize GDL Screen jmethodIDs"); } - if (!Window.initIDs()) { + if (!WindowDriver.initIDs()) { throw new NativeWindowException("Failed to initialize GDL Window jmethodIDs"); } } @@ -55,11 +56,12 @@ public class Display extends jogamp.newt.DisplayImpl { } - public Display() { + public DisplayDriver() { } + @Override protected void createNativeImpl() { - synchronized(Display.class) { + synchronized(DisplayDriver.class) { if(0==initCounter) { displayHandle = CreateDisplay(); if(0==displayHandle) { @@ -71,11 +73,12 @@ public class Display extends jogamp.newt.DisplayImpl { aDevice = new DefaultGraphicsDevice(NativeWindowFactory.TYPE_DEFAULT, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT, displayHandle); } - protected void closeNativeImpl() { + @Override + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { if(0==displayHandle) { throw new NativeWindowException("displayHandle null; initCnt "+initCounter); } - synchronized(Display.class) { + synchronized(DisplayDriver.class) { if(initCounter>0) { initCounter--; if(0==initCounter) { @@ -83,22 +86,24 @@ public class Display extends jogamp.newt.DisplayImpl { } } } + aDevice.close(); } + @Override protected void dispatchMessagesNative() { if(0!=displayHandle) { DispatchMessages(displayHandle, focusedWindow); } } - protected void setFocus(Window focus) { + protected void setFocus(WindowDriver focus) { focusedWindow = focus; } private long displayHandle = 0; - private Window focusedWindow = null; + private WindowDriver focusedWindow = null; private native long CreateDisplay(); private native void DestroyDisplay(long displayHandle); - private native void DispatchMessages(long displayHandle, Window focusedWindow); + private native void DispatchMessages(long displayHandle, WindowDriver focusedWindow); } diff --git a/src/newt/classes/jogamp/newt/driver/intel/gdl/Screen.java b/src/newt/classes/jogamp/newt/driver/intel/gdl/ScreenDriver.java index 66ad1c691..44802e348 100644 --- a/src/newt/classes/jogamp/newt/driver/intel/gdl/Screen.java +++ b/src/newt/classes/jogamp/newt/driver/intel/gdl/ScreenDriver.java @@ -1,21 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * + * Copyright (c) 2012 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 @@ -28,44 +29,87 @@ * 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.driver.intel.gdl; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.DefaultGraphicsScreen; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.Rectangle; -public class Screen extends jogamp.newt.ScreenImpl { +import jogamp.newt.MonitorModeProps; +import jogamp.newt.ScreenImpl; + +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +public class ScreenDriver extends jogamp.newt.ScreenImpl { static { - Display.initSingleton(); + DisplayDriver.initSingleton(); } - public Screen() { + public ScreenDriver() { } + @Override protected void createNativeImpl() { AbstractGraphicsDevice adevice = getDisplay().getGraphicsDevice(); GetScreenInfo(adevice.getHandle(), screen_idx); aScreen = new DefaultGraphicsScreen(adevice, screen_idx); } + @Override protected void closeNativeImpl() { } + @Override protected int validateScreenIndex(int idx) { - return 0; // only one screen available + return 0; // only one screen available + } + + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(MonitorModeProps.Cache cache) { + int[] props = new int[ MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL ]; + int i = 0; + props[i++] = MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL; + props[i++] = cachedWidth; // width + props[i++] = cachedHeight; // height + props[i++] = ScreenImpl.default_sm_bpp; // FIXME + props[i++] = ScreenImpl.default_sm_rate * 100; // FIXME + props[i++] = 0; // flags + props[i++] = 0; // mode_idx + props[i++] = 0; // rotation + final MonitorMode currentMode = MonitorModeProps.streamInMonitorMode(null, cache, props, 0); + + props = new int[MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES - 1 - MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES]; + i = 0; + props[i++] = props.length; + props[i++] = 0; // crt_idx + props[i++] = ScreenImpl.default_sm_widthmm; // FIXME + props[i++] = ScreenImpl.default_sm_heightmm; // FIXME + props[i++] = 0; // rotated viewport x + props[i++] = 0; // rotated viewport y + props[i++] = cachedWidth; // rotated viewport width + props[i++] = cachedWidth; // rotated viewport height + MonitorModeProps.streamInMonitorDevice(null, cache, this, cache.monitorModes, currentMode, props, 0); + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(final MonitorDevice monitor) { + return monitor.getSupportedModes().get(0); + } + + @Override + protected boolean setCurrentMonitorModeImpl(final MonitorDevice monitor, final MonitorMode mode) { + return false; } - - protected void getVirtualScreenOriginAndSize(Point virtualOrigin, Dimension virtualSize) { - virtualOrigin.setX(0); - virtualOrigin.setY(0); - virtualSize.setWidth(cachedWidth); - virtualSize.setHeight(cachedHeight); + + @Override + protected void calcVirtualScreenOriginAndSize(Rectangle vOriginSize) { + vOriginSize.set(0, 0, cachedWidth, cachedHeight); } - + //---------------------------------------------------------------------- // Internals only // @@ -78,7 +122,7 @@ public class Screen extends jogamp.newt.ScreenImpl { cachedWidth = width; cachedHeight = height; } - + private static int cachedWidth = 0; private static int cachedHeight = 0; } diff --git a/src/newt/classes/jogamp/newt/driver/intel/gdl/Window.java b/src/newt/classes/jogamp/newt/driver/intel/gdl/WindowDriver.java index 09e0e3016..0e96c65d0 100644 --- a/src/newt/classes/jogamp/newt/driver/intel/gdl/Window.java +++ b/src/newt/classes/jogamp/newt/driver/intel/gdl/WindowDriver.java @@ -1,21 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * + * Copyright (c) 2012 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 @@ -28,7 +29,7 @@ * 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.driver.intel.gdl; @@ -37,16 +38,17 @@ import javax.media.nativewindow.*; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; -public class Window extends jogamp.newt.WindowImpl { +public class WindowDriver extends jogamp.newt.WindowImpl { static { - Display.initSingleton(); + DisplayDriver.initSingleton(); } - public Window() { + public WindowDriver() { } static long nextWindowHandle = 1; + @Override protected void createNativeImpl() { if(0!=getParentWindowHandle()) { throw new NativeWindowException("GDL Window does not support window parenting"); @@ -54,14 +56,14 @@ public class Window extends jogamp.newt.WindowImpl { final AbstractGraphicsScreen aScreen = getScreen().getGraphicsScreen(); final AbstractGraphicsDevice aDevice = getScreen().getDisplay().getGraphicsDevice(); - final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(aDevice).chooseGraphicsConfiguration( - capsRequested, capsRequested, capabilitiesChooser, aScreen); + final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(aDevice, capsRequested).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, aScreen, VisualIDHolder.VID_UNDEFINED); if (null == cfg) { throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); } setGraphicsConfiguration(cfg); - synchronized(Window.class) { + synchronized(WindowDriver.class) { setWindowHandle(nextWindowHandle++); // just a marker surfaceHandle = CreateSurface(aDevice.getHandle(), getScreen().getWidth(), getScreen().getHeight(), getX(), getY(), getWidth(), getHeight()); @@ -71,18 +73,20 @@ public class Window extends jogamp.newt.WindowImpl { } } + @Override protected void closeNativeImpl() { if(0!=surfaceHandle) { - synchronized(Window.class) { + synchronized(WindowDriver.class) { CloseSurface(getDisplayHandle(), surfaceHandle); } surfaceHandle = 0; - ((Display)getScreen().getDisplay()).setFocus(null); + ((DisplayDriver)getScreen().getDisplay()).setFocus(null); } } + @Override protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { - Screen screen = (Screen) getScreen(); + ScreenDriver screen = (ScreenDriver) getScreen(); if(width>screen.getWidth()) { width=screen.getWidth(); @@ -103,16 +107,17 @@ public class Window extends jogamp.newt.WindowImpl { if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { if(0 != ( FLAG_IS_VISIBLE & flags)) { - ((Display)getScreen().getDisplay()).setFocus(this); + ((DisplayDriver)getScreen().getDisplay()).setFocus(this); } visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); } - + return true; } + @Override protected void requestFocusImpl(boolean reparented) { - ((Display)getScreen().getDisplay()).setFocus(this); + ((DisplayDriver)getScreen().getDisplay()).setFocus(this); } @Override @@ -120,14 +125,16 @@ public class Window extends jogamp.newt.WindowImpl { return surfaceHandle; } + @Override protected Point getLocationOnScreenImpl(int x, int y) { return new Point(x,y); } + @Override protected void updateInsetsImpl(Insets insets) { - // nop .. + // nop .. } - + //---------------------------------------------------------------------- // Internals only // diff --git a/src/newt/classes/jogamp/newt/driver/kd/KDDisplay.java b/src/newt/classes/jogamp/newt/driver/kd/DisplayDriver.java index 73bbe0b5b..6c706148a 100644 --- a/src/newt/classes/jogamp/newt/driver/kd/KDDisplay.java +++ b/src/newt/classes/jogamp/newt/driver/kd/DisplayDriver.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2012 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 @@ -29,7 +29,7 @@ * 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.driver.kd; @@ -42,15 +42,12 @@ import jogamp.newt.NEWTJNILibLoader; import jogamp.opengl.egl.EGL; import jogamp.opengl.egl.EGLDisplayUtil; -import com.jogamp.nativewindow.egl.EGLGraphicsDevice; - -public class KDDisplay extends DisplayImpl { - +public class DisplayDriver extends DisplayImpl { static { NEWTJNILibLoader.loadNEWT(); - if (!KDWindow.initIDs()) { - throw new NativeWindowException("Failed to initialize KDWindow jmethodIDs"); + if (!WindowDriver.initIDs()) { + throw new NativeWindowException("Failed to initialize kd.Window jmethodIDs"); } } @@ -59,27 +56,22 @@ public class KDDisplay extends DisplayImpl { } - public KDDisplay() { + public DisplayDriver() { } + @Override protected void createNativeImpl() { // FIXME: map name to EGL_*_DISPLAY - long handle = EGLDisplayUtil.eglGetDisplay(EGL.EGL_DEFAULT_DISPLAY); - if (handle == EGL.EGL_NO_DISPLAY) { - throw new NativeWindowException("eglGetDisplay failed"); - } - if (!EGLDisplayUtil.eglInitialize(handle, null, null)) { - throw new NativeWindowException("eglInitialize failed"); - } - aDevice = new EGLGraphicsDevice(handle, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); + aDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); + aDevice.open(); } - protected void closeNativeImpl() { - if (aDevice.getHandle() != EGL.EGL_NO_DISPLAY) { - EGLDisplayUtil.eglTerminate(aDevice.getHandle()); - } + @Override + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { + aDevice.close(); } + @Override protected void dispatchMessagesNative() { DispatchMessages(); } diff --git a/src/newt/classes/jogamp/newt/driver/kd/KDScreen.java b/src/newt/classes/jogamp/newt/driver/kd/KDScreen.java deleted file mode 100644 index ee3475880..000000000 --- a/src/newt/classes/jogamp/newt/driver/kd/KDScreen.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * Copyright (c) 2012 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.driver.kd; - -import javax.media.nativewindow.DefaultGraphicsScreen; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; - -import jogamp.newt.ScreenImpl; - -public class KDScreen extends ScreenImpl { - static { - KDDisplay.initSingleton(); - } - - public KDScreen() { - } - - protected void createNativeImpl() { - aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); - } - - protected void closeNativeImpl() { } - - protected int validateScreenIndex(int idx) { - return 0; // only one screen available - } - - protected void getVirtualScreenOriginAndSize(Point virtualOrigin, Dimension virtualSize) { - virtualOrigin.setX(0); - virtualOrigin.setY(0); - virtualSize.setWidth(cachedWidth); - virtualSize.setHeight(cachedHeight); - } - - protected void sizeChanged(int w, int h) { - cachedWidth = w; - cachedHeight = h; - } - - private static int cachedWidth = 0; - private static int cachedHeight = 0; -} diff --git a/src/newt/classes/jogamp/newt/driver/kd/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/kd/ScreenDriver.java new file mode 100644 index 000000000..9ebe2629a --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/kd/ScreenDriver.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2012 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.driver.kd; + +import javax.media.nativewindow.DefaultGraphicsScreen; +import javax.media.nativewindow.util.Rectangle; + +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +import jogamp.newt.MonitorModeProps; +import jogamp.newt.ScreenImpl; + +public class ScreenDriver extends ScreenImpl { + static { + DisplayDriver.initSingleton(); + } + + public ScreenDriver() { + } + + @Override + protected void createNativeImpl() { + aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); + } + + @Override + protected void closeNativeImpl() { } + + @Override + protected int validateScreenIndex(int idx) { + return 0; // only one screen available + } + + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(MonitorModeProps.Cache cache) { + int[] props = new int[ MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL ]; + int i = 0; + props[i++] = MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL; + props[i++] = cachedWidth; // width + props[i++] = cachedHeight; // height + props[i++] = ScreenImpl.default_sm_bpp; // FIXME + props[i++] = ScreenImpl.default_sm_rate * 100; // FIXME + props[i++] = 0; // flags + props[i++] = 0; // mode_idx + props[i++] = 0; // rotation + final MonitorMode currentMode = MonitorModeProps.streamInMonitorMode(null, cache, props, 0); + + props = new int[MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES - 1 - MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES]; + i = 0; + props[i++] = props.length; + props[i++] = 0; // crt_idx + props[i++] = ScreenImpl.default_sm_widthmm; // FIXME + props[i++] = ScreenImpl.default_sm_heightmm; // FIXME + props[i++] = 0; // rotated viewport x + props[i++] = 0; // rotated viewport y + props[i++] = cachedWidth; // rotated viewport width + props[i++] = cachedWidth; // rotated viewport height + MonitorModeProps.streamInMonitorDevice(null, cache, this, cache.monitorModes, currentMode, props, 0); + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(final MonitorDevice monitor) { + return monitor.getSupportedModes().get(0); + } + + @Override + protected boolean setCurrentMonitorModeImpl(final MonitorDevice monitor, final MonitorMode mode) { + return false; + } + + @Override + protected void calcVirtualScreenOriginAndSize(Rectangle vOriginSize) { + vOriginSize.set(0, 0, cachedWidth, cachedHeight); + } + + protected void sizeChanged(int w, int h) { + cachedWidth = w; + cachedHeight = h; + } + + private static int cachedWidth = 0; + private static int cachedHeight = 0; +} diff --git a/src/newt/classes/jogamp/newt/driver/kd/KDWindow.java b/src/newt/classes/jogamp/newt/driver/kd/WindowDriver.java index d34ffd593..158e6ab2f 100644 --- a/src/newt/classes/jogamp/newt/driver/kd/KDWindow.java +++ b/src/newt/classes/jogamp/newt/driver/kd/WindowDriver.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2012 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 @@ -29,7 +29,7 @@ * 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.driver.kd; @@ -37,6 +37,8 @@ package jogamp.newt.driver.kd; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.VisualIDHolder; +import javax.media.nativewindow.VisualIDHolder.VIDType; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; import javax.media.opengl.GLCapabilitiesImmutable; @@ -44,33 +46,35 @@ import javax.media.opengl.GLCapabilitiesImmutable; import jogamp.newt.WindowImpl; import jogamp.opengl.egl.EGLGraphicsConfiguration; -public class KDWindow extends WindowImpl { +public class WindowDriver extends WindowImpl { private static final String WINDOW_CLASS_NAME = "NewtWindow"; static { - KDDisplay.initSingleton(); + DisplayDriver.initSingleton(); } - public KDWindow() { + public WindowDriver() { } + @Override protected void createNativeImpl() { if(0!=getParentWindowHandle()) { throw new RuntimeException("Window parenting not supported (yet)"); } - final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice()).chooseGraphicsConfiguration( - capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen()); + final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice(), capsRequested).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen(), VisualIDHolder.VID_UNDEFINED); if (null == cfg) { throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); } setGraphicsConfiguration(cfg); GLCapabilitiesImmutable eglCaps = (GLCapabilitiesImmutable) cfg.getChosenCapabilities(); - int[] eglAttribs = EGLGraphicsConfiguration.GLCapabilities2AttribList(eglCaps); + int eglConfigID = eglCaps.getVisualID(VIDType.EGL_CONFIG); + long eglConfig = EGLGraphicsConfiguration.EGLConfigId2EGLConfig(getDisplayHandle(), eglConfigID); - eglWindowHandle = CreateWindow(getDisplayHandle(), eglAttribs); + eglWindowHandle = CreateWindow(getDisplayHandle(), eglConfig); if (eglWindowHandle == 0) { - throw new NativeWindowException("Error creating egl window: "+cfg); + throw new NativeWindowException("Error creating egl window: "+cfg+", eglConfigID "+eglConfigID+", eglConfig 0x"+Long.toHexString(eglConfig)); } setVisible0(eglWindowHandle, false); setWindowHandle(RealizeWindow(eglWindowHandle)); @@ -80,6 +84,7 @@ public class KDWindow extends WindowImpl { windowHandleClose = eglWindowHandle; } + @Override protected void closeNativeImpl() { if(0!=windowHandleClose) { CloseWindow(windowHandleClose, windowUserData); @@ -87,14 +92,16 @@ public class KDWindow extends WindowImpl { } } + @Override protected void requestFocusImpl(boolean reparented) { } + @Override protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { setVisible0(eglWindowHandle, 0 != ( FLAG_IS_VISIBLE & flags)); visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); } - + if(0!=eglWindowHandle) { if(0 != ( FLAG_CHANGE_FULLSCREEN & flags)) { final boolean fs = 0 != ( FLAG_IS_FULLSCREEN & flags) ; @@ -114,28 +121,30 @@ public class KDWindow extends WindowImpl { System.err.println("setPosition n/a in KD"); } } - + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); } - + return true; } + @Override protected Point getLocationOnScreenImpl(int x, int y) { return new Point(x,y); } + @Override protected void updateInsetsImpl(Insets insets) { - // nop .. + // nop .. } - + //---------------------------------------------------------------------- // Internals only // protected static native boolean initIDs(); - private native long CreateWindow(long displayHandle, int[] attributes); + private native long CreateWindow(long displayHandle, long eglConfig); private native long RealizeWindow(long eglWindowHandle); private native int CloseWindow(long eglWindowHandle, long userData); private native void setVisible0(long eglWindowHandle, boolean visible); @@ -149,7 +158,7 @@ public class KDWindow extends WindowImpl { @Override protected void sizeChanged(boolean defer, int newWidth, int newHeight, boolean force) { if(isFullscreen()) { - ((KDScreen)getScreen()).sizeChanged(getWidth(), getHeight()); + ((ScreenDriver)getScreen()).sizeChanged(getWidth(), getHeight()); } super.sizeChanged(defer, newWidth, newHeight, force); } diff --git a/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java b/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java new file mode 100644 index 000000000..b7c86a26d --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/linux/LinuxEventDeviceTracker.java @@ -0,0 +1,959 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.linux; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.lang.Integer; +import java.lang.Runnable; +import java.lang.String; +import java.lang.Thread; +import java.nio.ByteBuffer; + +import jogamp.newt.WindowImpl; + +import com.jogamp.common.nio.StructAccessor; +import com.jogamp.newt.Window; +import com.jogamp.newt.event.InputEvent; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.event.WindowListener; +import com.jogamp.newt.event.WindowUpdateEvent; +import com.jogamp.newt.event.KeyEvent; + +/** + * Experimental native event device tracker thread for GNU/Linux + * just reading <code>/dev/input/event*</code> + * within it's own polling thread. + */ + +public class LinuxEventDeviceTracker implements WindowListener { + + private static final LinuxEventDeviceTracker ledt; + + + static { + ledt = new LinuxEventDeviceTracker(); + final Thread t = new Thread(ledt.eventDeviceManager, "NEWT-LinuxEventDeviceManager"); + t.setDaemon(true); + t.start(); + } + + public static LinuxEventDeviceTracker getSingleton() { + return ledt; + } + + private WindowImpl focusedWindow = null; + private EventDeviceManager eventDeviceManager = new EventDeviceManager(); + + /* + The devices are in /dev/input: + + crw-r--r-- 1 root root 13, 64 Apr 1 10:49 event0 + crw-r--r-- 1 root root 13, 65 Apr 1 10:50 event1 + crw-r--r-- 1 root root 13, 66 Apr 1 10:50 event2 + crw-r--r-- 1 root root 13, 67 Apr 1 10:50 event3 + ... + + And so on up to event31. + */ + private EventDevicePoller[] eventDevicePollers = new EventDevicePoller[32]; + + @Override + public void windowResized(WindowEvent e) { } + + @Override + public void windowMoved(WindowEvent e) { } + + @Override + public void windowDestroyNotify(WindowEvent e) { + Object s = e.getSource(); + if(focusedWindow == s) { + focusedWindow = null; + } + } + + @Override + public void windowDestroyed(WindowEvent e) { } + + @Override + public void windowGainedFocus(WindowEvent e) { + Object s = e.getSource(); + if(s instanceof WindowImpl) { + focusedWindow = (WindowImpl) s; + } + } + + @Override + public void windowLostFocus(WindowEvent e) { + Object s = e.getSource(); + if(focusedWindow == s) { + focusedWindow = null; + } + } + + public static void main(String[] args ){ + System.setProperty("newt.debug.Window.KeyEvent", "true"); + LinuxEventDeviceTracker.getSingleton(); + try { + while(true) { + Thread.sleep(1000); + } + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + public void windowRepaint(WindowUpdateEvent e) { } + + class EventDeviceManager implements Runnable { + + private volatile boolean stop = false; + + @Override + public void run() { + File f = new File("/dev/input/"); + int number; + while(!stop){ + for(String path:f.list()){ + if(path.startsWith("event")) { + String stringNumber = path.substring(5); + number = Integer.parseInt(stringNumber); + if(number<32&&number>=0) { + if(eventDevicePollers[number]==null){ + eventDevicePollers[number] = new EventDevicePoller(number); + Thread t = new Thread(eventDevicePollers[number], "NEWT-LinuxEventDeviceTracker-event"+number); + t.setDaemon(true); + t.start(); + } else if(eventDevicePollers[number].stop) { + eventDevicePollers[number]=null; + } + } + } + } + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } + + class EventDevicePoller implements Runnable { + + private volatile boolean stop = false; + private String eventDeviceName; + + public EventDevicePoller(int eventDeviceNumber){ + this.eventDeviceName="/dev/input/event"+eventDeviceNumber; + } + + @Override + public void run() { + final byte[] b = new byte[16]; + /** + * The Linux input event interface. + * http://www.kernel.org/doc/Documentation/input/input.txt + * + * struct input_event { + * struct timeval time; + * unsigned short type; + * unsigned short code; + * unsigned int value; + * }; + */ + ByteBuffer bb = ByteBuffer.wrap(b); + StructAccessor s = new StructAccessor(bb); + final File f = new File(eventDeviceName); + f.setReadOnly(); + InputStream fis; + try { + fis = new FileInputStream(f); + } catch (FileNotFoundException e) { + stop=true; + return; + } + + int timeSeconds; + int timeSecondFraction; + short type; + short code; + int value; + + short keyCode=KeyEvent.VK_UNDEFINED; + char keyChar=' '; + short eventType=0; + int modifiers=0; + + loop: + while(!stop) { + int remaining=16; + while(remaining>0) { + int read = 0; + try { + read = fis.read(b, 0, remaining); + } catch (IOException e) { + stop = true; + break loop; + } + if(read<0) { + stop = true; // EOF of event device file !? + break loop; + } else { + remaining -= read; + } + } + + timeSeconds = s.getIntAt(0); + timeSecondFraction = s.getShortAt(4); + type = s.getShortAt(8); + code = s.getShortAt(10); + value = s.getIntAt(12); + + + /* + * Linux sends Keyboard events in the following order: + * EV_MSC (optional, contains scancode) + * EV_KEY + * SYN_REPORT (sent before next key) + */ + + switch(type) { + case 0: // SYN_REPORT + // Clear + eventType = 0; + keyCode = KeyEvent.VK_UNDEFINED; + keyChar = 0; // Print null for unprintable char. + if(Window.DEBUG_KEY_EVENT) { + System.out.println("[SYN_REPORT----]"); + } + break; + case 1: // EV_KEY + keyCode = LinuxEVKey2NewtVKey(code); // The device independent code. + keyChar = NewtVKey2Unicode(keyCode, modifiers); // The printable character w/ key modifiers. + if(Window.DEBUG_KEY_EVENT) { + System.out.println("[EV_KEY: [time "+timeSeconds+":"+timeSecondFraction+"] type "+type+" / code "+code+" = value "+value); + } + + switch(value) { + case 0: + eventType=KeyEvent.EVENT_KEY_RELEASED; + + switch(keyCode) { + case KeyEvent.VK_SHIFT: + modifiers &= ~InputEvent.SHIFT_MASK; + break; + case KeyEvent.VK_ALT: + modifiers &= ~InputEvent.ALT_MASK; + break; + case KeyEvent.VK_ALT_GRAPH: + modifiers &= ~InputEvent.ALT_GRAPH_MASK; + break; + case KeyEvent.VK_CONTROL: + modifiers &= ~InputEvent.CTRL_MASK; + break; + } + + if(null != focusedWindow) { + focusedWindow.sendKeyEvent(eventType, modifiers, keyCode, keyCode, keyChar); + } + if(Window.DEBUG_KEY_EVENT) { + System.out.println("[event released] keyCode: "+keyCode+" keyChar: "+keyChar+ " modifiers: "+modifiers); + } + break; + case 1: + eventType=KeyEvent.EVENT_KEY_PRESSED; + + switch(keyCode) { + case KeyEvent.VK_SHIFT: + modifiers |= InputEvent.SHIFT_MASK; + break; + case KeyEvent.VK_ALT: + modifiers |= InputEvent.ALT_MASK; + break; + case KeyEvent.VK_ALT_GRAPH: + modifiers |= InputEvent.ALT_GRAPH_MASK; + break; + case KeyEvent.VK_CONTROL: + modifiers |= InputEvent.CTRL_MASK; + break; + } + + if(null != focusedWindow) { + focusedWindow.sendKeyEvent(eventType, modifiers, keyCode, keyCode, keyChar); + } + if(Window.DEBUG_KEY_EVENT) { + System.out.println("[event pressed] keyCode: "+keyCode+" keyChar: "+keyChar+ " modifiers: "+modifiers); + } + break; + case 2: + eventType=KeyEvent.EVENT_KEY_PRESSED; + modifiers |= InputEvent.AUTOREPEAT_MASK; + + switch(keyCode) { + case KeyEvent.VK_SHIFT: + modifiers |= InputEvent.SHIFT_MASK; + break; + case KeyEvent.VK_ALT: + modifiers |= InputEvent.ALT_MASK; + break; + case KeyEvent.VK_ALT_GRAPH: + modifiers |= InputEvent.ALT_GRAPH_MASK; + break; + case KeyEvent.VK_CONTROL: + modifiers |= InputEvent.CTRL_MASK; + break; + } + + if(null != focusedWindow) { + //Send syntetic autorepeat release + focusedWindow.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keyCode, keyChar); + + focusedWindow.sendKeyEvent(eventType, modifiers, keyCode, keyCode, keyChar); + } + if(Window.DEBUG_KEY_EVENT) { + System.out.println("[event released auto] keyCode: "+keyCode+" keyChar: "+keyChar+ " modifiers: "+modifiers); + System.out.println("[event pressed auto] keyCode: "+keyCode+" keyChar: "+keyChar+ " modifiers: "+modifiers); + } + modifiers &= ~InputEvent.AUTOREPEAT_MASK; + break; + } + break; + case 4: // EV_MSC + if(code==4) { // MSC_SCAN + // scancode ignore, linux kernel specific + } + break; + // TODO: handle joystick events + // TODO: handle mouse events + // TODO: handle headphone/hdmi connector events + default: // Print number. + if(Window.DEBUG_KEY_EVENT) { + System.out.println("TODO EventDevicePoller: [time "+timeSeconds+":"+timeSecondFraction+"] type "+type+" / code "+code+" = value "+value); + } + } + } + + if(null != fis) { + try { + fis.close(); + } catch (IOException e) { + } + } + stop=true; + } + + private char NewtVKey2Unicode(short VK, int modifiers) { + if( KeyEvent.isPrintableKey(VK, true) ) { + if((modifiers & InputEvent.SHIFT_MASK) == InputEvent.SHIFT_MASK) { + return (char)VK; + } else { + return String.valueOf((char)VK).toLowerCase().charAt(0); + } + } + return 0; + } + + @SuppressWarnings("unused") + private char LinuxEVKey2Unicode(short EVKey) { + // This is the stuff normally mapped by a system keymap + + switch(EVKey) { + case 17: // w + return 'w'; + case 31: // s + return 's'; + case 30: // a + return 'a'; + case 32: // d + return 'd'; + case 1: // ESC + return 27; + case 28: // Enter + case 96: // Keypad Enter + return '\n'; + case 57: // Space + return ' '; + case 11: // 0 + case 82: // Numpad 0 + return '0'; + case 2: // 1 + case 79: // Numpad 1 + return '1'; + case 3: // 2 + case 80: // Numpad 1 + return '2'; + case 4: // 3 + case 81: // Numpad 3 + return '3'; + case 5: // 4 + case 75: // Numpad 4 + return '4'; + case 6: // 5 + case 76: // Numpad 5 + return '5'; + case 7: // 6 + case 77: // Numpad 6 + return '6'; + case 8: // 7 + case 71: // Numpad 7 + return '7'; + case 9: // 8 + case 72: // Numpad 8 + return '8'; + case 10: // 9 + case 73: // Numpad 9 + return '9'; + + default: + } + + return 0; + } + + private short LinuxEVKey2NewtVKey(short EVKey) { + + switch(EVKey) { + case 1: // ESC + return KeyEvent.VK_ESCAPE; + case 2: // 1 + return KeyEvent.VK_1; + case 79: // Numpad 1 + return KeyEvent.VK_NUMPAD1; + case 3: // 2 + return KeyEvent.VK_2; + case 80: // Numpad 2 + return KeyEvent.VK_NUMPAD2; + case 4: // 3 + return KeyEvent.VK_3; + case 81: // Numpad 3 + return KeyEvent.VK_NUMPAD3; + case 5: // 4 + return KeyEvent.VK_4; + case 75: // Numpad 4 + return KeyEvent.VK_NUMPAD4; + case 6: // 5 + return KeyEvent.VK_5; + case 76: // Numpad 5 + return KeyEvent.VK_NUMPAD5; + case 7: // 6 + return KeyEvent.VK_6; + case 77: // Numpad 6 + return KeyEvent.VK_NUMPAD6; + case 8: // 7 + return KeyEvent.VK_7; + case 71: // Numpad 7 + return KeyEvent.VK_NUMPAD7; + case 9: // 8 + return KeyEvent.VK_8; + case 72: // Numpad 8 + return KeyEvent.VK_NUMPAD8; + case 10: // 9 + return KeyEvent.VK_9; + case 73: // Numpad 9 + return KeyEvent.VK_NUMPAD9; + case 11: // 0 + return KeyEvent.VK_0; + case 82: // Numpad 0 + return KeyEvent.VK_NUMPAD0; + case 12: + return KeyEvent.VK_MINUS; + case 13: + return KeyEvent.VK_EQUALS; + case 14: // Backspace + return KeyEvent.VK_BACK_SPACE; + + case 15: + return KeyEvent.VK_TAB; + case 16: + return KeyEvent.VK_Q; + case 17: // w + return KeyEvent.VK_W; + case 18: + return KeyEvent.VK_E; + case 19: + return KeyEvent.VK_R; + case 20: + return KeyEvent.VK_T; + case 21: + return KeyEvent.VK_Y; + case 22: + return KeyEvent.VK_U; + case 23: + return KeyEvent.VK_I; + case 24: + return KeyEvent.VK_O; + case 25: + return KeyEvent.VK_P; + case 26: // left brace + return KeyEvent.VK_LEFT_PARENTHESIS; + case 27: // right brace + return KeyEvent.VK_RIGHT_PARENTHESIS; + case 28: // Enter + case 96: // Keypad Enter + return KeyEvent.VK_ENTER; + + case 29: // left ctrl + return KeyEvent.VK_CONTROL; + case 30: // a + return KeyEvent.VK_A; + case 31: // s + return KeyEvent.VK_S; + case 32: // d + return KeyEvent.VK_D; + case 33: + return KeyEvent.VK_F; + case 34: + return KeyEvent.VK_G; + case 35: + return KeyEvent.VK_H; + case 36: + return KeyEvent.VK_J; + case 37: + return KeyEvent.VK_K; + case 38: + return KeyEvent.VK_L; + case 39: + return KeyEvent.VK_SEMICOLON; + case 40: // apostrophe + return KeyEvent.VK_QUOTE; + case 41: // grave + return KeyEvent.VK_BACK_QUOTE; + + case 42: // left shift + return KeyEvent.VK_SHIFT; + case 43: + return KeyEvent.VK_BACK_SLASH; + case 44: + return KeyEvent.VK_Z; + case 45: + return KeyEvent.VK_X; + case 46: + return KeyEvent.VK_C; + case 47: + return KeyEvent.VK_V; + case 48: + return KeyEvent.VK_B; + case 49: + return KeyEvent.VK_N; + case 50: + return KeyEvent.VK_M; + case 51: + return KeyEvent.VK_COMMA; + case 52: // dot + return KeyEvent.VK_PERIOD; + case 53: + return KeyEvent.VK_SLASH; + case 54: + return KeyEvent.VK_SHIFT; + case 55: // kp asterisk + return KeyEvent.VK_ASTERISK; + case 56: // left alt + return KeyEvent.VK_ALT; + case 57: // Space + return KeyEvent.VK_SPACE; + case 58: + return KeyEvent.VK_CAPS_LOCK; + + case 59: + return KeyEvent.VK_F1; + case 60: + return KeyEvent.VK_F2; + case 61: + return KeyEvent.VK_F3; + case 62: + return KeyEvent.VK_F4; + case 63: + return KeyEvent.VK_F5; + case 64: + return KeyEvent.VK_F6; + case 65: + return KeyEvent.VK_F7; + case 66: + return KeyEvent.VK_F8; + case 67: + return KeyEvent.VK_F9; + case 68: + return KeyEvent.VK_F10; + + case 69: + return KeyEvent.VK_NUM_LOCK; + case 70: + return KeyEvent.VK_SCROLL_LOCK; + + case 74: // kp minus + return KeyEvent.VK_MINUS; + case 78: // kp plus + return KeyEvent.VK_PLUS; + case 83: // kp dot + return KeyEvent.VK_PERIOD; + + // TODO: add mappings for japanese special buttons + case 85: // zenkakuhankaku + case 86: // 102nd + break; // FIXME + + case 87: + return KeyEvent.VK_F11; + case 88: + return KeyEvent.VK_F12; + + case 89: // ro + return KeyEvent.VK_ROMAN_CHARACTERS; + case 90: // Katakana + return KeyEvent.VK_KATAKANA; + case 91: + return KeyEvent.VK_HIRAGANA; + + case 92: // kenkan + break; // FIXME + case 93: // katakana hiragana + break; // FIXME + case 94: // mu henkan + break; // FIXME + case 95: // kp jp comma + break; // FIXME + + case 97: // right ctrl + return KeyEvent.VK_CONTROL; + case 98: // kp slash + return KeyEvent.VK_SLASH; + + case 99: // sysrq + break; // FIXME + + case 100: // right alt + return KeyEvent.VK_ALT; + case 101: // linefeed + break; // FIXME + case 102: // home + return KeyEvent.VK_HOME; + case 103: // KEY_UP + return KeyEvent.VK_UP; + case 104: + return KeyEvent.VK_PAGE_UP; + case 105: // KEY_LEFT + return KeyEvent.VK_LEFT; + case 106: // KEY_RIGHT + return KeyEvent.VK_RIGHT; + case 107: + return KeyEvent.VK_END; + case 108: // KEY_DOWN + return KeyEvent.VK_DOWN; + case 109: + return KeyEvent.VK_PAGE_DOWN; + case 110: + return KeyEvent.VK_INSERT; + case 111: // del + return KeyEvent.VK_DELETE; + + case 112: // macro + break; // FIXME DEAD_MACRON? + case 113: // mute + break; // FIXME + case 114: // vol up + break; // FIXME + case 115: // vol down + break; // FIXME + case 116: // power + break; // FIXME + + case 117: // kp equals + return KeyEvent.VK_EQUALS; + case 118: // kp plus minux + break; // FIXME + case 119: // pause + return KeyEvent.VK_PAUSE; + case 120: // scale AL compiz scale expose + break; // FIXME + case 121: // kp comma + return KeyEvent.VK_COMMA; + case 122: // hangeul + break; // FIXME + case 123: // hanja + break; // FIXME + case 124: // yen + break; // FIXME + + case 125: // left meta + case 126: // right meta + return KeyEvent.VK_META; + case 127: // compose + return KeyEvent.VK_COMPOSE; + + case 128: // stop + return KeyEvent.VK_STOP; + case 129: // again + return KeyEvent.VK_AGAIN; + case 130: // properties + return KeyEvent.VK_PROPS; + case 131: // undo + return KeyEvent.VK_UNDO; + case 132: // front + break; // FIXME + case 133: // copy + return KeyEvent.VK_COPY; + case 134: // open + break; // FIXME + case 135: // paste + return KeyEvent.VK_PASTE; + case 136: // find + return KeyEvent.VK_FIND; + case 137: // cut + return KeyEvent.VK_CUT; + case 138: // help + return KeyEvent.VK_HELP; + case 139: // menu + break; // FIXME + case 140: // calc + break; // FIXME + case 141: // setup + break; // FIXME + case 142: // sleep + break; // FIXME + case 143: // wakeup + break; // FIXME + case 144: // file + break; // FIXME + case 145: // send file + break; // FIXME + case 146: // delete file + break; // FIXME + case 147: // xfer + break; // FIXME + case 148: // prog1 + break; // FIXME + case 149: // prog2 + break; // FIXME + case 150: // www + break; // FIXME + case 151: // msdos + break; // FIXME + case 152: // coffee + break; // FIXME + case 153: // direction + break; // FIXME + case 154: // cycle windows + break; // FIXME + case 155: // mail + break; // FIXME + case 156: // bookmarks + break; // FIXME + case 157: // computer + break; // FIXME + case 158: // back + break; // FIXME + case 159: // forward + break; // FIXME + case 160: // close cd + break; // FIXME + case 161: // eject cd + break; // FIXME + case 162: // eject close cd + break; // FIXME + case 163: // next song + break; // FIXME + case 164: // play pause + break; // FIXME + case 165: // previous song + break; // FIXME + case 166: // stop cd + break; // FIXME + case 167: // record + break; // FIXME + case 168: // rewind + break; // FIXME + case 169: // phone + break; // FIXME + case 170: // ISO + break; // FIXME + case 171: // config + break; // FIXME + case 172: // home page + break; // FIXME + case 173: // refresh + break; // FIXME + case 174: // exit + break; // FIXME + case 175: // move + break; // FIXME + case 176: // edit + break; // FIXME + case 177: // scroll up + break; // FIXME PAGE_UP? + case 178: // scroll down + break; // FIXME PAGE_DOWN? + case 179: // kp left paren + return KeyEvent.VK_LEFT_PARENTHESIS; + case 180: // kp right paren + return KeyEvent.VK_RIGHT_PARENTHESIS; + case 181: // new + break; // FIXME + case 182: // redo + break; // FIXME + + case 183: // F13 + return KeyEvent.VK_F13; + case 184: // F14 + return KeyEvent.VK_F14; + case 185: // F15 + return KeyEvent.VK_F15; + case 186: // F16 + return KeyEvent.VK_F16; + case 187: // F17 + return KeyEvent.VK_F17; + case 188: // F18 + return KeyEvent.VK_F18; + case 189: // F19 + return KeyEvent.VK_F19; + case 190: // F20 + return KeyEvent.VK_F20; + case 191: // F21 + return KeyEvent.VK_F21; + case 192: // F22 + return KeyEvent.VK_F22; + case 193: // F23 + return KeyEvent.VK_F23; + case 194: // F24 + return KeyEvent.VK_F24; + + case 200: // play cd + break; // FIXME + case 201: // pause cd + break; // FIXME + case 202: // prog 3 + break; // FIXME + case 203: // prog 4 + break; // FIXME + case 204: // dashboard + break; // FIXME + case 205: // suspend + break; // FIXME + case 206: // close + break; // FIXME + case 207: // play + break; // FIXME + case 208: // fast forward + break; // FIXME + case 210: // print + return KeyEvent.VK_PRINTSCREEN; // FIXME ? + case 211: // HP + break; // FIXME + case 212: // camera + break; // FIXME + case 213: // sound + break; // FIXME + case 214: // question + break; // FIXME + case 215: // email + break; // FIXME + case 216: // chat + break; // FIXME + case 217: // search + break; // FIXME + case 218: // connect + break; // FIXME + case 219: // finance + break; // FIXME + case 220: // sport + break; // FIXME + case 221: // shop + break; // FIXME + case 222: // alt erase + break; // FIXME + case 223: // cancel + break; // FIXME + case 224: // brightness down + break; // FIXME + case 225: // brightness up + break; // FIXME + case 226: // media + break; // FIXME + case 227: // switch video mode + break; // FIXME + case 228: // kb dillum toggle + break; // FIXME + case 229: // kb dillum down + break; // FIXME + case 230: // kb dillum up + break; // FIXME + case 231: // send + break; // FIXME + case 232: // reply + break; // FIXME + case 233: // forward mail + break; // FIXME + case 234: // save + break; // FIXME + case 235: // documents + break; // FIXME + case 236: // battery + break; // FIXME + case 237: // bluetooth + break; // FIXME + case 238: // wlan + break; // FIXME + case 239: // UWB + break; // FIXME + case 240: // unknown + return KeyEvent.VK_UNDEFINED; + case 241: // video next + break; // FIXME + case 242: // video prev + break; // FIXME + case 243: // brightness cycle + break; // FIXME + case 244: // brightness zero + break; // FIXME + case 245: // display off + break; // FIXME + case 246: // wimax + break; // FIXME + case 247: // rf kill radio off + break; // FIXME + case 248: // mic mute + break; // FIXME + + default: + } + + if(Window.DEBUG_KEY_EVENT) { + System.out.println("TODO LinuxEVKey2NewtVKey: Unmapped EVKey "+EVKey); + } + + return KeyEvent.VK_UNDEFINED; + } + } +} diff --git a/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java b/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java new file mode 100644 index 000000000..9d7b8931b --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java @@ -0,0 +1,224 @@ +/** + * Copyright 2012 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.linux; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import jogamp.newt.WindowImpl; + +import com.jogamp.newt.Window; +import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.event.WindowListener; +import com.jogamp.newt.event.WindowUpdateEvent; + +/** + * Experimental native mouse tracker thread for GNU/Linux + * just reading <code>/dev/input/mice</code> + * within it's own polling thread. + */ +public class LinuxMouseTracker implements WindowListener { + + private static final LinuxMouseTracker lmt; + + static { + lmt = new LinuxMouseTracker(); + final Thread t = new Thread(lmt.mouseDevicePoller, "NEWT-LinuxMouseTracker"); + t.setDaemon(true); + t.start(); + } + + public static LinuxMouseTracker getSingleton() { + return lmt; + } + + private volatile boolean stop = false; + private int x = 0; + private int y = 0; + private short buttonDown = 0; + private int old_x = 0; + private int old_y = 0; + private volatile int lastFocusedX = 0; + private volatile int lastFocusedY = 0; + private short old_buttonDown = 0; + private WindowImpl focusedWindow = null; + private final MouseDevicePoller mouseDevicePoller = new MouseDevicePoller(); + + public final int getLastX() { return lastFocusedX; } + public final int getLastY() { return lastFocusedY; } + + @Override + public void windowResized(WindowEvent e) { } + + @Override + public void windowMoved(WindowEvent e) { } + + @Override + public void windowDestroyNotify(WindowEvent e) { + Object s = e.getSource(); + if(focusedWindow == s) { + focusedWindow = null; + } + } + + @Override + public void windowDestroyed(WindowEvent e) { } + + @Override + public void windowGainedFocus(WindowEvent e) { + Object s = e.getSource(); + if(s instanceof WindowImpl) { + focusedWindow = (WindowImpl) s; + } + } + + @Override + public void windowLostFocus(WindowEvent e) { + Object s = e.getSource(); + if(focusedWindow == s) { + focusedWindow = null; + } + } + + @Override + public void windowRepaint(WindowUpdateEvent e) { } + + class MouseDevicePoller implements Runnable { + @Override + public void run() { + final byte[] b = new byte[3]; + final File f = new File("/dev/input/mice"); + f.setReadOnly(); + InputStream fis; + try { + fis = new FileInputStream(f); + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return; + } + int xd=0,yd=0; //x/y movement delta + boolean xo=false,yo=false; // x/y overflow (out of range -255 to +255) + boolean lb=false,mb=false,rb=false,hs=false,vs=false; //left/middle/right mousebutton + while(!stop) { + int remaining=3; + while(remaining>0) { + int read = 0; + try { + read = fis.read(b, 0, remaining); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if(read<0) { + stop = true; // EOF of mouse !? + } else { + remaining -= read; + } + } + lb=(b[0]&1)>0; + rb=(b[0]&2)>0; + mb=(b[0]&4)>0; + hs=(b[0]&16)>0; + vs=(b[0]&32)>0; + xo=(b[0]&64)>0; + yo=(b[0]&128)>0; + xd=b[1]; + yd=b[2]; + + x+=xd; + y-=yd; + + if(x<0) { + x=0; + } + if(y<0) { + y=0; + } + + buttonDown = 0; + if(lb) { + buttonDown = MouseEvent.BUTTON1; + } + if(mb) { + buttonDown = MouseEvent.BUTTON2; + } + if(rb) { + buttonDown = MouseEvent.BUTTON3; + } + + if(null != focusedWindow) { + if( x >= focusedWindow.getScreen().getWidth() ) { + x = focusedWindow.getScreen().getWidth() - 1; + } + if( y >= focusedWindow.getScreen().getHeight() ) { + y = focusedWindow.getScreen().getHeight() - 1; + } + final int wx = x - focusedWindow.getX(), wy = y - focusedWindow.getY(); + if(old_x != x || old_y != y) { + // mouse moved + lastFocusedX = wx; + lastFocusedY = wy; + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_MOVED, 0, wx, wy, (short)0, 0 ); + } + + if(old_buttonDown != buttonDown) { + // press/release + if( 0 != buttonDown ) { + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_PRESSED, 0, wx, wy, buttonDown, 0 ); + } else { + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_RELEASED, 0, wx, wy, old_buttonDown, 0 ); + } + } + } else { + if(Window.DEBUG_MOUSE_EVENT) { + System.out.println(x+"/"+y+", hs="+hs+",vs="+vs+",lb="+lb+",rb="+rb+",mb="+mb+",xo="+xo+",yo="+yo+"xd="+xd+",yd="+yd); + } + } + + old_x = x; + old_y = y; + old_buttonDown = buttonDown; + + } + if(null != fis) { + try { + fis.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } +} diff --git a/src/newt/classes/jogamp/newt/driver/macosx/MacDisplay.java b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java index 18f8d9538..d850a18af 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/MacDisplay.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java @@ -1,21 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * + * Copyright (c) 2012 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 @@ -28,29 +29,61 @@ * 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.driver.macosx; +import java.net.URLConnection; +import java.nio.Buffer; +import java.nio.ByteBuffer; + import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.util.PixelFormat; +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.IOUtil; import com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice; +import com.jogamp.newt.NewtFactory; +import com.jogamp.opengl.util.PNGPixelRect; import jogamp.newt.DisplayImpl; import jogamp.newt.NEWTJNILibLoader; -public class MacDisplay extends DisplayImpl { +public class DisplayDriver extends DisplayImpl { + private static final PNGPixelRect defaultIconData; + static { NEWTJNILibLoader.loadNEWT(); if(!initNSApplication0()) { throw new NativeWindowException("Failed to initialize native Application hook"); } - if(!MacWindow.initIDs0()) { + if(!WindowDriver.initIDs0()) { throw new NativeWindowException("Failed to initialize jmethodIDs"); } + { + PNGPixelRect image=null; + if( DisplayImpl.isPNGUtilAvailable() ) { + try { + // NOTE: MUST BE DIRECT BUFFER, since NSBitmapImageRep uses buffer directly! + final IOUtil.ClassResources iconRes = NewtFactory.getWindowIcons(); + final URLConnection urlConn = iconRes.resolve(iconRes.resourceCount()-1); + image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, true /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } catch (Exception e) { + e.printStackTrace(); + } + } + defaultIconData = image; + if( null != defaultIconData ) { + final Buffer pixels = defaultIconData.getPixels(); + DisplayDriver.setAppIcon0( + pixels, Buffers.getDirectBufferByteOffset(pixels), true /* pixels_is_direct */, + defaultIconData.getSize().getWidth(), defaultIconData.getSize().getHeight()); + } + } + if(DEBUG) { System.err.println("MacDisplay.init App and IDs OK "+Thread.currentThread().getName()); } @@ -59,19 +92,45 @@ public class MacDisplay extends DisplayImpl { public static void initSingleton() { // just exist to ensure static init has been run } - - public MacDisplay() { + + public DisplayDriver() { } + @Override protected void dispatchMessagesNative() { // nop } - + + @Override protected void createNativeImpl() { aDevice = new MacOSXGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); } - protected void closeNativeImpl() { } + @Override + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { + aDevice.close(); + } + + /** + * {@inheritDoc} + * <p> + * NOTE: MUST BE DIRECT BUFFER, since NSBitmapImageRep uses buffer directly! + * </p> + */ + @Override + public final boolean getNativePointerIconForceDirectNIO() { return true; } + + @Override + protected final long createPointerIconImpl(PixelFormat pixelformat, int width, int height, final ByteBuffer pixels, final int hotX, final int hotY) { + return createPointerIcon0( + pixels, Buffers.getDirectBufferByteOffset(pixels), true /* pixels_is_direct */, + width, height, hotX, hotY); + } + + @Override + protected final void destroyPointerIconImpl(final long displayHandle, long piHandle) { + destroyPointerIcon0(piHandle); + } public static void runNSApplication() { runNSApplication0(); @@ -83,5 +142,9 @@ public class MacDisplay extends DisplayImpl { private static native boolean initNSApplication0(); private static native void runNSApplication0(); private static native void stopNSApplication0(); + /* pp */ static native void setAppIcon0(Object pixels, int pixels_byte_offset, boolean pixels_is_direct, int width, int height); + private static native long createPointerIcon0(Object pixels, int pixels_byte_offset, boolean pixels_is_direct, int width, int height, int hotX, int hotY); + private static native long destroyPointerIcon0(long handle); + } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java b/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java index 46625f7a9..a89150d7c 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java @@ -1,59 +1,159 @@ +/** + * Copyright 2011 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ package jogamp.newt.driver.macosx; import com.jogamp.newt.event.KeyEvent; public class MacKeyUtil { - - // KeyCodes (independent) - private static final int kVK_Return = 0x24; - private static final int kVK_Tab = 0x30; - private static final int kVK_Space = 0x31; - private static final int kVK_Delete = 0x33; - private static final int kVK_Escape = 0x35; - private static final int kVK_Command = 0x37; - private static final int kVK_Shift = 0x38; - private static final int kVK_CapsLock = 0x39; - private static final int kVK_Option = 0x3A; - private static final int kVK_Control = 0x3B; - private static final int kVK_RightShift = 0x3C; - private static final int kVK_RightOption = 0x3D; - private static final int kVK_RightControl = 0x3E; - private static final int kVK_Function = 0x3F; - private static final int kVK_F17 = 0x40; - private static final int kVK_VolumeUp = 0x48; - private static final int kVK_VolumeDown = 0x49; - private static final int kVK_Mute = 0x4A; - private static final int kVK_F18 = 0x4F; - private static final int kVK_F19 = 0x50; - private static final int kVK_F20 = 0x5A; - private static final int kVK_F5 = 0x60; - private static final int kVK_F6 = 0x61; - private static final int kVK_F7 = 0x62; - private static final int kVK_F3 = 0x63; - private static final int kVK_F8 = 0x64; - private static final int kVK_F9 = 0x65; - private static final int kVK_F11 = 0x67; - private static final int kVK_F13 = 0x69; - private static final int kVK_F16 = 0x6A; - private static final int kVK_F14 = 0x6B; - private static final int kVK_F10 = 0x6D; - private static final int kVK_F12 = 0x6F; - private static final int kVK_F15 = 0x71; - private static final int kVK_Help = 0x72; - private static final int kVK_Home = 0x73; - private static final int kVK_PageUp = 0x74; - private static final int kVK_ForwardDelete = 0x75; - private static final int kVK_F4 = 0x76; - private static final int kVK_End = 0x77; - private static final int kVK_F2 = 0x78; - private static final int kVK_PageDown = 0x79; - private static final int kVK_F1 = 0x7A; - private static final int kVK_LeftArrow = 0x7B; - private static final int kVK_RightArrow = 0x7C; - private static final int kVK_DownArrow = 0x7D; - private static final int kVK_UpArrow = 0x7E; - + + // + // KeyCodes (Layout Dependent) + // + private static final short kVK_ANSI_A = 0x00; + private static final short kVK_ANSI_S = 0x01; + private static final short kVK_ANSI_D = 0x02; + private static final short kVK_ANSI_F = 0x03; + private static final short kVK_ANSI_H = 0x04; + private static final short kVK_ANSI_G = 0x05; + private static final short kVK_ANSI_Z = 0x06; + private static final short kVK_ANSI_X = 0x07; + private static final short kVK_ANSI_C = 0x08; + private static final short kVK_ANSI_V = 0x09; + private static final short kVK_ANSI_B = 0x0B; + private static final short kVK_ANSI_Q = 0x0C; + private static final short kVK_ANSI_W = 0x0D; + private static final short kVK_ANSI_E = 0x0E; + private static final short kVK_ANSI_R = 0x0F; + private static final short kVK_ANSI_Y = 0x10; + private static final short kVK_ANSI_T = 0x11; + private static final short kVK_ANSI_1 = 0x12; + private static final short kVK_ANSI_2 = 0x13; + private static final short kVK_ANSI_3 = 0x14; + private static final short kVK_ANSI_4 = 0x15; + private static final short kVK_ANSI_6 = 0x16; + private static final short kVK_ANSI_5 = 0x17; + private static final short kVK_ANSI_Equal = 0x18; + private static final short kVK_ANSI_9 = 0x19; + private static final short kVK_ANSI_7 = 0x1A; + private static final short kVK_ANSI_Minus = 0x1B; + private static final short kVK_ANSI_8 = 0x1C; + private static final short kVK_ANSI_0 = 0x1D; + private static final short kVK_ANSI_RightBracket = 0x1E; + private static final short kVK_ANSI_O = 0x1F; + private static final short kVK_ANSI_U = 0x20; + private static final short kVK_ANSI_LeftBracket = 0x21; + private static final short kVK_ANSI_I = 0x22; + private static final short kVK_ANSI_P = 0x23; + private static final short kVK_ANSI_L = 0x25; + private static final short kVK_ANSI_J = 0x26; + private static final short kVK_ANSI_Quote = 0x27; + private static final short kVK_ANSI_K = 0x28; + private static final short kVK_ANSI_Semicolon = 0x29; + private static final short kVK_ANSI_Backslash = 0x2A; + private static final short kVK_ANSI_Comma = 0x2B; + private static final short kVK_ANSI_Slash = 0x2C; + private static final short kVK_ANSI_N = 0x2D; + private static final short kVK_ANSI_M = 0x2E; + private static final short kVK_ANSI_Period = 0x2F; + private static final short kVK_ANSI_Grave = 0x32; + private static final short kVK_ANSI_KeypadDecimal = 0x41; + private static final short kVK_ANSI_KeypadMultiply = 0x43; + private static final short kVK_ANSI_KeypadPlus = 0x45; + private static final short kVK_ANSI_KeypadClear = 0x47; + private static final short kVK_ANSI_KeypadDivide = 0x4B; + private static final short kVK_ANSI_KeypadEnter = 0x4C; + private static final short kVK_ANSI_KeypadMinus = 0x4E; + private static final short kVK_ANSI_KeypadEquals = 0x51; + private static final short kVK_ANSI_Keypad0 = 0x52; + private static final short kVK_ANSI_Keypad1 = 0x53; + private static final short kVK_ANSI_Keypad2 = 0x54; + private static final short kVK_ANSI_Keypad3 = 0x55; + private static final short kVK_ANSI_Keypad4 = 0x56; + private static final short kVK_ANSI_Keypad5 = 0x57; + private static final short kVK_ANSI_Keypad6 = 0x58; + private static final short kVK_ANSI_Keypad7 = 0x59; + private static final short kVK_ANSI_Keypad8 = 0x5B; + private static final short kVK_ANSI_Keypad9 = 0x5C; + + // + // KeyCodes (Layout Independent) + // + private static final short kVK_Return = 0x24; + private static final short kVK_Tab = 0x30; + private static final short kVK_Space = 0x31; + private static final short kVK_Delete = 0x33; + private static final short kVK_Escape = 0x35; + private static final short kVK_Command = 0x37; + private static final short kVK_Shift = 0x38; + private static final short kVK_CapsLock = 0x39; + private static final short kVK_Option = 0x3A; + private static final short kVK_Control = 0x3B; + private static final short kVK_RightShift = 0x3C; + private static final short kVK_RightOption = 0x3D; + private static final short kVK_RightControl = 0x3E; + // private static final short kVK_Function = 0x3F; + private static final short kVK_F17 = 0x40; + // private static final short kVK_VolumeUp = 0x48; + // private static final short kVK_VolumeDown = 0x49; + // private static final short kVK_Mute = 0x4A; + private static final short kVK_F18 = 0x4F; + private static final short kVK_F19 = 0x50; + private static final short kVK_F20 = 0x5A; + private static final short kVK_F5 = 0x60; + private static final short kVK_F6 = 0x61; + private static final short kVK_F7 = 0x62; + private static final short kVK_F3 = 0x63; + private static final short kVK_F8 = 0x64; + private static final short kVK_F9 = 0x65; + private static final short kVK_F11 = 0x67; + private static final short kVK_F13 = 0x69; + private static final short kVK_F16 = 0x6A; + private static final short kVK_F14 = 0x6B; + private static final short kVK_F10 = 0x6D; + private static final short kVK_F12 = 0x6F; + private static final short kVK_F15 = 0x71; + private static final short kVK_Help = 0x72; + private static final short kVK_Home = 0x73; + private static final short kVK_PageUp = 0x74; + private static final short kVK_ForwardDelete = 0x75; + private static final short kVK_F4 = 0x76; + private static final short kVK_End = 0x77; + private static final short kVK_F2 = 0x78; + private static final short kVK_PageDown = 0x79; + private static final short kVK_F1 = 0x7A; + private static final short kVK_LeftArrow = 0x7B; + private static final short kVK_RightArrow = 0x7C; + private static final short kVK_DownArrow = 0x7D; + private static final short kVK_UpArrow = 0x7E; + + // // Key constants handled differently on Mac OS X than other platforms + // private static final char NSUpArrowFunctionKey = 0xF700; private static final char NSDownArrowFunctionKey = 0xF701; private static final char NSLeftArrowFunctionKey = 0xF702; @@ -82,6 +182,7 @@ public class MacKeyUtil { private static final char NSF22FunctionKey = 0xF719; private static final char NSF23FunctionKey = 0xF71A; private static final char NSF24FunctionKey = 0xF71B; + /** private static final char NSF25FunctionKey = 0xF71C; private static final char NSF26FunctionKey = 0xF71D; private static final char NSF27FunctionKey = 0xF71E; @@ -93,6 +194,7 @@ public class MacKeyUtil { private static final char NSF33FunctionKey = 0xF724; private static final char NSF34FunctionKey = 0xF725; private static final char NSF35FunctionKey = 0xF726; + */ private static final char NSInsertFunctionKey = 0xF727; private static final char NSDeleteFunctionKey = 0xF728; private static final char NSHomeFunctionKey = 0xF729; @@ -103,10 +205,11 @@ public class MacKeyUtil { private static final char NSPrintScreenFunctionKey = 0xF72E; private static final char NSScrollLockFunctionKey = 0xF72F; private static final char NSPauseFunctionKey = 0xF730; - private static final char NSSysReqFunctionKey = 0xF731; - private static final char NSBreakFunctionKey = 0xF732; - private static final char NSResetFunctionKey = 0xF733; + // private static final char NSSysReqFunctionKey = 0xF731; + // private static final char NSBreakFunctionKey = 0xF732; + // private static final char NSResetFunctionKey = 0xF733; private static final char NSStopFunctionKey = 0xF734; + /** private static final char NSMenuFunctionKey = 0xF735; private static final char NSUserFunctionKey = 0xF736; private static final char NSSystemFunctionKey = 0xF737; @@ -126,26 +229,99 @@ public class MacKeyUtil { private static final char NSFindFunctionKey = 0xF745; private static final char NSHelpFunctionKey = 0xF746; private static final char NSModeSwitchFunctionKey = 0xF747; - - static int validateKeyCode(int keyCode, char keyChar) { + */ + + static short validateKeyCode(short keyCode, char keyChar) { // OS X Virtual Keycodes switch(keyCode) { + // + // KeyCodes (Layout Dependent) + // + case kVK_ANSI_A: return KeyEvent.VK_A; + case kVK_ANSI_S: return KeyEvent.VK_S; + case kVK_ANSI_D: return KeyEvent.VK_D; + case kVK_ANSI_F: return KeyEvent.VK_F; + case kVK_ANSI_H: return KeyEvent.VK_H; + case kVK_ANSI_G: return KeyEvent.VK_G; + case kVK_ANSI_Z: return KeyEvent.VK_Z; + case kVK_ANSI_X: return KeyEvent.VK_X; + case kVK_ANSI_C: return KeyEvent.VK_C; + case kVK_ANSI_V: return KeyEvent.VK_V; + case kVK_ANSI_B: return KeyEvent.VK_B; + case kVK_ANSI_Q: return KeyEvent.VK_Q; + case kVK_ANSI_W: return KeyEvent.VK_W; + case kVK_ANSI_E: return KeyEvent.VK_E; + case kVK_ANSI_R: return KeyEvent.VK_R; + case kVK_ANSI_Y: return KeyEvent.VK_Y; + case kVK_ANSI_T: return KeyEvent.VK_T; + case kVK_ANSI_1: return KeyEvent.VK_1; + case kVK_ANSI_2: return KeyEvent.VK_2; + case kVK_ANSI_3: return KeyEvent.VK_3; + case kVK_ANSI_4: return KeyEvent.VK_4; + case kVK_ANSI_6: return KeyEvent.VK_6; + case kVK_ANSI_5: return KeyEvent.VK_5; + case kVK_ANSI_Equal: return KeyEvent.VK_EQUALS; + case kVK_ANSI_9: return KeyEvent.VK_9; + case kVK_ANSI_7: return KeyEvent.VK_7; + case kVK_ANSI_Minus: return KeyEvent.VK_MINUS; + case kVK_ANSI_8: return KeyEvent.VK_8; + case kVK_ANSI_0: return KeyEvent.VK_0; + case kVK_ANSI_RightBracket: return KeyEvent.VK_CLOSE_BRACKET; + case kVK_ANSI_O: return KeyEvent.VK_O; + case kVK_ANSI_U: return KeyEvent.VK_U; + case kVK_ANSI_LeftBracket: return KeyEvent.VK_OPEN_BRACKET; + case kVK_ANSI_I: return KeyEvent.VK_I; + case kVK_ANSI_P: return KeyEvent.VK_P; + case kVK_ANSI_L: return KeyEvent.VK_L; + case kVK_ANSI_J: return KeyEvent.VK_J; + case kVK_ANSI_Quote: return KeyEvent.VK_QUOTE; + case kVK_ANSI_K: return KeyEvent.VK_K; + case kVK_ANSI_Semicolon: return KeyEvent.VK_SEMICOLON; + case kVK_ANSI_Backslash: return KeyEvent.VK_BACK_SLASH; + case kVK_ANSI_Comma: return KeyEvent.VK_COMMA; + case kVK_ANSI_Slash: return KeyEvent.VK_SLASH; + case kVK_ANSI_N: return KeyEvent.VK_N; + case kVK_ANSI_M: return KeyEvent.VK_M; + case kVK_ANSI_Period: return KeyEvent.VK_PERIOD; + case kVK_ANSI_Grave: return KeyEvent.VK_BACK_QUOTE; // KeyEvent.VK_DEAD_GRAVE + case kVK_ANSI_KeypadDecimal: return KeyEvent.VK_DECIMAL; + case kVK_ANSI_KeypadMultiply: return KeyEvent.VK_MULTIPLY; + case kVK_ANSI_KeypadPlus: return KeyEvent.VK_PLUS; + case kVK_ANSI_KeypadClear: return KeyEvent.VK_CLEAR; + case kVK_ANSI_KeypadDivide: return KeyEvent.VK_DIVIDE; + case kVK_ANSI_KeypadEnter: return KeyEvent.VK_ENTER; + case kVK_ANSI_KeypadMinus: return KeyEvent.VK_MINUS; + case kVK_ANSI_KeypadEquals: return KeyEvent.VK_EQUALS; + case kVK_ANSI_Keypad0: return KeyEvent.VK_0; + case kVK_ANSI_Keypad1: return KeyEvent.VK_1; + case kVK_ANSI_Keypad2: return KeyEvent.VK_2; + case kVK_ANSI_Keypad3: return KeyEvent.VK_3; + case kVK_ANSI_Keypad4: return KeyEvent.VK_4; + case kVK_ANSI_Keypad5: return KeyEvent.VK_5; + case kVK_ANSI_Keypad6: return KeyEvent.VK_6; + case kVK_ANSI_Keypad7: return KeyEvent.VK_7; + case kVK_ANSI_Keypad8: return KeyEvent.VK_8; + case kVK_ANSI_Keypad9: return KeyEvent.VK_9; + + // + // KeyCodes (Layout Independent) + // case kVK_Return: return KeyEvent.VK_ENTER; case kVK_Tab: return KeyEvent.VK_TAB; case kVK_Space: return KeyEvent.VK_SPACE; case kVK_Delete: return KeyEvent.VK_BACK_SPACE; case kVK_Escape: return KeyEvent.VK_ESCAPE; - case kVK_Command: return KeyEvent.VK_ALT; + case kVK_Command: return KeyEvent.VK_WINDOWS; case kVK_Shift: return KeyEvent.VK_SHIFT; case kVK_CapsLock: return KeyEvent.VK_CAPS_LOCK; - case kVK_Option: return KeyEvent.VK_WINDOWS; + case kVK_Option: return KeyEvent.VK_ALT; case kVK_Control: return KeyEvent.VK_CONTROL; case kVK_RightShift: return KeyEvent.VK_SHIFT; - case kVK_RightOption: return KeyEvent.VK_WINDOWS; + case kVK_RightOption: return KeyEvent.VK_ALT_GRAPH; case kVK_RightControl: return KeyEvent.VK_CONTROL; // case kVK_Function: return KeyEvent.VK_F; case kVK_F17: return KeyEvent.VK_F17; - // case kVK_VolumeUp: + // case kVK_VolumeUp: // case kVK_VolumeDown: // case kVK_Mute: case kVK_F18: return KeyEvent.VK_F18; @@ -178,85 +354,73 @@ public class MacKeyUtil { case kVK_DownArrow: return KeyEvent.VK_DOWN; case kVK_UpArrow: return KeyEvent.VK_UP; } - - if (keyChar == '\r') { - // Turn these into \n - return KeyEvent.VK_ENTER; - } - - if (keyChar >= NSUpArrowFunctionKey && keyChar <= NSModeSwitchFunctionKey) { - switch (keyChar) { - case NSUpArrowFunctionKey: return KeyEvent.VK_UP; - case NSDownArrowFunctionKey: return KeyEvent.VK_DOWN; - case NSLeftArrowFunctionKey: return KeyEvent.VK_LEFT; - case NSRightArrowFunctionKey: return KeyEvent.VK_RIGHT; - case NSF1FunctionKey: return KeyEvent.VK_F1; - case NSF2FunctionKey: return KeyEvent.VK_F2; - case NSF3FunctionKey: return KeyEvent.VK_F3; - case NSF4FunctionKey: return KeyEvent.VK_F4; - case NSF5FunctionKey: return KeyEvent.VK_F5; - case NSF6FunctionKey: return KeyEvent.VK_F6; - case NSF7FunctionKey: return KeyEvent.VK_F7; - case NSF8FunctionKey: return KeyEvent.VK_F8; - case NSF9FunctionKey: return KeyEvent.VK_F9; - case NSF10FunctionKey: return KeyEvent.VK_F10; - case NSF11FunctionKey: return KeyEvent.VK_F11; - case NSF12FunctionKey: return KeyEvent.VK_F12; - case NSF13FunctionKey: return KeyEvent.VK_F13; - case NSF14FunctionKey: return KeyEvent.VK_F14; - case NSF15FunctionKey: return KeyEvent.VK_F15; - case NSF16FunctionKey: return KeyEvent.VK_F16; - case NSF17FunctionKey: return KeyEvent.VK_F17; - case NSF18FunctionKey: return KeyEvent.VK_F18; - case NSF19FunctionKey: return KeyEvent.VK_F19; - case NSF20FunctionKey: return KeyEvent.VK_F20; - case NSF21FunctionKey: return KeyEvent.VK_F21; - case NSF22FunctionKey: return KeyEvent.VK_F22; - case NSF23FunctionKey: return KeyEvent.VK_F23; - case NSF24FunctionKey: return KeyEvent.VK_F24; - case NSInsertFunctionKey: return KeyEvent.VK_INSERT; - case NSDeleteFunctionKey: return KeyEvent.VK_DELETE; - case NSHomeFunctionKey: return KeyEvent.VK_HOME; - case NSBeginFunctionKey: return KeyEvent.VK_BEGIN; - case NSEndFunctionKey: return KeyEvent.VK_END; - case NSPageUpFunctionKey: return KeyEvent.VK_PAGE_UP; - case NSPageDownFunctionKey: return KeyEvent.VK_PAGE_DOWN; - case NSPrintScreenFunctionKey: return KeyEvent.VK_PRINTSCREEN; - case NSScrollLockFunctionKey: return KeyEvent.VK_SCROLL_LOCK; - case NSPauseFunctionKey: return KeyEvent.VK_PAUSE; - // Not handled: - // NSSysReqFunctionKey - // NSBreakFunctionKey - // NSResetFunctionKey - case NSStopFunctionKey: return KeyEvent.VK_STOP; - // Not handled: - // NSMenuFunctionKey - // NSUserFunctionKey - // NSSystemFunctionKey - // NSPrintFunctionKey - // NSClearLineFunctionKey - // NSClearDisplayFunctionKey - // NSInsertLineFunctionKey - // NSDeleteLineFunctionKey - // NSInsertCharFunctionKey - // NSDeleteCharFunctionKey - // NSPrevFunctionKey - // NSNextFunctionKey - // NSSelectFunctionKey - // NSExecuteFunctionKey - // NSUndoFunctionKey - // NSRedoFunctionKey - // NSFindFunctionKey - // NSHelpFunctionKey - // NSModeSwitchFunctionKey - default: break; - } - } - if ('a' <= keyChar && keyChar <= 'z') { - return KeyEvent.VK_A + ( keyChar - 'a' ) ; + switch (keyChar) { + case NSUpArrowFunctionKey: return KeyEvent.VK_UP; + case NSDownArrowFunctionKey: return KeyEvent.VK_DOWN; + case NSLeftArrowFunctionKey: return KeyEvent.VK_LEFT; + case NSRightArrowFunctionKey: return KeyEvent.VK_RIGHT; + case NSF1FunctionKey: return KeyEvent.VK_F1; + case NSF2FunctionKey: return KeyEvent.VK_F2; + case NSF3FunctionKey: return KeyEvent.VK_F3; + case NSF4FunctionKey: return KeyEvent.VK_F4; + case NSF5FunctionKey: return KeyEvent.VK_F5; + case NSF6FunctionKey: return KeyEvent.VK_F6; + case NSF7FunctionKey: return KeyEvent.VK_F7; + case NSF8FunctionKey: return KeyEvent.VK_F8; + case NSF9FunctionKey: return KeyEvent.VK_F9; + case NSF10FunctionKey: return KeyEvent.VK_F10; + case NSF11FunctionKey: return KeyEvent.VK_F11; + case NSF12FunctionKey: return KeyEvent.VK_F12; + case NSF13FunctionKey: return KeyEvent.VK_F13; + case NSF14FunctionKey: return KeyEvent.VK_F14; + case NSF15FunctionKey: return KeyEvent.VK_F15; + case NSF16FunctionKey: return KeyEvent.VK_F16; + case NSF17FunctionKey: return KeyEvent.VK_F17; + case NSF18FunctionKey: return KeyEvent.VK_F18; + case NSF19FunctionKey: return KeyEvent.VK_F19; + case NSF20FunctionKey: return KeyEvent.VK_F20; + case NSF21FunctionKey: return KeyEvent.VK_F21; + case NSF22FunctionKey: return KeyEvent.VK_F22; + case NSF23FunctionKey: return KeyEvent.VK_F23; + case NSF24FunctionKey: return KeyEvent.VK_F24; + case NSInsertFunctionKey: return KeyEvent.VK_INSERT; + case NSDeleteFunctionKey: return KeyEvent.VK_DELETE; + case NSHomeFunctionKey: return KeyEvent.VK_HOME; + case NSBeginFunctionKey: return KeyEvent.VK_BEGIN; + case NSEndFunctionKey: return KeyEvent.VK_END; + case NSPageUpFunctionKey: return KeyEvent.VK_PAGE_UP; + case NSPageDownFunctionKey: return KeyEvent.VK_PAGE_DOWN; + case NSPrintScreenFunctionKey: return KeyEvent.VK_PRINTSCREEN; + case NSScrollLockFunctionKey: return KeyEvent.VK_SCROLL_LOCK; + case NSPauseFunctionKey: return KeyEvent.VK_PAUSE; + // Not handled: + // NSSysReqFunctionKey + // NSBreakFunctionKey + // NSResetFunctionKey + case NSStopFunctionKey: return KeyEvent.VK_STOP; + // Not handled: + // NSMenuFunctionKey + // NSUserFunctionKey + // NSSystemFunctionKey + // NSPrintFunctionKey + // NSClearLineFunctionKey + // NSClearDisplayFunctionKey + // NSInsertLineFunctionKey + // NSDeleteLineFunctionKey + // NSInsertCharFunctionKey + // NSDeleteCharFunctionKey + // NSPrevFunctionKey + // NSNextFunctionKey + // NSSelectFunctionKey + // NSExecuteFunctionKey + // NSUndoFunctionKey + // NSRedoFunctionKey + // NSFindFunctionKey + // NSHelpFunctionKey + // NSModeSwitchFunctionKey } - return (int) keyChar; // let's hope for the best (compatibility of keyChar/keyCode's) - } + return (short) keyChar; // let's hope for the best (compatibility of keyChar/keyCode's) + } } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/MacScreen.java b/src/newt/classes/jogamp/newt/driver/macosx/MacScreen.java deleted file mode 100644 index b9c725fd4..000000000 --- a/src/newt/classes/jogamp/newt/driver/macosx/MacScreen.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * Copyright (c) 2011 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.driver.macosx; - -import java.util.List; - -import javax.media.nativewindow.DefaultGraphicsScreen; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.DimensionImmutable; -import javax.media.nativewindow.util.Point; - -import jogamp.newt.ScreenImpl; - -import com.jogamp.common.util.IntObjectHashMap; -import com.jogamp.newt.ScreenMode; -import com.jogamp.newt.util.ScreenModeUtil; - -public class MacScreen extends ScreenImpl { - - // caching native CGDisplayScreenSize() results, since it's ridiculous slow (~6 ms each call) - private static IntObjectHashMap/*<int, DimensionImmutable>*/ scrnIdx2Dimension; - - static { - MacDisplay.initSingleton(); - scrnIdx2Dimension = new IntObjectHashMap(); - scrnIdx2Dimension.setKeyNotFoundValue(null); - } - - public MacScreen() { - } - - protected void createNativeImpl() { - aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); - } - - protected void closeNativeImpl() { } - - private static native int getWidthImpl0(int scrn_idx); - private static native int getHeightImpl0(int scrn_idx); - - private int[] getScreenModeIdx(int idx) { - // caching native CGDisplayScreenSize() results, since it's ridiculous slow (~6 ms each call) - DimensionImmutable dim = (DimensionImmutable) scrnIdx2Dimension.get(screen_idx); - if(null == dim) { - int[] res = getScreenSizeMM0(screen_idx); - if(null == res || 0 == res.length) { - return null; - } - dim = new Dimension(res[0], res[1]); - scrnIdx2Dimension.put(screen_idx, dim); - } - - int[] modeProps = getScreenMode0(screen_idx, idx, dim.getWidth(), dim.getHeight()); - if (null == modeProps || 0 == modeProps.length) { - return null; - } - if(modeProps.length < ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL) { - throw new RuntimeException("properties array too short, should be >= "+ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL+", is "+modeProps.length); - } - return modeProps; - } - - private int nativeModeIdx; - - protected int[] getScreenModeFirstImpl() { - nativeModeIdx = 0; - return getScreenModeNextImpl(); - } - - protected int[] getScreenModeNextImpl() { - int[] modeProps = getScreenModeIdx(nativeModeIdx); - if (null != modeProps && 0 < modeProps.length) { - nativeModeIdx++; - return modeProps; - } - return null; - } - - protected ScreenMode getCurrentScreenModeImpl() { - int[] modeProps = getScreenModeIdx(-1); - if (null != modeProps && 0 < modeProps.length) { - return ScreenModeUtil.streamIn(modeProps, 0); - } - return null; - } - - protected boolean setCurrentScreenModeImpl(final ScreenMode screenMode) { - final List<ScreenMode> screenModes = this.getScreenModesOrig(); - final int screenModeIdx = screenModes.indexOf(screenMode); - if(0>screenModeIdx) { - throw new RuntimeException("ScreenMode not element of ScreenMode list: "+screenMode); - } - final int nativeModeIdx = getScreenModesIdx2NativeIdx().get(screenModeIdx); - return setScreenMode0(screen_idx, nativeModeIdx); - } - - protected int validateScreenIndex(int idx) { - return idx; - } - - protected void getVirtualScreenOriginAndSize(Point virtualOrigin, Dimension virtualSize) { - virtualOrigin.setX(0); - virtualOrigin.setY(0); - virtualSize.setWidth(getWidthImpl0(screen_idx)); - virtualSize.setHeight(getHeightImpl0(screen_idx)); - } - - private native int[] getScreenSizeMM0(int screen_idx); - private native int[] getScreenMode0(int screen_index, int mode_index, int widthMM, int heightMM); - private native boolean setScreenMode0(int screen_index, int mode_idx); -} diff --git a/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java b/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java deleted file mode 100644 index b45c60e69..000000000 --- a/src/newt/classes/jogamp/newt/driver/macosx/MacWindow.java +++ /dev/null @@ -1,432 +0,0 @@ -/* - * 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.driver.macosx; - -import javax.media.nativewindow.AbstractGraphicsConfiguration; -import javax.media.nativewindow.GraphicsConfigurationFactory; -import javax.media.nativewindow.NativeWindow; -import javax.media.nativewindow.NativeWindowException; -import javax.media.nativewindow.SurfaceChangeable; -import javax.media.nativewindow.util.Insets; -import javax.media.nativewindow.util.InsetsImmutable; -import javax.media.nativewindow.util.Point; -import javax.media.nativewindow.util.PointImmutable; - -import jogamp.newt.WindowImpl; -import jogamp.newt.driver.DriverClearFocus; -import jogamp.newt.driver.DriverUpdatePosition; - -import com.jogamp.newt.event.KeyEvent; - -public class MacWindow extends WindowImpl implements SurfaceChangeable, DriverClearFocus, DriverUpdatePosition { - - static { - MacDisplay.initSingleton(); - } - - public MacWindow() { - } - - @Override - protected void createNativeImpl() { - final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice()).chooseGraphicsConfiguration( - capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen()); - if (null == cfg) { - throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); - } - setGraphicsConfiguration(cfg); - reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureFlags(FLAG_CHANGE_VISIBILITY, true)); - if (0 == getWindowHandle()) { - throw new NativeWindowException("Error creating window"); - } - } - - @Override - protected void closeNativeImpl() { - try { - if(DEBUG_IMPLEMENTATION) { System.err.println("MacWindow.CloseAction "+Thread.currentThread().getName()); } - final long handle = getWindowHandle(); - setWindowHandle(0); - surfaceHandle = 0; - sscSurfaceHandle = 0; - isOffscreenInstance = false; - if (0 != handle) { - close0(handle); - } - } catch (Throwable t) { - if(DEBUG_IMPLEMENTATION) { - Exception e = new Exception("Warning: closeNative failed - "+Thread.currentThread().getName(), t); - e.printStackTrace(); - } - } - } - - @Override - protected int lockSurfaceImpl() { - if(!isOffscreenInstance) { - return lockSurface0(getWindowHandle()) ? LOCK_SUCCESS : LOCK_SURFACE_NOT_READY; - } - return LOCK_SUCCESS; - } - - @Override - protected void unlockSurfaceImpl() { - if(!isOffscreenInstance) { - unlockSurface0(getWindowHandle()); - } - } - - @Override - public final long getSurfaceHandle() { - return 0 != sscSurfaceHandle ? sscSurfaceHandle : surfaceHandle; - } - - public void setSurfaceHandle(long surfaceHandle) { - if(DEBUG_IMPLEMENTATION) { - System.err.println("MacWindow.setSurfaceHandle(): 0x"+Long.toHexString(surfaceHandle)); - } - sscSurfaceHandle = surfaceHandle; - if (isNativeValid()) { - if (0 != sscSurfaceHandle) { - orderOut0( 0!=getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() ); - } /** this is done by recreation! - else if (isVisible()){ - orderFront0( 0!=getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() ); - } */ - } - } - - public void surfaceSizeChanged(int width, int height) { - sizeChanged(false, width, height, false); - } - - @Override - protected void setTitleImpl(final String title) { - setTitle0(getWindowHandle(), title); - } - - protected void requestFocusImpl(boolean force) { - if(!isOffscreenInstance) { - requestFocus0(getWindowHandle(), force); - } else { - focusChanged(false, true); - } - } - - public final void clearFocus() { - if(DEBUG_IMPLEMENTATION) { - System.err.println("MacWindow: clearFocus() - requestFocusParent, isOffscreenInstance "+isOffscreenInstance); - } - if(!isOffscreenInstance) { - requestFocusParent0(getWindowHandle()); - } else { - focusChanged(false, false); - } - } - - public void updatePosition() { - final Point pS = getTopLevelLocationOnScreen(getX(), getY()); - if(DEBUG_IMPLEMENTATION) { - System.err.println("MacWindow: updatePosition() - isOffscreenInstance "+isOffscreenInstance+", new abs pos: pS "+pS); - } - if( !isOffscreenInstance ) { - setFrameTopLeftPoint0(getParentWindowHandle(), getWindowHandle(), pS.getX(), pS.getY()); - } // else no offscreen position - // no native event (fullscreen, some reparenting) - super.positionChanged(true, getX(), getY()); - } - - - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { - final Point pS = getTopLevelLocationOnScreen(x, y); - isOffscreenInstance = 0 != sscSurfaceHandle || isOffscreenInstance(this, this.getParent()); - - if(DEBUG_IMPLEMENTATION) { - System.err.println("MacWindow reconfig: "+x+"/"+y+" -> "+pS+" - "+width+"x"+height+ - ", offscreenInstance "+isOffscreenInstance+ - ", "+getReconfigureFlagsAsString(null, flags)); - } - - if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) && 0 == ( FLAG_IS_VISIBLE & flags) ) { - if ( !isOffscreenInstance ) { - orderOut0(getWindowHandle()); - } - // no native event .. - visibleChanged(true, false); - } - if( 0 == getWindowHandle() && 0 != ( FLAG_IS_VISIBLE & flags) || - 0 != ( FLAG_CHANGE_DECORATION & flags) || - 0 != ( FLAG_CHANGE_PARENTING & flags) || - 0 != ( FLAG_CHANGE_FULLSCREEN & flags) ) { - createWindow(isOffscreenInstance, 0 != getWindowHandle(), pS, width, height, 0 != ( FLAG_IS_FULLSCREEN & flags)); - if(isVisible()) { flags |= FLAG_CHANGE_VISIBILITY; } - } - if(x>=0 && y>=0) { - if( !isOffscreenInstance ) { - setFrameTopLeftPoint0(getParentWindowHandle(), getWindowHandle(), pS.getX(), pS.getY()); - } // else no offscreen position - // no native event (fullscreen, some reparenting) - super.positionChanged(true, x, y); - } - if(width>0 && height>0) { - if( !isOffscreenInstance ) { - setContentSize0(getWindowHandle(), width, height); - } // else offscreen size is realized via recreation - // no native event (fullscreen, some reparenting) - sizeChanged(true, width, height, false); // incl. validation (incl. repositioning) - } - if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) && 0 != ( FLAG_IS_VISIBLE & flags) ) { - if( !isOffscreenInstance ) { - orderFront0(getWindowHandle()); - } - // no native event .. - visibleChanged(true, true); - } - if( !isOffscreenInstance ) { - setAlwaysOnTop0(getWindowHandle(), 0 != ( FLAG_IS_ALWAYSONTOP & flags)); - } - return true; - } - - protected Point getLocationOnScreenImpl(int x, int y) { - Point p = new Point(x, y); - // min val is 0 - p.setX(Math.max(p.getX(), 0)); - p.setY(Math.max(p.getY(), 0)); - - final NativeWindow parent = getParent(); - if( null != parent && 0 != parent.getWindowHandle() ) { - p.translate(parent.getLocationOnScreen(null)); - } - return p; - } - - private Point getTopLevelLocationOnScreen(int x, int y) { - final InsetsImmutable _insets = getInsets(); // zero if undecorated - // client position -> top-level window position - x -= _insets.getLeftWidth() ; - y -= _insets.getTopHeight() ; - return getLocationOnScreenImpl(x, y); - } - - protected void updateInsetsImpl(Insets insets) { - // nop - using event driven insetsChange(..) - } - - @Override - protected void sizeChanged(boolean defer, int newWidth, int newHeight, boolean force) { - if(getWidth() != newWidth || getHeight() != newHeight) { - final Point p0S = getTopLevelLocationOnScreen(getX(), getY()); - setFrameTopLeftPoint0(getParentWindowHandle(), getWindowHandle(), p0S.getX(), p0S.getY()); - } - super.sizeChanged(defer, newWidth, newHeight, force); - } - - @Override - protected void positionChanged(boolean defer, int newX, int newY) { - // passed coordinates are in screen position of the client area - if(getWindowHandle()!=0) { - // screen position -> window position - Point absPos = new Point(newX, newY); - final NativeWindow parent = getParent(); - if(null != parent) { - absPos.translate( parent.getLocationOnScreen(null).scale(-1, -1) ); - } - super.positionChanged(defer, absPos.getX(), absPos.getY()); - } - } - - @Override - protected boolean setPointerVisibleImpl(final boolean pointerVisible) { - if( !isOffscreenInstance ) { - return setPointerVisible0(getWindowHandle(), hasFocus(), pointerVisible); - } // else may need offscreen solution ? FIXME - return false; - } - - @Override - protected boolean confinePointerImpl(final boolean confine) { - if( !isOffscreenInstance ) { - return confinePointer0(getWindowHandle(), confine); - } // else may need offscreen solution ? FIXME - return false; - } - - @Override - protected void warpPointerImpl(final int x, final int y) { - if( !isOffscreenInstance ) { - warpPointer0(getWindowHandle(), x, y); - } // else may need offscreen solution ? FIXME - } - - @Override - public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { - // Note that we send the key char for the key code on this - // platform -- we do not get any useful key codes out of the system - final int keyCode2 = MacKeyUtil.validateKeyCode(keyCode, keyChar); - final boolean valid = validateKeyEvent(eventType, modifiers, keyCode); - if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.sendKeyEvent "+Thread.currentThread().getName()+" char: 0x"+Integer.toHexString(keyChar)+", code 0x"+Integer.toHexString(keyCode)+" -> 0x"+Integer.toHexString(keyCode2)+", valid "+valid); - if(valid) { - // only deliver keyChar on key Typed events, harmonizing platform behavior - keyChar = KeyEvent.EVENT_KEY_TYPED == eventType ? keyChar : (char)-1; - super.sendKeyEvent(eventType, modifiers, keyCode2, keyChar); - } - } - - @Override - public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - // Note that we send the key char for the key code on this - // platform -- we do not get any useful key codes out of the system - final int keyCode2 = MacKeyUtil.validateKeyCode(keyCode, keyChar); - final boolean valid = validateKeyEvent(eventType, modifiers, keyCode); - if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.enqueueKeyEvent "+Thread.currentThread().getName()+" char: 0x"+Integer.toHexString(keyChar)+", code 0x"+Integer.toHexString(keyCode)+" -> 0x"+Integer.toHexString(keyCode2)+", valid "+valid); - if(valid) { - // only deliver keyChar on key Typed events, harmonizing platform behavior - keyChar = KeyEvent.EVENT_KEY_TYPED == eventType ? keyChar : (char)-1; - super.enqueueKeyEvent(wait, eventType, modifiers, keyCode2, keyChar); - } - } - - private int keyDownModifiers = 0; - private int keyDownCode = 0; - - private boolean validateKeyEvent(int eventType, int modifiers, int keyCode) { - switch(eventType) { - case KeyEvent.EVENT_KEY_PRESSED: - keyDownModifiers = modifiers; - keyDownCode = keyCode; - return true; - case KeyEvent.EVENT_KEY_RELEASED: - return keyDownModifiers == modifiers && keyDownCode == keyCode; - case KeyEvent.EVENT_KEY_TYPED: - final boolean matchKeyDown = keyDownModifiers == modifiers && keyDownCode == keyCode; - keyDownModifiers = 0; - keyDownCode = 0; - return matchKeyDown; - default: - throw new NativeWindowException("Unexpected key event type " + eventType); - } - } - - - //---------------------------------------------------------------------- - // Internals only - // - - private void createWindow(final boolean offscreenInstance, final boolean recreate, - final PointImmutable pS, final int width, final int height, - final boolean fullscreen) { - - if(0!=getWindowHandle() && !recreate) { - return; - } - - try { - if(0!=getWindowHandle()) { - // save the view .. close the window - surfaceHandle = changeContentView0(getParentWindowHandle(), getWindowHandle(), 0); - if(recreate && 0==surfaceHandle) { - throw new NativeWindowException("Internal Error - recreate, window but no view"); - } - orderOut0(getWindowHandle()); - close0(getWindowHandle()); - setWindowHandle(0); - } else { - surfaceHandle = 0; - } - setWindowHandle(createWindow0(getParentWindowHandle(), - pS.getX(), pS.getY(), width, height, - (getGraphicsConfiguration().getChosenCapabilities().isBackgroundOpaque() && !offscreenInstance), - fullscreen, - ((isUndecorated() || offscreenInstance) ? - NSBorderlessWindowMask : - NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask), - NSBackingStoreBuffered, - getScreen().getIndex(), surfaceHandle)); - if (getWindowHandle() == 0) { - throw new NativeWindowException("Could create native window "+Thread.currentThread().getName()+" "+this); - } - surfaceHandle = contentView0(getWindowHandle()); - if( offscreenInstance ) { - orderOut0(0!=getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle()); - } else { - setTitle0(getWindowHandle(), getTitle()); - } - } catch (Exception ie) { - ie.printStackTrace(); - } - } - - protected static native boolean initIDs0(); - private native long createWindow0(long parentWindowHandle, int x, int y, int w, int h, - boolean opaque, boolean fullscreen, int windowStyle, - int backingStoreType, - int screen_idx, long view); - private native boolean lockSurface0(long window); - private native void unlockSurface0(long window); - private native void requestFocus0(long window, boolean force); - private native void requestFocusParent0(long window); - /** in case of a child window, it actually only issues orderBack(..) */ - private native void orderOut0(long window); - private native void orderFront0(long window); - private native void close0(long window); - private native void setTitle0(long window, String title); - private native long contentView0(long window); - private native long changeContentView0(long parentWindowOrViewHandle, long window, long view); - private native void setContentSize0(long window, int w, int h); - private native void setFrameTopLeftPoint0(long parentWindowHandle, long window, int x, int y); - private native void setAlwaysOnTop0(long window, boolean atop); - private static native Object getLocationOnScreen0(long windowHandle, int src_x, int src_y); - private static native boolean setPointerVisible0(long windowHandle, boolean hasFocus, boolean visible); - private static native boolean confinePointer0(long windowHandle, boolean confine); - private static native void warpPointer0(long windowHandle, int x, int y); - - // Window styles - private static final int NSBorderlessWindowMask = 0; - private static final int NSTitledWindowMask = 1 << 0; - private static final int NSClosableWindowMask = 1 << 1; - private static final int NSMiniaturizableWindowMask = 1 << 2; - private static final int NSResizableWindowMask = 1 << 3; - - // Window backing store types - private static final int NSBackingStoreRetained = 0; - private static final int NSBackingStoreNonretained = 1; - private static final int NSBackingStoreBuffered = 2; - - private volatile long surfaceHandle = 0; - private long sscSurfaceHandle = 0; - private boolean isOffscreenInstance = false; - -} diff --git a/src/newt/classes/jogamp/newt/driver/macosx/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/ScreenDriver.java new file mode 100644 index 000000000..4f3cc691b --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/macosx/ScreenDriver.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2011 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.driver.macosx; + +import javax.media.nativewindow.DefaultGraphicsScreen; + +import jogamp.newt.MonitorModeProps; +import jogamp.newt.ScreenImpl; + +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +public class ScreenDriver extends ScreenImpl { + + static { + DisplayDriver.initSingleton(); + } + + public ScreenDriver() { + } + + @Override + protected void createNativeImpl() { + aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); + } + + @Override + protected void closeNativeImpl() { } + + private MonitorMode getMonitorModeImpl(MonitorModeProps.Cache cache, int crt_idx, int mode_idx) { + final int[] modeProps = getMonitorMode0(crt_idx, mode_idx); + final MonitorMode res; + if (null == modeProps || 0 >= modeProps.length) { + res = null; + } else { + res = MonitorModeProps.streamInMonitorMode(null, cache, modeProps, 0); + } + return res; + } + + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(MonitorModeProps.Cache cache) { + int crtIdx = 0; + int modeIdx = 0; + ArrayHashSet<MonitorMode> supportedModes = new ArrayHashSet<MonitorMode>(); + do { + final MonitorMode mode = getMonitorModeImpl(cache, crtIdx, modeIdx); + if( null != mode ) { + supportedModes.getOrAdd(mode); + // next mode on same monitor + modeIdx++; + } else if( 0 < modeIdx ) { + // end of monitor modes - got at least one mode + final MonitorMode currentMode = getMonitorModeImpl(cache, crtIdx, -1); + if ( null == currentMode ) { + throw new InternalError("Could not gather current mode of device "+crtIdx+", but gathered "+modeIdx+" modes"); + } + final int[] monitorProps = getMonitorProps0(crtIdx); + if ( null == monitorProps ) { + throw new InternalError("Could not gather device "+crtIdx+", but gathered "+modeIdx+" modes"); + } + // merge monitor-props + supported modes + MonitorModeProps.streamInMonitorDevice(null, cache, this, supportedModes, currentMode, monitorProps, 0); + + // next monitor, 1st mode + supportedModes= new ArrayHashSet<MonitorMode>(); + crtIdx++; + modeIdx=0; + } else { + // end of monitor + break; + } + } while ( true ); + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(MonitorDevice monitor) { + return getMonitorModeImpl(null, monitor.getId(), -1); + } + + @Override + protected boolean setCurrentMonitorModeImpl(MonitorDevice monitor, MonitorMode mode) { + return setMonitorMode0(monitor.getId(), mode.getId(), mode.getRotation()); + } + + @Override + protected int validateScreenIndex(int idx) { + return 0; // big-desktop w/ multiple monitor attached, only one screen available + } + + private native int[] getMonitorProps0(int crt_idx); + private native int[] getMonitorMode0(int crt_index, int mode_idx); + private native boolean setMonitorMode0(int crt_index, int nativeId, int rot); +} diff --git a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java new file mode 100644 index 000000000..e2a57debc --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java @@ -0,0 +1,608 @@ +/* + * 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.driver.macosx; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.GraphicsConfigurationFactory; +import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.MutableSurface; +import javax.media.nativewindow.VisualIDHolder; +import javax.media.nativewindow.util.Insets; +import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.PointImmutable; + +import jogamp.nativewindow.macosx.OSXUtil; +import jogamp.newt.PointerIconImpl; +import jogamp.newt.WindowImpl; +import jogamp.newt.driver.DriverClearFocus; +import jogamp.newt.driver.DriverUpdatePosition; + +import com.jogamp.newt.event.InputEvent; +import com.jogamp.newt.event.KeyEvent; + +public class WindowDriver extends WindowImpl implements MutableSurface, DriverClearFocus, DriverUpdatePosition { + + static { + DisplayDriver.initSingleton(); + } + + public WindowDriver() { + } + + @Override + protected void createNativeImpl() { + final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice(), capsRequested).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen(), VisualIDHolder.VID_UNDEFINED); + if (null == cfg) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + setGraphicsConfiguration(cfg); + reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureFlags(FLAG_CHANGE_VISIBILITY, true)); + if (0 == getWindowHandle()) { + throw new NativeWindowException("Error creating window"); + } + } + + @Override + protected void closeNativeImpl() { + try { + if(DEBUG_IMPLEMENTATION) { System.err.println("MacWindow.CloseAction "+Thread.currentThread().getName()); } + final long handle = getWindowHandle(); + visibleChanged(true, false); + setWindowHandle(0); + surfaceHandle = 0; + sscSurfaceHandle = 0; + isOffscreenInstance = false; + if (0 != handle) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + close0( handle ); + } } ); + } + } catch (Throwable t) { + if(DEBUG_IMPLEMENTATION) { + Exception e = new Exception("Warning: closeNative failed - "+Thread.currentThread().getName(), t); + e.printStackTrace(); + } + } + } + + @Override + protected int lockSurfaceImpl() { + /** + * if( isOffscreenInstance ) { + * return LOCK_SUCCESS; + * } + */ + final long w = getWindowHandle(); + final long v = surfaceHandle; + if( 0 != v && 0 != w ) { + return lockSurface0(w, v) ? LOCK_SUCCESS : LOCK_SURFACE_NOT_READY; + } + return LOCK_SURFACE_NOT_READY; + } + + @Override + protected void unlockSurfaceImpl() { + /** + * if( isOffscreenInstance ) { + * return; + * } + */ + final long w = getWindowHandle(); + final long v = surfaceHandle; + if(0 != w && 0 != v) { + if( !unlockSurface0(w, v) ) { + throw new NativeWindowException("Failed to unlock surface, probably not locked!"); + } + } + } + + @Override + public final long getSurfaceHandle() { + return 0 != sscSurfaceHandle ? sscSurfaceHandle : surfaceHandle; + } + + @Override + public void setSurfaceHandle(long surfaceHandle) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.setSurfaceHandle(): 0x"+Long.toHexString(surfaceHandle)); + } + sscSurfaceHandle = surfaceHandle; + if (isNativeValid()) { + if (0 != sscSurfaceHandle) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + orderOut0( 0 != getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() ); + } } ); + } /** this is done by recreation! + else if (isVisible()){ + OSXUtil.RunOnMainThread(false, new Runnable() { + public void run() { + orderFront0( 0!=getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() ); + } } ); + } */ + } + } + + @Override + protected void setTitleImpl(final String title) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + setTitle0(getWindowHandle(), title); + } } ); + } + + @Override + protected void requestFocusImpl(final boolean force) { + final boolean _isFullscreen = isFullscreen(); + final boolean _isOffscreenInstance = isOffscreenInstance; + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow: requestFocusImpl(), isOffscreenInstance "+_isOffscreenInstance+", isFullscreen "+_isFullscreen); + } + if(!_isOffscreenInstance) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + requestFocus0(getWindowHandle(), force); + if(_isFullscreen) { + // 'NewtMacWindow::windowDidBecomeKey()' is not always called in fullscreen-mode! + focusChanged(false, true); + } + } } ); + } else { + focusChanged(false, true); + } + } + + @Override + public final void clearFocus() { + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow: clearFocus(), isOffscreenInstance "+isOffscreenInstance); + } + if(!isOffscreenInstance) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + resignFocus0(getWindowHandle()); + } } ); + } else { + focusChanged(false, false); + } + } + + private boolean useParent(NativeWindow parent) { return null != parent && 0 != parent.getWindowHandle(); } + + @Override + public void updatePosition(int x, int y) { + final long handle = getWindowHandle(); + if( 0 != handle && !isOffscreenInstance ) { + final NativeWindow parent = getParent(); + final boolean useParent = useParent(parent); + final int pX=parent.getX(), pY=parent.getY(); + final Point p0S = getLocationOnScreenImpl(x, y, parent, useParent); + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow: updatePosition() parent["+useParent+" "+pX+"/"+pY+"] "+x+"/"+y+" -> "+x+"/"+y+" rel-client-pos, "+p0S+" screen-client-pos"); + } + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + setWindowClientTopLeftPoint0(handle, p0S.getX(), p0S.getY(), isVisible()); + } } ); + // no native event (fullscreen, some reparenting) + positionChanged(true, x, y); + } + } + + @Override + protected void sizeChanged(boolean defer, int newWidth, int newHeight, boolean force) { + final long handle = getWindowHandle(); + if( 0 != handle && !isOffscreenInstance ) { + final NativeWindow parent = getParent(); + final boolean useParent = useParent(parent); + if( useParent && ( getWidth() != newWidth || getHeight() != newHeight ) ) { + final int x=getX(), y=getY(); + final Point p0S = getLocationOnScreenImpl(x, y, parent, useParent); + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow: sizeChanged() parent["+useParent+" "+x+"/"+y+"] "+getX()+"/"+getY()+" "+newWidth+"x"+newHeight+" -> "+p0S+" screen-client-pos"); + } + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY(), isVisible()); + } } ); + } + } + super.sizeChanged(defer, newWidth, newHeight, force); + } + + @Override + protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, int flags) { + final boolean _isOffscreenInstance = isOffscreenInstance(this, this.getParent()); + isOffscreenInstance = 0 != sscSurfaceHandle || _isOffscreenInstance; + final PointImmutable pClientLevelOnSreen; + if( isOffscreenInstance ) { + pClientLevelOnSreen = new Point(0, 0); + } else { + final NativeWindow parent = getParent(); + final boolean useParent = useParent(parent); + if( useParent ) { + pClientLevelOnSreen = getLocationOnScreenImpl(x, y, parent, useParent); + } else { + pClientLevelOnSreen = new Point(x, y); + } + } + + if(DEBUG_IMPLEMENTATION) { + final AbstractGraphicsConfiguration cWinCfg = this.getGraphicsConfiguration(); + final NativeWindow pWin = getParent(); + final AbstractGraphicsConfiguration pWinCfg = null != pWin ? pWin.getGraphicsConfiguration() : null; + System.err.println("MacWindow reconfig.0: "+x+"/"+y+" -> clientPos "+pClientLevelOnSreen+" - "+width+"x"+height+ + ",\n\t parent type "+(null != pWin ? pWin.getClass().getName() : null)+ + ",\n\t this-chosenCaps "+(null != cWinCfg ? cWinCfg.getChosenCapabilities() : null)+ + ",\n\t parent-chosenCaps "+(null != pWinCfg ? pWinCfg.getChosenCapabilities() : null)+ + ", isOffscreenInstance(sscSurfaceHandle "+toHexString(sscSurfaceHandle)+ + ", ioi: "+_isOffscreenInstance+ + ") -> "+isOffscreenInstance+ + "\n\t, "+getReconfigureFlagsAsString(null, flags)); + // Thread.dumpStack(); + } + + final boolean setVisible = 0 != ( FLAG_IS_VISIBLE & flags); + + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) && !setVisible ) { + if ( !isOffscreenInstance ) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + orderOut0(getWindowHandle()); + visibleChanged(true, false); + } } ); + } else { + visibleChanged(true, false); + } + } + if( 0 == getWindowHandle() && setVisible || + 0 != ( FLAG_CHANGE_DECORATION & flags) || + 0 != ( FLAG_CHANGE_PARENTING & flags) || + 0 != ( FLAG_CHANGE_FULLSCREEN & flags) ) { + if(isOffscreenInstance) { + createWindow(true, 0 != getWindowHandle(), pClientLevelOnSreen, 64, 64, false, setVisible, false); + } else { + createWindow(false, 0 != getWindowHandle(), pClientLevelOnSreen, width, height, + 0 != ( FLAG_IS_FULLSCREEN & flags), setVisible, 0 != ( FLAG_IS_ALWAYSONTOP & flags)); + } + } else { + if( width>0 && height>0 ) { + if( !isOffscreenInstance ) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + setWindowClientTopLeftPointAndSize0(getWindowHandle(), pClientLevelOnSreen.getX(), pClientLevelOnSreen.getY(), width, height, setVisible); + } } ); + } // else offscreen size is realized via recreation + // no native event (fullscreen, some reparenting) + positionChanged(true, x, y); + sizeChanged(true, width, height, false); + } + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) && setVisible ) { + if( !isOffscreenInstance ) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + orderFront0(getWindowHandle()); + visibleChanged(true, true); + } } ); + } else { + visibleChanged(true, true); + } + } + if( !isOffscreenInstance ) { + setAlwaysOnTop0(getWindowHandle(), 0 != ( FLAG_IS_ALWAYSONTOP & flags)); + } + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow reconfig.X: clientPos "+pClientLevelOnSreen+", "+width+"x"+height+" -> clientPos "+getLocationOnScreenImpl(0, 0)+", insets: "+getInsets()); + } + return true; + } + + @Override + protected Point getLocationOnScreenImpl(int x, int y) { + final NativeWindow parent = getParent(); + final boolean useParent = useParent(parent); + return getLocationOnScreenImpl(x, y, parent, useParent); + } + + private Point getLocationOnScreenImpl(final int x, final int y, final NativeWindow parent, final boolean useParent) { + if( !useParent && !isOffscreenInstance && 0 != surfaceHandle) { + return OSXUtil.GetLocationOnScreen(surfaceHandle, true, x, y); + } + + final Point p = new Point(x, y); + if( useParent ) { + p.translate( parent.getLocationOnScreen(null) ); + } + return p; + } + + @Override + protected void updateInsetsImpl(Insets insets) { + // nop - using event driven insetsChange(..) + } + + /** Callback for native screen position change event of the client area. */ + protected void screenPositionChanged(boolean defer, int newX, int newY) { + // passed coordinates are in screen position of the client area + if(getWindowHandle()!=0) { + final NativeWindow parent = getParent(); + if( null == parent || isOffscreenInstance ) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.positionChanged.0 (Screen Pos - TOP): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY); + } + positionChanged(defer, newX, newY); + } else { + // screen position -> rel child window position + Point absPos = new Point(newX, newY); + Point parentOnScreen = parent.getLocationOnScreen(null); + absPos.translate( parentOnScreen.scale(-1, -1) ); + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.positionChanged.1 (Screen Pos - CHILD): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> absPos "+newX+"/"+newY+", parentOnScreen "+parentOnScreen+" -> "+absPos); + } + positionChanged(defer, absPos.getX(), absPos.getY()); + } + } else if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.positionChanged.2 (Screen Pos - IGN): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY); + } + } + + @Override + protected void setPointerIconImpl(final PointerIconImpl pi) { + if( !isOffscreenInstance ) { + final long piHandle = null != pi ? pi.validatedHandle() : 0; + OSXUtil.RunOnMainThread(true, new Runnable() { // waitUntildone due to PointerIconImpl's Lifecycle ! + @Override + public void run() { + setPointerIcon0(getWindowHandle(), piHandle); + } } ); + } + } + + @Override + protected boolean setPointerVisibleImpl(final boolean pointerVisible) { + if( !isOffscreenInstance ) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + setPointerVisible0(getWindowHandle(), hasFocus(), pointerVisible); + } } ); + return true; + } + return false; + } + + @Override + protected boolean confinePointerImpl(final boolean confine) { + if( !isOffscreenInstance ) { + confinePointer0(getWindowHandle(), confine); + return true; + } // else may need offscreen solution ? FIXME + return false; + } + + @Override + protected void warpPointerImpl(final int x, final int y) { + if( !isOffscreenInstance ) { + warpPointer0(getWindowHandle(), x, y); + } // else may need offscreen solution ? FIXME + } + + @Override + public final void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); + } + + @Override + public final void enqueueKeyEvent(boolean wait, short eventType, int modifiers, short _keyCode, short _keySym, char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); + } + + protected final void enqueueKeyEvent(boolean wait, short eventType, int modifiers, short _keyCode, char keyChar, char keySymChar) { + // Note that we send the key char for the key code on this + // platform -- we do not get any useful key codes out of the system + final short keyCode = MacKeyUtil.validateKeyCode(_keyCode, keyChar); + final short keySym; + { + short _keySym = KeyEvent.NULL_CHAR != keySymChar ? KeyEvent.utf16ToVKey(keySymChar) : KeyEvent.VK_UNDEFINED; + keySym = KeyEvent.VK_UNDEFINED != _keySym ? _keySym : keyCode; + } + /** + { + final boolean isModifierKeyCode = KeyEvent.isModifierKey(keyCode); + System.err.println("*** handleKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+ + ", keyCode 0x"+Integer.toHexString(_keyCode)+" -> 0x"+Integer.toHexString(keyCode)+ + ", keySymChar '"+keySymChar+"', 0x"+Integer.toHexString(keySymChar)+" -> 0x"+Integer.toHexString(keySym)+ + ", mods "+toHexString(modifiers)+ + ", was: pressed "+isKeyPressed(keyCode)+", isModifierKeyCode "+isModifierKeyCode+ + ", nativeValid "+isNativeValid()+", isOffscreen "+isOffscreenInstance); + } */ + + // OSX delivery order is PRESSED (t0), RELEASED (t1) and TYPED (t2) -> NEWT order: PRESSED (t0) and RELEASED (t1) + // Auto-Repeat: OSX delivers only PRESSED, inject auto-repeat RELEASE key _before_ PRESSED + switch(eventType) { + case KeyEvent.EVENT_KEY_RELEASED: + if( isKeyCodeTracked(keyCode) ) { + setKeyPressed(keyCode, false); + } + break; + case KeyEvent.EVENT_KEY_PRESSED: + if( isKeyCodeTracked(keyCode) ) { + if( setKeyPressed(keyCode, true) ) { + // key was already pressed + modifiers |= InputEvent.AUTOREPEAT_MASK; + super.enqueueKeyEvent(wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keySym, keyChar); // RELEASED + } + } + break; + } + super.enqueueKeyEvent(wait, eventType, modifiers, keyCode, keySym, keyChar); + } + + //---------------------------------------------------------------------- + // Internals only + // + + private void createWindow(final boolean offscreenInstance, final boolean recreate, + final PointImmutable pS, final int width, final int height, + final boolean fullscreen, final boolean visible, final boolean alwaysOnTop) { + + final long parentWinHandle = getParentWindowHandle(); + final long preWinHandle = getWindowHandle(); + + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.createWindow on thread "+Thread.currentThread().getName()+ + ": offscreen "+offscreenInstance+", recreate "+recreate+ + ", pS "+pS+", "+width+"x"+height+", fullscreen "+fullscreen+", visible "+visible+ + ", alwaysOnTop "+alwaysOnTop+", preWinHandle "+toHexString(preWinHandle)+", parentWin "+toHexString(parentWinHandle)+ + ", surfaceHandle "+toHexString(surfaceHandle)); + // Thread.dumpStack(); + } + + try { + if( 0 != preWinHandle ) { + setWindowHandle(0); + if( 0 == surfaceHandle ) { + throw new NativeWindowException("Internal Error - create w/ window, but no Newt NSView"); + } + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + changeContentView0(parentWinHandle, preWinHandle, 0); + close0( preWinHandle ); + } } ); + } else { + if( 0 != surfaceHandle ) { + throw new NativeWindowException("Internal Error - create w/o window, but has Newt NSView"); + } + surfaceHandle = createView0(pS.getX(), pS.getY(), width, height, fullscreen); + if( 0 == surfaceHandle ) { + throw new NativeWindowException("Could not create native view "+Thread.currentThread().getName()+" "+this); + } + } + + final long newWin = createWindow0( pS.getX(), pS.getY(), width, height, fullscreen, + ( isUndecorated() || offscreenInstance ) ? NSBorderlessWindowMask : + NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask, + NSBackingStoreBuffered, surfaceHandle); + if ( newWin == 0 ) { + throw new NativeWindowException("Could not create native window "+Thread.currentThread().getName()+" "+this); + } + setWindowHandle( newWin ); + + final boolean isOpaque = getGraphicsConfiguration().getChosenCapabilities().isBackgroundOpaque() && !offscreenInstance; + // Blocking initialization on main-thread! + OSXUtil.RunOnMainThread(true, new Runnable() { + @Override + public void run() { + initWindow0( parentWinHandle, newWin, pS.getX(), pS.getY(), width, height, + isOpaque, visible && !offscreenInstance, surfaceHandle); + if( offscreenInstance ) { + orderOut0(0!=parentWinHandle ? parentWinHandle : newWin); + } else { + setTitle0(newWin, getTitle()); + setAlwaysOnTop0(getWindowHandle(), alwaysOnTop); + } + visibleChanged(true, visible); + } } ); + } catch (Exception ie) { + ie.printStackTrace(); + } + } + + protected static native boolean initIDs0(); + private native long createView0(int x, int y, int w, int h, boolean fullscreen); + private native long createWindow0(int x, int y, int w, int h, boolean fullscreen, int windowStyle, int backingStoreType, long view); + /** Must be called on Main-Thread */ + private native void initWindow0(long parentWindow, long window, int x, int y, int w, int h, boolean opaque, boolean visible, long view); + private native boolean lockSurface0(long window, long view); + private native boolean unlockSurface0(long window, long view); + /** Must be called on Main-Thread */ + private native void requestFocus0(long window, boolean force); + /** Must be called on Main-Thread */ + private native void resignFocus0(long window); + /** Must be called on Main-Thread. In case this is a child window and parent is still visible, orderBack(..) is issued instead of orderOut(). */ + private native void orderOut0(long window); + /** Must be called on Main-Thread */ + private native void orderFront0(long window); + /** Must be called on Main-Thread */ + private native void close0(long window); + /** Must be called on Main-Thread */ + private native void setTitle0(long window, String title); + private native long contentView0(long window); + /** Must be called on Main-Thread */ + private native void changeContentView0(long parentWindowOrView, long window, long view); + /** Must be called on Main-Thread */ + private native void setWindowClientTopLeftPointAndSize0(long window, int x, int y, int w, int h, boolean display); + /** Must be called on Main-Thread */ + private native void setWindowClientTopLeftPoint0(long window, int x, int y, boolean display); + /** Must be called on Main-Thread */ + private native void setAlwaysOnTop0(long window, boolean atop); + private static native Object getLocationOnScreen0(long windowHandle, int src_x, int src_y); + private static native void setPointerIcon0(long windowHandle, long handle); + private static native void setPointerVisible0(long windowHandle, boolean hasFocus, boolean visible); + private static native void confinePointer0(long windowHandle, boolean confine); + private static native void warpPointer0(long windowHandle, int x, int y); + + // Window styles + private static final int NSBorderlessWindowMask = 0; + private static final int NSTitledWindowMask = 1 << 0; + private static final int NSClosableWindowMask = 1 << 1; + private static final int NSMiniaturizableWindowMask = 1 << 2; + private static final int NSResizableWindowMask = 1 << 3; + + // Window backing store types + private static final int NSBackingStoreRetained = 0; + private static final int NSBackingStoreNonretained = 1; + private static final int NSBackingStoreBuffered = 2; + + private volatile long surfaceHandle = 0; + private long sscSurfaceHandle = 0; + private boolean isOffscreenInstance = false; + +} diff --git a/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java b/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java new file mode 100644 index 000000000..551929b8d --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java @@ -0,0 +1,89 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.newt.driver.opengl; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URLConnection; +import java.nio.ByteBuffer; + +import javax.media.nativewindow.util.PixelFormat; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.util.PNGPixelRect; + +public class JoglUtilPNGIcon { + + public static ByteBuffer arrayToX11BGRAImages(IOUtil.ClassResources resources, int[] data_size, int[] elem_bytesize) throws UnsupportedOperationException, InterruptedException, IOException, MalformedURLException { + final PNGPixelRect[] images = new PNGPixelRect[resources.resourceCount()]; + data_size[0] = 0; + for(int i=0; i<resources.resourceCount(); i++) { + final URLConnection urlConn = resources.resolve(i); + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + data_size[0] += 2 + image.getSize().getWidth() * image.getSize().getHeight(); + images[i] = image; + } + final boolean is64Bit = Platform.is64Bit(); + elem_bytesize[0] = is64Bit ? Buffers.SIZEOF_LONG : Buffers.SIZEOF_INT; + final ByteBuffer buffer = Buffers.newDirectByteBuffer( data_size[0] * elem_bytesize[0] ); + + for(int i=0; i<images.length; i++) { + final PNGPixelRect image1 = images[i]; + final int width = image1.getSize().getWidth(); + final int height = image1.getSize().getHeight(); + if( is64Bit ) { + buffer.putLong(width); + buffer.putLong(height); + } else { + buffer.putInt(width); + buffer.putInt(height); + } + final ByteBuffer bb = image1.getPixels(); + final int stride = image1.getStride(); + for(int y=0; y<height; y++) { + int bbOff = y * stride; + for(int x=0; x<width; x++) { + long pixel; + pixel = ( 0xffL & bb.get(bbOff++) ); // B + pixel |= ( 0xffL & bb.get(bbOff++) ) << 8; // G + pixel |= ( 0xffL & bb.get(bbOff++) ) << 16; // R + pixel |= ( 0xffL & bb.get(bbOff++) ) << 24; // A + if( is64Bit ) { + buffer.putLong(pixel); + } else { + buffer.putInt((int)pixel); + } + } + } + } + buffer.rewind(); + return buffer; + } +} diff --git a/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java new file mode 100644 index 000000000..a30431788 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java @@ -0,0 +1,156 @@ +/* + * 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.driver.windows; + +import java.net.URLConnection; +import java.nio.Buffer; +import java.nio.ByteBuffer; + +import jogamp.nativewindow.windows.RegisteredClass; +import jogamp.nativewindow.windows.RegisteredClassFactory; +import jogamp.newt.DisplayImpl; +import jogamp.newt.NEWTJNILibLoader; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.util.PixelFormat; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.IOUtil; +import com.jogamp.nativewindow.windows.WindowsGraphicsDevice; +import com.jogamp.newt.NewtFactory; +import com.jogamp.opengl.util.PNGPixelRect; + +public class DisplayDriver extends DisplayImpl { + + private static final String newtClassBaseName = "_newt_clazz" ; + private static final long[] defaultIconHandles; + private static RegisteredClassFactory sharedClassFactory; + + static { + NEWTJNILibLoader.loadNEWT(); + { + long[] _defaultIconHandle = { 0, 0 }; + if( DisplayImpl.isPNGUtilAvailable() ) { + try { + final IOUtil.ClassResources iconRes = NewtFactory.getWindowIcons(); + { + final URLConnection urlConn = iconRes.resolve(0); + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + _defaultIconHandle[0] = DisplayDriver.createBGRA8888Icon0(image.getPixels(), image.getSize().getWidth(), image.getSize().getHeight(), false, 0, 0); + } + { + final URLConnection urlConn = iconRes.resolve(iconRes.resourceCount()-1); + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + _defaultIconHandle[1] = DisplayDriver.createBGRA8888Icon0(image.getPixels(), image.getSize().getWidth(), image.getSize().getHeight(), false, 0, 0); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + defaultIconHandles = _defaultIconHandle; + } + sharedClassFactory = new RegisteredClassFactory(newtClassBaseName, WindowDriver.getNewtWndProc0(), + false /* useDummyDispatchThread */, defaultIconHandles[0], defaultIconHandles[1]); + + if (!WindowDriver.initIDs0(RegisteredClassFactory.getHInstance())) { + throw new NativeWindowException("Failed to initialize WindowsWindow jmethodIDs"); + } + } + + public static void initSingleton() { + // just exist to ensure static init has been run + } + + protected static long getHInstance() { + return RegisteredClassFactory.getHInstance(); + } + + private RegisteredClass sharedClass; + + public DisplayDriver() { + } + + @Override + protected void createNativeImpl() { + sharedClass = sharedClassFactory.getSharedClass(); + aDevice = new WindowsGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); + } + + @Override + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { + sharedClassFactory.releaseSharedClass(); + aDevice.close(); + } + + @Override + protected void dispatchMessagesNative() { + DispatchMessages0(); + } + + protected String getWindowClassName() { + return sharedClass.getName(); + } + + @Override + protected final long createPointerIconImpl(PixelFormat pixelformat, int width, int height, final ByteBuffer pixels, final int hotX, final int hotY) { + return createBGRA8888Icon0(pixels, width, height, true, hotX, hotY); + } + + @Override + protected final void destroyPointerIconImpl(final long displayHandle, long piHandle) { + destroyIcon0(piHandle); + } + + //---------------------------------------------------------------------- + // Internals only + // + private static native void DispatchMessages0(); + + static long createBGRA8888Icon0(Buffer pixels, int width, int height, boolean isCursor, int hotX, int hotY) { + if( null == pixels ) { + throw new IllegalArgumentException("data buffer/size"); + } + final boolean pixels_is_direct = Buffers.isDirect(pixels); + return createBGRA8888Icon0( + pixels_is_direct ? pixels : Buffers.getArray(pixels), + pixels_is_direct ? Buffers.getDirectBufferByteOffset(pixels) : Buffers.getIndirectBufferByteOffset(pixels), + pixels_is_direct, + width, height, isCursor, hotX, hotY); + } + private static native long createBGRA8888Icon0(Object pixels, int pixels_byte_offset, boolean pixels_is_direct, int width, int height, boolean isCursor, int hotX, int hotY); + private static native void destroyIcon0(long handle); +} + diff --git a/src/newt/classes/jogamp/newt/driver/windows/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/windows/ScreenDriver.java new file mode 100644 index 000000000..e789b995f --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/windows/ScreenDriver.java @@ -0,0 +1,173 @@ +/* + * 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.driver.windows; + +import javax.media.nativewindow.DefaultGraphicsScreen; +import javax.media.nativewindow.util.Rectangle; + +import jogamp.newt.MonitorModeProps; +import jogamp.newt.ScreenImpl; + +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; +import com.jogamp.newt.Screen; + +public class ScreenDriver extends ScreenImpl { + + static { + DisplayDriver.initSingleton(); + if( Screen.DEBUG ) { + dumpMonitorInfo0(); + } + } + + public ScreenDriver() { + } + + @Override + protected void createNativeImpl() { + aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); + } + + @Override + protected void closeNativeImpl() { + } + + private final String getAdapterName(int crt_idx) { + return getAdapterName0(crt_idx); + } + private final String getActiveMonitorName(String adapterName, int monitor_idx) { + return getActiveMonitorName0(adapterName, monitor_idx); + } + + private final MonitorMode getMonitorModeImpl(MonitorModeProps.Cache cache, String adapterName, int crtModeIdx) { + if( null == adapterName ) { + return null; + } + final String activeMonitorName = getActiveMonitorName(adapterName, 0); + final int[] modeProps = null != activeMonitorName ? getMonitorMode0(adapterName, crtModeIdx) : null; + if ( null == modeProps || 0 >= modeProps.length) { + return null; + } + return MonitorModeProps.streamInMonitorMode(null, cache, modeProps, 0); + } + + @Override + protected void collectNativeMonitorModesAndDevicesImpl(MonitorModeProps.Cache cache) { + int crtIdx = 0; + ArrayHashSet<MonitorMode> supportedModes = new ArrayHashSet<MonitorMode>(); + String adapterName = getAdapterName(crtIdx); + while( null != adapterName ) { + int crtModeIdx = 0; + MonitorMode mode; + do { + mode = getMonitorModeImpl(cache, adapterName, crtModeIdx); + if( null != mode ) { + supportedModes.getOrAdd(mode); + // next mode on same monitor + crtModeIdx++; + } + } while( null != mode); + if( 0 < crtModeIdx ) { + // has at least one mode -> add device + final MonitorMode currentMode = getMonitorModeImpl(cache, adapterName, -1); + if ( null != currentMode ) { // enabled + final int[] monitorProps = getMonitorDevice0(adapterName, crtIdx); + // merge monitor-props + supported modes + MonitorModeProps.streamInMonitorDevice(null, cache, this, supportedModes, currentMode, monitorProps, 0); + + // next monitor, 1st mode + supportedModes= new ArrayHashSet<MonitorMode>(); + } + } + crtIdx++; + adapterName = getAdapterName(crtIdx); + } + } + + @Override + protected Rectangle getNativeMonitorDeviceViewportImpl(MonitorDevice monitor) { + final String adapterName = getAdapterName(monitor.getId()); + if( null != adapterName ) { + final String activeMonitorName = getActiveMonitorName(adapterName, 0); + if( null != activeMonitorName ) { + final int[] monitorProps = getMonitorDevice0(adapterName, monitor.getId()); + int offset = MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT; + return new Rectangle(monitorProps[offset++], monitorProps[offset++], monitorProps[offset++], monitorProps[offset++]); + } + } + return null; + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(MonitorDevice monitor) { + return getMonitorModeImpl(null, getAdapterName(monitor.getId()), -1); + } + + @Override + protected boolean setCurrentMonitorModeImpl(MonitorDevice monitor, MonitorMode mode) { + return setMonitorMode0(monitor.getId(), + -1, -1, // no fixed position! + mode.getSurfaceSize().getResolution().getWidth(), + mode.getSurfaceSize().getResolution().getHeight(), + mode.getSurfaceSize().getBitsPerPixel(), + (int)mode.getRefreshRate(), // simply cut-off, orig is int + mode.getFlags(), + mode.getRotation()); + } + + @Override + protected int validateScreenIndex(int idx) { + return 0; // big-desktop w/ multiple monitor attached, only one screen available + } + + @Override + protected void calcVirtualScreenOriginAndSize(Rectangle vOriginSize) { + vOriginSize.set(getVirtualOriginX0(), getVirtualOriginY0(), getVirtualWidthImpl0(), getVirtualHeightImpl0()); + } + + // Native calls + private native int getVirtualOriginX0(); + private native int getVirtualOriginY0(); + private native int getVirtualWidthImpl0(); + private native int getVirtualHeightImpl0(); + + private static native void dumpMonitorInfo0(); + private native String getAdapterName0(int crt_index); + private native String getActiveMonitorName0(String adapterName, int crtModeIdx); + private native int[] getMonitorMode0(String adapterName, int crtModeIdx); + private native int[] getMonitorDevice0(String adapterName, int monitor_index); + private native boolean setMonitorMode0(int monitor_index, int x, int y, int width, int height, int bits, int freq, int flags, int rot); +} diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java index a30aa133c..a48fe2f62 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowsWindow.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,38 +29,45 @@ * 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.driver.windows; +import java.nio.ByteBuffer; + import jogamp.nativewindow.windows.GDI; import jogamp.nativewindow.windows.GDIUtil; +import jogamp.newt.PointerIconImpl; import jogamp.newt.WindowImpl; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.VisualIDHolder; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.VersionNumber; +import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.KeyEvent; -import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.MouseEvent.PointerType; + +public class WindowDriver extends WindowImpl { -public class WindowsWindow extends WindowImpl { + static { + DisplayDriver.initSingleton(); + } private long hmon; private long hdc; private long hdc_old; private long windowHandleClose; - static { - WindowsDisplay.initSingleton(); - } - - public WindowsWindow() { + public WindowDriver() { } @Override @@ -68,31 +75,37 @@ public class WindowsWindow extends WindowImpl { if (0 != hdc) { throw new InternalError("surface not released"); } - hdc = GDI.GetDC(getWindowHandle()); - hmon = MonitorFromWindow0(getWindowHandle()); - + final long hWnd = getWindowHandle(); + hdc = GDI.GetDC(hWnd); + // return ( 0 == hdc ) ? LOCK_SURFACE_NOT_READY : ( hdc_old != hdc ) ? LOCK_SURFACE_CHANGED : LOCK_SUCCESS ; - if( 0 == hdc ) { + if( 0 == hdc ) { return LOCK_SURFACE_NOT_READY; } + hmon = MonitorFromWindow0(hWnd); + + // Let's not trigger on HDC change, GLDrawableImpl.'s destroy/create is a nop here anyways. + // FIXME: Validate against EGL surface creation: ANGLE uses HWND -> fine! + return LOCK_SUCCESS; + + /** if( hdc_old == hdc ) { return LOCK_SUCCESS; } - if(DEBUG_IMPLEMENTATION) { + if(DEBUG_IMPLEMENTATION) { System.err.println("WindowsWindow: surface change "+toHexString(hdc_old)+" -> "+toHexString(hdc)); // Thread.dumpStack(); } - return LOCK_SURFACE_CHANGED; + return LOCK_SURFACE_CHANGED; */ } @Override protected void unlockSurfaceImpl() { - if (0 == hdc) { - throw new InternalError("surface not acquired"); + if (0 != hdc) { + GDI.ReleaseDC(getWindowHandle(), hdc); + hdc_old = hdc; + hdc=0; } - GDI.ReleaseDC(getWindowHandle(), hdc); - hdc_old = hdc; - hdc=0; } @Override @@ -117,55 +130,49 @@ public class WindowsWindow extends WindowImpl { return false; } + @Override protected void createNativeImpl() { - final WindowsScreen screen = (WindowsScreen) getScreen(); - final WindowsDisplay display = (WindowsDisplay) screen.getDisplay(); - final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(display.getGraphicsDevice()).chooseGraphicsConfiguration( - capsRequested, capsRequested, capabilitiesChooser, screen.getGraphicsScreen()); + final ScreenDriver screen = (ScreenDriver) getScreen(); + final DisplayDriver display = (DisplayDriver) screen.getDisplay(); + final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(display.getGraphicsDevice(), capsRequested).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, screen.getGraphicsScreen(), VisualIDHolder.VID_UNDEFINED); if (null == cfg) { throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); } setGraphicsConfiguration(cfg); - final int flags = getReconfigureFlags(0, true) & + final VersionNumber winVer = Platform.getOSVersionNumber(); + final int flags = getReconfigureFlags(0, true) & ( FLAG_IS_ALWAYSONTOP | FLAG_IS_UNDECORATED ) ; - setWindowHandle(CreateWindow0(display.getHInstance(), display.getWindowClassName(), display.getWindowClassName(), - getParentWindowHandle(), getX(), getY(), getWidth(), getHeight(), autoPosition(), flags)); - if (getWindowHandle() == 0) { + final long _windowHandle = CreateWindow0(DisplayDriver.getHInstance(), display.getWindowClassName(), display.getWindowClassName(), + winVer.getMajor(), winVer.getMinor(), + getParentWindowHandle(), getX(), getY(), getWidth(), getHeight(), autoPosition(), flags); + if ( 0 == _windowHandle ) { throw new NativeWindowException("Error creating window"); } - windowHandleClose = getWindowHandle(); - addMouseListener(new MouseTracker()); - + setWindowHandle(_windowHandle); + windowHandleClose = _windowHandle; + if(DEBUG_IMPLEMENTATION) { Exception e = new Exception("Info: Window new window handle "+Thread.currentThread().getName()+ " (Parent HWND "+toHexString(getParentWindowHandle())+ - ") : HWND "+toHexString(getWindowHandle())+", "+Thread.currentThread()); + ") : HWND "+toHexString(_windowHandle)+", "+Thread.currentThread()); e.printStackTrace(); } } - - class MouseTracker extends MouseAdapter { - public void mouseEntered(MouseEvent e) { - WindowsWindow.trackPointerLeave0(WindowsWindow.this.getWindowHandle()); - } - } + @Override protected void closeNativeImpl() { - if (hdc != 0) { - if(windowHandleClose != 0) { + if( 0 != windowHandleClose ) { + if ( 0 != hdc ) { try { GDI.ReleaseDC(windowHandleClose, hdc); } catch (Throwable t) { - if(DEBUG_IMPLEMENTATION) { + if(DEBUG_IMPLEMENTATION) { Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); e.printStackTrace(); } } } - hdc = 0; - hdc_old = 0; - } - if(windowHandleClose != 0) { try { GDI.DestroyWindow(windowHandleClose); } catch (Throwable t) { @@ -173,25 +180,27 @@ public class WindowsWindow extends WindowImpl { Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); e.printStackTrace(); } - } finally { - windowHandleClose = 0; } } + windowHandleClose = 0; + hdc = 0; + hdc_old = 0; } + @Override protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { if(DEBUG_IMPLEMENTATION) { System.err.println("WindowsWindow reconfig: "+x+"/"+y+" "+width+"x"+height+", "+ getReconfigureFlagsAsString(null, flags)); } - + if(0 == ( FLAG_IS_UNDECORATED & flags)) { final InsetsImmutable i = getInsets(); - + // client position -> top-level window position x -= i.getLeftWidth() ; y -= i.getTopHeight() ; - + if(0<width && 0<height) { // client size -> top-level window size width += i.getTotalWidth(); @@ -199,13 +208,14 @@ public class WindowsWindow extends WindowImpl { } } reconfigureWindow0( getParentWindowHandle(), getWindowHandle(), x, y, width, height, flags); - + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { - visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); + visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); } return true; } + @Override protected void requestFocusImpl(boolean force) { requestFocus0(getWindowHandle(), force); } @@ -216,34 +226,42 @@ public class WindowsWindow extends WindowImpl { } @Override + protected void setPointerIconImpl(final PointerIconImpl pi) { + setPointerIcon0(getWindowHandle(), null != pi ? pi.validatedHandle() : 0); + } + + @Override protected boolean setPointerVisibleImpl(final boolean pointerVisible) { - final Boolean[] res = new Boolean[] { Boolean.FALSE }; - + final boolean[] res = new boolean[] { false }; + this.runOnEDTIfAvail(true, new Runnable() { + @Override public void run() { - res[0] = Boolean.valueOf(setPointerVisible0(getWindowHandle(), pointerVisible)); + res[0] = setPointerVisible0(getWindowHandle(), pointerVisible); } }); - return res[0].booleanValue(); + return res[0]; } @Override protected boolean confinePointerImpl(final boolean confine) { final Boolean[] res = new Boolean[] { Boolean.FALSE }; - + this.runOnEDTIfAvail(true, new Runnable() { + @Override public void run() { final Point p0 = getLocationOnScreenImpl(0, 0); - res[0] = Boolean.valueOf(confinePointer0(getWindowHandle(), confine, + res[0] = Boolean.valueOf(confinePointer0(getWindowHandle(), confine, p0.getX(), p0.getY(), p0.getX()+getWidth(), p0.getY()+getHeight())); } }); return res[0].booleanValue(); } - + @Override - protected void warpPointerImpl(final int x, final int y) { + protected void warpPointerImpl(final int x, final int y) { this.runOnEDTIfAvail(true, new Runnable() { + @Override public void run() { final Point sPos = getLocationOnScreenImpl(x, y); warpPointer0(getWindowHandle(), sPos.getX(), sPos.getY()); @@ -251,54 +269,106 @@ public class WindowsWindow extends WindowImpl { }); return; } - + + @Override protected Point getLocationOnScreenImpl(int x, int y) { return GDIUtil.GetRelativeLocation( getWindowHandle(), 0 /*root win*/, x, y); } + @Override protected void updateInsetsImpl(Insets insets) { - // nop - using event driven insetsChange(..) + // nop - using event driven insetsChange(..) } - - private final int validateKeyCode(int eventType, int keyCode) { + + // + // PointerEvent Handling + // + /** + * Send multiple-pointer {@link MouseEvent.PointerType#TouchScreen} event to be directly consumed + * <p> + * Assumes non normal pointer names and rotation/scroll will be determined by a gesture handler. + * </p> + * <p> + * See {@link #doPointerEvent(boolean, boolean, PointerType[], short, int, int, boolean, int[], int[], int[], float[], float, float[], float)} + * for details. + * </p> + */ + public final void sendTouchScreenEvent(short eventType, int modifiers, + int pActionIdx, int[] pNames, + int[] pX, int[] pY, float[] pPressure, float maxPressure) { + final int pCount = pNames.length; + final MouseEvent.PointerType[] pTypes = new MouseEvent.PointerType[pCount]; + for(int i=pCount-1; i>=0; i--) { pTypes[i] = PointerType.TouchScreen; } + doPointerEvent(false /*enqueue*/, false /*wait*/, + pTypes, eventType, modifiers, pActionIdx, false /*normalPNames*/, pNames, + pX, pY, pPressure, maxPressure, new float[] { 0f, 0f, 0f} /*rotationXYZ*/, 1f/*rotationScale*/); + } + + // + // KeyEvent Handling + // + private short repeatedKey = KeyEvent.VK_UNDEFINED; + + private final boolean handlePressTypedAutoRepeat(boolean isModifierKey, int modifiers, short keyCode, short keySym, char keyChar) { + if( setKeyPressed(keyCode, true) ) { + // AR: Key was already pressed: Either [enter | within] AR mode + final boolean withinAR = repeatedKey == keyCode; + repeatedKey = keyCode; + if( !isModifierKey ) { + // AR: Key was already pressed: Either [enter | within] AR mode + modifiers |= InputEvent.AUTOREPEAT_MASK; + if( withinAR ) { + // AR: Within AR mode + super.sendKeyEvent(KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keySym, keyChar); + } // else { AR: Enter AR mode - skip already send PRESSED ; or ALT } + super.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keySym, keyChar); + } + return true; + } + return false; + } + + @Override + public final void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + final boolean isModifierKey = KeyEvent.isModifierKey(keySym); + // System.err.println("*** sendKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", keyCode "+toHexString(keyCode)+", keyChar <"+keyChar+">, mods "+toHexString(modifiers)+ + // ", isKeyCodeTracked "+isKeyCodeTracked(keyCode)+", was: pressed "+isKeyPressed(keyCode)+", printableKey "+KeyEvent.isPrintableKey(keyCode, false)+" [modifierKey "+isModifierKey+"] - "+System.currentTimeMillis()); + + // Reorder: WINDOWS delivery order is PRESSED (t0), TYPED (t0) and RELEASED (t1) -> NEWT order: PRESSED (t0) and RELEASED (t1) + // Auto-Repeat: WINDOWS delivers only PRESSED (t0) and TYPED (t0). switch(eventType) { - case KeyEvent.EVENT_KEY_PRESSED: - lastPressedKeyCode = keyCode; + case KeyEvent.EVENT_KEY_RELEASED: + if( isKeyCodeTracked(keyCode) ) { + if( repeatedKey == keyCode && !isModifierKey ) { + // AR out - send out missing PRESSED + super.sendKeyEvent(KeyEvent.EVENT_KEY_PRESSED, modifiers | InputEvent.AUTOREPEAT_MASK, keyCode, keySym, keyChar); + } + setKeyPressed(keyCode, false); + repeatedKey = KeyEvent.VK_UNDEFINED; + } + super.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keySym, keyChar); break; - case KeyEvent.EVENT_KEY_TYPED: - if(-1==keyCode) { - keyCode = lastPressedKeyCode; + case KeyEvent.EVENT_KEY_PRESSED: + if( !handlePressTypedAutoRepeat(isModifierKey, modifiers, keyCode, keySym, keyChar) ) { + super.sendKeyEvent(KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keySym, keyChar); } - lastPressedKeyCode = -1; break; } - return keyCode; - } - private int lastPressedKeyCode = 0; - - @Override - public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { - // Note that we have to regenerate the keyCode for EVENT_KEY_TYPED on this platform - keyCode = validateKeyCode(eventType, keyCode); - super.sendKeyEvent(eventType, modifiers, keyCode, keyChar); } - + @Override - public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - // Note that we have to regenerate the keyCode for EVENT_KEY_TYPED on this platform - keyCode = validateKeyCode(eventType, keyCode); - super.enqueueKeyEvent(wait, eventType, modifiers, keyCode, keyChar); + public final void enqueueKeyEvent(boolean wait, short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); } - + //---------------------------------------------------------------------- // Internals only // - protected static native boolean initIDs0(); protected static native long getNewtWndProc0(); + protected static native boolean initIDs0(long hInstance); - private native long CreateWindow0(long hInstance, String wndClassName, String wndName, - long parentWindowHandle, - int x, int y, int width, int height, boolean autoPosition, int flags); + private native long CreateWindow0(long hInstance, String wndClassName, String wndName, int winMajor, int winMinor, + long parentWindowHandle, int x, int y, int width, int height, boolean autoPosition, int flags); private native long MonitorFromWindow0(long windowHandle); private native void reconfigureWindow0(long parentWindowHandle, long windowHandle, int x, int y, int width, int height, int flags); @@ -307,6 +377,8 @@ public class WindowsWindow extends WindowImpl { private static native boolean setPointerVisible0(long windowHandle, boolean visible); private static native boolean confinePointer0(long windowHandle, boolean grab, int l, int t, int r, int b); - private static native void warpPointer0(long windowHandle, int x, int y); - private static native void trackPointerLeave0(long windowHandle); + private static native void warpPointer0(long windowHandle, int x, int y); + private static native ByteBuffer newDirectByteBuffer(long addr, long capacity); + + private static native void setPointerIcon0(long windowHandle, long iconHandle); } diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowsDisplay.java b/src/newt/classes/jogamp/newt/driver/windows/WindowsDisplay.java deleted file mode 100644 index 225b115e4..000000000 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowsDisplay.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.driver.windows; - -import jogamp.nativewindow.windows.RegisteredClass; -import jogamp.nativewindow.windows.RegisteredClassFactory; -import jogamp.newt.DisplayImpl; -import jogamp.newt.NEWTJNILibLoader; -import javax.media.nativewindow.AbstractGraphicsDevice; -import javax.media.nativewindow.NativeWindowException; - -import com.jogamp.nativewindow.windows.WindowsGraphicsDevice; - -public class WindowsDisplay extends DisplayImpl { - - private static final String newtClassBaseName = "_newt_clazz" ; - private static RegisteredClassFactory sharedClassFactory; - - static { - NEWTJNILibLoader.loadNEWT(); - - if (!WindowsWindow.initIDs0()) { - throw new NativeWindowException("Failed to initialize WindowsWindow jmethodIDs"); - } - sharedClassFactory = new RegisteredClassFactory(newtClassBaseName, WindowsWindow.getNewtWndProc0()); - } - - public static void initSingleton() { - // just exist to ensure static init has been run - } - - private RegisteredClass sharedClass; - - public WindowsDisplay() { - } - - protected void createNativeImpl() { - sharedClass = sharedClassFactory.getSharedClass(); - aDevice = new WindowsGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); - } - - protected void closeNativeImpl() { - sharedClassFactory.releaseSharedClass(); - } - - protected void dispatchMessagesNative() { - DispatchMessages0(); - } - - protected long getHInstance() { - return sharedClass.getHandle(); - } - - protected String getWindowClassName() { - return sharedClass.getName(); - } - - //---------------------------------------------------------------------- - // Internals only - // - private static native void DispatchMessages0(); -} - diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowsScreen.java b/src/newt/classes/jogamp/newt/driver/windows/WindowsScreen.java deleted file mode 100644 index f8bce9da3..000000000 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowsScreen.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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.driver.windows; - -import javax.media.nativewindow.DefaultGraphicsScreen; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; - -import jogamp.newt.ScreenImpl; - -import com.jogamp.newt.ScreenMode; -import com.jogamp.newt.util.ScreenModeUtil; - -public class WindowsScreen extends ScreenImpl { - - static { - WindowsDisplay.initSingleton(); - } - - public WindowsScreen() { - } - - protected void createNativeImpl() { - aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); - } - - protected void closeNativeImpl() { - } - - private int[] getScreenModeIdx(int idx) { - int[] modeProps = getScreenMode0(screen_idx, idx); - if (null == modeProps || 0 == modeProps.length) { - return null; - } - if(modeProps.length < ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL) { - throw new RuntimeException("properties array too short, should be >= "+ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL+", is "+modeProps.length); - } - return modeProps; - } - - private int nativeModeIdx; - - protected int[] getScreenModeFirstImpl() { - nativeModeIdx = 0; - return getScreenModeNextImpl(); - } - - protected int[] getScreenModeNextImpl() { - int[] modeProps = getScreenModeIdx(nativeModeIdx); - if (null != modeProps && 0 < modeProps.length) { - nativeModeIdx++; - return modeProps; - } - return null; - } - - protected ScreenMode getCurrentScreenModeImpl() { - int[] modeProps = getScreenModeIdx(-1); - if (null != modeProps && 0 < modeProps.length) { - return ScreenModeUtil.streamIn(modeProps, 0); - } - return null; - } - - protected boolean setCurrentScreenModeImpl(ScreenMode sm) { - return setScreenMode0(screen_idx, - sm.getMonitorMode().getSurfaceSize().getResolution().getWidth(), - sm.getMonitorMode().getSurfaceSize().getResolution().getHeight(), - sm.getMonitorMode().getSurfaceSize().getBitsPerPixel(), - sm.getMonitorMode().getRefreshRate(), - sm.getRotation()); - } - - protected int validateScreenIndex(int idx) { - return 0; // big-desktop, only one screen available - } - - protected void getVirtualScreenOriginAndSize(Point virtualOrigin, Dimension virtualSize) { - virtualOrigin.setX(getOriginX0(screen_idx)); - virtualOrigin.setY(getOriginY0(screen_idx)); - virtualSize.setWidth(getWidthImpl0(screen_idx)); - virtualSize.setHeight(getHeightImpl0(screen_idx)); - } - - // Native calls - private native int getOriginX0(int screen_idx); - private native int getOriginY0(int screen_idx); - private native int getWidthImpl0(int scrn_idx); - private native int getHeightImpl0(int scrn_idx); - - private native int[] getScreenMode0(int screen_index, int mode_index); - private native boolean setScreenMode0(int screen_index, int width, int height, int bits, int freq, int rot); -} diff --git a/src/newt/classes/jogamp/newt/driver/x11/X11Display.java b/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java index 9464b979b..5c2820dab 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/X11Display.java +++ b/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,43 +29,46 @@ * 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.driver.x11; +import java.nio.Buffer; +import java.nio.ByteBuffer; + import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; -import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.util.PixelFormat; +import com.jogamp.common.nio.Buffers; import com.jogamp.nativewindow.x11.X11GraphicsDevice; import jogamp.nativewindow.x11.X11Util; import jogamp.newt.DisplayImpl; import jogamp.newt.NEWTJNILibLoader; -public class X11Display extends DisplayImpl { +public class DisplayDriver extends DisplayImpl { static { NEWTJNILibLoader.loadNEWT(); - if ( !initIDs0() ) { + if ( !initIDs0(X11Util.XERROR_STACKDUMP) ) { throw new NativeWindowException("Failed to initialize X11Display jmethodIDs"); } - if (!X11Window.initIDs0()) { + if (!WindowDriver.initIDs0()) { throw new NativeWindowException("Failed to initialize X11Window jmethodIDs"); } } - public static void initSingleton() { - // just exist to ensure static init has been run - } - + /** Ensure static init has been run. */ + /* pp */static void initSingleton() { } - public X11Display() { + public DisplayDriver() { } + @Override public String validateDisplayName(String name, long handle) { return X11Util.validateDisplayName(name, handle); } @@ -74,98 +77,102 @@ public class X11Display extends DisplayImpl { * {@inheritDoc} * * We use a private non-shared X11 Display instance for EDT window operations and one for exposed animation, eg. OpenGL. - * <p> - * In case {@link X11Util#HAS_XLOCKDISPLAY_BUG} and {@link X11Util#XINITTHREADS_ALWAYS_ENABLED}, - * we use null locking. Even though this seems not to be rational, it gives most stable results on all platforms. - * </p> - * <p> - * Otherwise we use basic locking via the constructor {@link X11GraphicsDevice#X11GraphicsDevice(long, int, boolean)}, - * since it is possible to share this device via {@link com.jogamp.newt.NewtFactory#createDisplay(String, boolean)}. - * </p> */ - @SuppressWarnings("unused") + @Override protected void createNativeImpl() { + X11Util.setX11ErrorHandler(true, DEBUG ? false : true); // make sure X11 error handler is set long handle = X11Util.openDisplay(name); if( 0 == handle ) { throw new RuntimeException("Error creating display(Win): "+name); } - if(USE_SEPARATE_DISPLAY_FOR_EDT) { - edtDisplayHandle = X11Util.openDisplay(name); - if( 0 == edtDisplayHandle ) { - X11Util.closeDisplay(handle); - throw new RuntimeException("Error creating display(EDT): "+name); - } - } else { - edtDisplayHandle = handle; - } + aDevice = new X11GraphicsDevice(handle, AbstractGraphicsDevice.DEFAULT_UNIT, true /* owner */); try { - CompleteDisplay0(edtDisplayHandle); + CompleteDisplay0(aDevice.getHandle()); } catch(RuntimeException e) { - closeNativeImpl(); + closeNativeImpl(aDevice); throw e; } - - // see API doc above! - if(X11Util.XINITTHREADS_ALWAYS_ENABLED && X11Util.HAS_XLOCKDISPLAY_BUG) { - aDevice = new X11GraphicsDevice(handle, AbstractGraphicsDevice.DEFAULT_UNIT, NativeWindowFactory.getNullToolkitLock(), false); - } else { - aDevice = new X11GraphicsDevice(handle, AbstractGraphicsDevice.DEFAULT_UNIT, false); - } } - protected void closeNativeImpl() { - DisplayRelease0(edtDisplayHandle, javaObjectAtom, windowDeleteAtom); + @Override + protected void closeNativeImpl(AbstractGraphicsDevice aDevice) { + DisplayRelease0(aDevice.getHandle(), javaObjectAtom, windowDeleteAtom /*, kbdHandle */); // XKB disabled for now javaObjectAtom = 0; windowDeleteAtom = 0; - // closing using ATI driver bug 'same order' - final long handle = getHandle(); - X11Util.closeDisplay(handle); - if(handle != edtDisplayHandle) { - X11Util.closeDisplay(edtDisplayHandle); - } - edtDisplayHandle = 0; + // kbdHandle = 0; + aDevice.close(); // closes X11 display } + @Override protected void dispatchMessagesNative() { - if(0 != edtDisplayHandle) { - DispatchMessages0(edtDisplayHandle, javaObjectAtom, windowDeleteAtom); + final AbstractGraphicsDevice _aDevice = aDevice; // aDevice could be pulled by destroy event + _aDevice.lock(); + try { + final long handle = _aDevice.getHandle(); + if(0 != handle) { + DispatchMessages0(handle, javaObjectAtom, windowDeleteAtom /*, kbdHandle */); // XKB disabled for now + } + } finally { + _aDevice.unlock(); } } - protected long getEDTHandle() { return edtDisplayHandle; } protected long getJavaObjectAtom() { return javaObjectAtom; } protected long getWindowDeleteAtom() { return windowDeleteAtom; } - + // protected long getKbdHandle() { return kbdHandle; } // XKB disabled for now + + /** Returns <code>null</code> if !{@link #isNativeValid()}, otherwise the Boolean value of {@link X11GraphicsDevice#isXineramaEnabled()}. */ + protected Boolean isXineramaEnabled() { return isNativeValid() ? Boolean.valueOf(((X11GraphicsDevice)aDevice).isXineramaEnabled()) : null; } + + @Override + public final PixelFormat getNativePointerIconPixelFormat() { return PixelFormat.RGBA8888; } + + @Override + protected final long createPointerIconImpl(PixelFormat pixelformat, int width, int height, final ByteBuffer pixels, final int hotX, final int hotY) { + return createPointerIcon(getHandle(), pixels, width, height, hotX, hotY); + } + + @Override + protected final void destroyPointerIconImpl(final long displayHandle, long piHandle) { + destroyPointerIcon0(displayHandle, piHandle); + } + //---------------------------------------------------------------------- // Internals only // - private static native boolean initIDs0(); + + private static native boolean initIDs0(boolean debug); private native void CompleteDisplay0(long handle); - private void displayCompleted(long javaObjectAtom, long windowDeleteAtom) { + private void displayCompleted(long javaObjectAtom, long windowDeleteAtom /*, long kbdHandle */) { this.javaObjectAtom=javaObjectAtom; this.windowDeleteAtom=windowDeleteAtom; + // this.kbdHandle = kbdHandle; // XKB disabled for now } - private native void DisplayRelease0(long handle, long javaObjectAtom, long windowDeleteAtom); + private native void DisplayRelease0(long handle, long javaObjectAtom, long windowDeleteAtom /*, long kbdHandle */); // XKB disabled for now - private native void DispatchMessages0(long display, long javaObjectAtom, long windowDeleteAtom); + private native void DispatchMessages0(long display, long javaObjectAtom, long windowDeleteAtom /* , long kbdHandle */); // XKB disabled for now + + private static long createPointerIcon(long display, Buffer pixels, int width, int height, int hotX, int hotY) { + final boolean pixels_is_direct = Buffers.isDirect(pixels); + return createPointerIcon0(display, + pixels_is_direct ? pixels : Buffers.getArray(pixels), + pixels_is_direct ? Buffers.getDirectBufferByteOffset(pixels) : Buffers.getIndirectBufferByteOffset(pixels), + pixels_is_direct, + width, height, hotX, hotY); + } + private static native long createPointerIcon0(long display, Object pixels, int pixels_byte_offset, boolean pixels_is_direct, int width, int height, int hotX, int hotY); + + private static native void destroyPointerIcon0(long display, long handle); - /** - * 2011/06/14 libX11 1.4.2 and libxcb 1.7 bug 20708 - Multithreading Issues w/ OpenGL, .. - * https://bugs.freedesktop.org/show_bug.cgi?id=20708 - * https://jogamp.org/bugzilla/show_bug.cgi?id=502 - * Affects: Ubuntu 11.04, OpenSuSE 11, .. - * Workaround: Using a separate X11 Display connection for event dispatching (EDT) - */ - private final boolean USE_SEPARATE_DISPLAY_FOR_EDT = true; - - private long edtDisplayHandle; - /** X11 Window delete atom marker used on EDT */ private long windowDeleteAtom; - + /** X11 Window java object property used on EDT */ private long javaObjectAtom; + + /** X11 Keyboard handle used on EDT */ + // private long kbdHandle; // XKB disabled for now } diff --git a/src/newt/classes/jogamp/newt/driver/x11/RandR.java b/src/newt/classes/jogamp/newt/driver/x11/RandR.java new file mode 100644 index 000000000..8b065d1f0 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/x11/RandR.java @@ -0,0 +1,86 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.newt.driver.x11; + +import java.util.List; + +import javax.media.nativewindow.util.RectangleImmutable; + +import jogamp.newt.MonitorModeProps; + +import com.jogamp.common.util.VersionNumber; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +public interface RandR { + public static final VersionNumber version110 = new VersionNumber(1, 1, 0); + public static final VersionNumber version130 = new VersionNumber(1, 3, 0); + public static final VersionNumber version140 = new VersionNumber(1, 4, 0); + + VersionNumber getVersion(); + + void dumpInfo(final long dpy, final int screen_idx); + + /** + * Encapsulate initial device query allowing caching of internal data structures. + * Methods covered: + * <ul> + * <li>{@link #getMonitorDeviceCount(long, ScreenDriver)}</li> + * <li>{@link #getAvailableRotations(long, ScreenDriver, int)}</li> + * <li>{@link #getMonitorModeProps(long, ScreenDriver, int)}</li> + * <li>{@link #getCurrentMonitorModeProps(long, ScreenDriver, int)</li> + * <li>{@link #getMonitorDeviceProps(long, ScreenDriver, List, int, MonitorMode)}</li> + * </ul> + * <p> + * Above methods may be called w/o begin/end, in which case no + * internal data structures can be cached: + * </p> + * @param dpy TODO + * @param screen TODO + * @return TODO + */ + boolean beginInitialQuery(long dpy, ScreenDriver screen); + void endInitialQuery(long dpy, ScreenDriver screen); + + int getMonitorDeviceCount(final long dpy, final ScreenDriver screen); + int[] getAvailableRotations(final long dpy, final ScreenDriver screen, final int crt_idx); + /** + * + * @param dpy + * @param screen + * @param mode_idx w/o indexing rotation + * @return props w/o actual rotation + */ + int[] getMonitorModeProps(final long dpy, final ScreenDriver screen, final int mode_idx); + int[] getMonitorDeviceProps(final long dpy, final ScreenDriver screen, MonitorModeProps.Cache cache, final int crt_idx); + int[] getMonitorDeviceViewport(final long dpy, final ScreenDriver screen, final int crt_idx); + int[] getCurrentMonitorModeProps(final long dpy, final ScreenDriver screen, final int crt_idx); + boolean setCurrentMonitorMode(final long dpy, final ScreenDriver screen, MonitorDevice monitor, final MonitorMode mode); + + public void updateScreenViewport(final long dpy, final ScreenDriver screen, RectangleImmutable viewport); +} diff --git a/src/newt/classes/jogamp/newt/driver/x11/RandR11.java b/src/newt/classes/jogamp/newt/driver/x11/RandR11.java new file mode 100644 index 000000000..6c22a3fc6 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/x11/RandR11.java @@ -0,0 +1,365 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.newt.driver.x11; + +import javax.media.nativewindow.util.RectangleImmutable; + +import jogamp.newt.MonitorModeProps; +import jogamp.newt.ScreenImpl; + +import com.jogamp.common.util.VersionNumber; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +class RandR11 implements RandR { + private static final boolean DEBUG = ScreenDriver.DEBUG; + + RandR11() { + } + + @Override + public final VersionNumber getVersion() { + return version110; + } + + @Override + public void dumpInfo(final long dpy, final int screen_idx) { + // NOP + } + + private int widthMM=0, heightMM=0; + private int modeCount = 0; + private int resolutionCount = 0; + private int[][] nrates = null; // [nres_number][nrate_number] + private int[] idx_rate = null, idx_res = null; + + @Override + public boolean beginInitialQuery(long dpy, ScreenDriver screen) { + // initialize iterators and static data + final int screen_idx = screen.getIndex(); + resolutionCount = getNumScreenResolutions0(dpy, screen_idx); + if(0==resolutionCount) { + endInitialQuery(dpy, screen); + return false; + } + + nrates = new int[resolutionCount][]; + for(int i=0; i<resolutionCount; i++) { + nrates[i] = getScreenRates0(dpy, screen_idx, i); + if(null==nrates[i] || 0==nrates[i].length) { + endInitialQuery(dpy, screen); + return false; + } + } + + for(int nresIdx=0; nresIdx < resolutionCount; nresIdx++) { + modeCount += nrates[nresIdx].length; + } + + idx_rate = new int[modeCount]; + idx_res = new int[modeCount]; + + int modeIdx=0; + for(int nresIdx=0; nresIdx < resolutionCount; nresIdx++) { + for(int nrateIdx=0; nrateIdx < nrates[nresIdx].length; nrateIdx++) { + idx_rate[modeIdx] = nrateIdx; + idx_res[modeIdx] = nresIdx; + modeIdx++; + } + } + return true; + } + + @Override + public void endInitialQuery(long dpy, ScreenDriver screen) { + idx_rate=null; + idx_res=null; + nrates=null; + } + + @Override + public int getMonitorDeviceCount(final long dpy, final ScreenDriver screen) { + return 1; + } + + @Override + public int[] getAvailableRotations(final long dpy, final ScreenDriver screen, final int crt_idx) { + if( 0 < crt_idx ) { + // RandR11 only supports 1 CRT + return null; + } + final int screen_idx = screen.getIndex(); + final int[] availRotations = getAvailableScreenRotations0(dpy, screen_idx); + if(null==availRotations || 0==availRotations.length) { + return null; + } + return availRotations; + } + + @Override + public int[] getMonitorModeProps(final long dpy, final ScreenDriver screen, final int mode_idx) { + if( mode_idx >= modeCount ) { + return null; + } + final int screen_idx = screen.getIndex(); + + final int nres_index = idx_res[mode_idx]; + final int nrate_index = idx_rate[mode_idx]; + + final int[] res = getScreenResolution0(dpy, screen_idx, nres_index); + if(null==res || 0==res.length) { + return null; + } + if(0>=res[0] || 0>=res[1]) { + throw new InternalError("invalid resolution: "+res[0]+"x"+res[1]+" for res idx "+nres_index+"/"+resolutionCount); + } + if( res[2] > widthMM ) { + widthMM = res[2]; + } + if( res[3] > heightMM ) { + heightMM = res[3]; + } + + int rate = nrates[nres_index][nrate_index]; + if(0>=rate) { + rate = ScreenImpl.default_sm_rate; + if(DEBUG) { + System.err.println("Invalid rate: "+rate+" at index "+nrate_index+"/"+nrates.length+", using default: "+ScreenImpl.default_sm_rate); + } + } + + int[] props = new int[ MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL ]; + int i = 0; + props[i++] = MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL; + props[i++] = res[0]; // width + props[i++] = res[1]; // height + props[i++] = ScreenImpl.default_sm_bpp; // bpp n/a in RandR11 + props[i++] = rate*100; // rate (Hz*100) + props[i++] = 0; // flags; + props[i++] = nres_index; + props[i++] = -1; // rotation placeholder; + if( MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL != i ) { + throw new InternalError("XX"); + } + return props; + } + + @Override + public int[] getMonitorDeviceProps(final long dpy, final ScreenDriver screen, final MonitorModeProps.Cache cache, final int crt_idx) { + if( 0 < crt_idx ) { + // RandR11 only supports 1 CRT + return null; + } + final int[] currentModeProps = getCurrentMonitorModeProps(dpy, screen, crt_idx); + if( null == currentModeProps) { // disabled + return null; + } + final MonitorMode currentMode = MonitorModeProps.streamInMonitorMode(null, cache, currentModeProps, 0); + final int allModesCount = cache.monitorModes.size(); + final int[] props = new int[MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES - 1 + allModesCount]; + int i = 0; + props[i++] = props.length; + props[i++] = crt_idx; + props[i++] = widthMM; + props[i++] = heightMM; + props[i++] = 0; // rotated viewport x + props[i++] = 0; // rotated viewport y + props[i++] = currentMode.getRotatedWidth(); // rotated viewport width + props[i++] = currentMode.getRotatedHeight(); // rotated viewport height + props[i++] = currentMode.getId(); // current mode id + props[i++] = currentMode.getRotation(); + for(int j=0; j<allModesCount; j++) { + props[i++] = cache.monitorModes.get(j).getId(); + } + return props; + } + + @Override + public int[] getMonitorDeviceViewport(final long dpy, final ScreenDriver screen, final int crt_idx) { + if( 0 < crt_idx ) { + // RandR11 only supports 1 CRT + return null; + } + final int screen_idx = screen.getIndex(); + long screenConfigHandle = getScreenConfiguration0(dpy, screen_idx); + if(0 == screenConfigHandle) { + return null; + } + int[] res; + final int nres_idx; + try { + int resNumber = getNumScreenResolutions0(dpy, screen_idx); + if(0==resNumber) { + return null; + } + + nres_idx = getCurrentScreenResolutionIndex0(screenConfigHandle); + if(0>nres_idx) { + return null; + } + if(nres_idx>=resNumber) { + throw new RuntimeException("Invalid resolution index: ! "+nres_idx+" < "+resNumber); + } + res = getScreenResolution0(dpy, screen_idx, nres_idx); + if(null==res || 0==res.length) { + return null; + } + if(0>=res[0] || 0>=res[1]) { + throw new InternalError("invalid resolution: "+res[0]+"x"+res[1]+" for res idx "+nres_idx+"/"+resNumber); + } + } finally { + freeScreenConfiguration0(screenConfigHandle); + } + int[] props = new int[4]; + int i = 0; + props[i++] = 0; + props[i++] = 0; + props[i++] = res[0]; // width + props[i++] = res[1]; // height + return props; + } + + @Override + public int[] getCurrentMonitorModeProps(final long dpy, final ScreenDriver screen, final int crt_idx) { + if( 0 < crt_idx ) { + // RandR11 only supports 1 CRT + return null; + } + final int screen_idx = screen.getIndex(); + long screenConfigHandle = getScreenConfiguration0(dpy, screen_idx); + if(0 == screenConfigHandle) { + return null; + } + int[] res; + int rate, rot; + final int nres_idx; + try { + int resNumber = getNumScreenResolutions0(dpy, screen_idx); + if(0==resNumber) { + return null; + } + + nres_idx = getCurrentScreenResolutionIndex0(screenConfigHandle); + if(0>nres_idx) { + return null; + } + if(nres_idx>=resNumber) { + throw new RuntimeException("Invalid resolution index: ! "+nres_idx+" < "+resNumber); + } + res = getScreenResolution0(dpy, screen_idx, nres_idx); + if(null==res || 0==res.length) { + return null; + } + if(0>=res[0] || 0>=res[1]) { + throw new InternalError("invalid resolution: "+res[0]+"x"+res[1]+" for res idx "+nres_idx+"/"+resNumber); + } + rate = getCurrentScreenRate0(screenConfigHandle); + if(0>rate) { + return null; + } + rot = getCurrentScreenRotation0(screenConfigHandle); + if(0>rot) { + return null; + } + } finally { + freeScreenConfiguration0(screenConfigHandle); + } + int[] props = new int[ MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL ]; + int i = 0; + props[i++] = MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL; + props[i++] = res[0]; // width + props[i++] = res[1]; // height + props[i++] = ScreenImpl.default_sm_bpp; + props[i++] = rate*100; // rate (Hz*100) + props[i++] = 0; // flags; + props[i++] = nres_idx; // mode_idx; + props[i++] = rot; + if( MonitorModeProps.NUM_MONITOR_MODE_PROPERTIES_ALL != i ) { + throw new InternalError("XX"); + } + return props; + } + + @Override + public boolean setCurrentMonitorMode(final long dpy, final ScreenDriver screen, MonitorDevice monitor, final MonitorMode mode) { + final long t0 = System.currentTimeMillis(); + boolean done = false; + final int screen_idx = screen.getIndex(); + long screenConfigHandle = getScreenConfiguration0(dpy, screen_idx); + if(0 == screenConfigHandle) { + return Boolean.valueOf(done); + } + try { + final int resId = mode.getId(); + if(0>resId || resId>=resolutionCount) { + throw new RuntimeException("Invalid resolution index: ! 0 < "+resId+" < "+resolutionCount+", "+monitor+", "+mode); + } + final int f = (int)mode.getRefreshRate(); // simply cut-off, orig is int + final int r = mode.getRotation(); + + if( setCurrentScreenModeStart0(dpy, screen_idx, screenConfigHandle, resId, f, r) ) { + while(!done && System.currentTimeMillis()-t0 < ScreenImpl.SCREEN_MODE_CHANGE_TIMEOUT) { + done = setCurrentScreenModePollEnd0(dpy, screen_idx, resId, f, r); + if(!done) { + try { Thread.sleep(10); } catch (InterruptedException e) { } + } + } + } + } finally { + freeScreenConfiguration0(screenConfigHandle); + } + return done; + } + + @Override + public final void updateScreenViewport(final long dpy, final ScreenDriver screen, RectangleImmutable viewport) { + // nop + } + + /** @return int[] { rot1, .. } */ + private static native int[] getAvailableScreenRotations0(long display, int screen_index); + + private static native int getNumScreenResolutions0(long display, int screen_index); + + /** @return int[] { width, height, widthmm, heightmm } */ + private static native int[] getScreenResolution0(long display, int screen_index, int mode_index); + + private static native int[] getScreenRates0(long display, int screen_index, int mode_index); + + private static native long getScreenConfiguration0(long display, int screen_index); + private static native void freeScreenConfiguration0(long screenConfiguration); + + private static native int getCurrentScreenResolutionIndex0(long screenConfiguration); + private static native int getCurrentScreenRate0(long screenConfiguration); + private static native int getCurrentScreenRotation0(long screenConfiguration); + + /** needs own Display connection for XRANDR event handling */ + private static native boolean setCurrentScreenModeStart0(long display, int screen_index, long screenConfiguration, int mode_index, int freq, int rot); + private static native boolean setCurrentScreenModePollEnd0(long display, int screen_index, int mode_index, int freq, int rot); + +} diff --git a/src/newt/classes/jogamp/newt/driver/x11/RandR13.java b/src/newt/classes/jogamp/newt/driver/x11/RandR13.java new file mode 100644 index 000000000..a08741d9e --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/x11/RandR13.java @@ -0,0 +1,274 @@ +/** + * Copyright 2013 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.newt.driver.x11; + +import java.util.Iterator; + +import javax.media.nativewindow.util.RectangleImmutable; + +import jogamp.newt.MonitorModeProps; + +import com.jogamp.common.util.IntLongHashMap; +import com.jogamp.common.util.VersionNumber; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +/** + * Mapping details: + * <pre> + * MonitorMode.id == XRR mode-id (not index) + * MonitorDevice.id == XRR monitor-idx (not id) + * </pre> + */ +class RandR13 implements RandR { + private static final boolean DEBUG = ScreenDriver.DEBUG; + + RandR13() { + } + + @Override + public final VersionNumber getVersion() { + return version130; + } + + @Override + public void dumpInfo(final long dpy, final int screen_idx) { + long screenResources = getScreenResources0(dpy, screen_idx); + if(0 == screenResources) { + return; + } + try { + dumpInfo0(dpy, screen_idx, screenResources); + } finally { + freeScreenResources0(screenResources); + } + } + + long sessionScreenResources = 0; + IntLongHashMap crtInfoHandleMap = null; + + @Override + public boolean beginInitialQuery(long dpy, ScreenDriver screen) { + final int screen_idx = screen.getIndex(); + sessionScreenResources = getScreenResources0(dpy, screen_idx); + if( 0 != sessionScreenResources ) { + crtInfoHandleMap = new IntLongHashMap(); + crtInfoHandleMap.setKeyNotFoundValue(0); + return true; + } else { + return false; + } + } + + @Override + public void endInitialQuery(long dpy, ScreenDriver screen) { + if( null != crtInfoHandleMap ) { + for(Iterator<IntLongHashMap.Entry> iter = crtInfoHandleMap.iterator(); iter.hasNext(); ) { + final IntLongHashMap.Entry entry = iter.next(); + freeMonitorInfoHandle0(entry.value); + } + crtInfoHandleMap.clear(); + crtInfoHandleMap = null; + } + if( 0 != sessionScreenResources ) { + freeScreenResources0( sessionScreenResources ); + sessionScreenResources = 0; + } + } + + private final long getScreenResourceHandle(final long dpy, final int screen_idx) { + if( 0 != sessionScreenResources ) { + return sessionScreenResources; + } + return getScreenResources0(dpy, screen_idx); + } + private final void releaseScreenResourceHandle(final long screenResourceHandle) { + if( 0 == sessionScreenResources ) { + freeScreenResources0( screenResourceHandle ); + } + } + + private final long getMonitorInfoHandle(final long dpy, final int screen_idx, long screenResources, final int monitor_idx) { + if( null != crtInfoHandleMap ) { + long h = crtInfoHandleMap.get(monitor_idx); + if( 0 == h ) { + h = getMonitorInfoHandle0(dpy, screen_idx, screenResources, monitor_idx); + crtInfoHandleMap.put(monitor_idx, h); + } + return h; + } else { + return getMonitorInfoHandle0(dpy, screen_idx, screenResources, monitor_idx); + } + } + private final void releaseMonitorInfoHandle(final long monitorInfoHandle) { + if( null == crtInfoHandleMap ) { + freeMonitorInfoHandle0(monitorInfoHandle); + } + } + + @Override + public int getMonitorDeviceCount(final long dpy, final ScreenDriver screen) { + final int screen_idx = screen.getIndex(); + final long screenResources = getScreenResourceHandle(dpy, screen_idx); + try { + return getMonitorDeviceCount0(screenResources); + } finally { + releaseScreenResourceHandle(screenResources); + } + } + + @Override + public int[] getAvailableRotations(final long dpy, final ScreenDriver screen, final int crt_idx) { + final int screen_idx = screen.getIndex(); + final long screenResources = getScreenResourceHandle(dpy, screen_idx); + try { + final long monitorInfo = getMonitorInfoHandle(dpy, screen_idx, screenResources, crt_idx); + try { + final int[] availRotations = getAvailableRotations0(monitorInfo); + if(null==availRotations || 0==availRotations.length) { + return null; + } + return availRotations; + } finally { + releaseMonitorInfoHandle(monitorInfo); + } + } finally { + releaseScreenResourceHandle(screenResources); + } + } + + @Override + public int[] getMonitorModeProps(final long dpy, final ScreenDriver screen, final int mode_idx) { + final int screen_idx = screen.getIndex(); + final long screenResources = getScreenResourceHandle(dpy, screen_idx); + try { + return getMonitorMode0(screenResources, mode_idx); + } finally { + releaseScreenResourceHandle(screenResources); + } + } + + @Override + public int[] getMonitorDeviceProps(final long dpy, final ScreenDriver screen, MonitorModeProps.Cache cache, final int crt_idx) { + final int screen_idx = screen.getIndex(); + final long screenResources = getScreenResourceHandle(dpy, screen_idx); + try { + final long monitorInfo = getMonitorInfoHandle(dpy, screen_idx, screenResources, crt_idx); + try { + return getMonitorDevice0(dpy, screenResources, monitorInfo, crt_idx); + } finally { + releaseMonitorInfoHandle(monitorInfo); + } + } finally { + releaseScreenResourceHandle(screenResources); + } + } + + @Override + public int[] getMonitorDeviceViewport(final long dpy, final ScreenDriver screen, final int crt_idx) { + final int screen_idx = screen.getIndex(); + final long screenResources = getScreenResourceHandle(dpy, screen_idx); + try { + final long monitorInfo = getMonitorInfoHandle(dpy, screen_idx, screenResources, crt_idx); + try { + return getMonitorViewport0(monitorInfo); + } finally { + releaseMonitorInfoHandle(monitorInfo); + } + } finally { + releaseScreenResourceHandle(screenResources); + } + } + + @Override + public int[] getCurrentMonitorModeProps(final long dpy, final ScreenDriver screen, final int crt_idx) { + final int screen_idx = screen.getIndex(); + final long screenResources = getScreenResourceHandle(dpy, screen_idx); + try { + final long monitorInfo = getMonitorInfoHandle(dpy, screen_idx, screenResources, crt_idx); + try { + return getMonitorCurrentMode0(screenResources, monitorInfo); + } finally { + releaseMonitorInfoHandle(monitorInfo); + } + } finally { + releaseScreenResourceHandle(screenResources); + } + } + + @Override + public boolean setCurrentMonitorMode(final long dpy, final ScreenDriver screen, MonitorDevice monitor, final MonitorMode mode) { + final int screen_idx = screen.getIndex(); + final long screenResources = getScreenResourceHandle(dpy, screen_idx); + final boolean res; + try { + final long monitorInfo = getMonitorInfoHandle(dpy, screen_idx, screenResources, monitor.getId()); + try { + res = setMonitorMode0(dpy, screenResources, monitorInfo, monitor.getId(), mode.getId(), mode.getRotation(), + -1, -1); // no fixed position! + } finally { + releaseMonitorInfoHandle(monitorInfo); + } + } finally { + releaseScreenResourceHandle(screenResources); + } + return res; + } + + @Override + public final void updateScreenViewport(final long dpy, final ScreenDriver screen, final RectangleImmutable viewport) { + final int screen_idx = screen.getIndex(); + final long screenResources = getScreenResourceHandle(dpy, screen_idx); + try { + setScreenViewport0(dpy, screen_idx, screenResources, viewport.getX(), viewport.getY(), viewport.getWidth(), viewport.getHeight()); + } finally { + dumpInfo0(dpy, screen_idx, screenResources); + releaseScreenResourceHandle(screenResources); + } + } + + private static native long getScreenResources0(long display, int screen_index); + private static native void freeScreenResources0(long screenResources); + private static native void dumpInfo0(long display, int screen_index, long screenResources); + + private static native int getMonitorDeviceCount0(long screenResources); + + private static native long getMonitorInfoHandle0(long display, int screen_index, long screenResources, int monitor_index); + private static native void freeMonitorInfoHandle0(long monitorInfoHandle); + + private static native int[] getAvailableRotations0(long monitorInfo); + private static native int[] getMonitorViewport0(long monitorInfo); + private static native int[] getMonitorCurrentMode0(long monitorInfo); + + private static native int[] getMonitorMode0(long screenResources, int mode_index); + private static native int[] getMonitorCurrentMode0(long screenResources, long monitorInfo); + private static native int[] getMonitorDevice0(long display, long screenResources, long monitorInfo, int monitor_idx); + + private static native boolean setMonitorMode0(long display, long screenResources, long monitorInfo, int monitor_idx, int mode_id, int rotation, int x, int y); + private static native boolean setScreenViewport0(long display, int screen_index, long screenResources, int x, int y, int width, int height); +} diff --git a/src/newt/classes/jogamp/newt/driver/x11/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/x11/ScreenDriver.java new file mode 100644 index 000000000..bef7f60ec --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/x11/ScreenDriver.java @@ -0,0 +1,290 @@ +/* + * 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.driver.x11; + +import java.util.ArrayList; +import java.util.List; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.util.Rectangle; +import javax.media.nativewindow.util.RectangleImmutable; + +import jogamp.nativewindow.x11.X11Util; +import jogamp.newt.Debug; +import jogamp.newt.DisplayImpl; +import jogamp.newt.MonitorModeProps; +import jogamp.newt.DisplayImpl.DisplayRunnable; +import jogamp.newt.ScreenImpl; + +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.common.util.VersionNumber; +import com.jogamp.nativewindow.x11.X11GraphicsDevice; +import com.jogamp.nativewindow.x11.X11GraphicsScreen; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; + +public class ScreenDriver extends ScreenImpl { + protected static final boolean DEBUG_TEST_RANDR13_DISABLED; + + static { + Debug.initSingleton(); + DEBUG_TEST_RANDR13_DISABLED = Debug.isPropertyDefined("newt.test.Screen.disableRandR13", true); + + DisplayDriver.initSingleton(); + } + + /** Ensure static init has been run. */ + /* pp */static void initSingleton() { } + + public ScreenDriver() { + } + + @Override + protected void createNativeImpl() { + // validate screen index + Long handle = runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Long>() { + @Override + public Long run(long dpy) { + return new Long(GetScreen0(dpy, screen_idx)); + } } ); + if (handle.longValue() == 0) { + throw new RuntimeException("Error creating screen: " + screen_idx); + } + final X11GraphicsDevice x11dev = (X11GraphicsDevice) getDisplay().getGraphicsDevice(); + final long dpy = x11dev.getHandle(); + aScreen = new X11GraphicsScreen(x11dev, screen_idx); + { + int v[] = getRandRVersion0(dpy); + randrVersion = new VersionNumber(v[0], v[1], 0); + } + { + if( !DEBUG_TEST_RANDR13_DISABLED && randrVersion.compareTo(RandR.version130) >= 0 ) { + rAndR = new RandR13(); + } else if( randrVersion.compareTo(RandR.version110) >= 0 ) { + rAndR = new RandR11(); + } else { + rAndR = null; + } + } + if( DEBUG ) { + System.err.println("RandR "+randrVersion+", "+rAndR); + rAndR.dumpInfo(dpy, screen_idx); + } + } + + @Override + protected void closeNativeImpl() { + } + + private VersionNumber randrVersion; + private RandR rAndR; + + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(MonitorModeProps.Cache cache) { + if( null == rAndR ) { return; } + final AbstractGraphicsDevice device = getDisplay().getGraphicsDevice(); + device.lock(); + try { + if( rAndR.beginInitialQuery(device.getHandle(), this) ) { + try { + final int crtCount = rAndR.getMonitorDeviceCount(device.getHandle(), this); + + // Gather all available rotations + final ArrayHashSet<Integer> availableRotations = new ArrayHashSet<Integer>(); + for(int i = 0; i < crtCount; i++) { + final int[] rotations = rAndR.getAvailableRotations(device.getHandle(), this, i); + if( null != rotations ) { + final List<Integer> rotationList = new ArrayList<Integer>(rotations.length); + for(int j=0; j<rotations.length; j++ ) { rotationList.add(rotations[j]); } + availableRotations.addAll(rotationList); + } + } + + // collect all modes, while injecting all available rotations + { + int modeIdx = 0; + int[] props; + do { + props = rAndR.getMonitorModeProps(device.getHandle(), this, modeIdx++); + if( null != props ) { + for(int i = 0; i < availableRotations.size(); i++) { + props[MonitorModeProps.IDX_MONITOR_MODE_ROT] = availableRotations.get(i); + MonitorModeProps.streamInMonitorMode(null, cache, props, 0); + } + } + } while( null != props); + } + if( cache.monitorModes.size() > 0 ) { + for(int i = 0; i < crtCount; i++) { + final int[] monitorProps = rAndR.getMonitorDeviceProps(device.getHandle(), this, cache, i); + if( null != monitorProps && + MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES <= monitorProps[0] && // Enabled ? I.e. contains active modes ? + MonitorModeProps.MIN_MONITOR_DEVICE_PROPERTIES <= monitorProps.length ) { + MonitorModeProps.streamInMonitorDevice(null, cache, this, monitorProps, 0); + } + } + } + } finally { + rAndR.endInitialQuery(device.getHandle(), this); + } + } + } finally { + device.unlock(); + } + } + + @Override + protected Rectangle getNativeMonitorDeviceViewportImpl(MonitorDevice monitor) { + final AbstractGraphicsDevice device = getDisplay().getGraphicsDevice(); + device.lock(); + try { + int[] viewportProps = rAndR.getMonitorDeviceViewport(device.getHandle(), this, monitor.getId()); + return new Rectangle(viewportProps[0], viewportProps[1], viewportProps[2], viewportProps[3]); + } finally { + device.unlock(); + } + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(final MonitorDevice monitor) { + if( null == rAndR ) { return null; } + + return runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<MonitorMode>() { + @Override + public MonitorMode run(long dpy) { + final int[] currentModeProps = rAndR.getCurrentMonitorModeProps(dpy, ScreenDriver.this, monitor.getId()); + return MonitorModeProps.streamInMonitorMode(null, null, currentModeProps, 0); + } } ); + } + + @Override + protected boolean setCurrentMonitorModeImpl(final MonitorDevice monitor, final MonitorMode mode) { + if( null == rAndR ) { return false; } + + final long t0 = System.currentTimeMillis(); + boolean done = runWithOptTempDisplayHandle( new DisplayImpl.DisplayRunnable<Boolean>() { + @Override + public Boolean run(long dpy) { + return Boolean.valueOf( rAndR.setCurrentMonitorMode(dpy, ScreenDriver.this, monitor, mode) ); + } + }).booleanValue(); + + if(DEBUG || !done) { + System.err.println("X11Screen.setCurrentMonitorModeImpl: TO ("+SCREEN_MODE_CHANGE_TIMEOUT+") reached: "+ + (System.currentTimeMillis()-t0)+"ms; "+monitor.getCurrentMode()+" -> "+mode); + } + return done; + } + + private DisplayImpl.DisplayRunnable<Boolean> xineramaEnabledQueryWithTemp = new DisplayImpl.DisplayRunnable<Boolean>() { + @Override + public Boolean run(long dpy) { + return new Boolean(X11Util.XineramaIsEnabled(dpy)); + } }; + + @Override + protected int validateScreenIndex(final int idx) { + final DisplayDriver x11Display = (DisplayDriver) getDisplay(); + final Boolean r = x11Display.isXineramaEnabled(); + if( null != r ) { + return r.booleanValue() ? 0 : idx; + } else { + return runWithTempDisplayHandle( xineramaEnabledQueryWithTemp ).booleanValue() ? 0 : idx; + } + } + + @Override + protected void calcVirtualScreenOriginAndSize(final Rectangle vOriginSize) { + final RectangleImmutable ov = (RectangleImmutable) getViewport().cloneMutable(); + /** + if( null != rAndR && rAndR.getVersion().compareTo(RandR.version130) >= 0 && getMonitorDevices().size()>0 ) { + super.calcVirtualScreenOriginAndSize(vOriginSize); + if( DEBUG ) { + System.err.println("X11Screen.calcVirtualScreenOriginAndSize: UpdatingViewport "+ov+" -> "+vOriginSize); + } + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + public Object run(long dpy) { + rAndR.updateScreenViewport(dpy, ScreenDriver.this, vOriginSize); + return null; + } } ); + } else */ { + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(long dpy) { + vOriginSize.set(0, 0, getWidth0(dpy, screen_idx), getHeight0(dpy, screen_idx)); + return null; + } } ); + if( DEBUG ) { + System.err.println("X11Screen.calcVirtualScreenOriginAndSize: Querying X11: "+ov+" -> "+vOriginSize); + } + } + } + + //---------------------------------------------------------------------- + // Internals only + // + private final <T> T runWithLockedDisplayDevice(DisplayRunnable<T> action) { + return display.runWithLockedDisplayDevice(action); + } + + private final <T> T runWithTempDisplayHandle(DisplayRunnable<T> action) { + final long displayHandle = X11Util.openDisplay(display.getName()); + if(0 == displayHandle) { + throw new RuntimeException("null device"); + } + T res; + try { + res = action.run(displayHandle); + } finally { + X11Util.closeDisplay(displayHandle); + } + return res; + } + + private final <T> T runWithOptTempDisplayHandle(DisplayRunnable<T> action) { + if( null != rAndR && rAndR.getVersion().compareTo(RandR.version130) >= 0 ) { + return display.runWithLockedDisplayDevice(action); + } else { + return runWithTempDisplayHandle(action); + } + } + + private static native long GetScreen0(long dpy, int scrn_idx); + + private static native int getWidth0(long display, int scrn_idx); + + private static native int getHeight0(long display, int scrn_idx); + + private static native int[] getRandRVersion0(long display); +} diff --git a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java new file mode 100644 index 000000000..0eda37eac --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java @@ -0,0 +1,469 @@ +/* + * 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.driver.x11; + +import java.nio.Buffer; + +import jogamp.nativewindow.x11.X11Lib; +import jogamp.nativewindow.x11.X11Util; +import jogamp.newt.DisplayImpl; +import jogamp.newt.DisplayImpl.DisplayRunnable; +import jogamp.newt.PointerIconImpl; +import jogamp.newt.WindowImpl; +import jogamp.newt.driver.PNGIcon; + +import javax.media.nativewindow.*; +import javax.media.nativewindow.VisualIDHolder.VIDType; +import javax.media.nativewindow.util.Insets; +import javax.media.nativewindow.util.InsetsImmutable; +import javax.media.nativewindow.util.Point; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.nativewindow.x11.X11GraphicsDevice; +import com.jogamp.nativewindow.x11.X11GraphicsScreen; +import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.event.InputEvent; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.MouseEvent; + +public class WindowDriver extends WindowImpl { + private static final String WINDOW_CLASS_NAME = "NewtWindow"; + private static final int X11_WHEEL_ONE_UP_BUTTON = 4; + private static final int X11_WHEEL_ONE_DOWN_BUTTON = 5; + private static final int X11_WHEEL_TWO_UP_BUTTON = 6; + private static final int X11_WHEEL_TWO_DOWN_BUTTON = 7; + + private static final int defaultIconDataSize; + private static final Buffer defaultIconData; + + static { + ScreenDriver.initSingleton(); + + int _icon_data_size=0, _icon_elem_bytesize=0; + Buffer _icon_data=null; + if( PNGIcon.isAvailable() ) { + try { + // NOTE: MUST BE DIRECT BUFFER, since _NET_WM_ICON Atom uses buffer directly! + final int[] data_size = { 0 }, elem_bytesize = { 0 }; + _icon_data = PNGIcon.arrayToX11BGRAImages(NewtFactory.getWindowIcons(), data_size, elem_bytesize); + _icon_data_size = data_size[0]; + _icon_elem_bytesize = elem_bytesize[0]; + } catch (Exception e) { + e.printStackTrace(); + } + } + defaultIconDataSize = _icon_data_size; + defaultIconData = _icon_data; + if(DEBUG_IMPLEMENTATION) { + System.err.println("Def. Icon: data_size "+defaultIconDataSize+" * elem_size "+_icon_elem_bytesize+" = data "+defaultIconData); + } + } + + public WindowDriver() { + } + + @Override + protected void createNativeImpl() { + final ScreenDriver screen = (ScreenDriver) getScreen(); + final DisplayDriver display = (DisplayDriver) screen.getDisplay(); + final AbstractGraphicsDevice edtDevice = display.getGraphicsDevice(); + + // Decoupled X11 Device/Screen allowing X11 display lock-free off-thread rendering + final long renderDeviceHandle = X11Util.openDisplay(edtDevice.getConnection()); + if( 0 == renderDeviceHandle ) { + throw new RuntimeException("Error creating display(GfxCfg/Render): "+edtDevice.getConnection()); + } + renderDevice = new X11GraphicsDevice(renderDeviceHandle, AbstractGraphicsDevice.DEFAULT_UNIT, true /* owner */); + final AbstractGraphicsScreen renderScreen = new X11GraphicsScreen(renderDevice, screen.getIndex()); + + final GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(display.getGraphicsDevice(), capsRequested); + final AbstractGraphicsConfiguration cfg = factory.chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, renderScreen, VisualIDHolder.VID_UNDEFINED); + if(DEBUG_IMPLEMENTATION) { + System.err.println("X11Window.createNativeImpl() factory: "+factory+", chosen config: "+cfg); + } + if (null == cfg) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + final int visualID = cfg.getVisualID(VIDType.NATIVE); + if(VisualIDHolder.VID_UNDEFINED == visualID) { + throw new NativeWindowException("Chosen Configuration w/o native visual ID: "+cfg); + } + setGraphicsConfiguration(cfg); + final int flags = getReconfigureFlags(0, true) & + ( FLAG_IS_ALWAYSONTOP | FLAG_IS_UNDECORATED ) ; + edtDevice.lock(); + try { + setWindowHandle(CreateWindow(getParentWindowHandle(), + edtDevice.getHandle(), screen.getIndex(), visualID, + display.getJavaObjectAtom(), display.getWindowDeleteAtom(), + getX(), getY(), getWidth(), getHeight(), autoPosition(), flags, + defaultIconDataSize, defaultIconData)); + } finally { + edtDevice.unlock(); + } + windowHandleClose = getWindowHandle(); + if (0 == windowHandleClose) { + throw new NativeWindowException("Error creating window"); + } + } + + @Override + protected void closeNativeImpl() { + if(0!=windowHandleClose && null!=getScreen() ) { + DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); + final AbstractGraphicsDevice edtDevice = display.getGraphicsDevice(); + edtDevice.lock(); + try { + CloseWindow0(edtDevice.getHandle(), windowHandleClose, + display.getJavaObjectAtom(), display.getWindowDeleteAtom() /* , display.getKbdHandle() */); // XKB disabled for now + } catch (Throwable t) { + if(DEBUG_IMPLEMENTATION) { + Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); + e.printStackTrace(); + } + } finally { + edtDevice.unlock(); + windowHandleClose = 0; + } + } + if(null != renderDevice) { + renderDevice.close(); // closes X11 display + renderDevice = null; + } + } + + /** + * <p> + * X11 Window supports {@link #FLAG_IS_FULLSCREEN_SPAN} + * </p> + * {@inheritDoc} + */ + @Override + protected boolean isReconfigureFlagSupported(int changeFlags) { + return true; // all flags! + } + + @Override + protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, int flags) { + final int _x, _y; + final InsetsImmutable _insets; + if( 0 == ( FLAG_IS_UNDECORATED & flags) ) { + // client position -> top-level window position + _insets = getInsets(); + _x = x - _insets.getLeftWidth() ; + _y = y - _insets.getTopHeight() ; + } else { + _insets = null; + _x = x; + _y = y; + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("X11Window reconfig: "+x+"/"+y+" -> "+_x+"/"+_y+" "+width+"x"+height+", insets "+_insets+", "+ getReconfigureFlagsAsString(null, flags)); + } + if( 0 != ( FLAG_CHANGE_FULLSCREEN & flags ) ) { + if( 0 != ( FLAG_IS_FULLSCREEN & flags) && 0 == ( FLAG_IS_ALWAYSONTOP & flags) ) { + tempFSAlwaysOnTop = true; + flags |= FLAG_IS_ALWAYSONTOP; + if(DEBUG_IMPLEMENTATION) { + System.err.println("X11Window reconfig.2: temporary "+getReconfigureFlagsAsString(null, flags)); + } + } else { + tempFSAlwaysOnTop = false; + } + } + final int fflags = flags; + final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(long dpy) { + reconfigureWindow0( dpy, getScreenIndex(), + getParentWindowHandle(), getWindowHandle(), display.getWindowDeleteAtom(), + _x, _y, width, height, fflags); + return null; + } + }); + return true; + } + volatile boolean tempFSAlwaysOnTop = false; + + /** + * <p> + * Deal w/ tempAlwaysOnTop. + * </p> + * {@inheritDoc} + */ + @Override + protected void focusChanged(boolean defer, boolean focusGained) { + if( isNativeValid() && isFullscreen() && tempFSAlwaysOnTop && hasFocus() != focusGained ) { + final int flags = getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible()) | ( focusGained ? FLAG_IS_ALWAYSONTOP : 0 ); + if(DEBUG_IMPLEMENTATION) { + System.err.println("X11Window reconfig.3 (focus): temporary "+getReconfigureFlagsAsString(null, flags)); + } + final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(long dpy) { + reconfigureWindow0( dpy, getScreenIndex(), + getParentWindowHandle(), getWindowHandle(), display.getWindowDeleteAtom(), + getX(), getY(), getWidth(), getHeight(), flags); + return null; + } + }); + } + super.focusChanged(defer, focusGained); + } + + protected void reparentNotify(long newParentWindowHandle) { + if(DEBUG_IMPLEMENTATION) { + final long p0 = getParentWindowHandle(); + System.err.println("Window.reparentNotify ("+getThreadName()+"): "+toHexString(p0)+" -> "+toHexString(newParentWindowHandle)); + } + } + + @Override + protected void requestFocusImpl(final boolean force) { + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(long dpy) { + requestFocus0(dpy, getWindowHandle(), force); + return null; + } + }); + } + + @Override + protected void setTitleImpl(final String title) { + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(long dpy) { + setTitle0(dpy, getWindowHandle(), title); + return null; + } + }); + } + + @Override + protected void setPointerIconImpl(final PointerIconImpl pi) { + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(long dpy) { + try { + setPointerIcon0(dpy, getWindowHandle(), null != pi ? pi.validatedHandle() : 0); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + }); + } + + @Override + protected boolean setPointerVisibleImpl(final boolean pointerVisible) { + return runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Boolean>() { + @Override + public Boolean run(long dpy) { + final PointerIconImpl pi = (PointerIconImpl)getPointerIcon(); + final boolean res; + if( pointerVisible && null != pi ) { + setPointerIcon0(dpy, getWindowHandle(), null != pi ? pi.validatedHandle() : 0); + res = true; + } else { + res = setPointerVisible0(dpy, getWindowHandle(), pointerVisible); + } + return Boolean.valueOf(res); + } + }).booleanValue(); + } + + @Override + protected boolean confinePointerImpl(final boolean confine) { + return runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Boolean>() { + @Override + public Boolean run(long dpy) { + return Boolean.valueOf(confinePointer0(dpy, getWindowHandle(), confine)); + } + }).booleanValue(); + } + + @Override + protected void warpPointerImpl(final int x, final int y) { + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(long dpy) { + warpPointer0(dpy, getWindowHandle(), x, y); + return null; + } + }); + } + + @Override + protected Point getLocationOnScreenImpl(final int x, final int y) { + return runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Point>() { + @Override + public Point run(long dpy) { + return X11Lib.GetRelativeLocation(dpy, getScreenIndex(), getWindowHandle(), 0 /*root win*/, x, y); + } + } ); + } + + @Override + protected void updateInsetsImpl(Insets insets) { + // nop - using event driven insetsChange(..) + } + + @Override + protected final void doMouseEvent(boolean enqueue, boolean wait, short eventType, int modifiers, + int x, int y, short button, float[] rotationXYZ, float rotationScale) { + switch(eventType) { + case MouseEvent.EVENT_MOUSE_PRESSED: + switch(button) { + case X11_WHEEL_ONE_UP_BUTTON: + case X11_WHEEL_ONE_DOWN_BUTTON: + case X11_WHEEL_TWO_UP_BUTTON: + case X11_WHEEL_TWO_DOWN_BUTTON: + // ignore wheel pressed ! + return; + } + break; + case MouseEvent.EVENT_MOUSE_RELEASED: + final boolean shiftPressed = 0 != ( modifiers & InputEvent.SHIFT_MASK ); + switch(button) { + case X11_WHEEL_ONE_UP_BUTTON: // vertical scroll up + eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; + button = 1; + rotationXYZ[shiftPressed ? 0 : 1] = 1; + break; + case X11_WHEEL_ONE_DOWN_BUTTON: // vertical scroll down + eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; + button = 1; + rotationXYZ[shiftPressed ? 0 : 1] = -1; + break; + case X11_WHEEL_TWO_UP_BUTTON: // horizontal scroll left + eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; + button = 1; + rotationXYZ[0] = 1; + modifiers |= InputEvent.SHIFT_MASK; + break; + case X11_WHEEL_TWO_DOWN_BUTTON: // horizontal scroll right + eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; + button = 1; + rotationXYZ[0] = -1; + modifiers |= InputEvent.SHIFT_MASK; + break; + } + break; + } + super.doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, rotationXYZ, rotationScale); + } + + /** Called by native TK */ + protected final void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar0, String keyString) { + // handleKeyEvent(true, false, eventType, modifiers, keyCode, keyChar); + final boolean isModifierKey = KeyEvent.isModifierKey(keyCode); + final boolean isAutoRepeat = 0 != ( KeyEvent.AUTOREPEAT_MASK & modifiers ); + final char keyChar = ( null != keyString ) ? keyString.charAt(0) : keyChar0; + // System.err.println("*** sendKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", keyCode "+toHexString(keyCode)+", keyChar <"+keyChar0+">/<"+keyChar+">, keyString "+keyString+", mods "+toHexString(modifiers)+ + // ", isKeyCodeTracked "+isKeyCodeTracked(keyCode)+", was: pressed "+isKeyPressed(keyCode)+", repeat "+isAutoRepeat+", [modifierKey "+isModifierKey+"] - "+System.currentTimeMillis()); + + if( !isAutoRepeat || !isModifierKey ) { // ! ( isModifierKey && isAutoRepeat ) + switch(eventType) { + case KeyEvent.EVENT_KEY_PRESSED: + super.sendKeyEvent(KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keySym, keyChar); + break; + + case KeyEvent.EVENT_KEY_RELEASED: + super.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keySym, keyChar); + break; + } + } + } + + @Override + public final void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); + } + @Override + public final void enqueueKeyEvent(boolean wait, short eventType, int modifiers, short keyCode, short keySym, char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); + } + + //---------------------------------------------------------------------- + // Internals only + // + private static final String getCurrentThreadName() { return Thread.currentThread().getName(); } // Callback for JNI + private static final void dumpStack() { Thread.dumpStack(); } // Callback for JNI + + private final <T> T runWithLockedDisplayDevice(DisplayRunnable<T> action) { + return ((DisplayDriver) getScreen().getDisplay()).runWithLockedDisplayDevice(action); + } + + protected static native boolean initIDs0(); + + private long CreateWindow(long parentWindowHandle, long display, int screen_index, + int visualID, long javaObjectAtom, long windowDeleteAtom, + int x, int y, int width, int height, boolean autoPosition, int flags, + int pixelDataSize, Buffer pixels) { + // NOTE: MUST BE DIRECT BUFFER, since _NET_WM_ICON Atom uses buffer directly! + if( !Buffers.isDirect(pixels) ) { + throw new IllegalArgumentException("data buffer is not direct "+pixels); + } + return CreateWindow0(parentWindowHandle, display, screen_index, + visualID, javaObjectAtom, windowDeleteAtom, + x, y, width, height, autoPosition, flags, + pixelDataSize, + pixels, Buffers.getDirectBufferByteOffset(pixels), true /* pixels_is_direct */); + } + private native long CreateWindow0(long parentWindowHandle, long display, int screen_index, + int visualID, long javaObjectAtom, long windowDeleteAtom, + int x, int y, int width, int height, boolean autoPosition, int flags, + int pixelDataSize, Object pixels, int pixels_byte_offset, boolean pixels_is_direct); + private native void CloseWindow0(long display, long windowHandle, long javaObjectAtom, long windowDeleteAtom /*, long kbdHandle*/ ); // XKB disabled for now + private native void reconfigureWindow0(long display, int screen_index, long parentWindowHandle, long windowHandle, + long windowDeleteAtom, int x, int y, int width, int height, int flags); + private native void requestFocus0(long display, long windowHandle, boolean force); + + private static native void setTitle0(long display, long windowHandle, String title); + + private static native void setPointerIcon0(long display, long windowHandle, long handle); + + private static native long getParentWindow0(long display, long windowHandle); + private static native boolean setPointerVisible0(long display, long windowHandle, boolean visible); + private static native boolean confinePointer0(long display, long windowHandle, boolean grab); + private static native void warpPointer0(long display, long windowHandle, int x, int y); + + private long windowHandleClose; + private X11GraphicsDevice renderDevice; +} diff --git a/src/newt/classes/jogamp/newt/driver/x11/X11Screen.java b/src/newt/classes/jogamp/newt/driver/x11/X11Screen.java deleted file mode 100644 index 93db854ac..000000000 --- a/src/newt/classes/jogamp/newt/driver/x11/X11Screen.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * 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.driver.x11; - -import java.util.List; - -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; - -import jogamp.nativewindow.x11.X11Util; -import jogamp.newt.DisplayImpl; -import jogamp.newt.DisplayImpl.DisplayRunnable; -import jogamp.newt.ScreenImpl; - -import com.jogamp.nativewindow.x11.X11GraphicsDevice; -import com.jogamp.nativewindow.x11.X11GraphicsScreen; -import com.jogamp.newt.ScreenMode; -import com.jogamp.newt.util.ScreenModeUtil; - -public class X11Screen extends ScreenImpl { - - static { - X11Display.initSingleton(); - } - - public X11Screen() { - } - - protected void createNativeImpl() { - // validate screen index - Long handle = display.runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<Long>() { - public Long run(long dpy) { - return new Long(GetScreen0(dpy, screen_idx)); - } } ); - if (handle.longValue() == 0) { - throw new RuntimeException("Error creating screen: " + screen_idx); - } - aScreen = new X11GraphicsScreen((X11GraphicsDevice) getDisplay().getGraphicsDevice(), screen_idx); - } - - protected void closeNativeImpl() { - } - - private int[] nrotations; - private int nrotation_index; - private int nres_number; - private int nres_index; - private int[] nrates; - private int nrate_index; - private int nmode_number; - - protected int[] getScreenModeFirstImpl() { - return runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<int[]>() { - public int[] run(long dpy) { - // initialize iterators and static data - nrotations = getAvailableScreenModeRotations0(dpy, screen_idx); - if(null==nrotations || 0==nrotations.length) { - return null; - } - nrotation_index = 0; - - nres_number = getNumScreenModeResolutions0(dpy, screen_idx); - if(0==nres_number) { - return null; - } - nres_index = 0; - - nrates = getScreenModeRates0(dpy, screen_idx, nres_index); - if(null==nrates || 0==nrates.length) { - return null; - } - nrate_index = 0; - - nmode_number = 0; - - return getScreenModeNextImpl(); - } } ); - } - - protected int[] getScreenModeNextImpl() { - // assemble: w x h x bpp x f x r - return runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<int[]>() { - public int[] run(long dpy) { - /** - System.err.println("******** mode: "+nmode_number); - System.err.println("rot "+nrotation_index); - System.err.println("rate "+nrate_index); - System.err.println("res "+nres_index); */ - - int[] res = getScreenModeResolution0(dpy, screen_idx, nres_index); - if(null==res || 0==res.length) { - return null; - } - if(0>=res[0] || 0>=res[1]) { - throw new InternalError("invalid resolution: "+res[0]+"x"+res[1]+" for res idx "+nres_index+"/"+nres_number); - } - int rate = nrates[nrate_index]; - if(0>=rate) { - rate = default_sm_rate; - if(DEBUG) { - System.err.println("Invalid rate: "+rate+" at index "+nrate_index+"/"+nrates.length+", using default: "+default_sm_rate); - } - } - int rotation = nrotations[nrotation_index]; - - int[] props = new int[ 1 + ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL ]; - int i = 0; - props[i++] = nres_index; // use resolution index, not unique for native -> ScreenMode - props[i++] = 0; // set later for verification of iterator - props[i++] = res[0]; // width - props[i++] = res[1]; // height - props[i++] = default_sm_bpp; // FIXME - props[i++] = res[2]; // widthmm - props[i++] = res[3]; // heightmm - props[i++] = rate; // rate - props[i++] = rotation; - props[i - ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL] = i - 1; // count without extra element - - nmode_number++; - - // iteration: r -> f -> bpp -> [w x h] - nrotation_index++; - if(nrotation_index == nrotations.length) { - nrotation_index=0; - nrate_index++; - if(null == nrates || nrate_index == nrates.length){ - nres_index++; - if(nres_index == nres_number) { - // done - nrates=null; - nrotations=null; - return null; - } - - nrates = getScreenModeRates0(dpy, screen_idx, nres_index); - if(null==nrates || 0==nrates.length) { - return null; - } - nrate_index = 0; - } - } - - return props; - } } ); - } - - protected ScreenMode getCurrentScreenModeImpl() { - return runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<ScreenMode>() { - public ScreenMode run(long dpy) { - long screenConfigHandle = getScreenConfiguration0(dpy, screen_idx); - if(0 == screenConfigHandle) { - return null; - } - int[] res; - int rate, rot; - try { - int resNumber = getNumScreenModeResolutions0(dpy, screen_idx); - if(0==resNumber) { - return null; - } - - int resIdx = getCurrentScreenResolutionIndex0(screenConfigHandle); - if(0>resIdx) { - return null; - } - if(resIdx>=resNumber) { - throw new RuntimeException("Invalid resolution index: ! "+resIdx+" < "+resNumber); - } - res = getScreenModeResolution0(dpy, screen_idx, resIdx); - if(null==res || 0==res.length) { - return null; - } - if(0>=res[0] || 0>=res[1]) { - throw new InternalError("invalid resolution: "+res[0]+"x"+res[1]+" for res idx "+resIdx+"/"+resNumber); - } - rate = getCurrentScreenRate0(screenConfigHandle); - if(0>rate) { - return null; - } - rot = getCurrentScreenRotation0(screenConfigHandle); - if(0>rot) { - return null; - } - } finally { - freeScreenConfiguration0(screenConfigHandle); - } - int[] props = new int[ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL]; - int i = 0; - props[i++] = 0; // set later for verification of iterator - props[i++] = res[0]; // width - props[i++] = res[1]; // height - props[i++] = default_sm_bpp; // FIXME - props[i++] = res[2]; // widthmm - props[i++] = res[3]; // heightmm - props[i++] = rate; // rate - props[i++] = rot; - props[i - ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL] = i; // count - return ScreenModeUtil.streamIn(props, 0); - } } ); - } - - protected boolean setCurrentScreenModeImpl(final ScreenMode screenMode) { - final List<ScreenMode> screenModes = this.getScreenModesOrig(); - final int screenModeIdx = screenModes.indexOf(screenMode); - if(0>screenModeIdx) { - throw new RuntimeException("ScreenMode not element of ScreenMode list: "+screenMode); - } - final long t0 = System.currentTimeMillis(); - boolean done = runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<Boolean>() { - public Boolean run(long dpy) { - boolean done = false; - long screenConfigHandle = getScreenConfiguration0(dpy, screen_idx); - if(0 == screenConfigHandle) { - return Boolean.valueOf(done); - } - try { - int resNumber = getNumScreenModeResolutions0(dpy, screen_idx); - int resIdx = getScreenModesIdx2NativeIdx().get(screenModeIdx); - if(0>resIdx || resIdx>=resNumber) { - throw new RuntimeException("Invalid resolution index: ! 0 < "+resIdx+" < "+resNumber+", screenMode["+screenModeIdx+"] "+screenMode); - } - - final int f = screenMode.getMonitorMode().getRefreshRate(); - final int r = screenMode.getRotation(); - - if( setCurrentScreenModeStart0(dpy, screen_idx, screenConfigHandle, resIdx, f, r) ) { - while(!done && System.currentTimeMillis()-t0 < SCREEN_MODE_CHANGE_TIMEOUT) { - done = setCurrentScreenModePollEnd0(dpy, screen_idx, resIdx, f, r); - if(!done) { - try { Thread.sleep(10); } catch (InterruptedException e) { } - } - } - } - } finally { - freeScreenConfiguration0(screenConfigHandle); - } - return Boolean.valueOf(done); - } - }).booleanValue(); - - if(DEBUG || !done) { - System.err.println("X11Screen.setCurrentScreenModeImpl: TO ("+SCREEN_MODE_CHANGE_TIMEOUT+") reached: "+ - (System.currentTimeMillis()-t0)+"ms; Current: "+getCurrentScreenMode()+"; Desired: "+screenMode); - } - return done; - } - - private class XineramaEnabledQuery implements DisplayImpl.DisplayRunnable<Boolean> { - public Boolean run(long dpy) { - return new Boolean(X11Util.XineramaIsEnabled(dpy)); - } - } - private XineramaEnabledQuery xineramaEnabledQuery = new XineramaEnabledQuery(); - - protected int validateScreenIndex(final int idx) { - if(getDisplay().isNativeValid()) { - return runWithLockedDisplayHandle( xineramaEnabledQuery ).booleanValue() ? 0 : idx; - } else { - return runWithTempDisplayHandle( xineramaEnabledQuery ).booleanValue() ? 0 : idx; - } - } - - protected void getVirtualScreenOriginAndSize(final Point virtualOrigin, final Dimension virtualSize) { - display.runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<Object>() { - public Object run(long dpy) { - virtualOrigin.setX(0); - virtualOrigin.setY(0); - virtualSize.setWidth(getWidth0(dpy, screen_idx)); - virtualSize.setHeight(getHeight0(dpy, screen_idx)); - return null; - } } ); - } - - //---------------------------------------------------------------------- - // Internals only - // - private final <T> T runWithLockedDisplayHandle(DisplayRunnable<T> action) { - return display.runWithLockedDisplayHandle(action); - // return runWithTempDisplayHandle(action); - // return runWithoutLock(action); - } - - private final <T> T runWithTempDisplayHandle(DisplayRunnable<T> action) { - final long displayHandle = X11Util.openDisplay(display.getName()); - if(0 == displayHandle) { - throw new RuntimeException("null device"); - } - T res; - try { - res = action.run(displayHandle); - } finally { - X11Util.closeDisplay(displayHandle); - } - return res; - } - private final <T> T runWithoutLock(DisplayRunnable<T> action) { - return action.run(display.getHandle()); - } - - private static native long GetScreen0(long dpy, int scrn_idx); - - private static native int getWidth0(long display, int scrn_idx); - - private static native int getHeight0(long display, int scrn_idx); - - /** @return int[] { rot1, .. } */ - private static native int[] getAvailableScreenModeRotations0(long display, int screen_index); - - private static native int getNumScreenModeResolutions0(long display, int screen_index); - - /** @return int[] { width, height, widthmm, heightmm } */ - private static native int[] getScreenModeResolution0(long display, int screen_index, int mode_index); - - private static native int[] getScreenModeRates0(long display, int screen_index, int mode_index); - - private static native long getScreenConfiguration0(long display, int screen_index); - private static native void freeScreenConfiguration0(long screenConfiguration); - - private static native int getCurrentScreenResolutionIndex0(long screenConfiguration); - private static native int getCurrentScreenRate0(long screenConfiguration); - private static native int getCurrentScreenRotation0(long screenConfiguration); - - /** needs own Display connection for XRANDR event handling */ - private static native boolean setCurrentScreenModeStart0(long display, int screen_index, long screenConfiguration, int mode_index, int freq, int rot); - private static native boolean setCurrentScreenModePollEnd0(long display, int screen_index, int mode_index, int freq, int rot); -} diff --git a/src/newt/classes/jogamp/newt/driver/x11/X11Window.java b/src/newt/classes/jogamp/newt/driver/x11/X11Window.java deleted file mode 100644 index 143b94a57..000000000 --- a/src/newt/classes/jogamp/newt/driver/x11/X11Window.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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.driver.x11; - -import jogamp.nativewindow.x11.X11Lib; -import jogamp.newt.DisplayImpl; -import jogamp.newt.DisplayImpl.DisplayRunnable; -import jogamp.newt.WindowImpl; -import javax.media.nativewindow.*; -import javax.media.nativewindow.VisualIDHolder.VIDType; -import javax.media.nativewindow.util.Insets; -import javax.media.nativewindow.util.InsetsImmutable; -import javax.media.nativewindow.util.Point; - -import com.jogamp.newt.event.MouseEvent; - -public class X11Window extends WindowImpl { - private static final String WINDOW_CLASS_NAME = "NewtWindow"; - private static final int X11_WHEEL_ONE_UP_BUTTON = 4; - private static final int X11_WHEEL_ONE_DOWN_BUTTON = 5; - private static final int X11_WHEEL_TWO_UP_BUTTON = 6; - private static final int X11_WHEEL_TWO_DOWN_BUTTON = 7; - - static { - X11Display.initSingleton(); - } - - public X11Window() { - } - - protected void createNativeImpl() { - final X11Screen screen = (X11Screen) getScreen(); - final X11Display display = (X11Display) screen.getDisplay(); - final GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(display.getGraphicsDevice()); - final AbstractGraphicsConfiguration cfg = factory.chooseGraphicsConfiguration( - capsRequested, capsRequested, capabilitiesChooser, screen.getGraphicsScreen()); - if(DEBUG_IMPLEMENTATION) { - System.err.println("X11Window.createNativeImpl() factory: "+factory+", chosen config: "+cfg); - } - if (null == cfg) { - throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); - } - final int visualID = cfg.getVisualID(VIDType.NATIVE); - if(VisualIDHolder.VID_UNDEFINED == visualID) { - throw new NativeWindowException("Chosen Configuration w/o native visual ID: "+cfg); - } - setGraphicsConfiguration(cfg); - final int flags = getReconfigureFlags(0, true) & - ( FLAG_IS_ALWAYSONTOP | FLAG_IS_UNDECORATED ) ; - setWindowHandle(CreateWindow0(getParentWindowHandle(), - display.getEDTHandle(), screen.getIndex(), visualID, - display.getJavaObjectAtom(), display.getWindowDeleteAtom(), - getX(), getY(), getWidth(), getHeight(), autoPosition(), flags)); - windowHandleClose = getWindowHandle(); - if (0 == windowHandleClose) { - throw new NativeWindowException("Error creating window"); - } - } - - protected void closeNativeImpl() { - if(0!=windowHandleClose && null!=getScreen() ) { - X11Display display = (X11Display) getScreen().getDisplay(); - try { - CloseWindow0(display.getEDTHandle(), windowHandleClose, - display.getJavaObjectAtom(), display.getWindowDeleteAtom()); - } catch (Throwable t) { - if(DEBUG_IMPLEMENTATION) { - Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); - e.printStackTrace(); - } - } finally { - windowHandleClose = 0; - } - } - } - - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { - if(DEBUG_IMPLEMENTATION) { - System.err.println("X11Window reconfig: "+x+"/"+y+" "+width+"x"+height+", "+ - getReconfigureFlagsAsString(null, flags)); - } - if(0 == ( FLAG_IS_UNDECORATED & flags)) { - final InsetsImmutable i = getInsets(); - - // client position -> top-level window position - x -= i.getLeftWidth() ; - y -= i.getTopHeight() ; - } - final X11Display display = (X11Display) getScreen().getDisplay(); - reconfigureWindow0( getDisplayEDTHandle(), getScreenIndex(), - getParentWindowHandle(), getWindowHandle(), display.getWindowDeleteAtom(), - x, y, width, height, flags); - - return true; - } - - protected void reparentNotify(long newParentWindowHandle) { - if(DEBUG_IMPLEMENTATION) { - final long p0 = getParentWindowHandle(); - System.err.println("Window.reparentNotify ("+getThreadName()+"): "+toHexString(p0)+" -> "+toHexString(newParentWindowHandle)); - } - } - - protected void requestFocusImpl(boolean force) { - requestFocus0(getDisplayEDTHandle(), getWindowHandle(), force); - } - - @Override - protected void setTitleImpl(final String title) { - runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<Object>() { - public Object run(long dpy) { - setTitle0(dpy, getWindowHandle(), title); - return null; - } - }); - } - - @Override - protected boolean setPointerVisibleImpl(final boolean pointerVisible) { - return runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<Boolean>() { - public Boolean run(long dpy) { - return Boolean.valueOf(setPointerVisible0(getDisplayEDTHandle(), getWindowHandle(), pointerVisible)); - } - }).booleanValue(); - } - - @Override - protected boolean confinePointerImpl(final boolean confine) { - return runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<Boolean>() { - public Boolean run(long dpy) { - return Boolean.valueOf(confinePointer0(getDisplayEDTHandle(), getWindowHandle(), confine)); - } - }).booleanValue(); - } - - @Override - protected void warpPointerImpl(final int x, final int y) { - runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<Object>() { - public Object run(long dpy) { - warpPointer0(getDisplayEDTHandle(), getWindowHandle(), x, y); - return null; - } - }); - } - - protected Point getLocationOnScreenImpl(final int x, final int y) { - // X11Util.GetRelativeLocation: locks display already ! - return X11Lib.GetRelativeLocation( getScreen().getDisplay().getHandle(), getScreenIndex(), getWindowHandle(), 0 /*root win*/, x, y); - } - - protected void updateInsetsImpl(Insets insets) { - // nop - using event driven insetsChange(..) - } - - protected void doMouseEvent(boolean enqueue, boolean wait, int eventType, int modifiers, - int x, int y, int button, int rotation) { - switch(eventType) { - case MouseEvent.EVENT_MOUSE_PRESSED: - switch(button) { - case X11_WHEEL_ONE_UP_BUTTON: - case X11_WHEEL_ONE_DOWN_BUTTON: - case X11_WHEEL_TWO_UP_BUTTON: - case X11_WHEEL_TWO_DOWN_BUTTON: - // ignore wheel pressed ! - return; - } - break; - case MouseEvent.EVENT_MOUSE_RELEASED: - switch(button) { - case X11_WHEEL_ONE_UP_BUTTON: - eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; - button = 1; - rotation = 1; - break; - case X11_WHEEL_ONE_DOWN_BUTTON: - eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; - button = 1; - rotation = -1; - break; - case X11_WHEEL_TWO_UP_BUTTON: - eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; - button = 2; - rotation = 1; - break; - case X11_WHEEL_TWO_DOWN_BUTTON: - eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; - button = 2; - rotation = -1; - break; - } - break; - } - super.doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, rotation); - } - - - //---------------------------------------------------------------------- - // Internals only - // - - private final long getDisplayEDTHandle() { - return ((X11Display) getScreen().getDisplay()).getEDTHandle(); - } - private final <T> T runWithLockedDisplayHandle(DisplayRunnable<T> action) { - return ((DisplayImpl) getScreen().getDisplay()).runWithLockedDisplayHandle(action); - // return runWithTempDisplayHandle(action); - } - - protected static native boolean initIDs0(); - - private native long CreateWindow0(long parentWindowHandle, long display, int screen_index, - int visualID, long javaObjectAtom, long windowDeleteAtom, - int x, int y, int width, int height, boolean autoPosition, int flags); - private native void CloseWindow0(long display, long windowHandle, long javaObjectAtom, long windowDeleteAtom); - private native void reconfigureWindow0(long display, int screen_index, long parentWindowHandle, long windowHandle, - long windowDeleteAtom, int x, int y, int width, int height, int flags); - private native void requestFocus0(long display, long windowHandle, boolean force); - - private static native void setTitle0(long display, long windowHandle, String title); - private static native long getParentWindow0(long display, long windowHandle); - private static native boolean setPointerVisible0(long display, long windowHandle, boolean visible); - private static native boolean confinePointer0(long display, long windowHandle, boolean grab); - private static native void warpPointer0(long display, long windowHandle, int x, int y); - - private long windowHandleClose; -} diff --git a/src/newt/classes/jogamp/newt/event/NEWTEventTask.java b/src/newt/classes/jogamp/newt/event/NEWTEventTask.java index fae6560b4..38a434279 100644 --- a/src/newt/classes/jogamp/newt/event/NEWTEventTask.java +++ b/src/newt/classes/jogamp/newt/event/NEWTEventTask.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package jogamp.newt.event; import com.jogamp.newt.event.NEWTEvent; @@ -35,17 +35,22 @@ import com.jogamp.newt.event.NEWTEvent; * which notifies after sending the event for the <code>invokeAndWait()</code> semantics. */ public class NEWTEventTask { - NEWTEvent event; - Object notifyObject; + private NEWTEvent event; + private Object notifyObject; + private RuntimeException exception; public NEWTEventTask(NEWTEvent event, Object notifyObject) { this.event = event ; this.notifyObject = notifyObject ; + this.exception = null; } - public NEWTEvent get() { return event; } + public final NEWTEvent get() { return event; } + public final void setException(RuntimeException e) { exception = e; } + public final RuntimeException getException() { return exception; } + public final boolean isCallerWaiting() { return null != notifyObject; } - public void notifyIssuer() { + public void notifyCaller() { if(null != notifyObject) { synchronized (notifyObject) { notifyObject.notifyAll(); diff --git a/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java b/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java new file mode 100644 index 000000000..db89690f4 --- /dev/null +++ b/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java @@ -0,0 +1,364 @@ +/** + * Copyright 2012 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.newt.swt; + +import javax.media.nativewindow.NativeWindowException; + +import jogamp.newt.Debug; + +import com.jogamp.common.util.RunnableTask; +import com.jogamp.newt.util.EDTUtil; + +/** + * Simple {@link EDTUtil} implementation utilizing the SWT UI thread + * of the given {@link Display}. + */ +public class SWTEDTUtil implements EDTUtil { + public static final boolean DEBUG = Debug.debug("EDT"); + + private final Object edtLock = new Object(); // locking the EDT start/stop state + private final ThreadGroup threadGroup; + private final String name; + private final Runnable dispatchMessages; + private final org.eclipse.swt.widgets.Display swtDisplay; + private NEDT nedt = null; + private int start_iter=0; + private static long pollPeriod = EDTUtil.defaultEDTPollPeriod; + + public SWTEDTUtil(final com.jogamp.newt.Display newtDisplay, org.eclipse.swt.widgets.Display swtDisplay) { + this.threadGroup = Thread.currentThread().getThreadGroup(); + this.name=Thread.currentThread().getName()+"-SWTDisplay-"+newtDisplay.getFQName()+"-EDT-"; + this.dispatchMessages = new Runnable() { + @Override + public void run() { + ((jogamp.newt.DisplayImpl) newtDisplay).dispatchMessages(); + } }; + this.swtDisplay = swtDisplay; + this.nedt = new NEDT(threadGroup, name); + this.nedt.setDaemon(true); // don't stop JVM from shutdown .. + } + + public final org.eclipse.swt.widgets.Display getDisplay() { + return swtDisplay; + } + + @Override + public long getPollPeriod() { + return pollPeriod; + } + + @Override + public void setPollPeriod(long ms) { + pollPeriod = ms; + } + + @Override + public final boolean start() throws IllegalStateException { + final boolean swtDisposed = swtDisplay.isDisposed(); + synchronized(edtLock) { + if( nedt.isRunning() ) { + final Thread curT = Thread.currentThread(); + final Thread swtT = !swtDisposed ? swtDisplay.getThread() : null; + final boolean onSWTEDT = swtT == curT; + throw new IllegalStateException("EDT still running and not subject to stop. Curr "+curT.getName()+", NEDT "+nedt.getName()+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", SWT-EDT "+swtT.getName()+", on SWT-EDT "+onSWTEDT); + } + if(DEBUG) { + System.err.println(Thread.currentThread()+": SWT-EDT reset - edt: "+nedt+", swtDisposed (skipping) "+swtDisposed); + } + if( !swtDisposed ) { + if( nedt.getState() != Thread.State.NEW ) { + nedt = new NEDT(threadGroup, name); + nedt.setDaemon(true); // don't stop JVM from shutdown .. + } + startImpl(); + } + } + if( !swtDisposed ) { + return invoke(true, nullTask); + } else { + return false; + } + } + + private final void startImpl() { + if(nedt.isAlive()) { + throw new RuntimeException("SWT-EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", edt: "+nedt); + } + start_iter++; + nedt.setName(name+start_iter); + if(DEBUG) { + System.err.println(Thread.currentThread()+": SWT-EDT START - edt: "+nedt); + // Thread.dumpStack(); + } + nedt.start(); + } + + @Override + public boolean isCurrentThreadEDT() { + return !swtDisplay.isDisposed() && swtDisplay.getThread() == Thread.currentThread(); + } + + @Override + public final boolean isCurrentThreadNEDT() { + return nedt == Thread.currentThread(); + } + + @Override + public final boolean isCurrentThreadEDTorNEDT() { + final Thread ct = Thread.currentThread(); + return ( !swtDisplay.isDisposed() && ct == swtDisplay.getThread() ) || ct == nedt ; + } + + @Override + public boolean isRunning() { + return nedt.isRunning(); + } + + @Override + public final boolean invokeStop(boolean wait, Runnable task) { + return invokeImpl(wait, task, true); + } + + @Override + public final boolean invoke(boolean wait, Runnable task) { + return invokeImpl(wait, task, false); + } + + private static Runnable nullTask = new Runnable() { + @Override + public void run() { } + }; + + private final boolean invokeImpl(boolean wait, Runnable task, boolean stop) { + Throwable throwable = null; + RunnableTask rTask = null; + final Object rTaskLock = new Object(); + synchronized(rTaskLock) { // lock the optional task execution + synchronized(edtLock) { // lock the EDT status + if( nedt.shouldStop ) { + // drop task .. + if(DEBUG) { + System.err.println(Thread.currentThread()+": Warning: SWT-EDT about (1) to stop, won't enqueue new task: "+nedt+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop); + Thread.dumpStack(); + } + return false; + } + if( swtDisplay.isDisposed() ) { + stop = true; + } + + if( isCurrentThreadEDT() ) { + if(null != task) { + task.run(); + } + wait = false; // running in same thread (EDT) -> no wait + if( stop ) { + nedt.shouldStop = true; + } + } else { + if( !nedt.isRunning && !swtDisplay.isDisposed() ) { + if( null != task ) { + if( stop ) { + System.err.println(Thread.currentThread()+": Warning: SWT-EDT is about (3) to stop and stopped already, dropping task. NEDT "+nedt); + } else { + System.err.println(Thread.currentThread()+": Warning: SWT-EDT is not running, dropping task. NEDT "+nedt); + } + if(DEBUG) { + Thread.dumpStack(); + } + } + return false; + } else if( stop ) { + if( nedt.isRunning ) { + if(DEBUG) { + System.err.println(Thread.currentThread()+": SWT-EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop); + } + synchronized(nedt.sync) { + nedt.shouldStop = true; + nedt.sync.notifyAll(); // stop immediate if waiting (poll freq) + } + } + if( swtDisplay.isDisposed() ) { + System.err.println(Thread.currentThread()+": Warning: SWT-EDT is about (3) to stop and stopped already, dropping task. "+nedt); + if(DEBUG) { + Thread.dumpStack(); + } + return false; + } + } + + if( null != task ) { + rTask = new RunnableTask(task, + wait ? rTaskLock : null, + true /* always catch and report Exceptions, don't disturb EDT */, + wait ? null : System.err); + swtDisplay.asyncExec(rTask); + } + } + } + if( wait ) { + try { + rTaskLock.wait(); // free lock, allow execution of rTask + } catch (InterruptedException ie) { + throwable = ie; + } + if(null==throwable) { + throwable = rTask.getThrowable(); + } + if(null!=throwable) { + if(throwable instanceof NativeWindowException) { + throw (NativeWindowException)throwable; + } + throw new RuntimeException(throwable); + } + } + return true; + } + } + + @Override + final public boolean waitUntilIdle() { + final NEDT _nedt; + synchronized(edtLock) { + _nedt = nedt; + } + final Thread ct = Thread.currentThread(); + if( !_nedt.isRunning || _nedt == ct || swtDisplay.isDisposed() || swtDisplay.getThread() == ct ) { + return false; + } + try { + swtDisplay.syncExec(new Runnable() { + @Override + public void run() { } + }); + } catch (Exception e) { } + return true; + } + + @Override + final public boolean waitUntilStopped() { + synchronized(edtLock) { + final Thread curT = Thread.currentThread(); + final Thread swtT = !swtDisplay.isDisposed() ? swtDisplay.getThread() : null; + final boolean onSWTEDT = swtT == curT; + if( nedt.isRunning && nedt != curT && !onSWTEDT ) { + while( nedt.isRunning ) { + try { + edtLock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return true; + } else { + return false; + } + } + } + + class NEDT extends Thread { + volatile boolean shouldStop = false; + volatile boolean isRunning = false; + Object sync = new Object(); + + public NEDT(ThreadGroup tg, String name) { + super(tg, name); + } + + final public boolean isRunning() { + return isRunning && !shouldStop; + } + + @Override + final public void start() throws IllegalThreadStateException { + isRunning = true; + super.start(); + } + + /** + * Utilizing locking only on tasks and its execution, + * not for event dispatching. + */ + @Override + final public void run() { + if(DEBUG) { + System.err.println(getName()+": SWT-EDT run() START "+ getName()); + } + RuntimeException error = null; + try { + do { + // event dispatch + if(!shouldStop) { + // EDT invoke thread is SWT-EDT, + // hence dispatching is required to run on SWT-EDT as well. + // Otherwise a deadlock may happen due to dispatched event's + // triggering a locking action. + if ( !swtDisplay.isDisposed() ) { + swtDisplay.syncExec(dispatchMessages); + } else { + dispatchMessages.run(); + } + } + // wait + synchronized(sync) { + if(!shouldStop) { + try { + sync.wait(pollPeriod); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } while(!shouldStop) ; + } catch (Throwable t) { + // handle errors .. + shouldStop = true; + if(t instanceof RuntimeException) { + error = (RuntimeException) t; + } else { + error = new RuntimeException("Within SWT-EDT", t); + } + } finally { + if(DEBUG) { + System.err.println(getName()+": SWT-EDT run() END "+ getName()+", "+error); + } + synchronized(edtLock) { + isRunning = false; + edtLock.notifyAll(); + } + if(DEBUG) { + System.err.println(getName()+": SWT-EDT run() EXIT "+ getName()+", exception: "+error); + } + if(null!=error) { + throw error; + } + } // finally + } // run() + } // EventDispatchThread + +} diff --git a/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java b/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java new file mode 100644 index 000000000..b5c45c1aa --- /dev/null +++ b/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java @@ -0,0 +1,375 @@ +/** + * Copyright 2012 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.swt.event; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +import com.jogamp.newt.event.InputEvent; +import com.jogamp.newt.event.MouseEvent; + +/** + * SWT event translator to NEWT, inclusive dispatch listener. + * <p> + * <b>Disclaimer:</b> This code is merely tested and subject to change. + * </p> + */ +public class SWTNewtEventFactory { + + public static final short eventTypeSWT2NEWT(int swtType) { + switch( swtType ) { + // case SWT.MouseXXX: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED; + case SWT.MouseDown: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED; + case SWT.MouseUp: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED; + case SWT.MouseMove: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED; + case SWT.MouseEnter: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_ENTERED; + case SWT.MouseExit: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_EXITED; + // case SWT.MouseXXX: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED; + case SWT.MouseVerticalWheel: return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED; + + case SWT.KeyDown: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_PRESSED; + case SWT.KeyUp: return com.jogamp.newt.event.KeyEvent.EVENT_KEY_RELEASED; + } + return (short)0; + } + + public static final int swtModifiers2Newt(int awtMods, boolean mouseHint) { + int newtMods = 0; + if ((awtMods & SWT.SHIFT) != 0) newtMods |= com.jogamp.newt.event.InputEvent.SHIFT_MASK; + if ((awtMods & SWT.CTRL) != 0) newtMods |= com.jogamp.newt.event.InputEvent.CTRL_MASK; + if ((awtMods & SWT.ALT) != 0) newtMods |= com.jogamp.newt.event.InputEvent.ALT_MASK; + return newtMods; + } + + public static short swtKeyCode2NewtKeyCode(final int swtKeyCode) { + final short defNEWTKeyCode = (short)swtKeyCode; + switch (swtKeyCode) { + case SWT.HOME : return com.jogamp.newt.event.KeyEvent.VK_HOME; + case SWT.END : return com.jogamp.newt.event.KeyEvent.VK_END; + case SWT.PRINT_SCREEN : return com.jogamp.newt.event.KeyEvent.VK_PRINTSCREEN; + case SWT.BS : return com.jogamp.newt.event.KeyEvent.VK_BACK_SPACE; + case SWT.TAB : return com.jogamp.newt.event.KeyEvent.VK_TAB; + case SWT.LF : return com.jogamp.newt.event.KeyEvent.VK_ENTER; + case SWT.PAGE_DOWN : return com.jogamp.newt.event.KeyEvent.VK_PAGE_DOWN; + case SWT.PAGE_UP : return com.jogamp.newt.event.KeyEvent.VK_PAGE_UP; + case SWT.CONTROL : return com.jogamp.newt.event.KeyEvent.VK_CONTROL; + case SWT.CAPS_LOCK : return com.jogamp.newt.event.KeyEvent.VK_CAPS_LOCK; + case SWT.PAUSE : return com.jogamp.newt.event.KeyEvent.VK_PAUSE; + case SWT.SCROLL_LOCK : return com.jogamp.newt.event.KeyEvent.VK_SCROLL_LOCK; + case SWT.CANCEL : return com.jogamp.newt.event.KeyEvent.VK_CANCEL; + case SWT.INSERT : return com.jogamp.newt.event.KeyEvent.VK_INSERT; + case SWT.ESC : return com.jogamp.newt.event.KeyEvent.VK_ESCAPE; + case SWT.SPACE : return com.jogamp.newt.event.KeyEvent.VK_SPACE; + case SWT.F1 : return com.jogamp.newt.event.KeyEvent.VK_F1; + case SWT.F2 : return com.jogamp.newt.event.KeyEvent.VK_F2; + case SWT.F3 : return com.jogamp.newt.event.KeyEvent.VK_F3; + case SWT.F4 : return com.jogamp.newt.event.KeyEvent.VK_F4; + case SWT.F5 : return com.jogamp.newt.event.KeyEvent.VK_F5; + case SWT.F6 : return com.jogamp.newt.event.KeyEvent.VK_F6; + case SWT.F7 : return com.jogamp.newt.event.KeyEvent.VK_F7; + case SWT.F8 : return com.jogamp.newt.event.KeyEvent.VK_F8; + case SWT.F9 : return com.jogamp.newt.event.KeyEvent.VK_F9; + case SWT.F10 : return com.jogamp.newt.event.KeyEvent.VK_F10; + case SWT.F11 : return com.jogamp.newt.event.KeyEvent.VK_F11; + case SWT.F12 : return com.jogamp.newt.event.KeyEvent.VK_F12; + case SWT.F13 : return com.jogamp.newt.event.KeyEvent.VK_F13; + case SWT.F14 : return com.jogamp.newt.event.KeyEvent.VK_F14; + case SWT.F15 : return com.jogamp.newt.event.KeyEvent.VK_F15; + case SWT.F16 : return com.jogamp.newt.event.KeyEvent.VK_F16; + case SWT.F17 : return com.jogamp.newt.event.KeyEvent.VK_F17; + case SWT.F18 : return com.jogamp.newt.event.KeyEvent.VK_F18; + case SWT.F19 : return com.jogamp.newt.event.KeyEvent.VK_F19; + case SWT.F20 : return com.jogamp.newt.event.KeyEvent.VK_F20; + case SWT.DEL : return com.jogamp.newt.event.KeyEvent.VK_DELETE; + case SWT.KEYPAD_0 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD0; + case SWT.KEYPAD_1 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD1; + case SWT.KEYPAD_2 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD2; + case SWT.KEYPAD_3 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD3; + case SWT.KEYPAD_4 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD4; + case SWT.KEYPAD_5 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD5; + case SWT.KEYPAD_6 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD6; + case SWT.KEYPAD_7 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD7; + case SWT.KEYPAD_8 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD8; + case SWT.KEYPAD_9 : return com.jogamp.newt.event.KeyEvent.VK_NUMPAD9; + case SWT.KEYPAD_DECIMAL: return com.jogamp.newt.event.KeyEvent.VK_DECIMAL; + case SWT.KEYPAD_ADD : return com.jogamp.newt.event.KeyEvent.VK_ADD; + case SWT.KEYPAD_SUBTRACT: return com.jogamp.newt.event.KeyEvent.VK_SUBTRACT; + case SWT.KEYPAD_MULTIPLY: return com.jogamp.newt.event.KeyEvent.VK_MULTIPLY; + case SWT.KEYPAD_DIVIDE : return com.jogamp.newt.event.KeyEvent.VK_DIVIDE; + case SWT.NUM_LOCK : return com.jogamp.newt.event.KeyEvent.VK_NUM_LOCK; + case SWT.ARROW_LEFT : return com.jogamp.newt.event.KeyEvent.VK_LEFT; + case SWT.ARROW_UP : return com.jogamp.newt.event.KeyEvent.VK_UP; + case SWT.ARROW_RIGHT : return com.jogamp.newt.event.KeyEvent.VK_RIGHT; + case SWT.ARROW_DOWN : return com.jogamp.newt.event.KeyEvent.VK_DOWN; + case SWT.HELP : return com.jogamp.newt.event.KeyEvent.VK_HELP; + } + return defNEWTKeyCode; + } + + public static int newtKeyCode2SWTKeyCode(final short newtKeyCode) { + final int defSWTKeyCode = 0xFFFF & (int)newtKeyCode; + switch (newtKeyCode) { + case com.jogamp.newt.event.KeyEvent.VK_HOME : return SWT.HOME; + case com.jogamp.newt.event.KeyEvent.VK_END : return SWT.END; + case com.jogamp.newt.event.KeyEvent.VK_PRINTSCREEN : return SWT.PRINT_SCREEN; + case com.jogamp.newt.event.KeyEvent.VK_BACK_SPACE : return SWT.BS; + case com.jogamp.newt.event.KeyEvent.VK_TAB : return SWT.TAB; + case com.jogamp.newt.event.KeyEvent.VK_ENTER : return SWT.LF; + case com.jogamp.newt.event.KeyEvent.VK_PAGE_DOWN : return SWT.PAGE_DOWN; + case com.jogamp.newt.event.KeyEvent.VK_PAGE_UP : return SWT.PAGE_UP; + case com.jogamp.newt.event.KeyEvent.VK_CONTROL : return SWT.CONTROL; + case com.jogamp.newt.event.KeyEvent.VK_CAPS_LOCK : return SWT.CAPS_LOCK; + case com.jogamp.newt.event.KeyEvent.VK_PAUSE : return SWT.PAUSE; + case com.jogamp.newt.event.KeyEvent.VK_SCROLL_LOCK : return SWT.SCROLL_LOCK; + case com.jogamp.newt.event.KeyEvent.VK_CANCEL : return SWT.CANCEL; + case com.jogamp.newt.event.KeyEvent.VK_INSERT : return SWT.INSERT; + case com.jogamp.newt.event.KeyEvent.VK_ESCAPE : return SWT.ESC; + case com.jogamp.newt.event.KeyEvent.VK_SPACE : return SWT.SPACE; + case com.jogamp.newt.event.KeyEvent.VK_F1 : return SWT.F1; + case com.jogamp.newt.event.KeyEvent.VK_F2 : return SWT.F2; + case com.jogamp.newt.event.KeyEvent.VK_F3 : return SWT.F3; + case com.jogamp.newt.event.KeyEvent.VK_F4 : return SWT.F4; + case com.jogamp.newt.event.KeyEvent.VK_F5 : return SWT.F5; + case com.jogamp.newt.event.KeyEvent.VK_F6 : return SWT.F6; + case com.jogamp.newt.event.KeyEvent.VK_F7 : return SWT.F7; + case com.jogamp.newt.event.KeyEvent.VK_F8 : return SWT.F8; + case com.jogamp.newt.event.KeyEvent.VK_F9 : return SWT.F9; + case com.jogamp.newt.event.KeyEvent.VK_F10 : return SWT.F10; + case com.jogamp.newt.event.KeyEvent.VK_F11 : return SWT.F11; + case com.jogamp.newt.event.KeyEvent.VK_F12 : return SWT.F12; + case com.jogamp.newt.event.KeyEvent.VK_F13 : return SWT.F13; + case com.jogamp.newt.event.KeyEvent.VK_F14 : return SWT.F14; + case com.jogamp.newt.event.KeyEvent.VK_F15 : return SWT.F15; + case com.jogamp.newt.event.KeyEvent.VK_F16 : return SWT.F16; + case com.jogamp.newt.event.KeyEvent.VK_F17 : return SWT.F17; + case com.jogamp.newt.event.KeyEvent.VK_F18 : return SWT.F18; + case com.jogamp.newt.event.KeyEvent.VK_F19 : return SWT.F19; + case com.jogamp.newt.event.KeyEvent.VK_F20 : return SWT.F20; + case com.jogamp.newt.event.KeyEvent.VK_DELETE : return SWT.DEL; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD0 : return SWT.KEYPAD_0; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD1 : return SWT.KEYPAD_1; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD2 : return SWT.KEYPAD_2; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD3 : return SWT.KEYPAD_3; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD4 : return SWT.KEYPAD_4; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD5 : return SWT.KEYPAD_5; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD6 : return SWT.KEYPAD_6; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD7 : return SWT.KEYPAD_7; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD8 : return SWT.KEYPAD_8; + case com.jogamp.newt.event.KeyEvent.VK_NUMPAD9 : return SWT.KEYPAD_9; + case com.jogamp.newt.event.KeyEvent.VK_DECIMAL : return SWT.KEYPAD_DECIMAL; + case com.jogamp.newt.event.KeyEvent.VK_ADD : return SWT.KEYPAD_ADD; + case com.jogamp.newt.event.KeyEvent.VK_SUBTRACT : return SWT.KEYPAD_SUBTRACT; + case com.jogamp.newt.event.KeyEvent.VK_MULTIPLY : return SWT.KEYPAD_MULTIPLY; + case com.jogamp.newt.event.KeyEvent.VK_DIVIDE : return SWT.KEYPAD_DIVIDE; + case com.jogamp.newt.event.KeyEvent.VK_NUM_LOCK : return SWT.NUM_LOCK; + case com.jogamp.newt.event.KeyEvent.VK_LEFT : return SWT.ARROW_LEFT; + case com.jogamp.newt.event.KeyEvent.VK_UP : return SWT.ARROW_UP; + case com.jogamp.newt.event.KeyEvent.VK_RIGHT : return SWT.ARROW_RIGHT; + case com.jogamp.newt.event.KeyEvent.VK_DOWN : return SWT.ARROW_DOWN; + case com.jogamp.newt.event.KeyEvent.VK_HELP : return SWT.HELP; + } + return defSWTKeyCode; + } + + + public static final com.jogamp.newt.event.InputEvent createInputEvent(org.eclipse.swt.widgets.Event event, Object source) { + com.jogamp.newt.event.InputEvent res = createMouseEvent(event, source); + if(null == res) { + res = createKeyEvent(event, source); + } + return res; + } + + public static final com.jogamp.newt.event.MouseEvent createMouseEvent(org.eclipse.swt.widgets.Event event, Object source) { + switch(event.type) { + case SWT.MouseDown: + case SWT.MouseUp: + case SWT.MouseMove: + case SWT.MouseEnter: + case SWT.MouseExit: + case SWT.MouseVerticalWheel: + break; + default: + return null; + } + final short type = eventTypeSWT2NEWT(event.type); + if( (short)0 != type ) { + float rotation = 0; + if (SWT.MouseVerticalWheel == event.type) { + // SWT/NEWT rotation is reversed - AWT +1 is down, NEWT +1 is up. + // rotation = -1 * (int) event.rotation; + rotation = (float) event.rotation; + } + + int mods = swtModifiers2Newt(event.stateMask, true); + + if( source instanceof com.jogamp.newt.Window) { + final com.jogamp.newt.Window newtSource = (com.jogamp.newt.Window)source; + if(newtSource.isPointerConfined()) { + mods |= InputEvent.CONFINED_MASK; + } + if(!newtSource.isPointerVisible()) { + mods |= InputEvent.INVISIBLE_MASK; + } + } + + return new com.jogamp.newt.event.MouseEvent( + type, (null==source)?(Object)event.data:source, (0xFFFFFFFFL & (long)event.time), + mods, event.x, event.y, (short)event.count, (short)event.button, MouseEvent.getRotationXYZ(rotation, mods), 1f); + } + return null; // no mapping .. + } + + public static final com.jogamp.newt.event.KeyEvent createKeyEvent(org.eclipse.swt.widgets.Event event, Object source) { + switch(event.type) { + case SWT.KeyDown: + case SWT.KeyUp: + break; + default: + return null; + } + final short type = eventTypeSWT2NEWT(event.type); + if( (short)0 != type ) { + final short newtKeyCode = swtKeyCode2NewtKeyCode( event.keyCode ); + return com.jogamp.newt.event.KeyEvent.create( + type, (null==source)?(Object)event.data:source, (0xFFFFFFFFL & (long)event.time), + swtModifiers2Newt(event.stateMask, false), + newtKeyCode, newtKeyCode, event.character); + } + return null; // no mapping .. + } + + // + // + // + + short dragButtonDown = 0; + + public SWTNewtEventFactory() { + resetButtonsDown(); + } + + final void resetButtonsDown() { + dragButtonDown = 0; + } + + public final boolean dispatchMouseEvent(org.eclipse.swt.widgets.Event event, Object source, com.jogamp.newt.event.MouseListener l) { + com.jogamp.newt.event.MouseEvent res = createMouseEvent(event, source); + if(null != res) { + if(null != l) { + switch(event.type) { + case SWT.MouseDown: + dragButtonDown = (short) event.button; + l.mousePressed(res); break; + case SWT.MouseUp: + dragButtonDown = 0; + l.mouseReleased(res); + { + final com.jogamp.newt.event.MouseEvent res2 = new com.jogamp.newt.event.MouseEvent( + com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED, + res.getSource(), + res.getWhen(), res.getModifiers(), + res.getX(), res.getY(), res.getClickCount(), + res.getButton(), res.getRotation(), res.getRotationScale()); + l.mouseClicked(res2); + } + break; + case SWT.MouseMove: + if( 0 < dragButtonDown ) { + final com.jogamp.newt.event.MouseEvent res2 = new com.jogamp.newt.event.MouseEvent( + com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED, + res.getSource(), + res.getWhen(), res.getModifiers(), + res.getX(), res.getY(), res.getClickCount(), + dragButtonDown, res.getRotation(), res.getRotationScale()); + l.mouseDragged( res2 ); + } else { + l.mouseMoved(res); + } + break; + case SWT.MouseEnter: + l.mouseEntered(res); + break; + case SWT.MouseExit: + resetButtonsDown(); + l.mouseExited(res); + break; + case SWT.MouseVerticalWheel: + l.mouseWheelMoved(res); + break; + } + } + return true; + } + return false; + } + + public final boolean dispatchKeyEvent(org.eclipse.swt.widgets.Event event, Object source, com.jogamp.newt.event.KeyListener l) { + com.jogamp.newt.event.KeyEvent res = createKeyEvent(event, source); + if(null != res) { + if(null != l) { + switch(event.type) { + case SWT.KeyDown: + l.keyPressed(res); + break; + case SWT.KeyUp: + l.keyReleased(res); + break; + } + } + return true; + } + return false; + } + + public final void attachDispatchListener(final org.eclipse.swt.widgets.Control ctrl, final Object source, + final com.jogamp.newt.event.MouseListener ml, + final com.jogamp.newt.event.KeyListener kl) { + final Listener listener = new Listener () { + @Override + public void handleEvent (Event event) { + if( dispatchMouseEvent( event, source, ml ) ) { + return; + } + if( dispatchKeyEvent( event, source, kl ) ) { + return; + } + } }; + ctrl.addListener(SWT.MouseDown, listener); + ctrl.addListener(SWT.MouseUp, listener); + ctrl.addListener(SWT.MouseMove, listener); + ctrl.addListener(SWT.MouseEnter, listener); + ctrl.addListener(SWT.MouseExit, listener); + ctrl.addListener(SWT.MouseVerticalWheel, listener); + ctrl.addListener(SWT.KeyDown, listener); + ctrl.addListener(SWT.KeyUp, listener); + } +} + |