diff options
Diffstat (limited to 'src/newt/classes')
112 files changed, 18899 insertions, 7186 deletions
diff --git a/src/newt/classes/com/jogamp/newt/Display.java b/src/newt/classes/com/jogamp/newt/Display.java index 1e9a0e9eb..c4b919951 100644 --- a/src/newt/classes/com/jogamp/newt/Display.java +++ b/src/newt/classes/com/jogamp/newt/Display.java @@ -28,31 +28,212 @@ 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 */ - public boolean equals(Object obj) { + @Override + public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof Display) { - Display d = (Display)obj; + final Display d = (Display)obj; return d.getFQName().equals(getFQName()); } return false; } /** + * 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 PixelFormat} for pointer-icon pixel data. + * <p> + * Using this value will avoid conversion within {@link #createPointerIcon(PixelRectangle, int, int)}. + * </p> + * <p> + * Known native pixel formats are: + * <ul> + * <li>X11: {@link PixelFormat#BGRA8888}</li> + * <li>Windows: {@link PixelFormat#BGRA8888}</li> + * <li>OSX: {@link PixelFormat#RGBA8888}</li> + * </ul> + * </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 +258,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 +269,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 +277,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 +287,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,53 +328,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>newEDTUtil</code> is <code>null</code>, + * 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 the new one, - * it's being stopped, wait-until-idle and reset to allow restart. + * If a previous one exists and it differs from <code>usrEDTUtil</code>, + * it's being stopped, wait-until-idle. * </p> * <p> - * If <code>newEDTUtil</code> is not null and equals the previous one, - * <code>null</code> is returned and no change is being made. + * If <code>usrEDTUtil</code> is not null and equals the previous one, + * no change is being made. * </p> */ - public abstract EDTUtil setEDTUtil(EDTUtil newEDTUtil); - + 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) { + public static void dumpDisplayList(final 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(final String type, final String name, final int fromIndex, final boolean shared) { + return getDisplayOfImpl(type, name, fromIndex, 1, shared); } /** @@ -195,33 +389,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. + * @param 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(final String type, final String name, final int fromIndex, final 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(final String type, final String name, final int fromIndex, final int incr, final 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(final 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; } @@ -236,15 +466,15 @@ public abstract class Display { return Thread.currentThread().getName(); } - public static String toHexString(int hex) { + public static String toHexString(final int hex) { return "0x" + Integer.toHexString(hex); } - public static String toHexString(long hex) { + public static String toHexString(final long hex) { return "0x" + Long.toHexString(hex); } - public static int hashCodeNullSafe(Object o) { + public static int hashCodeNullSafe(final Object o) { return ( null != o ) ? o.hashCode() : 0; } } 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..126162006 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/MonitorDevice.java @@ -0,0 +1,299 @@ +/** + * 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 javax.media.nativewindow.util.SurfaceSize; + +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> + * <p> + * All values of this interface are represented in pixel units, if not stated otherwise. + * </p> + */ +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 viewportPU; // in pixel units + protected Rectangle viewportWU; // in window units + + protected MonitorDevice(final Screen screen, final int nativeId, final DimensionImmutable sizeMM, final Rectangle viewportPU, final Rectangle viewportWU, final MonitorMode currentMode, final ArrayHashSet<MonitorMode> supportedModes) { + this.screen = screen; + this.nativeId = nativeId; + this.sizeMM = sizeMM; + this.originalMode = currentMode; + this.supportedModes = supportedModes; + this.currentMode = currentMode; + this.viewportPU = viewportPU; + this.viewportWU = viewportWU; + 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(final Object obj) { + if (this == obj) { return true; } + if (obj instanceof MonitorDevice) { + final 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 <i>pixels per millimeter</i> value according to the <i>current</i> {@link MonitorMode mode}'s + * {@link SurfaceSize#getResolution() surface resolution}. + * <p> + * To convert the result to <i>dpi</i>, i.e. dots-per-inch, multiply both components with <code>25.4f</code>. + * </p> + * @param ppmmStore float[2] storage for the ppmm result + * @return the passed storage containing the ppmm for chaining + */ + public final float[] getPixelsPerMM(final float[] ppmmStore) { + return getPixelsPerMM(getCurrentMode(), ppmmStore); + } + + /** + * Returns the <i>pixels per millimeter</i> value according to the given {@link MonitorMode mode}'s + * {@link SurfaceSize#getResolution() surface resolution}. + * <p> + * To convert the result to <i>dpi</i>, i.e. dots-per-inch, multiply both components with <code>25.4f</code>. + * </p> + * @param mode + * @param ppmmStore float[2] storage for the ppmm result + * @return the passed storage containing the ppmm for chaining + */ + public final float[] getPixelsPerMM(final MonitorMode mode, final float[] ppmmStore) { + final DimensionImmutable sdim = getSizeMM(); + final DimensionImmutable spix = mode.getSurfaceSize().getResolution(); + ppmmStore[0] = (float)spix.getWidth() / (float)sdim.getWidth(); + ppmmStore[1] = (float)spix.getHeight() / (float)sdim.getHeight(); + return ppmmStore; + } + + /** + * 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 <b>rotated</b> virtual {@link Screen} size in pixel units + * represented by this monitor, i.e. top-left origin and size. + * @see Screen + */ + public final RectangleImmutable getViewport() { + return viewportPU; + } + + /** + * Returns the {@link RectangleImmutable rectangular} portion + * of the <b>rotated</b> virtual {@link Screen} size in window units + * represented by this monitor, i.e. top-left origin and size. + * @see Screen + */ + public final RectangleImmutable getViewportInWindowUnits() { + return viewportWU; + } + + /** + * Returns <code>true</code> if given screen coordinates in pixel units + * are contained by this {@link #getViewport() viewport}, otherwise <code>false</code>. + * @param x x-coord in pixel units + * @param y y-coord in pixel units + */ + public final boolean contains(final int x, final int y) { + return x >= viewportPU.getX() && + x < viewportPU.getX() + viewportPU.getWidth() && + y >= viewportPU.getY() && + y < viewportPU.getY() + viewportPU.getHeight() ; + } + + /** + * Calculates the union of the given monitor's {@link #getViewport() viewport} in pixel- and window units. + * @param viewport storage for result in pixel units, maybe null + * @param viewportInWindowUnits storage for result in window units, maybe null + * @param monitors given list of monitors + */ + public static void unionOfViewports(final Rectangle viewport, final Rectangle viewportInWindowUnits, final List<MonitorDevice> monitors) { + int x1PU=Integer.MAX_VALUE, y1PU=Integer.MAX_VALUE; + int x2PU=Integer.MIN_VALUE, y2PU=Integer.MIN_VALUE; + int x1WU=Integer.MAX_VALUE, y1WU=Integer.MAX_VALUE; + int x2WU=Integer.MIN_VALUE, y2WU=Integer.MIN_VALUE; + for(int i=monitors.size()-1; i>=0; i--) { + if( null != viewport ) { + final RectangleImmutable viewPU = monitors.get(i).getViewport(); + x1PU = Math.min(x1PU, viewPU.getX()); + x2PU = Math.max(x2PU, viewPU.getX() + viewPU.getWidth()); + y1PU = Math.min(y1PU, viewPU.getY()); + y2PU = Math.max(y2PU, viewPU.getY() + viewPU.getHeight()); + } + if( null != viewportInWindowUnits ) { + final RectangleImmutable viewWU = monitors.get(i).getViewportInWindowUnits(); + x1WU = Math.min(x1WU, viewWU.getX()); + x2WU = Math.max(x2WU, viewWU.getX() + viewWU.getWidth()); + y1WU = Math.min(y1WU, viewWU.getY()); + y2WU = Math.max(y2WU, viewWU.getY() + viewWU.getHeight()); + } + } + if( null != viewport ) { + viewport.set(x1PU, y1PU, x2PU - x1PU, y2PU - y1PU); + } + if( null != viewportInWindowUnits ) { + viewportInWindowUnits.set(x1WU, y1WU, x2WU - x1WU, y2WU - y1WU); + } + } + + 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> + * @see #queryCurrentMode() + */ + 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> + * @see #getCurrentMode() + */ + 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 "+viewportPU+ " [pixels], "+viewportWU+" [window], 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..09bc06ebd --- /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 rectangle 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 in pixel units + DimensionImmutable 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(final MonitorMode mm1, final 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(final MonitorMode mm1, final 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 in pixel units */ + 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(final SurfaceSize surfaceSize, final float refreshRate, final 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(final 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(final 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(final 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(final int nativeId, final SizeAndRRate sizeAndRRate, final 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(final SurfaceSize surfaceSize, final float refreshRate, final int flags, final 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 in pixel units, + * derived from <code>getMonitorMode().getSurfaceSize().getResolution()</code> + * and <code>getRotation()</code> + */ + public final int getRotatedWidth() { + return getRotatedWH(true); + } + + /** Returns the rotated screen height in pixel units, + * 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(final Object obj) { + if (this == obj) { return true; } + if (obj instanceof MonitorMode) { + final 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(final 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 0644c9164..9606fae08 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,9 @@ import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowFactory; +import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.PropertyAccess; + import jogamp.newt.Debug; import jogamp.newt.DisplayImpl; import jogamp.newt.ScreenImpl; @@ -49,20 +56,60 @@ 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(); // 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 = PropertyAccess.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; + } } ); } - public static Class<?> getCustomClass(String packageName, String classBaseName) { + /** + * 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(final IOUtil.ClassResources cres) { defaultWindowIcons = cres; } + + public static Class<?> getCustomClass(final String packageName, final 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) { + } catch (final Throwable t) { if(DEBUG_IMPLEMENTATION) { System.err.println("Warning: Failed to find class <"+clazzName+">: "+t.getMessage()); t.printStackTrace(); @@ -74,12 +121,12 @@ 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> */ - public static synchronized void setUseEDT(boolean onoff) { + public static synchronized void setUseEDT(final boolean onoff) { useEDT = onoff; } @@ -92,13 +139,13 @@ 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. * @return the new or reused Display instance */ - public static Display createDisplay(String name) { + public static Display createDisplay(final String name) { return createDisplay(name, true); } @@ -116,7 +163,7 @@ public class NewtFactory { * @param reuse attempt to reuse an existing Display with same <code>name</code> if set true, otherwise create a new instance. * @return the new or reused Display instance */ - public static Display createDisplay(String name, boolean reuse) { + public static Display createDisplay(final String name, final boolean reuse) { return DisplayImpl.create(NativeWindowFactory.getNativeWindowType(true), name, 0, reuse); } @@ -133,7 +180,7 @@ public class NewtFactory { * see {@link AbstractGraphicsDevice#getConnection()}. Use <code>null</code> for default. * @return the new or reused Display instance */ - public static Display createDisplay(String type, String name) { + public static Display createDisplay(final String type, final String name) { return createDisplay(type, name, true); } @@ -152,7 +199,7 @@ public class NewtFactory { * @param reuse attempt to reuse an existing Display with same <code>name</code> if set true, otherwise create a new instance. * @return the new or reused Display instance */ - public static Display createDisplay(String type, String name, boolean reuse) { + public static Display createDisplay(final String type, final String name, final boolean reuse) { return DisplayImpl.create(type, name, 0, reuse); } @@ -166,7 +213,7 @@ public class NewtFactory { * and {@link Display#removeReference()}. * </p> */ - public static Screen createScreen(Display display, int index) { + public static Screen createScreen(final Display display, final int index) { return ScreenImpl.create(display, index); } @@ -183,7 +230,7 @@ public class NewtFactory { * and {@link Screen#removeReference()}. * </p> */ - public static Window createWindow(CapabilitiesImmutable caps) { + public static Window createWindow(final CapabilitiesImmutable caps) { return createWindowImpl(NativeWindowFactory.getNativeWindowType(true), caps); } @@ -197,8 +244,8 @@ public class NewtFactory { * and {@link Screen#removeReference()}. * </p> */ - public static Window createWindow(Screen screen, CapabilitiesImmutable caps) { - return createWindowImpl(screen, caps); + public static Window createWindow(final Screen screen, final CapabilitiesImmutable caps) { + return WindowImpl.create(null, 0, screen, caps); } /** @@ -223,8 +270,11 @@ public class NewtFactory { * * @param parentWindowObject either a NativeWindow instance */ - public static Window createWindow(NativeWindow parentWindow, CapabilitiesImmutable caps) { + public static Window createWindow(final NativeWindow parentWindow, final CapabilitiesImmutable caps) { final String type = NativeWindowFactory.getNativeWindowType(true); + if( null == parentWindow ) { + return createWindowImpl(type, caps); + } Screen screen = null; Window newtParentWindow = null; @@ -234,18 +284,18 @@ public class NewtFactory { screen = newtParentWindow.getScreen(); } else { // create a Display/Screen compatible to the NativeWindow - AbstractGraphicsConfiguration parentConfig = parentWindow.getGraphicsConfiguration(); + final AbstractGraphicsConfiguration parentConfig = parentWindow.getGraphicsConfiguration(); if(null!=parentConfig) { - AbstractGraphicsScreen parentScreen = parentConfig.getScreen(); - AbstractGraphicsDevice parentDevice = parentScreen.getDevice(); - Display display = NewtFactory.createDisplay(type, parentDevice.getHandle(), true); + final AbstractGraphicsScreen parentScreen = parentConfig.getScreen(); + final AbstractGraphicsDevice parentDevice = parentScreen.getDevice(); + final Display display = NewtFactory.createDisplay(type, parentDevice.getHandle(), true); screen = NewtFactory.createScreen(display, parentScreen.getIndex()); } else { - Display display = NewtFactory.createDisplay(type, null, true); // local display + final Display display = NewtFactory.createDisplay(type, null, true); // local display 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,32 +305,26 @@ 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) { - Display display = NewtFactory.createDisplay(type, null, true); // local display - Screen screen = NewtFactory.createScreen(display, 0); // screen 0 + private static Window createWindowImpl(final String type, final CapabilitiesImmutable caps) { + final Display display = NewtFactory.createDisplay(type, null, true); // local display + final Screen screen = NewtFactory.createScreen(display, 0); // screen 0 return WindowImpl.create(null, 0, screen, caps); } /** * 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(final String displayConnection, final int screenIdx, final long parentWindowHandle, final CapabilitiesImmutable caps) { + final String type = NativeWindowFactory.getNativeWindowType(true); + final Display display = NewtFactory.createDisplay(type, displayConnection, true); + final Screen screen = NewtFactory.createScreen(display, screenIdx); + return WindowImpl.create(null, parentWindowHandle, screen, caps); } /** @@ -290,26 +334,26 @@ public class NewtFactory { * * @param undecorated only impacts if the window is in top-level state, while attached to a parent window it's rendered undecorated always */ - public static Window createWindow(Object[] cstrArguments, Screen screen, CapabilitiesImmutable caps) { + public static Window createWindow(final Object[] cstrArguments, final Screen screen, final CapabilitiesImmutable caps) { return WindowImpl.create(cstrArguments, screen, caps); } /** * 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); + public static Display createDisplay(final String type, final long handle, final boolean reuse) { + return DisplayImpl.create(type, null, handle, reuse); } - public static boolean isScreenCompatible(NativeWindow parent, Screen childScreen) { + public static boolean isScreenCompatible(final NativeWindow parent, final Screen childScreen) { // Get parent's NativeWindow details - AbstractGraphicsConfiguration parentConfig = (AbstractGraphicsConfiguration) parent.getGraphicsConfiguration(); - AbstractGraphicsScreen parentScreen = (AbstractGraphicsScreen) parentConfig.getScreen(); - AbstractGraphicsDevice parentDevice = (AbstractGraphicsDevice) parentScreen.getDevice(); + final AbstractGraphicsConfiguration parentConfig = parent.getGraphicsConfiguration(); + final AbstractGraphicsScreen parentScreen = parentConfig.getScreen(); + final AbstractGraphicsDevice parentDevice = parentScreen.getDevice(); - DisplayImpl childDisplay = (DisplayImpl) childScreen.getDisplay(); - String parentDisplayName = childDisplay.validateDisplayName(null, parentDevice.getHandle()); - String childDisplayName = childDisplay.getName(); + final DisplayImpl childDisplay = (DisplayImpl) childScreen.getDisplay(); + final String parentDisplayName = childDisplay.validateDisplayName(null, parentDevice.getHandle()); + final String childDisplayName = childDisplay.getName(); if( ! parentDisplayName.equals( childDisplayName ) ) { return false; } @@ -320,23 +364,23 @@ public class NewtFactory { return true; } - public static Screen createCompatibleScreen(NativeWindow parent) { + public static Screen createCompatibleScreen(final NativeWindow parent) { return createCompatibleScreen(parent, null); } - public static Screen createCompatibleScreen(NativeWindow parent, Screen childScreen) { + public static Screen createCompatibleScreen(final NativeWindow parent, final Screen childScreen) { // Get parent's NativeWindow details - AbstractGraphicsConfiguration parentConfig = (AbstractGraphicsConfiguration) parent.getGraphicsConfiguration(); - AbstractGraphicsScreen parentScreen = (AbstractGraphicsScreen) parentConfig.getScreen(); - AbstractGraphicsDevice parentDevice = (AbstractGraphicsDevice) parentScreen.getDevice(); + final AbstractGraphicsConfiguration parentConfig = parent.getGraphicsConfiguration(); + final AbstractGraphicsScreen parentScreen = parentConfig.getScreen(); + final AbstractGraphicsDevice parentDevice = parentScreen.getDevice(); if(null != childScreen) { // check if child Display/Screen is compatible already - DisplayImpl childDisplay = (DisplayImpl) childScreen.getDisplay(); - String parentDisplayName = childDisplay.validateDisplayName(null, parentDevice.getHandle()); - String childDisplayName = childDisplay.getName(); - boolean displayEqual = parentDisplayName.equals( childDisplayName ); - boolean screenEqual = parentScreen.getIndex() == childScreen.getIndex(); + final DisplayImpl childDisplay = (DisplayImpl) childScreen.getDisplay(); + final String parentDisplayName = childDisplay.validateDisplayName(null, parentDevice.getHandle()); + final String childDisplayName = childDisplay.getName(); + final boolean displayEqual = parentDisplayName.equals( childDisplayName ); + final boolean screenEqual = parentScreen.getIndex() == childScreen.getIndex(); if(DEBUG_IMPLEMENTATION) { System.err.println("NewtFactory.createCompatibleScreen: Display: "+ parentDisplayName+" =? "+childDisplayName+" : "+displayEqual+"; Screen: "+ @@ -350,7 +394,7 @@ public class NewtFactory { // Prep NEWT's Display and Screen according to the parent final String type = NativeWindowFactory.getNativeWindowType(true); - Display display = NewtFactory.createDisplay(type, parentDevice.getHandle(), true); + final Display display = NewtFactory.createDisplay(type, parentDevice.getHandle(), true); return NewtFactory.createScreen(display, parentScreen.getIndex()); } } diff --git a/src/newt/classes/com/jogamp/newt/NewtVersion.java b/src/newt/classes/com/jogamp/newt/NewtVersion.java index 961ffdf6a..f4cdee487 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; @@ -38,7 +38,7 @@ public class NewtVersion extends JogampVersion { protected static volatile NewtVersion jogampCommonVersionInfo; - protected NewtVersion(String packageName, Manifest mf) { + protected NewtVersion(final String packageName, final Manifest mf) { super(packageName, mf); } @@ -46,16 +46,17 @@ 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); } } } return jogampCommonVersionInfo; } - public static void main(String args[]) { + public static void main(final String args[]) { System.err.println(VersionUtil.getPlatformInfo()); System.err.println(GlueGenVersion.getInstance()); System.err.println(NativeWindowVersion.getInstance()); diff --git a/src/newt/classes/com/jogamp/newt/Screen.java b/src/newt/classes/com/jogamp/newt/Screen.java index 26f19ad6b..0ba557972 100644 --- a/src/newt/classes/com/jogamp/newt/Screen.java +++ b/src/newt/classes/com/jogamp/newt/Screen.java @@ -27,14 +27,34 @@ */ 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. + * <p> + * All values of this interface are represented in pixel units, if not stated otherwise. + * </p> + * + * <a name="coordinateSystem"><h5>Coordinate System</h5></a> + * <p> + * <ul> + * <li>Screen space has it's origin in the top-left corner, and may not be at 0/0.</li> + * <li>{@link #getViewport() Virtual viewport} covers all {@link MonitorDevice}s {@link MonitorDevice#getViewport() viewports} and has it's origin in the top-left corner, and may not be at 0/0.</li> + * </ul> + * </p> +*/ public abstract class Screen { /** @@ -46,20 +66,22 @@ 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 */ - public boolean equals(Object obj) { + @Override + public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof Screen) { - Screen s = (Screen)obj; + final Screen s = (Screen)obj; return s.getFQName().equals(getFQName()); } return false; } /** - * 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 +111,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 +129,7 @@ public abstract class Screen { /** * See {@link Display#removeReference()} * + * @return number of references post operation * @see #addReference() * @see #setDestroyWhenUnused(boolean) * @see #getDestroyWhenUnused() @@ -120,81 +144,121 @@ public abstract class Screen { public abstract int getIndex(); /** - * @return the x position of the virtual top-left origin. + * See <a href="#coordinateSystem"> Coordinate System</a>. + * + * @return the x position of the virtual viewport's top-left origin in pixel units. */ public abstract int getX(); - + /** - * @return the y position of the virtual top-left origin. + * See <a href="#coordinateSystem"> Coordinate System</a>. + * + * @return the y position of the virtual viewport's top-left origin in pixel units. */ public abstract int getY(); - + /** - * @return the <b>rotated</b> virtual width. + * @return the <b>rotated</b> virtual viewport's width in pixel units. */ public abstract int getWidth(); /** - * @return the <b>rotated</b> virtual height. + * @return the <b>rotated</b> virtual viewport's height in pixel units. */ public abstract int getHeight(); /** + * See <a href="#coordinateSystem"> Coordinate System</a>. + * + * @return the <b>rotated</b> virtual viewport, i.e. top-left origin and size, in pixel units. + * @see #getViewportInWindowUnits() + */ + public abstract RectangleImmutable getViewport(); + + /** + * See <a href="#coordinateSystem"> Coordinate System</a>. + * + * @return the <b>rotated</b> virtual viewport, i.e. top-left origin and size, in window units. + * @see #getViewport() + */ + public abstract RectangleImmutable getViewportInWindowUnits(); + + /** * @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} with the highest {@link MonitorDevice#getViewportInWindowUnits() viewport} + * {@link RectangleImmutable#coverage(RectangleImmutable) coverage} of the given rectangle in window units. * <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> + * @param r arbitrary rectangle in window units */ - public abstract List<ScreenMode> getScreenModes(); + public final MonitorDevice getMainMonitor(final 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.getViewportInWindowUnits().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()}. + * Calculates the union of all monitor's {@link MonitorDevice#getViewport() viewport} in pixel- and window units. + * <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 viewport storage for result in pixel units, maybe null + * @param viewportInWindowUnits storage for result in window units, maybe null */ - public abstract ScreenMode getOriginalScreenMode(); + public final void unionOfMonitorViewports(final Rectangle viewport, final Rectangle viewportInWindowUnits) { + MonitorDevice.unionOfViewports(viewport, viewportInWindowUnits, 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; /** @@ -204,7 +268,7 @@ public abstract class Screen { * @param fromIndex start index, then increasing until found or end of list * * @return */ - public static Screen getFirstScreenOf(Display display, int idx, int fromIndex) { + public static Screen getFirstScreenOf(final Display display, final int idx, final int fromIndex) { return getScreenOfImpl(display, idx, fromIndex, 1); } @@ -215,29 +279,64 @@ public abstract class Screen { * @param fromIndex start index, then decreasing until found or end of list. -1 is interpreted as size - 1. * @return */ - public static Screen getLastScreenOf(Display display, int idx, int fromIndex) { + public static Screen getLastScreenOf(final Display display, final int idx, final int fromIndex) { return getScreenOfImpl(display, idx, fromIndex, -1); } - private static Screen getScreenOfImpl(Display display, int idx, int fromIndex, int incr) { + private static Screen getScreenOfImpl(final Display display, final int idx, final int fromIndex, final int incr) { 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 = 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(final 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 78e2abc6e..ec6d767fa 100644 --- a/src/newt/classes/com/jogamp/newt/Window.java +++ b/src/newt/classes/com/jogamp/newt/Window.java @@ -28,16 +28,28 @@ 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.ScalableSurface; import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.nativewindow.util.Rectangle; +import javax.media.nativewindow.util.RectangleImmutable; +import javax.media.nativewindow.util.SurfaceSize; /** * Specifying NEWT's Window functionality: @@ -50,12 +62,41 @@ import javax.media.nativewindow.WindowClosingProtocol; * <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 + * 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> + * <p> + * All values of this interface are represented in window units, if not stated otherwise. + * </p> + * + * <a name="coordinateSystem"><h5>Coordinate System</h5></a> + * <p> + * <ul> + * <li>Screen space has it's origin in the top-left corner, and may not be at 0/0.</li> + * <li>Window origin is in it's top-left corner, see {@link #getX()} and {@link #getY()}. </li> + * <li>Window client-area excludes {@link #getInsets() insets}, i.e. window decoration.</li> + * <li>Window origin is relative to it's parent window if exist, or the screen position (top-level).</li> + * </ul> + * See {@link NativeWindow} and {@link Screen}. + * </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 interface Window extends NativeWindow, WindowClosingProtocol, ScalableSurface { public static final boolean DEBUG_MOUSE_EVENT = Debug.debug("Window.MouseEvent"); public static final boolean DEBUG_KEY_EVENT = Debug.debug("Window.KeyEvent"); public static final boolean DEBUG_IMPLEMENTATION = Debug.debug("Window"); @@ -77,11 +118,20 @@ public interface Window extends NativeWindow, WindowClosingProtocol { boolean isNativeValid(); /** - * @return The associated Screen + * @return The associated {@link Screen} */ Screen getScreen(); /** + * Returns the {@link MonitorDevice} with the highest {@link MonitorDevice#getViewportInWindowUnits() viewport} + * {@link RectangleImmutable#coverage(RectangleImmutable) coverage} of this window. + * <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 @@ -104,14 +154,26 @@ public interface Window extends NativeWindow, WindowClosingProtocol { 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) */ @@ -119,17 +181,38 @@ public interface Window extends NativeWindow, WindowClosingProtocol { void destroy(); /** - * <code>setVisible</code> makes the window and children visible if <code>visible</code> is true, + * Set a custom action handling destruction issued by a {@link WindowImpl#windowDestroyNotify(boolean) toolkit triggered window destroy} + * replacing the default {@link #destroy()} action. + * <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> - * The <code>setVisible(true)</code> is responsible to actual create the native window. + * <code>setVisible(wait, true)</code> is responsible to actual create the native window. + * </p> + * <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 ) { @@ -139,12 +222,12 @@ public interface Window extends NativeWindow, WindowClosingProtocol { * </pre></p> * <p> * In case this window is a child window and has a {@link javax.media.nativewindow.NativeWindow} parent,<br> - * <code>setVisible(true)</code> has no effect as long the parent's is not valid yet, + * <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(true)</code> shall be repeated when the parent becomes valid. + * <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(); @@ -166,14 +249,40 @@ public interface Window extends NativeWindow, WindowClosingProtocol { // /** - * Sets the size of the window's client area, excluding decorations. + * Returns a newly created {@link Rectangle} containing window origin, {@link #getX()} & {@link #getY()}, + * and size, {@link #getWidth()} & {@link #getHeight()}, in window units. + */ + Rectangle getBounds(); + + /** + * Returns the <i>pixels per millimeter</i> of this window's {@link NativeSurface} + * according to the {@link #getMainMonitor() main monitor}'s <i>current</i> {@link MonitorMode mode}'s + * {@link SurfaceSize#getResolution() surface resolution}. + * <p> + * Method takes the {@link #getCurrentSurfaceScale(int[]) current surface-scale} and {@link #getNativeSurfaceScale(int[]) native surface-scale} + * into account, i.e.: + * <pre> + * surfacePpMM = monitorPpMM * currentSurfaceScale / nativeSurfaceScale, + * with PpMM == pixel per millimeter + * </pre> + * </p> + * <p> + * To convert the result to <i>dpi</i>, i.e. dots-per-inch, multiply both components with <code>25.4f</code>. + * </p> + * @param ppmmStore float[2] storage for the ppmm result + * @return the passed storage containing the ppmm for chaining + */ + float[] getPixelsPerMM(final float[] ppmmStore); + + /** + * Sets the size of the window's client area in window units, 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 .. @@ -182,22 +291,54 @@ public interface Window extends NativeWindow, WindowClosingProtocol { * <p> * This call is ignored if in fullscreen mode.<br></p> * - * @param width of the window's client area - * @param height of the window's client area + * @param width of the window's client area in window units + * @param height of the window's client area in window units * + * @see #setSurfaceSize(int, int) + * @see #setTopLevelSize(int, int) * @see #getInsets() */ void setSize(int width, int height); /** - * Sets the size of the top-level window including insets (window decorations). + * Sets the size of the window's surface in pixel units which claims the window's client area excluding decorations. + * + * <p> + * In multiple monitor mode, setting the window's surface size in pixel units + * might not be possible due to unknown <i>scale</i> values of the target display. + * Hence re-setting the pixel unit size after window creation is recommended. + * </p> + * <p> + * Zero size semantics are respected, see {@link #setVisible(boolean)}:<br> + * <pre> + * if ( visible && 0 != windowHandle && ( 0 ≥ width || 0 ≥ height ) ) { + * setVisible(false); + * } else if ( visible && 0 == windowHandle && 0 < width && 0 < height ) { + * setVisible(true); + * } else { + * // as expected .. + * } + * </pre></p> + * <p> + * This call is ignored if in fullscreen mode.<br></p> + * + * @param pixelWidth of the window's client area in pixel units + * @param pixelHeight of the window's client area in pixel units + * + * @see #setSize(int, int) + * @see #getInsets() + */ + void setSurfaceSize(int pixelWidth, int pixelHeight); + + /** + * Sets the size of the top-level window including insets (window decorations) in window units. * * <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 + * @param width of the top-level window area in window units + * @param height of the top-level window area in window units * * @see #setSize(int, int) * @see #getInsets() @@ -205,19 +346,19 @@ public interface Window extends NativeWindow, WindowClosingProtocol { void setTopLevelSize(int width, int height); /** - * Sets the location of the window's client area, excluding insets (window decorations).<br> + * Sets the location of the window's client area excluding insets (window decorations) in window units.<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 + * @param x coord of the client-area's top left corner in window units + * @param y coord of the client-area's top left corner in window units * * @see #getInsets() */ void setPosition(int x, int y); /** - * Sets the location of the top-level window inclusive insets (window decorations).<br> + * Sets the location of the top-level window inclusive insets (window decorations) in window units.<br> * * <p> * Note: Insets (if supported) are available only after the window is set visible and hence has been created. @@ -225,8 +366,8 @@ public interface Window extends NativeWindow, WindowClosingProtocol { * * 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 + * @param x coord of the top-level left corner in window units + * @param y coord of the top-level left corner in window units * * @see #setPosition(int, int) * @see #getInsets() @@ -257,6 +398,20 @@ public interface Window extends NativeWindow, WindowClosingProtocol { */ 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(); @@ -276,10 +431,10 @@ public interface Window extends NativeWindow, WindowClosingProtocol { void confinePointer(boolean confine); /** - * Moves the pointer to x/y relative to this window's origin. + * Moves the pointer to x/y relative to this window's origin in pixel units. * - * @param x relative pointer x position within this window - * @param y relative pointer y position within this window + * @param x relative pointer x position within this window in pixel units + * @param y relative pointer y position within this window in pixel units * * @see #confinePointer(boolean) */ @@ -303,6 +458,11 @@ public interface Window extends NativeWindow, WindowClosingProtocol { 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> @@ -312,15 +472,40 @@ public interface Window extends NativeWindow, WindowClosingProtocol { * 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 in window units, use -1 for default position. + * @param y new top-level position in window units, 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); - - ReparentOperation reparentWindow(NativeWindow newParent, boolean forceDestroyCreate); + 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 { @@ -345,7 +530,11 @@ public interface Window extends NativeWindow, WindowClosingProtocol { * 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 */ @@ -372,8 +561,20 @@ public interface Window extends NativeWindow, WindowClosingProtocol { */ void requestFocus(boolean wait); + /** + * Trigger window repaint while passing the dirty region in pixel units. + * @param x dirty-region y-pos in pixel units + * @param y dirty-region x-pos in pixel units + * @param width dirty-region width in pixel units + * @param height dirty-region height in pixel units + */ void windowRepaint(int x, int y, int width, int height); + /** + * Enqueues a {@link com.jogamp.newt.event.NEWTEvent NEWT event}. + * @param wait Passing <code>true</code> will block until the event has been processed, otherwise method returns immediately. + * @param event The {@link com.jogamp.newt.event.NEWTEvent event} to enqueue. + */ void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event); void runOnEDTIfAvail(boolean wait, final Runnable task); @@ -383,10 +584,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. */ @@ -468,15 +672,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. @@ -487,10 +688,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 89a749c51..c470f6840 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; @@ -42,23 +48,33 @@ import java.security.PrivilegedAction; import java.util.Set; import javax.media.nativewindow.NativeWindow; -import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.OffscreenLayerOption; -import javax.media.nativewindow.OffscreenLayerSurface; import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLException; +import javax.media.opengl.GLOffscreenAutoDrawable; 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; @@ -67,199 +83,265 @@ 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 + * 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 awtWinAdapter; + 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() { + if( componentAdded ) { + NewtCanvasAWT.this.destroyImpl(false /* removeNotify */, true /* windowClosing */); + } + } + }, new Runnable() { + @Override public void run() { - NewtCanvasAWT.this.destroy(); + 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); + awtWinAdapter = (AWTParentWindowAdapter) new AWTParentWindowAdapter().addTo(this); + awtWinAdapter.removeWindowClosingFrom(this); // we utilize AWTWindowClosingProtocol triggered destruction! } /** * Instantiates a NewtCanvas without a NEWT child.<br> */ - public NewtCanvasAWT(GraphicsConfiguration gc) { + public NewtCanvasAWT(final GraphicsConfiguration gc) { super(gc); + awtMouseAdapter = new AWTMouseAdapter().addTo(this); + awtKeyAdapter = new AWTKeyAdapter().addTo(this); + awtWinAdapter = (AWTParentWindowAdapter) new AWTParentWindowAdapter().addTo(this); + awtWinAdapter.removeWindowClosingFrom(this); // we utilize AWTWindowClosingProtocol triggered destruction! } /** * Instantiates a NewtCanvas with a NEWT child. */ - public NewtCanvasAWT(Window child) { + public NewtCanvasAWT(final Window child) { super(); + awtMouseAdapter = new AWTMouseAdapter().addTo(this); + awtKeyAdapter = new AWTKeyAdapter().addTo(this); + awtWinAdapter = (AWTParentWindowAdapter) new AWTParentWindowAdapter().addTo(this); + awtWinAdapter.removeWindowClosingFrom(this); // we utilize AWTWindowClosingProtocol triggered destruction! setNEWTChild(child); } /** * Instantiates a NewtCanvas with a NEWT child. */ - public NewtCanvasAWT(GraphicsConfiguration gc, Window child) { + public NewtCanvasAWT(final GraphicsConfiguration gc, final Window child) { super(gc); + awtMouseAdapter = new AWTMouseAdapter().addTo(this); + awtKeyAdapter = new AWTKeyAdapter().addTo(this); + awtWinAdapter = (AWTParentWindowAdapter) new AWTParentWindowAdapter().addTo(this); + awtWinAdapter.removeWindowClosingFrom(this); // we utilize AWTWindowClosingProtocol triggered destruction! setNEWTChild(child); } - - public void setShallUseOffscreenLayer(boolean v) { + + @Override + public void setShallUseOffscreenLayer(final 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 windowGainedFocus(WindowEvent arg0) { + public void windowResized(final WindowEvent e) { + updateLayoutSize(); + } + @Override + public void windowGainedFocus(final WindowEvent arg0) { if( isParent() && !isFullscreen() ) { - MenuSelectionManager.defaultManager().clearSelectedPath(); + AWTEDTExecutor.singleton.invoke(false, awtClearSelectedMenuPath); } } }; class FocusTraversalKeyListener implements KeyListener { - boolean suppress = false; - - public void keyPressed(KeyEvent e) { + @Override + public void keyPressed(final KeyEvent e) { if( isParent() && !isFullscreen() ) { handleKey(e, false); } } - public void keyReleased(KeyEvent e) { + @Override + public void keyReleased(final 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(final KeyEvent evt, final 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 { - public void propertyChange(PropertyChangeEvent evt) { + @Override + public void propertyChange(final 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) { @@ -267,40 +349,64 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } } } - } + } } private final FocusPropertyChangeListener focusPropertyChangeListener = new FocusPropertyChangeListener(); private volatile KeyboardFocusManager keyboardFocusManager = null; - /** + 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. + * 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. + * 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. + * @return the previous attached newt child. */ - public Window setNEWTChild(Window newChild) { - 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) { - reparentWindow( false, cont ); - newtChild = null; + public Window setNEWTChild(final 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; } - // add new one, reparent only if ready - newtChild = newChild; - if( isDisplayable() && null != newChild) { - reparentWindow( true, cont ); + } + + private final void updateLayoutSize() { + final Window w = newtChild; + if( null != w ) { + // use NEWT child's size for min/pref size! + final java.awt.Dimension minSize = new java.awt.Dimension(w.getWidth(), w.getHeight()); + setMinimumSize(minSize); + setPreferredSize(minSize); } - return prevChild; } /** @return the current NEWT child */ @@ -311,158 +417,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(); } - public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { + @Override + public WindowClosingMode setDefaultCloseOperation(final 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; - } - if(null != keyboardFocusManager) { - keyboardFocusManager.removePropertyChangeListener("focusOwner", focusPropertyChangeListener); - keyboardFocusManager = null; - } - - if( null != newtChild ) { - newtChild.setKeyboardFocusHandler(null); - 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(final 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); + 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)); + } + } } - reparentWindow(true, cont); } @Override public void removeNotify() { - java.awt.Container cont = AWTMisc.getContainer(this); - if(DEBUG) { - System.err.println("NewtCanvasAWT.removeNotify: "+newtChild+", from "+cont); - } - final OffscreenLayerSurface ols = NativeWindowFactory.getOffscreenLayerSurface(newtChild, true); - if(null != ols && ols.isSurfaceLayerAttached()) { - ols.detachSurfaceLayer(); - } - reparentWindow(false, cont); - - if(null != jawtWindow) { - NewtFactoryAWT.destroyNativeWindow(jawtWindow); - jawtWindow=null; + 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()); + } } - - super.removeNotify(); - } - - void reparentWindow(boolean add, java.awt.Container cont) { - if(null==newtChild) { - return; // nop - } - if(DEBUG) { - System.err.println("NewtCanvasAWT.reparentWindow.0: add="+add+", win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()); - } - - newtChild.setFocusAction(null); // no AWT focus traversal .. - if(add) { - if(DEBUG) { - System.err.println("NewtCanvasAWT.reparentWindow: newtChild: "+newtChild); - } - if(null == jawtWindow) { - jawtWindow = NewtFactoryAWT.getNativeWindow(this, newtChild.getRequestedCapabilities()); - jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); - } - 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 - - // 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(DEBUG) { - System.err.println("NewtCanvasAWT.reparentWindow.X: add="+add+", win "+newtWinHandleToHexString(newtChild)+", EDTUtil: cur "+newtChild.getScreen().getDisplay().getEDTUtil()); - } } /** @@ -472,88 +512,438 @@ 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(final boolean removeNotify, final 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()); + public void paint(final Graphics g) { + 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()); - } + public void update(final Graphics g) { + paint(g); } - private final void requestFocusNEWTChild() { - if(null!=newtChild) { - newtChild.setFocusAction(null); - if(isOnscreen) { - KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); + @SuppressWarnings("deprecation") + @Override + public void reshape(final int x, final int y, final int width, final 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(final double scaleMatX, final double scaleMatY, final int numSamples, final int tileWidth, final 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( !isVisible() ) { + if(DEBUG) { + System.err.println(currentThreadName()+": Info: NewtCanvasAWT setupPrint - skipped GL render, canvas not visible"); + } + 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 GLCapabilitiesImmutable gladCaps = glad.getChosenGLCapabilities(); + final int printNumSamples = printAWTTiles.getNumSamples(gladCaps); + GLDrawable printDrawable = printGLAD.getDelegatedDrawable(); + final boolean reqNewGLADSamples = printNumSamples != gladCaps.getNumSamples(); + final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() || + printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight(); + final boolean reqNewGLADOnscrn = gladCaps.isOnscreen(); + + final GLCapabilities newGLADCaps = (GLCapabilities)gladCaps.cloneMutable(); + newGLADCaps.setDoubleBuffered(false); + newGLADCaps.setOnscreen(false); + if( printNumSamples != newGLADCaps.getNumSamples() ) { + newGLADCaps.setSampleBuffers(0 < printNumSamples); + newGLADCaps.setNumSamples(printNumSamples); + } + final boolean reqNewGLADSafe = GLDrawableUtil.isSwapGLContextSafe(glad.getRequestedGLCapabilities(), gladCaps, newGLADCaps); + + final boolean reqNewGLAD = ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize ) && reqNewGLADSafe; + + if( DEBUG ) { + System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+", safe "+reqNewGLADSafe+"], "+ + ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+ + ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+ + ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+ + ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator); + } + if( reqNewGLAD ) { + final GLDrawableFactory factory = GLDrawableFactory.getFactory(newGLADCaps.getGLProfile()); + GLOffscreenAutoDrawable offGLAD = null; + try { + offGLAD = factory.createOffscreenAutoDrawable(null, newGLADCaps, null, + printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE, + printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE); + } catch (final GLException gle) { + if( DEBUG ) { + System.err.println("Caught: "+gle.getMessage()); + gle.printStackTrace(); + } + } + if( null != offGLAD ) { + printGLAD = offGLAD; + GLDrawableUtil.swapGLContextAndAllGLEventListener(glad, printGLAD); + printDrawable = printGLAD.getDelegatedDrawable(); + } + } + printAWTTiles.setGLOrientation(printGLAD.isGLOriented(), printGLAD.isGLOriented()); + printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0); + printAWTTiles.renderer.attachAutoDrawable(printGLAD); + if( DEBUG ) { + System.err.println("AWT print.setup "+printAWTTiles); + System.err.println("AWT print.setup AA "+printNumSamples+", "+newGLADCaps); + System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD); + System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+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(final 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 (final NoninvertibleTransformException nte) { + System.err.println("Caught: 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(final 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 res; + + return true; + } + + private final void configureNewtChild(final boolean attach) { + awtWinAdapter.clear(); + awtKeyAdapter.clear(); + awtMouseAdapter.clear(); + + if(null != keyboardFocusManager) { + keyboardFocusManager.removePropertyChangeListener("focusOwner", focusPropertyChangeListener); + keyboardFocusManager = null; + } + + if( null != newtChild ) { + newtChild.setKeyboardFocusHandler(null); + if(attach) { + if(null == jawtWindow.getGraphicsConfiguration()) { + throw new InternalError("XXX"); + } + isOnscreen = jawtWindow.getGraphicsConfiguration().getChosenCapabilities().isOnscreen(); + awtWinAdapter.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(); + final boolean isNValid = newtChild.isNativeValid(); + if(DEBUG) { + System.err.println("NewtCanvasAWT.attachNewtChild.2: size "+w+"x"+h+", isNValid "+isNValid); + } + newtChild.setVisible(false); + newtChild.setSize(w, h); + final int[] reqSurfaceScale = new int[2]; + if( isNValid ) { + newtChild.getCurrentSurfaceScale(reqSurfaceScale); + } else { + newtChild.getRequestedSurfaceScale(reqSurfaceScale); + } + jawtWindow.setSurfaceScale(reqSurfaceScale); + 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(final 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 @@ -567,6 +957,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(); @@ -576,16 +967,16 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto clazz.getDeclaredMethod("disableBackgroundErase", new Class[] { Canvas.class }); disableBackgroundEraseMethod.setAccessible(true); - } catch (Exception e) { + } catch (final Exception e) { clazz = clazz.getSuperclass(); } } - } catch (Exception e) { + } catch (final Exception e) { } return null; } }); - } catch (Exception e) { + } catch (final Exception e) { } disableBackgroundEraseInitialized = true; if(DEBUG) { @@ -597,7 +988,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto Throwable t=null; try { disableBackgroundEraseMethod.invoke(getToolkit(), new Object[] { this }); - } catch (Exception e) { + } catch (final Exception e) { t = e; } if(DEBUG) { @@ -605,11 +996,13 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } } } - - static String newtWinHandleToHexString(Window w) { + + protected static String currentThreadName() { return "["+Thread.currentThread().getName()+", isAWT-EDT "+EventQueue.isDispatchThread()+"]"; } + + static String newtWinHandleToHexString(final Window w) { return null != w ? toHexString(w.getWindowHandle()) : "nil"; } - static String toHexString(long l) { + static String toHexString(final 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..72b3e43f3 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,9 +32,11 @@ 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; +import java.util.Arrays; import javax.media.nativewindow.WindowClosingProtocol.WindowClosingMode; import javax.media.opengl.FPSCounter; @@ -42,15 +44,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 +69,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 +99,19 @@ 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"); - } - if(!(this instanceof Container)) { - throw new RuntimeException("This Applet is not a AWT Container"); + System.err.println("JOGLNewtApplet1Run.init() START - "+currentThreadName()); } - Container container = (Container) this; + final Container container = this; String glEventListenerClazzName=null; String glProfileName=null; @@ -134,20 +137,20 @@ 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); glHeight = JOGLNewtAppletBase.str2Int(getParameter("gl_height"), glHeight); glNoDefaultKeyListener = JOGLNewtAppletBase.str2Bool(getParameter("gl_nodefaultkeyListener"), glNoDefaultKeyListener); appletDebugTestBorder = JOGLNewtAppletBase.str2Bool(getParameter("appletDebugTestBorder"), appletDebugTestBorder); - } catch (Exception e) { + } catch (final Exception e) { e.printStackTrace(); } 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 +170,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, @@ -176,7 +179,7 @@ public class JOGLNewtApplet1Run extends Applet { glTrace); try { - GLCapabilities caps = new GLCapabilities(GLProfile.get(glProfileName)); + final GLCapabilities caps = new GLCapabilities(GLProfile.get(glProfileName)); caps.setAlphaBits(glAlphaBits); if(0<glNumMultisampleBuffer) { caps.setSampleBuffers(true); @@ -190,14 +193,17 @@ 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()) { - GLEventListener glEventListener = base.getGLEventListener(); + final GLEventListener glEventListener = base.getGLEventListener(); if(glEventListener instanceof MouseListener) { addMouseListener((MouseListener)glEventListener); @@ -209,32 +215,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) { + } catch (final 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; @@ -243,7 +262,7 @@ public class JOGLNewtApplet1Run extends Applet { } System.err.println("JOGLNewtApplet1Run start:"); System.err.println("TopComponent: "+topC.getLocation()+" rel, "+topC.getLocationOnScreen()+" screen, visible "+topC.isVisible()+", "+topC); - System.err.println("Applet Pos: "+this.getLocation()+" rel, "+p0+" screen, visible "+this.isVisible()+", "+this); + System.err.println("Applet Pos: "+this.getLocation()+" rel, "+Arrays.toString(p0)+" screen, visible "+this.isVisible()+", "+this); if(null != newtCanvasAWT) { System.err.println("NewtCanvasAWT Pos: "+newtCanvasAWT.getLocation()+" rel, "+newtCanvasAWT.getLocationOnScreen()+" screen, visible "+newtCanvasAWT.isVisible()+", "+newtCanvasAWT); } @@ -251,34 +270,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..7fec29259 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/DoubleTapScrollGesture.java @@ -0,0 +1,349 @@ +/** + * 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.common.util.PropertyAccess; + +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 = PropertyAccess.getIntProperty("newt.event.scroll_slop_pixel", true, 16); + DOUBLE_TAP_SLOP_PIXEL = PropertyAccess.getIntProperty("newt.event.double_tap_slop_pixel", true, 104); + SCROLL_SLOP_MM = PropertyAccess.getIntProperty("newt.event.scroll_slop_mm", true, 3); + DOUBLE_TAP_SLOP_MM = PropertyAccess.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 final 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(final float x1, final float y1, final float x2, final 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(final int scaledScrollSlop, final 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(final 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..ea0f76fbb --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/GestureHandler.java @@ -0,0 +1,155 @@ +/** + * 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; + private final InputEvent ie; + + /** + * Creates a gesture event with default type {@link #EVENT_GESTURE_DETECTED}. + * + * @param source + * @param when + * @param modifiers + * @param handler + * @param trigger TODO + */ + public GestureEvent(final Object source, final long when, final int modifiers, final GestureHandler handler, final InputEvent trigger) { + super(EVENT_GESTURE_DETECTED, source, when, modifiers); + this.handler = handler; + this.ie = trigger; + } + + /** + * 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 + * @param trigger TODO + */ + public GestureEvent(final short event_type, final Object source, final long when, final int modifiers, final GestureHandler handler, final InputEvent trigger) { + super(event_type, source, when, modifiers); + this.handler = handler; + this.ie = trigger; + } + + /** Return the {@link GestureHandler}, which produced the event. */ + public final GestureHandler getHandler() { return handler; } + + /** Triggering {@link InputEvent} */ + public final InputEvent getTrigger() { return ie; } + + public String toString() { + return "GestureEvent[handler "+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 148ac5962..fbda79d46 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,100 +29,111 @@ * 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 { + /** 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 << 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 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 << 15; - + public static final int AUTOREPEAT_MASK = 1 << 29; + /** Pointer is confined, see {@link Window#confinePointer(boolean)}. */ - public static final int CONFINED_MASK = 1 << 16; - + public static final int CONFINED_MASK = 1 << 30; + /** Pointer is invisible, see {@link Window#setPointerVisible(boolean)}. */ - public static final int INVISIBLE_MASK = 1 << 17; + 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 ) ; + public static final int getButtonMask(final int 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(final short eventType, final Object source, final long when, final 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() { + public final int getModifiers() { return modifiers; } /** {@link #getModifiers()} contains {@link #ALT_MASK}. */ - public boolean isAltDown() { + public final boolean isAltDown() { return (modifiers&ALT_MASK)!=0; } /** {@link #getModifiers()} contains {@link #ALT_GRAPH_MASK}. */ - public boolean isAltGraphDown() { + public final boolean isAltGraphDown() { return (modifiers&ALT_GRAPH_MASK)!=0; } /** {@link #getModifiers()} contains {@link #CTRL_MASK}. */ - public boolean isControlDown() { + public final boolean isControlDown() { return (modifiers&CTRL_MASK)!=0; } /** {@link #getModifiers()} contains {@link #META_MASK}. */ - public boolean isMetaDown() { + public final boolean isMetaDown() { return (modifiers&META_MASK)!=0; } /** {@link #getModifiers()} contains {@link #SHIFT_MASK}. */ - public boolean isShiftDown() { + public final boolean isShiftDown() { return (modifiers&SHIFT_MASK)!=0; } /** {@link #getModifiers()} contains {@link #AUTOREPEAT_MASK}. */ - public boolean isAutoRepeat() { + public final boolean isAutoRepeat() { return (modifiers&AUTOREPEAT_MASK)!=0; } /** {@link #getModifiers()} contains {@link #CONFINED_MASK}. Pointer is confined, see {@link Window#confinePointer(boolean)}. */ - public boolean isConfined() { + public final boolean isConfined() { return (modifiers&CONFINED_MASK)!=0; } - /** {@link #getModifiers()} contains {@link #INVISIBLE_MASK}. Pointer is invisible, see {@link Window#setPointerVisible(boolean)}. */ - public boolean isInvisible() { + /** {@link #getModifiers()} contains {@link #INVISIBLE_MASK}. Pointer is invisible, see {@link Window#setPointerVisible(boolean)}. */ + public final boolean isInvisible() { return (modifiers&INVISIBLE_MASK)!=0; } - - public StringBuilder getModifiersString(StringBuilder sb) { + + public final StringBuilder getModifiersString(StringBuilder sb) { if(null == sb) { sb = new StringBuilder(); } @@ -130,46 +141,81 @@ public abstract class InputEvent extends NEWTEvent 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(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("altg"); } + 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_NUMBER; i++) { + 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; } - public final boolean isButtonDown(int button) { + /** + * 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(final 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 toString(null).toString(); } - + + @Override public StringBuilder toString(StringBuilder sb) { if(null == sb) { sb = new StringBuilder(); diff --git a/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java b/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java index 93c8409b1..0f147a722 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 { - public void keyPressed(KeyEvent e) { - } - public void keyReleased(KeyEvent e) { + @Override + public void keyPressed(final KeyEvent e) { } - public void keyTyped(KeyEvent e) { + @Override + public void keyReleased(final 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 7daaeada6..f29e9eb8c 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,734 +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: - * <ol> - * <li>{@link #EVENT_KEY_PRESSED}</li> - * <li>{@link #EVENT_KEY_RELEASED}</li> - * <li>{@link #EVENT_KEY_TYPED}</li> - * </ol> + * <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. + * 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> - * Besides regular modifiers like {@link InputEvent#SHIFT_MASK} etc., - * the {@link InputEvent#AUTOREPEAT_MASK} bit is added if repetition is detected. + * 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 on all platforms at {@link KeyListener#keyTyped(KeyEvent)} */ - public char getKeyChar() { - return keyChar; - } - - /** Always valid. */ - public int getKeyCode() { - return keyCode; - } - - public String toString() { - return toString(null).toString(); - } - - public StringBuilder toString(StringBuilder sb) { - if(null == sb) { - sb = new StringBuilder(); - } - sb.append("KeyEvent[").append(getEventTypeString(getEventType())).append(", code ").append(keyCode).append("(").append(toHexString(keyCode)).append("), char '").append(keyChar).append("' (").append(toHexString((int)keyChar)).append("), isActionKey ").append(isActionKey()).append(", "); - return super.toString(sb).append("]"); - } - - 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 + ")"; - } - } - - 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; + private KeyEvent(final short eventType, final Object source, final long when, final int modifiers, final short keyCode, final short keySym, final int keySymModMask, final 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); + } + } } - 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(final short eventType, final Object source, final long when, final int modifiers, final short keyCode, final short keySym, final 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(final 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(final 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(final 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(final 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(final short min, final short max, final 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, "]" + * This value, {@value}, is used to indicate that the keyCode is unknown. */ - 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 - */ - 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; - - /** - * Constant for the input method on/off key. - * @since 1.3 - */ - /* 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; + public static final short VK_KANA_LOCK = (short) 0xF89F; /** - * Constant for the Begin key. - * @since 1.5 + * Constant for Keyboard became invisible, e.g. Android's soft keyboard Back button hit while keyboard is visible. */ - 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..2e42f3392 --- /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 (final short eventType, final MonitorDevice source, final long when, final 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(final 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..679155d10 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,31 +20,39 @@ * 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 { - public void mouseClicked(MouseEvent e) { + @Override + public void mouseClicked(final MouseEvent e) { } - public void mouseEntered(MouseEvent e) { + @Override + public void mouseEntered(final MouseEvent e) { } - public void mouseExited(MouseEvent e) { + @Override + public void mouseExited(final MouseEvent e) { } - public void mousePressed(MouseEvent e) { + @Override + public void mousePressed(final MouseEvent e) { } - public void mouseReleased(MouseEvent e) { + @Override + public void mouseReleased(final MouseEvent e) { } - public void mouseMoved(MouseEvent e) { + @Override + public void mouseMoved(final MouseEvent e) { } - public void mouseDragged(MouseEvent e) { + @Override + public void mouseDragged(final MouseEvent e) { } - public void mouseWheelMoved(MouseEvent e) { + @Override + public void mouseWheelMoved(final 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 d293d2db7..2d68c5788 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,192 +29,568 @@ * 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 java.util.Arrays; + +/** + * 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="coordUnit"><h5>Unit of Coordinates</h5></a> + * <p> + * All pointer coordinates of this interface are represented in pixel units, + * see {@link NativeSurface} and {@link NativeWindow}. + * </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> + * Multiple-pointer event's {@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. + * </p> + * <p> + * Users can query the pressed button and pointer count via {@link InputEvent#getButtonDownCount()} + * or use the simple query {@link InputEvent#isAnyButtonDown()}. + * </p> + * <p> + * If representing a single-pointer {@link PointerType#Mouse} event, {@link #getPointerId(int) pointer-ID} is <code>0</code> + * and a {@link #getButton() button value} of <code>0</code> denotes no button activity, i.e. {@link PointerType#Mouse} move. + * </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(final 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(final 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(final 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; - public static final int getClickTimeout() { - return 300; + /** Maximum number of buttons, value <code>16</code> */ + public static final short BUTTON_COUNT = 16; + + /** 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 MouseEvent(int eventType, Object source, long when, - int modifiers, int x, int y, int clickCount, int button, - int rotation) + public static final short getClickTimeout() { + return 300; + } + + /** + * 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(final short eventType, final Object source, final long when, + final int modifiers, final int x, final int y, final short clickCount, final short button, + final float[] rotationXYZ, final 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)0 }; 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(final short eventType, final Object source, final long when, final int modifiers, + final PointerType pointerType[], final short[] pointerID, + final int[] x, final int[] y, final float[] pressure, final float maxPressure, + final short button, final short clickCount, final float[] rotationXYZ, final 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 final MouseEvent createVariant(final 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(final int index) { + if(0 > index || index >= pointerType.length) { + return null; + } + return pointerType[index]; } - + /** - * @return the pointer id for the data at index. - * return -1 if index not available. + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @return array of all {@link PointerType}s for all pointers */ - public int getPointerId(int index) { - if(index >= pointerids.length) + public final PointerType[] getAllPointerTypes() { + return pointerType; + } + + /** + * 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> + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * </p> + */ + public final short getPointerId(final 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(final 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() { + + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @return X-Coord of the triggering pointer-index zero in pixel units. + */ + public final int getX() { return x[0]; } - - public int getY() { + + /** + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @return Y-Coord of the triggering pointer-index zero in pixel units. + */ + 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 in pixel units. * @see getPointerId(index) */ - public int getX(int index) { + public final int getX(final 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 in pixel units. + * @see getPointerId(index) + */ + public final int getY(final 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 in pixel units. + */ + public final int[] getAllX() { + return x; } - + /** - * @return the pressure associated with the pointer at index. - * the value of zero is return if not available. + * See details for <a href="#multiPtrEvent">multiple-pointer events</a>. + * @return array of all Y-Coords for all pointers in pixel units. + */ + public final int[] getAllY() { + return y; + } + + /** + * @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 final float getPressure(final boolean normalized){ + return normalized ? pressure[0] / maxPressure : pressure[0]; + } + + /** + * 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(final int index, final 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 float getPressure(int index){ - return pressure[index]; + public final float getMaxPressure() { + return maxPressure; } - + /** - * <i>Usually</i> a wheel rotation of <b>> 0 is up</b>, - * and <b>< 0 is down</b>.<br> + * 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> + * 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> - * The events will be send usually in steps of one, ie. <i>-1</i> and <i>1</i>. + * 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> - * The button number refers to the wheel number. - * </p> - * @return + * 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 int getWheelRotation() { - return wheelRotation; + public final float[] getRotation() { + return rotationXYZ; } - public String toString() { + /** + * 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> + * 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 final float getRotationScale() { + return rotationScale; + } + + @Override + public final String toString() { return toString(null).toString(); } - public StringBuilder toString(StringBuilder sb) { + @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(", ").append(Arrays.toString(x)).append("/").append(Arrays.toString(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(", "); return super.toString(sb).append("]"); } - public static String getEventTypeString(int type) { + public static String getEventTypeString(final 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. */ + 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..5e62306f3 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); - public void mouseEntered(MouseEvent e); - 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() } */ - public void mouseWheelMoved(MouseEvent e); + 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); + + /** + * 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 fd5b69ccc..a9d9d8bdb 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(final short eventType, final Object source, final 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,25 +78,60 @@ 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; + public final void setAttachment(final Object attachment) { + this.attachment = attachment; } - /** + /** * @return The user application specific attachment, or null */ public final Object getAttachment() { return attachment; } + /** + * 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; + } + + /** + * 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(final boolean consumed) { + if( consumed ) { + setAttachment( consumedTag ); + } else if( consumedTag == attachment ) { + setAttachment( null ); + } + } + + @Override public String toString() { return toString(null).toString(); } @@ -158,14 +140,10 @@ public class NEWTEvent extends java.util.EventObject { if(null == sb) { sb = new StringBuilder(); } - return sb.append("NEWTEvent[sys:").append(isSystemEvent()).append(", source:").append(getSource().getClass().getName()).append(", when:").append(getWhen()).append(" d ").append((System.currentTimeMillis()-getWhen())).append("ms]"); - } - - public static String toHexString(int hex) { - return "0x" + Integer.toHexString(hex); + 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(long hex) { - return "0x" + Long.toHexString(hex); + public static String toHexString(final short hex) { + return "0x" + Integer.toHexString( 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..dd6395354 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,22 +20,22 @@ * 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; public class NEWTEventFiFo { - private LinkedList/*<NEWTEvent>*/ events = new LinkedList/*<NEWTEvent>*/(); + private final LinkedList/*<NEWTEvent>*/ events = new LinkedList/*<NEWTEvent>*/(); /** Add NEWTEvent to tail */ - public synchronized void put(NEWTEvent event) { + public synchronized void put(final NEWTEvent event) { events.addLast(event); notifyAll(); } 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..bd3a11a13 --- /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(final short eventType, final Object source, final 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..9c7a4bcb8 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/PinchToZoomGesture.java @@ -0,0 +1,252 @@ +/** + * 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 com.jogamp.newt.event.MouseEvent.PointerClass; + +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 float zoom; + private final float delta; + private final float scale; + public ZoomEvent(final Object source, final long when, final int modifiers, final GestureHandler handler, final MouseEvent pe, + final float zoom, final float delta, final float scale) { + super(source, when, modifiers, handler, pe); + this.zoom = zoom; + this.delta = delta; + this.scale = scale; + } + /** Zoom value lies within [0..2], with 1 as <i>1:1</i>. */ + public final float getZoom() { return zoom; } + /** Delta to last zoom value lies within [-1..1]. */ + public final float getDelta() { return delta; } + /** + * Returns the scale used to determine the {@link #getZoom() zoom} + * and hence it's {@link #getDelta() delta} 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> + * 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 * delta</code> reproduces the screen distance in pixels the finger[s] have moved. + * </p> + */ + public final float getScale() { return scale; } + + public final String toString() { + return "ZoomEvent[zoom "+zoom+", delta "+delta+", scale "+scale+", trigger "+getTrigger()+", handler "+getHandler()+"]"; + } + } + + 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(final NativeSurface surface, final 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(final 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(final 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.getSurfaceWidth() >= surface.getSurfaceHeight(); // 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.getSurfaceHeight() : surface.getSurfaceWidth(); + final float delta = 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 "+delta+", zoom "+zoom+" -> "+(zoom+delta)); + } + zoom += delta; + // 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, delta, screenEdge); + } + } + } + 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..50eae9626 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 { @@ -36,21 +36,19 @@ public class TraceKeyAdapter implements KeyListener { this.downstream = null; } - public TraceKeyAdapter(KeyListener downstream) { + public TraceKeyAdapter(final KeyListener downstream) { this.downstream = downstream; } - public void keyPressed(KeyEvent e) { + @Override + public void keyPressed(final KeyEvent e) { System.err.println(e); if(null!=downstream) { downstream.keyPressed(e); } } - public void keyReleased(KeyEvent e) { + @Override + public void keyReleased(final 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..e54c3730f 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 { @@ -36,39 +36,47 @@ public class TraceMouseAdapter implements MouseListener { this.downstream = null; } - public TraceMouseAdapter(MouseListener downstream) { + public TraceMouseAdapter(final MouseListener downstream) { this.downstream = downstream; } - public void mouseClicked(MouseEvent e) { + @Override + public void mouseClicked(final MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseClicked(e); } } - public void mouseEntered(MouseEvent e) { + @Override + public void mouseEntered(final MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseEntered(e); } } - public void mouseExited(MouseEvent e) { + @Override + public void mouseExited(final MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseExited(e); } } - public void mousePressed(MouseEvent e) { + @Override + public void mousePressed(final MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mousePressed(e); } } - public void mouseReleased(MouseEvent e) { + @Override + public void mouseReleased(final MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseReleased(e); } } - public void mouseMoved(MouseEvent e) { + @Override + public void mouseMoved(final MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseMoved(e); } } - public void mouseDragged(MouseEvent e) { + @Override + public void mouseDragged(final MouseEvent e) { System.err.println(e); if(null!=downstream) { downstream.mouseDragged(e); } } - public void mouseWheelMoved(MouseEvent e) { + @Override + public void mouseWheelMoved(final 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..299a6ff90 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 { @@ -36,35 +36,42 @@ public class TraceWindowAdapter implements WindowListener { this.downstream = null; } - public TraceWindowAdapter(WindowListener downstream) { + public TraceWindowAdapter(final WindowListener downstream) { this.downstream = downstream; } - public void windowResized(WindowEvent e) { + @Override + public void windowResized(final WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowResized(e); } } - public void windowMoved(WindowEvent e) { + @Override + public void windowMoved(final WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowMoved(e); } } - public void windowDestroyNotify(WindowEvent e) { + @Override + public void windowDestroyNotify(final WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowDestroyNotify(e); } } - public void windowDestroyed(WindowEvent e) { + @Override + public void windowDestroyed(final WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowDestroyed(e); } } - public void windowGainedFocus(WindowEvent e) { + @Override + public void windowGainedFocus(final WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowGainedFocus(e); } } - public void windowLostFocus(WindowEvent e) { + @Override + public void windowLostFocus(final WindowEvent e) { System.err.println(e); if(null!=downstream) { downstream.windowLostFocus(e); } } - public void windowRepaint(WindowUpdateEvent e) { + @Override + public void windowRepaint(final 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..c1bae40ea 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 { - public void windowResized(WindowEvent e) { + @Override + public void windowResized(final WindowEvent e) { } - public void windowMoved(WindowEvent e) { + @Override + public void windowMoved(final WindowEvent e) { } - public void windowDestroyNotify(WindowEvent e) { + @Override + public void windowDestroyNotify(final WindowEvent e) { } - public void windowDestroyed(WindowEvent e) { + @Override + public void windowDestroyed(final WindowEvent e) { } - public void windowGainedFocus(WindowEvent e) { + @Override + public void windowGainedFocus(final WindowEvent e) { } - public void windowLostFocus(WindowEvent e) { + @Override + public void windowLostFocus(final WindowEvent e) { } - public void windowRepaint(WindowUpdateEvent e) { + @Override + public void windowRepaint(final 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 163b51439..021bd6cae 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; @@ -41,19 +41,19 @@ package com.jogamp.newt.event; */ @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(final short eventType, final Object source, final long when) { super(eventType, source, when); } - public static String getEventTypeString(int type) { + public static String getEventTypeString(final short type) { switch(type) { case EVENT_WINDOW_RESIZED: return "WINDOW_RESIZED"; case EVENT_WINDOW_MOVED: return "WINDOW_MOVED"; @@ -65,11 +65,13 @@ public class WindowEvent extends NEWTEvent { default: return "unknown (" + type + ")"; } } - + + @Override public String toString() { return toString(null).toString(); } + @Override public StringBuilder toString(StringBuilder sb) { if(null == sb) { sb = new StringBuilder(); 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 e3f0373ec..4a5fecd83 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,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 javax.media.nativewindow.util.Rectangle; @@ -34,7 +34,7 @@ import javax.media.nativewindow.util.Rectangle; public class WindowUpdateEvent extends WindowEvent { final Rectangle bounds; - public WindowUpdateEvent(int eventType, Object source, long when, Rectangle bounds) + public WindowUpdateEvent(final short eventType, final Object source, final long when, final Rectangle bounds) { super(eventType, source, when); this.bounds = bounds; @@ -44,10 +44,12 @@ public class WindowUpdateEvent extends WindowEvent { return bounds; } + @Override public String toString() { return toString(null).toString(); } + @Override public StringBuilder toString(StringBuilder sb) { if(null == sb) { sb = new StringBuilder(); 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..a49c10a1e 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,14 +20,16 @@ * 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 javax.media.nativewindow.NativeSurfaceHolder; + import jogamp.newt.Debug; /** @@ -48,10 +50,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 +98,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 +110,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,60 +119,138 @@ public abstract class AWTAdapter implements java.util.EventListener com.jogamp.newt.event.NEWTEventListener newtListener; com.jogamp.newt.Window newtWindow; + NativeSurfaceHolder nsHolder; + boolean consumeAWTEvent; + protected boolean isSetup; - /** - * Simply wrap aroung a NEWT EventListener, exposed as an AWT EventListener.<br> + /** + * Create a proxy adapter, wrapping around an NEWT EventListener, exposed as an AWT EventListener,<br> + * where the given {@link NativeSurfaceHolder} impersonates the event's source. * The NEWT EventListener will be called when an event happens.<br> */ - public AWTAdapter(com.jogamp.newt.event.NEWTEventListener newtListener) { + protected AWTAdapter(final com.jogamp.newt.event.NEWTEventListener newtListener, final NativeSurfaceHolder nsProxy) { if(null==newtListener) { - throw new RuntimeException("Argument newtListener is null"); + throw new IllegalArgumentException("Argument newtListener is null"); + } + if(null==nsProxy) { + throw new IllegalArgumentException("Argument nwProxy is null"); } this.newtListener = newtListener; this.newtWindow = null; + this.nsHolder = nsProxy; + 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. + /** + * Create a proxy adapter, wrapping around an NEWT EventListener, exposed as an AWT EventListener,<br> + * where the given {@link com.jogamp.newt.Window NEWT Window}, a {@link NativeSurfaceHolder}, impersonates 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(final com.jogamp.newt.event.NEWTEventListener newtListener, final com.jogamp.newt.Window newtProxy) { if(null==newtListener) { - throw new RuntimeException("Argument newtListener is null"); + throw new IllegalArgumentException("Argument newtListener is null"); } if(null==newtProxy) { - throw new RuntimeException("Argument newtProxy is null"); + throw new IllegalArgumentException("Argument newtProxy is null"); } this.newtListener = newtListener; this.newtWindow = newtProxy; + this.nsHolder = 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! + * @throws IllegalStateException if EDT is not enabled */ - public AWTAdapter(com.jogamp.newt.Window downstream) { + protected AWTAdapter(final com.jogamp.newt.Window downstream) throws IllegalStateException { + 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! + * @throws IllegalStateException if EDT is not enabled + */ + public synchronized AWTAdapter setDownstream(final com.jogamp.newt.Window downstream) throws IllegalStateException { if(null==downstream) { throw new RuntimeException("Argument downstream is null"); } this.newtListener = null; this.newtWindow = downstream; + this.nsHolder = downstream; if( null == newtWindow.getScreen().getDisplay().getEDTUtil() ) { - throw new RuntimeException("EDT not enabled"); + throw new IllegalStateException("EDT not enabled"); } + this.isSetup = true; + return this; + } + + /** + * Removes all references, downstream and NEWT-EventListener. + * <p> + * Also sets the internal <code>setup</code> flag and {@link #setConsumeAWTEvent(boolean)} to <code>false</code>. + * </p> + */ + public synchronized AWTAdapter clear() { + this.newtListener = null; + this.newtWindow = null; + this.nsHolder = null; + this.isSetup = false; + this.consumeAWTEvent = false; + return this; + } + + public final synchronized void setConsumeAWTEvent(final boolean v) { + this.consumeAWTEvent = v; } - public final com.jogamp.newt.Window getNewtWindow() { - return newtWindow; + /** + * Returns the {@link NativeSurfaceHolder} acting {@link #AWTAdapter(com.jogamp.newt.Window) as downstream}, + * {@link #AWTAdapter(com.jogamp.newt.event.NEWTEventListener, com.jogamp.newt.Window) NEWT window proxy} + * or as an {@link #AWTAdapter(com.jogamp.newt.event.NEWTEventListener, NativeSurfaceHolder) NativeSurfaceHolder proxy}. + * <p> + * Returned value is never null. + * </p> + */ + public final synchronized NativeSurfaceHolder getNativeSurfaceHolder() { + return nsHolder; + } + + /** + * Returns the {@link com.jogamp.newt.Window NEWT Window} acting {@link #AWTAdapter(com.jogamp.newt.Window) as downstream} + * or as a {@link #AWTAdapter(com.jogamp.newt.event.NEWTEventListener, com.jogamp.newt.Window) NEWT window proxy}. + * <p> + * Returned value maybe null if instance is used to be a + * {@link #AWTAdapter(com.jogamp.newt.event.NEWTEventListener, NativeSurfaceHolder) NativeSurfaceHolder proxy}. + * </p> + */ + public final synchronized com.jogamp.newt.Window getNewtWindow() { + return newtWindow; } - - public final com.jogamp.newt.event.NEWTEventListener getNewtEventListener() { - return newtListener; + + /** + * Returns the {@link com.jogamp.newt.event.NEWTEventListener NEWT event-listener} if instance + * is used as an {@link #AWTAdapter(com.jogamp.newt.event.NEWTEventListener, NativeSurfaceHolder) NativeSurfaceHolder proxy} + * or {@link #AWTAdapter(com.jogamp.newt.event.NEWTEventListener, com.jogamp.newt.Window) NEWT window proxy}, + * otherwise method returns <code>null</code>. + */ + 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 +261,46 @@ public abstract class AWTAdapter implements java.util.EventListener /** @see #addTo(java.awt.Component) */ public abstract AWTAdapter removeFrom(java.awt.Component awtComponent); - void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { - newtWindow.enqueueEvent(wait, event); + /** + * Return value for {@link AWTAdapter#processEvent(boolean, com.jogamp.newt.event.NEWTEvent) event processing}. + */ + static enum EventProcRes { + /** Event shall be dispatched appropriately */ + DISPATCH, + /** Event has been enqueued */ + ENQUEUED, + /** No known processing method applies */ + NOP + } + + /** + * Process the event. + * <p> + * If {@link #getNewtEventListener()} is not <code>null</code>, + * {@link EventProcRes#DISPATCH DISPATCH} is returned and caller shall dispatch the event appropriately. + * </p> + * <p> + * If {@link #getNewtWindow()} is not <code>null</code>, + * {@link EventProcRes#ENQUEUED ENQUEUED} is returned and the event has been {@link com.jogamp.newt.Window#enqueueEvent(boolean, com.jogamp.newt.event.NEWTEvent) enqueued already}. + * </p> + * <p> + * If none of the above matches, {@link EventProcRes#NOP NOP} is returned and none of the above processing method applies. + * </p> + * + * @param wait In case the event will be {@link EventProcRes#ENQUEUED ENQUEUED}, + * passing <code>true</code> will block until the event has been processed, otherwise method returns immediately. + * @param event The {@link com.jogamp.newt.event.NEWTEvent event} to enqueue. + * @return One of the {@link EventProcRes} values, see above. + */ + EventProcRes processEvent(final boolean wait, final com.jogamp.newt.event.NEWTEvent event) { + if(null != newtListener) { + return EventProcRes.DISPATCH; + } + if( null != newtWindow ) { + newtWindow.enqueueEvent(wait, event); + return EventProcRes.ENQUEUED; + } + return EventProcRes.NOP; } } 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..4f11e8772 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,64 +20,82 @@ * 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 javax.media.nativewindow.NativeSurfaceHolder; + 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) { - super(newtListener); + public AWTKeyAdapter(final com.jogamp.newt.event.KeyListener newtListener, final NativeSurfaceHolder nsProxy) { + super(newtListener, nsProxy); } - public AWTKeyAdapter(com.jogamp.newt.event.KeyListener newtListener, com.jogamp.newt.Window newtProxy) { + public AWTKeyAdapter(final com.jogamp.newt.event.KeyListener newtListener, final com.jogamp.newt.Window newtProxy) { super(newtListener, newtProxy); } - public AWTKeyAdapter(com.jogamp.newt.Window downstream) { + public AWTKeyAdapter(final com.jogamp.newt.Window downstream) { super(downstream); } - public AWTAdapter addTo(java.awt.Component awtComponent) { + public AWTKeyAdapter() { + super(); + } + + @Override + public synchronized AWTAdapter addTo(final java.awt.Component awtComponent) { awtComponent.addKeyListener(this); return this; } - public AWTAdapter removeFrom(java.awt.Component awtComponent) { + @Override + public synchronized AWTAdapter removeFrom(final 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); - if(null!=newtListener) { + @Override + public synchronized void keyPressed(final 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, nsHolder); + if( consumeAWTEvent ) { + e.consume(); + } + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.KeyListener)newtListener).keyPressed(event); - } else { - enqueueEvent(false, event); } } - public void keyReleased(java.awt.event.KeyEvent e) { - com.jogamp.newt.event.KeyEvent event = AWTNewtEventFactory.createKeyEvent(e, newtWindow); - if(null!=newtListener) { + @Override + public synchronized void keyReleased(final 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, nsHolder); + if( consumeAWTEvent ) { + e.consume(); + } + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.KeyListener)newtListener).keyReleased(event); - } else { - enqueueEvent(false, event); } } - 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(final 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..d9531cd5f 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,115 +20,147 @@ * 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 javax.media.nativewindow.NativeSurfaceHolder; + 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 { - public AWTMouseAdapter(com.jogamp.newt.event.MouseListener newtListener) { - super(newtListener); + public AWTMouseAdapter(final com.jogamp.newt.event.MouseListener newtListener, final NativeSurfaceHolder nsProxy) { + super(newtListener, nsProxy); } - public AWTMouseAdapter(com.jogamp.newt.event.MouseListener newtListener, com.jogamp.newt.Window newtProxy) { + public AWTMouseAdapter(final com.jogamp.newt.event.MouseListener newtListener, final com.jogamp.newt.Window newtProxy) { super(newtListener, newtProxy); } - public AWTMouseAdapter(com.jogamp.newt.Window downstream) { + public AWTMouseAdapter(final com.jogamp.newt.Window downstream) { super(downstream); } - public AWTAdapter addTo(java.awt.Component awtComponent) { + public AWTMouseAdapter() { + super(); + } + + @Override + public synchronized AWTAdapter addTo(final 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(final java.awt.Component awtComponent) { awtComponent.removeMouseListener(this); awtComponent.removeMouseMotionListener(this); awtComponent.removeMouseWheelListener(this); return this; } - public void mouseClicked(java.awt.event.MouseEvent e) { - com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); - if(null!=newtListener) { + @Override + public synchronized void mouseClicked(final java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, nsHolder); + if( consumeAWTEvent ) { + e.consume(); + } + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseClicked(event); - } else { - enqueueEvent(false, event); } } - public void mouseEntered(java.awt.event.MouseEvent e) { - com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); - if(null!=newtListener) { + @Override + public synchronized void mouseEntered(final java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, nsHolder); + if( consumeAWTEvent ) { + e.consume(); + } + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseEntered(event); - } else { - enqueueEvent(false, event); } } - public void mouseExited(java.awt.event.MouseEvent e) { - com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); - if(null!=newtListener) { + @Override + public synchronized void mouseExited(final java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, nsHolder); + if( consumeAWTEvent ) { + e.consume(); + } + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseExited(event); - } else { - enqueueEvent(false, event); } } - public void mousePressed(java.awt.event.MouseEvent e) { - com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); - if(null!=newtListener) { + @Override + public synchronized void mousePressed(final java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, nsHolder); + if( consumeAWTEvent ) { + e.consume(); + } + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.MouseListener)newtListener).mousePressed(event); - } else { - enqueueEvent(false, event); } } - public void mouseReleased(java.awt.event.MouseEvent e) { - com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); - if(null!=newtListener) { + @Override + public synchronized void mouseReleased(final java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, nsHolder); + if( consumeAWTEvent ) { + e.consume(); + } + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseReleased(event); - } else { - enqueueEvent(false, event); } } - public void mouseDragged(java.awt.event.MouseEvent e) { - com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); - if(null!=newtListener) { + @Override + public synchronized void mouseDragged(final java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, nsHolder); + if( consumeAWTEvent ) { + e.consume(); + } + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseDragged(event); - } else { - enqueueEvent(false, event); } } - public void mouseMoved(java.awt.event.MouseEvent e) { - com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); - if(null!=newtListener) { + @Override + public synchronized void mouseMoved(final java.awt.event.MouseEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, nsHolder); + if( consumeAWTEvent ) { + e.consume(); + } + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseMoved(event); - } else { - enqueueEvent(false, event); } } - public void mouseWheelMoved(java.awt.event.MouseWheelEvent e) { - com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); - if(null!=newtListener) { + @Override + public synchronized void mouseWheelMoved(final java.awt.event.MouseWheelEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, nsHolder); + if( consumeAWTEvent ) { + e.consume(); + } + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.MouseListener)newtListener).mouseWheelMoved(event); - } else { - enqueueEvent(false, event); } } } 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 2d63ca455..a8105403d 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,59 +20,70 @@ * 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 javax.media.nativewindow.NativeSurfaceHolder; + 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; - public AWTWindowAdapter(com.jogamp.newt.event.WindowListener newtListener) { - super(newtListener); + public AWTWindowAdapter(final com.jogamp.newt.event.WindowListener newtListener, final NativeSurfaceHolder nsProxy) { + super(newtListener, nsProxy); } - public AWTWindowAdapter(com.jogamp.newt.event.WindowListener newtListener, com.jogamp.newt.Window newtProxy) { + public AWTWindowAdapter(final com.jogamp.newt.event.WindowListener newtListener, final com.jogamp.newt.Window newtProxy) { super(newtListener, newtProxy); } - public AWTWindowAdapter(com.jogamp.newt.Window downstream) { + public AWTWindowAdapter(final com.jogamp.newt.Window downstream) { super(downstream); } - public AWTAdapter addTo(java.awt.Component awtComponent) { - java.awt.Window win = getWindow(awtComponent); + public AWTWindowAdapter() { + super(); + } + + @Override + public synchronized AWTAdapter addTo(final java.awt.Component awtComponent) { + final 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) { ((java.awt.Window)awtComponent).addWindowListener(this); } - return this; + return this; } - - public AWTAdapter removeFrom(java.awt.Component awtComponent) { - awtComponent.removeFocusListener(this); - awtComponent.removeComponentListener(this); - java.awt.Window win = getWindow(awtComponent); + + public synchronized AWTAdapter removeWindowClosingFrom(final java.awt.Component awtComponent) { + final java.awt.Window win = getWindow(awtComponent); if( null != win && null != windowClosingListener ) { win.removeWindowListener(windowClosingListener); } + return this; + } + + @Override + public synchronized AWTAdapter removeFrom(final java.awt.Component awtComponent) { + awtComponent.removeFocusListener(this); + awtComponent.removeComponentListener(this); + removeWindowClosingFrom(awtComponent); if(awtComponent instanceof java.awt.Window) { ((java.awt.Window)awtComponent).removeWindowListener(this); } @@ -83,38 +94,37 @@ public class AWTWindowAdapter while( null != comp && !(comp instanceof java.awt.Window) ) { comp = comp.getParent(); } - if(comp instanceof java.awt.Window) { - return (java.awt.Window) comp; - } - return null; + return (java.awt.Window) comp; // either null or a 'java.awt.Window' } - public void focusGained(java.awt.event.FocusEvent e) { - com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); + @Override + public synchronized void focusGained(final java.awt.event.FocusEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, nsHolder); if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: focusGained: "+e+" -> "+event); } - if(null!=newtListener) { + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.WindowListener)newtListener).windowGainedFocus(event); - } else { - enqueueEvent(false, event); } } - public void focusLost(java.awt.event.FocusEvent e) { - com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); + @Override + public synchronized void focusLost(final java.awt.event.FocusEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, nsHolder); if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: focusLost: "+e+" -> "+event); } - if(null!=newtListener) { + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.WindowListener)newtListener).windowLostFocus(event); - } else { - enqueueEvent(false, event); } } - public void componentResized(java.awt.event.ComponentEvent e) { - com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); + @Override + public synchronized void componentResized(final java.awt.event.ComponentEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, nsHolder); if(DEBUG_IMPLEMENTATION) { final java.awt.Component c = e.getComponent(); final java.awt.Dimension sz = c.getSize(); @@ -130,26 +140,26 @@ public class AWTWindowAdapter } System.err.println("AWT: componentResized: "+sz+" ( "+insets+", "+sz2+" ), "+e+" -> "+event); } - if(null!=newtListener) { + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.WindowListener)newtListener).windowResized(event); - } else { - enqueueEvent(false, event); } } - public void componentMoved(java.awt.event.ComponentEvent e) { - com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); + @Override + public synchronized void componentMoved(final java.awt.event.ComponentEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, nsHolder); if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: componentMoved: "+e+" -> "+event); } - if(null!=newtListener) { + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.WindowListener)newtListener).windowMoved(event); - } else { - enqueueEvent(false, event); } } - public void componentShown(java.awt.event.ComponentEvent e) { + @Override + public synchronized void componentShown(final java.awt.event.ComponentEvent e) { + if( !isSetup ) { return; } final java.awt.Component comp = e.getComponent(); if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: componentShown: "+comp); @@ -166,7 +176,9 @@ public class AWTWindowAdapter }*/ } - public void componentHidden(java.awt.event.ComponentEvent e) { + @Override + public synchronized void componentHidden(final java.awt.event.ComponentEvent e) { + if( !isSetup ) { return; } final java.awt.Component comp = e.getComponent(); if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: componentHidden: "+comp); @@ -183,50 +195,71 @@ public class AWTWindowAdapter }*/ } - public void windowActivated(java.awt.event.WindowEvent e) { - com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); - if(null!=newtListener) { + @Override + public synchronized void windowActivated(final java.awt.event.WindowEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, nsHolder); + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.WindowListener)newtListener).windowGainedFocus(event); - } else { - enqueueEvent(false, event); } } - public void windowClosed(java.awt.event.WindowEvent e) { } + @Override + public synchronized void windowClosed(final java.awt.event.WindowEvent e) { } - public void windowClosing(java.awt.event.WindowEvent e) { } + @Override + public synchronized void windowClosing(final java.awt.event.WindowEvent e) { } - public void windowDeactivated(java.awt.event.WindowEvent e) { - com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); - if(null!=newtListener) { + @Override + public synchronized void windowDeactivated(final java.awt.event.WindowEvent e) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, nsHolder); + if( EventProcRes.DISPATCH == processEvent(false, event) ) { ((com.jogamp.newt.event.WindowListener)newtListener).windowLostFocus(event); - } else { - enqueueEvent(false, event); } } - public void windowDeiconified(java.awt.event.WindowEvent e) { } + @Override + public synchronized void windowDeiconified(final java.awt.event.WindowEvent e) { } - public void windowIconified(java.awt.event.WindowEvent e) { } + @Override + public synchronized void windowIconified(final java.awt.event.WindowEvent e) { } - public void windowOpened(java.awt.event.WindowEvent e) { } + @Override + public synchronized void windowOpened(final java.awt.event.WindowEvent e) { } class WindowClosingListener implements java.awt.event.WindowListener { - 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); + @Override + public void windowClosing(final java.awt.event.WindowEvent e) { + synchronized( AWTWindowAdapter.this ) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, nsHolder); + if( EventProcRes.DISPATCH == processEvent(true, event) ) { + ((com.jogamp.newt.event.WindowListener)newtListener).windowDestroyNotify(event); + } + } + } + @Override + public void windowClosed(final java.awt.event.WindowEvent e) { + synchronized( AWTWindowAdapter.this ) { + if( !isSetup ) { return; } + final com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, nsHolder); + if( EventProcRes.DISPATCH == processEvent(true, event) ) { + ((com.jogamp.newt.event.WindowListener)newtListener).windowDestroyed(event); + } } } - public void windowActivated(java.awt.event.WindowEvent e) { } - public void windowClosed(java.awt.event.WindowEvent e) { } - public void windowDeactivated(java.awt.event.WindowEvent e) { } - public void windowDeiconified(java.awt.event.WindowEvent e) { } - public void windowIconified(java.awt.event.WindowEvent e) { } - public void windowOpened(java.awt.event.WindowEvent e) { } + @Override + public void windowActivated(final java.awt.event.WindowEvent e) { } + @Override + public void windowDeactivated(final java.awt.event.WindowEvent e) { } + @Override + public void windowDeiconified(final java.awt.event.WindowEvent e) { } + @Override + public void windowIconified(final java.awt.event.WindowEvent e) { } + @Override + public void windowOpened(final 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 a89ccaedb..2ba030f76 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -34,18 +34,24 @@ package com.jogamp.newt.opengl; +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.NativeWindowFactory; import javax.media.nativewindow.SurfaceUpdatedListener; import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.Rectangle; 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; @@ -53,9 +59,13 @@ 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 javax.media.opengl.GLSharedContextSetter; import jogamp.newt.WindowImpl; import jogamp.opengl.GLAutoDrawableBase; @@ -65,30 +75,45 @@ 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.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> + * 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> + * <p> + * <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> + * To share a {@link GLContext} see the following note in the documentation overview: + * <a href="../../../../overview-summary.html#SHARING">context sharing</a> + * as well as {@link GLSharedContextSetter}. * </p> */ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Window, NEWTEventConsumer, FPSCounter { @@ -97,25 +122,25 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind /** * Constructor. Do not call this directly -- use {@link #create()} instead. */ - protected GLWindow(Window window) { + protected GLWindow(final Window window) { super(null, null, false /* always handle device lifecycle ourselves */); this.window = (WindowImpl) window; - this.window.setHandleDestroyNotify(false); + this.window.setWindowDestroyNotifyAction( new Runnable() { + @Override + public void run() { + defaultWindowDestroyNotifyOp(); + } } ); window.addWindowListener(new WindowAdapter() { @Override - public void windowRepaint(WindowUpdateEvent e) { + public void windowRepaint(final WindowUpdateEvent e) { defaultWindowRepaintOp(); } @Override - public void windowResized(WindowEvent e) { - defaultWindowResizedOp(getWidth(), getHeight()); + public void windowResized(final WindowEvent e) { + defaultWindowResizedOp(getSurfaceWidth(), getSurfaceHeight()); } - @Override - public void windowDestroyNotify(WindowEvent e) { - defaultWindowDestroyNotifyOp(); - } }); this.window.setLifecycleHook(new GLLifecycleHook()); } @@ -124,7 +149,12 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind public final Object getUpstreamWidget() { return window; } - + + @Override + public final RecursiveLock getUpstreamLock() { + return window.getLock(); + } + /** * Creates a new GLWindow attaching a new Window referencing a * new default Screen and default Display with the given GLCapabilities. @@ -134,7 +164,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind * </p> * The default Display will be reused if already instantiated. */ - public static GLWindow create(GLCapabilitiesImmutable caps) { + public static GLWindow create(final GLCapabilitiesImmutable caps) { return new GLWindow(NewtFactory.createWindow(caps)); } @@ -146,7 +176,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind * and {@link Screen#removeReference()}. * </p> */ - public static GLWindow create(Screen screen, GLCapabilitiesImmutable caps) { + public static GLWindow create(final Screen screen, final GLCapabilitiesImmutable caps) { return new GLWindow(NewtFactory.createWindow(screen, caps)); } @@ -157,7 +187,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind * and {@link Screen#removeReference()}. * </p> */ - public static GLWindow create(Window window) { + public static GLWindow create(final Window window) { return new GLWindow(window); } @@ -174,7 +204,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind * and {@link Screen#removeReference()}. * </p> */ - public static GLWindow create(NativeWindow parentNativeWindow, GLCapabilitiesImmutable caps) { + public static GLWindow create(final NativeWindow parentNativeWindow, final GLCapabilitiesImmutable caps) { return new GLWindow(NewtFactory.createWindow(parentNativeWindow, caps)); } @@ -187,7 +217,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { + public WindowClosingMode setDefaultCloseOperation(final WindowClosingMode op) { return window.setDefaultCloseOperation(op); } @@ -196,14 +226,14 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind // @Override - public CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) { + public CapabilitiesChooser setCapabilitiesChooser(final CapabilitiesChooser chooser) { return window.setCapabilitiesChooser(chooser); } @Override public final CapabilitiesImmutable getChosenCapabilities() { final GLDrawable _drawable = drawable; - return null != _drawable ? _drawable.getChosenGLCapabilities() : window.getChosenCapabilities(); + return null != _drawable ? _drawable.getChosenGLCapabilities() : window.getChosenCapabilities(); } @Override @@ -227,7 +257,12 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public final void setTitle(String title) { + public final MonitorDevice getMainMonitor() { + return window.getMainMonitor(); + } + + @Override + public final void setTitle(final String title) { window.setTitle(title); } @@ -242,27 +277,37 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public final void setPointerVisible(boolean mouseVisible) { + public final void setPointerVisible(final boolean 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) { + public final void confinePointer(final boolean grab) { window.confinePointer(grab); } @Override - public final void setUndecorated(boolean value) { + public final void setUndecorated(final boolean value) { window.setUndecorated(value); } @Override - public final void warpPointer(int x, int y) { + public final void warpPointer(final int x, final int y) { window.warpPointer(x, y); } @Override @@ -271,7 +316,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public final void setAlwaysOnTop(boolean value) { + public final void setAlwaysOnTop(final boolean value) { window.setAlwaysOnTop(value); } @@ -281,12 +326,12 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public final void setFocusAction(FocusRunnable focusAction) { + public final void setFocusAction(final FocusRunnable focusAction) { window.setFocusAction(focusAction); } @Override - public void setKeyboardFocusHandler(KeyListener l) { + public void setKeyboardFocusHandler(final KeyListener l) { window.setKeyboardFocusHandler(l); } @@ -296,7 +341,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public final void requestFocus(boolean wait) { + public final void requestFocus(final boolean wait) { window.requestFocus(wait); } @@ -319,7 +364,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind public final int getY() { return window.getY(); } - + @Override public final int getWidth() { return window.getWidth(); @@ -329,22 +374,77 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind public final int getHeight() { return window.getHeight(); } - + + @Override + public final Rectangle getBounds() { + return window.getBounds(); + } + + @Override + public final int getSurfaceWidth() { + return window.getSurfaceWidth(); + } + + @Override + public final int getSurfaceHeight() { + return window.getSurfaceHeight(); + } + + @Override + public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) { + return window.convertToWindowUnits(pixelUnitsAndResult); + } + + @Override + public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) { + return window.convertToPixelUnits(windowUnitsAndResult); + } + + @Override + public final void setSurfaceScale(final int[] pixelScale) { + window.setSurfaceScale(pixelScale); + } + + @Override + public final int[] getRequestedSurfaceScale(final int[] result) { + return window.getRequestedSurfaceScale(result); + } + + @Override + public final int[] getCurrentSurfaceScale(final int[] result) { + return window.getCurrentSurfaceScale(result); + } + + @Override + public final int[] getNativeSurfaceScale(final int[] result) { + return window.getNativeSurfaceScale(result); + } + @Override - public final void setPosition(int x, int y) { + public final float[] getPixelsPerMM(final float[] ppmmStore) { + return window.getPixelsPerMM(ppmmStore); + } + + @Override + public final void setPosition(final int x, final int y) { window.setPosition(x, y); } @Override - public void setTopLevelPosition(int x, int y) { + public void setTopLevelPosition(final int x, final int y) { window.setTopLevelPosition(x, y); } @Override - public final boolean setFullscreen(boolean fullscreen) { + public final boolean setFullscreen(final boolean fullscreen) { return window.setFullscreen(fullscreen); } @Override + public boolean setFullscreen(final List<MonitorDevice> monitors) { + return window.setFullscreen(monitors); + } + + @Override public final boolean isFullscreen() { return window.isFullscreen(); } @@ -361,22 +461,17 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public final ReparentOperation reparentWindow(NativeWindow newParent) { - return window.reparentWindow(newParent); + public final ReparentOperation reparentWindow(final NativeWindow newParent, final int x, final int y, final int hints) { + return window.reparentWindow(newParent, x, y, hints); } @Override - public final ReparentOperation reparentWindow(NativeWindow newParent, boolean forceDestroyCreate) { - return window.reparentWindow(newParent, forceDestroyCreate); - } - - @Override - public final boolean removeChild(NativeWindow win) { + public final boolean removeChild(final NativeWindow win) { return window.removeChild(win); } @Override - public final boolean addChild(NativeWindow win) { + public final boolean addChild(final NativeWindow win) { return window.addChild(win); } @@ -390,16 +485,30 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public final void setVisible(boolean visible) { + public void setWindowDestroyNotifyAction(final Runnable r) { + window.setWindowDestroyNotifyAction(r); + } + + @Override + public final void setVisible(final boolean visible) { window.setVisible(visible); } @Override - public final void setSize(int width, int height) { + public void setVisible(final boolean wait, final boolean visible) { + window.setVisible(wait, visible); + } + + @Override + public final void setSize(final int width, final int height) { window.setSize(width, height); } @Override - public void setTopLevelSize(int width, int height) { + public final void setSurfaceSize(final int pixelWidth, final int pixelHeight) { + window.setSurfaceSize(pixelWidth, pixelHeight); + } + @Override + public void setTopLevelSize(final int width, final int height) { window.setTopLevelSize(width, height); } @@ -409,7 +518,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public Point getLocationOnScreen(Point storage) { + public Point getLocationOnScreen(final Point storage) { return window.getLocationOnScreen(storage); } @@ -417,6 +526,11 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind protected class GLLifecycleHook implements WindowImpl.LifecycleHook { @Override + public void preserveGLStateAtDestroy(final boolean value) { + GLWindow.this.preserveGLStateAtDestroy(value); + } + + @Override public synchronized void destroyActionPreLock() { // nop } @@ -424,7 +538,7 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind @Override public synchronized void destroyActionInLock() { if(Window.DEBUG_IMPLEMENTATION) { - String msg = "GLWindow.destroy() "+WindowImpl.getThreadName()+", start"; + final String msg = "GLWindow.destroy() "+WindowImpl.getThreadName()+", start"; System.err.println(msg); //Exception e1 = new Exception(msg); //e1.printStackTrace(); @@ -443,10 +557,14 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind 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) { + public synchronized void setVisibleActionPost(final boolean visible, final boolean nativeWindowCreated) { long t0; if(Window.DEBUG_IMPLEMENTATION) { t0 = System.nanoTime(); @@ -455,26 +573,29 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind 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<getSurfaceWidth()*getSurfaceHeight()) { + if( ( null != context ) ) { + throw new InternalError("GLWindow.LifecycleHook.setVisiblePost: "+WindowImpl.getThreadName()+" - Null drawable, but valid context - "+GLWindow.this); } - if(null==drawable) { - drawable = (GLDrawableImpl) 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 = (GLContextImpl) drawable.createContext(sharedContext); - context.setContextCreationFlags(additionalCtxCreationFlags); } if(Window.DEBUG_IMPLEMENTATION) { System.err.println("GLWindow.setVisibleActionPost("+visible+", "+nativeWindowCreated+") "+WindowImpl.getThreadName()+", fin: dt "+ (System.nanoTime()-t0)/1e6 +"ms"); @@ -485,10 +606,12 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind @Override public synchronized boolean pauseRenderingAction() { - boolean animatorPaused = false; + final boolean animatorPaused; savedAnimator = GLWindow.this.getAnimator(); if ( null != savedAnimator ) { animatorPaused = savedAnimator.pause(); + } else { + animatorPaused = false; } return animatorPaused; } @@ -499,31 +622,40 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind 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(final Throwable t) { + if( DEBUG ) { + System.err.println("Caught "+t.getClass().getName()+": "+t.getMessage()); + t.printStackTrace(); + } + } + } + return null; + } } ); + } + } + } } //---------------------------------------------------------------------- // OpenGL-related methods and state // - private GLContext sharedContext = null; - - @Override - protected final RecursiveLock getLock() { - return window.getLock(); - } - - /** - * 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; - } - @Override public void display() { if( !isNativeValid() || !isVisible() ) { return; } @@ -548,12 +680,21 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } finally { lock.unlock(); } - if( !done && 0<getWidth()*getHeight() ) { + if( !done && ( 0 < getSurfaceWidth() && 0 < getSurfaceHeight() ) ) { // retry drawable and context creation, will itself issue resize -> display setVisible(true); } } + /** + * {@inheritDoc} + * <p> + * GLWindow supports GL state preservation, hence returns <code>true</code>. + * </p> + */ + @Override + public final boolean isGLStatePreservationSupported() { return true; } + //---------------------------------------------------------------------- // GLDrawable methods // @@ -565,19 +706,15 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public final void setRealized(boolean realized) { - } - - @Override public final void swapBuffers() throws GLException { defaultSwapBuffers(); } - + //---------------------------------------------------------------------- // NEWTEventConsumer // @Override - public boolean consumeEvent(NEWTEvent event) { + public boolean consumeEvent(final NEWTEvent event) { return window.consumeEvent(event); } @@ -585,42 +722,27 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind // Window completion // @Override - public final void windowRepaint(int x, int y, int width, int height) { + public final void windowRepaint(final int x, final int y, final int width, final int height) { window.windowRepaint(x, y, width, height); } @Override - public final void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { + public final void enqueueEvent(final boolean wait, final com.jogamp.newt.event.NEWTEvent event) { window.enqueueEvent(wait, event); } @Override - public final void runOnEDTIfAvail(boolean wait, final Runnable task) { + public final void runOnEDTIfAvail(final boolean wait, final Runnable task) { window.runOnEDTIfAvail(wait, task); } @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 void sendWindowEvent(int eventType) { + public void sendWindowEvent(final int eventType) { window.sendWindowEvent(eventType); } @Override - public final WindowListener getWindowListener(int index) { + public final WindowListener getWindowListener(final int index) { return window.getWindowListener(index); } @@ -630,22 +752,22 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public final void removeWindowListener(WindowListener l) { + public final void removeWindowListener(final WindowListener l) { window.removeWindowListener(l); } @Override - public final void addWindowListener(WindowListener l) { + public final void addWindowListener(final WindowListener l) { window.addWindowListener(l); } @Override - public final void addWindowListener(int index, WindowListener l) throws IndexOutOfBoundsException { + public final void addWindowListener(final int index, final WindowListener l) throws IndexOutOfBoundsException { window.addWindowListener(index, l); } @Override - public final void setKeyboardVisible(boolean visible) { + public final void setKeyboardVisible(final boolean visible) { window.setKeyboardVisible(visible); } @@ -655,22 +777,22 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public final void addKeyListener(KeyListener l) { + public final void addKeyListener(final KeyListener l) { window.addKeyListener(l); } @Override - public final void addKeyListener(int index, KeyListener l) { + public final void addKeyListener(final int index, final KeyListener l) { window.addKeyListener(index, l); } @Override - public final void removeKeyListener(KeyListener l) { + public final void removeKeyListener(final KeyListener l) { window.removeKeyListener(l); } @Override - public final KeyListener getKeyListener(int index) { + public final KeyListener getKeyListener(final int index) { return window.getKeyListener(index); } @@ -680,22 +802,22 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override - public final void addMouseListener(MouseListener l) { + public final void addMouseListener(final MouseListener l) { window.addMouseListener(l); } @Override - public final void addMouseListener(int index, MouseListener l) { + public final void addMouseListener(final int index, final MouseListener l) { window.addMouseListener(index, l); } @Override - public final void removeMouseListener(MouseListener l) { + public final void removeMouseListener(final MouseListener l) { window.removeMouseListener(l); } @Override - public final MouseListener getMouseListener(int index) { + public final MouseListener getMouseListener(final int index) { return window.getMouseListener(index); } @@ -704,6 +826,39 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind return window.getMouseListeners(); } + @Override + public void setDefaultGesturesEnabled(final boolean enable) { + window.setDefaultGesturesEnabled(enable); + } + @Override + public boolean areDefaultGesturesEnabled() { + return window.areDefaultGesturesEnabled(); + } + @Override + public final void addGestureHandler(final GestureHandler gh) { + window.addGestureHandler(gh); + } + @Override + public final void addGestureHandler(final int index, final GestureHandler gh) { + window.addGestureHandler(index, gh); + } + @Override + public final void removeGestureHandler(final GestureHandler gh) { + window.removeGestureHandler(gh); + } + @Override + public final void addGestureListener(final GestureHandler.GestureListener gl) { + window.addGestureListener(-1, gl); + } + @Override + public final void addGestureListener(final int index, final GestureHandler.GestureListener gl) { + window.addGestureListener(index, gl); + } + @Override + public final void removeGestureListener(final GestureHandler.GestureListener gl) { + window.removeGestureListener(gl); + } + //---------------------------------------------------------------------- // NativeWindow completion // @@ -735,6 +890,26 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override + public final void removeSurfaceUpdatedListener(final SurfaceUpdatedListener l) { + window.removeSurfaceUpdatedListener(l); + } + + @Override + public final void addSurfaceUpdatedListener(final SurfaceUpdatedListener l) { + window.addSurfaceUpdatedListener(l); + } + + @Override + public final void addSurfaceUpdatedListener(final int index, final SurfaceUpdatedListener l) throws IndexOutOfBoundsException { + window.addSurfaceUpdatedListener(index, l); + } + + @Override + public final void surfaceUpdated(final Object updater, final NativeSurface ns, final long when) { + window.surfaceUpdated(updater, ns, when); + } + + @Override public final long getWindowHandle() { return window.getWindowHandle(); @@ -761,46 +936,102 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind return window.getScreenIndex(); } - @Override - 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[]) { + public static void main(final 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, null, true).toString()); - final GLProfile glp = GLProfile.getDefault(); + 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); + final GLWindow glWindow = GLWindow.create(caps); glWindow.setSize(128, 128); glWindow.addGLEventListener(new GLEventListener() { @Override - public void init(GLAutoDrawable drawable) { - GL gl = drawable.getGL(); + public void init(final GLAutoDrawable drawable) { + final 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() ) { + final GL4ES3 _gl = gl.getGL4ES3(); + System.err.println("GL4ES3 retrieved, impl. class "+_gl.getClass().getName()); + } + if( gl.isGL3() ) { + final GL3 _gl = gl.getGL3(); + System.err.println("GL3 retrieved, impl. class "+_gl.getClass().getName()); + } + if( gl.isGLES3() ) { + final GLES3 _gl = gl.getGLES3(); + System.err.println("GLES3 retrieved, impl. class "+_gl.getClass().getName()); + } + if( gl.isGLES2() ) { + final 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) { + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { } @Override - public void display(GLAutoDrawable drawable) { + public void display(final GLAutoDrawable drawable) { } @Override - public void dispose(GLAutoDrawable drawable) { + public void dispose(final GLAutoDrawable drawable) { } }); diff --git a/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java index 36bc3f28f..76af2d0ec 100644 --- a/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.java +++ b/src/newt/classes/com/jogamp/newt/swt/NewtCanvasSWT.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,13 +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.swt; import javax.media.nativewindow.AbstractGraphicsConfiguration; @@ -44,18 +43,20 @@ 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.events.ControlAdapter; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.PaintEvent; -import org.eclipse.swt.events.PaintListener; 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; @@ -65,111 +66,159 @@ 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 static final boolean isOSX = NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false); - - private final AbstractGraphicsScreen screen; - + + 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 + private volatile boolean postSetPos = false; // pending pos - /** - * Creates an instance using {@link #NewtCanvasSWT(Composite, int, Window)} + /** + * 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)}. + * 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 + * @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 }; + 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)}. + * 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 + * @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) { + public NewtCanvasSWT(final Composite parent, final int style, final Window child) { super(parent, style | SWT.NO_BACKGROUND); - + SWTAccessor.setRealized(this, true); clientArea = getClientArea(); final AbstractGraphicsDevice device = SWTAccessor.getDevice(this); - screen = SWTAccessor.getScreen(device, 0); + screen = SWTAccessor.getScreen(device, -1 /* default */); nativeWindow = null; - + if(null != child) { setNEWTChild(child); } - - /* Register SWT listeners (e.g. PaintListener) to render/resize GL surface. */ - /* TODO: verify that these do not need to be manually de-registered when destroying the SWT component */ - addPaintListener(new PaintListener() { + + final Listener listener = new Listener () { @Override - public void paintControl(final PaintEvent arg0) { - if( null != nativeWindow || validateNative() ) { - if( null !=newtChild ) { - newtChild.windowRepaint(0, 0, clientArea.width, clientArea.height); + public void handleEvent (final Event event) { + switch (event.type) { + case SWT.Paint: + if( DEBUG ) { + System.err.println("NewtCanvasSWT.Event.PAINT, "+event); + } + if( null != nativeWindow || validateNative() ) { + if( newtChildReady ) { + if( postSetSize ) { + newtChild.setSize(clientArea.width, clientArea.height); + postSetSize = false; + } + if( postSetPos ) { + newtChild.setPosition(clientArea.x, clientArea.y); + postSetPos = false; + } + newtChild.windowRepaint(0, 0, clientArea.width, clientArea.height); + } + } + break; + case SWT.Move: + if( DEBUG ) { + System.err.println("NewtCanvasSWT.Event.MOVE, "+event); + } + // updatePosSizeCheck(); + break; + case SWT.Resize: + if( DEBUG ) { + System.err.println("NewtCanvasSWT.Event.RESIZE, "+event); + } + updateSizeCheck(); + break; + case SWT.Dispose: + if( DEBUG ) { + System.err.println("NewtCanvasSWT.Event.DISPOSE, "+event); + } + NewtCanvasSWT.this.dispose(); + break; + default: + if( DEBUG ) { + System.err.println("NewtCanvasSWT.Event.misc: "+event.type+", "+event); } } } - }); + }; + // addListener (SWT.Move, listener); + addListener (SWT.Resize, listener); + addListener (SWT.Paint, listener); + addListener (SWT.Dispose, listener); + } - addControlListener(new ControlAdapter() { - @Override - public void controlResized(final ControlEvent arg0) { - updateSizeCheck(); - } - }); + @Override + public void setBounds(final int x, final int y, final int width, final int height) { + super.setBounds(x, y, width, height); + if( DEBUG ) { + System.err.println("NewtCanvasSWT.setBounds: "+x+"/"+y+" "+width+"x"+height); + } + if( SWTAccessor.isOSX ) { + // Force newtChild to update its size and position (OSX only) + updatePosSizeCheck(x, y, width, height, true /* updatePos */); + } } - + /** assumes nativeWindow == null ! */ protected final boolean validateNative() { - if( isDisposed() ) { - return false; - } updateSizeCheck(); final Rectangle nClientArea = clientArea; - if(0 == nClientArea.width * nClientArea.height) { + 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). + /* 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); @@ -177,27 +226,64 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { 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 ); + if( SWTAccessor.isOSX && newtChildReady ) { + // initial positioning for OSX, called when the window is created + newtChild.setPosition(getLocation().x, getLocation().y); + } } 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( null != newtChild ) { - newtChild.setSize(clientArea.width, clientArea.height); + if( null != nClientArea ) { + updatePosSizeCheck(nClientArea.x, nClientArea.y, nClientArea.width, nClientArea.height, false /* updatePos */); + } + } + protected final void updatePosSizeCheck() { + final Rectangle nClientArea = getClientArea(); + if( null != nClientArea ) { + updatePosSizeCheck(nClientArea.x, nClientArea.y, nClientArea.width, nClientArea.height, true /* updatePos */); + } + } + protected final void updatePosSizeCheck(final int newX, final int newY, final int newWidth, final int newHeight, final boolean updatePos) { + final boolean sizeChanged, posChanged; + final Rectangle nClientArea; + { + final Rectangle oClientArea = clientArea; + sizeChanged = newWidth != oClientArea.width || newHeight != oClientArea.height; + posChanged = newX != oClientArea.x || newY != oClientArea.y; + if( sizeChanged || posChanged ) { + nClientArea = new Rectangle(updatePos ? newX : oClientArea.x, updatePos ? newY : oClientArea.y, newWidth, newHeight); + clientArea = nClientArea; + } else { + nClientArea = clientArea; + } + } + if(DEBUG) { + final long nsh = newtChildReady ? newtChild.getSurfaceHandle() : 0; + System.err.println("NewtCanvasSWT.updatePosSizeCheck: sizeChanged "+sizeChanged+", posChanged "+posChanged+", updatePos "+updatePos+", ("+Thread.currentThread().getName()+"): newtChildReady "+newtChildReady+", "+nClientArea.x+"/"+nClientArea.y+" "+nClientArea.width+"x"+nClientArea.height+" - surfaceHandle 0x"+Long.toHexString(nsh)); + } + if( sizeChanged ) { + if( newtChildReady ) { + newtChild.setSize(nClientArea.width, nClientArea.height); + } else { + postSetSize = true; + } + } + if( updatePos && posChanged ) { + if( newtChildReady ) { + newtChild.setPosition(nClientArea.x, nClientArea.y); + } else { + postSetPos = true; } } } @@ -206,7 +292,7 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { public void update() { // don't paint background etc .. nop avoids flickering } - + /** * Destroys this resource: * <ul> @@ -226,49 +312,61 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { } configureNewtChild(false); newtChild.setVisible(false); - newtChild.reparentWindow(null); + newtChild.reparentWindow(null, -1, -1, 0 /* hint */); newtChild.destroy(); newtChild = null; } + screen.getDevice().close(); nativeWindow = null; - super.dispose(); + super.dispose(); } - + + private Point getParentLocationOnScreen() { + final org.eclipse.swt.graphics.Point[] parentLoc = new org.eclipse.swt.graphics.Point[] { null }; + SWTAccessor.invoke(true, new Runnable() { + public void run() { + parentLoc[0] = getParent().toDisplay(0,0); + } } ); + return new Point(parentLoc[0].x, parentLoc[0].y); + } + /** @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; // FIXME + return newtChildCloseOp; // TODO: implement ?! } - public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { - return newtChildCloseOp = op; // FIXME + @Override + public WindowClosingMode setDefaultCloseOperation(final WindowClosingMode op) { + return newtChildCloseOp = op; // TODO: implement ?! } boolean isParent() { - return null!=newtChild ; + 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. + * 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. + * 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)}. + * via {@link Display#setEDTUtil(EDTUtil)}. * </p> - * @return the previous attached newt child. + * @return the previous attached newt child. */ public Window setNEWTChild(final Window newChild) { final Window prevChild = newtChild; @@ -285,62 +383,67 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { if(null != nativeWindow && null != newChild) { reparentWindow( true ); } - return prevChild; + return prevChild; } - + /** @return the current NEWT child */ public Window getNEWTChild() { return newtChild; } - + @Override - public boolean setParent(Composite parent) { + public boolean setParent(final Composite parent) { return super.setParent(parent); } - /* package */ void configureNewtChild(boolean attach) { - + /* package */ void configureNewtChild(final boolean attach) { + newtChildReady = attach; if( null != newtChild ) { newtChild.setKeyboardFocusHandler(null); - if(attach) { - newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); + if(attach) { + newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingMode.DO_NOTHING_ON_CLOSE); } else { newtChild.setFocusAction(null); - newtChild.setDefaultCloseOperation(newtChildCloseOp); + newtChild.setDefaultCloseOperation(newtChildCloseOp); } } } - void reparentWindow(boolean add) { + void reparentWindow(final 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; - final Display newtDisplay = newtChild.getScreen().getDisplay(); - newtDisplay.setEDTUtil(new SWTEDTUtil(newtDisplay, getDisplay())); - + // 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); + 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 this it is completely covered by the newtChild (z-order). + // 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); + 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()); @@ -348,34 +451,34 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { } private final void requestFocusNEWTChild() { - if( null != newtChild ) { + if( newtChildReady ) { newtChild.setFocusAction(null); newtChild.requestFocus(); } } - + @Override public boolean forceFocus() { final boolean res = NewtCanvasSWT.super.forceFocus(); requestFocusNEWTChild(); - return res; + 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) { + + public SWTNativeWindow(final AbstractGraphicsConfiguration config, final long nativeWindowHandle) { this.config = config; this.nativeWindowHandle = nativeWindowHandle; - if(isOSX) { + 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; @@ -400,31 +503,54 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { } @Override - public void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { } + public void addSurfaceUpdatedListener(final SurfaceUpdatedListener l) { } @Override - public void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException { + public void addSurfaceUpdatedListener(final int index, final SurfaceUpdatedListener l) throws IndexOutOfBoundsException { } @Override - public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { } + public void removeSurfaceUpdatedListener(final SurfaceUpdatedListener l) { } @Override - public long getSurfaceHandle() { + public long getSurfaceHandle() { return 0; } @Override public int getWidth() { - return clientArea.width; + return getSurfaceWidth(); // FIXME: Use 'scale' or an actual window-width } @Override public int getHeight() { + return getSurfaceHeight(); // FIXME: Use 'scale' or an actual window-width + } + + @Override + public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) { + return pixelUnitsAndResult; // FIXME HiDPI: use 'pixelScale' + } + + @Override + public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) { + return windowUnitsAndResult; // FIXME HiDPI: use 'pixelScale' + } + + @Override + public int getSurfaceWidth() { + return clientArea.width; + } + + @Override + public int getSurfaceHeight() { return clientArea.height; } @Override + public final NativeSurface getNativeSurface() { return this; } + + @Override public AbstractGraphicsConfiguration getGraphicsConfiguration() { return config; } @@ -440,7 +566,7 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { } @Override - public void surfaceUpdated(Object updater, NativeSurface ns, long when) { } + public void surfaceUpdated(final Object updater, final NativeSurface ns, final long when) { } @Override public void destroy() { } @@ -471,36 +597,38 @@ public class NewtCanvasSWT extends Canvas implements WindowClosingProtocol { } @Override - public Point getLocationOnScreen(Point point) { - if( isOSX ) { - final Point los = OSXUtil.GetLocationOnScreen(nativeWindowHandle, false, 0, 0); - // top-level position -> client window position - los.setX(los.getX() + insets.getLeftWidth()); - los.setY(los.getY() + insets.getTopHeight()); - if(null!=point) { - return point.translate(los); - } else { - return los; - } + public Point getLocationOnScreen(final Point point) { + final Point los; // client window location on screen + if( SWTAccessor.isOSX ) { + // let getLOS provide the point where the child window may be placed + // from, as taken from SWT Control.toDisplay(); + los = getParentLocationOnScreen(); + } 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 { - // client position on 'normal' windowing systems is 0/0 - if(null == point) { - point = new Point(0, 0); - } - return point; + // 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) { + static String newtWinHandleToHexString(final Window w) { return null != w ? toHexString(w.getWindowHandle()) : "nil"; } - static String toHexString(long l) { + static String toHexString(final long l) { return "0x"+Long.toHexString(l); } } diff --git a/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java b/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java deleted file mode 100644 index d4b83d891..000000000 --- a/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java +++ /dev/null @@ -1,280 +0,0 @@ -/** - * 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 java.awt.EventQueue; - -import jogamp.newt.Debug; - -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 NewtEventDispatchThread 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() { - public void run() { - ((jogamp.newt.DisplayImpl) newtDisplay).dispatchMessages(); - } }; - this.swtDisplay = swtDisplay; - this.nedt = new NewtEventDispatchThread(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 void reset() { - synchronized(edtLock) { - waitUntilStopped(); - if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT reset - edt: "+nedt); - } - this.nedt = new NewtEventDispatchThread(threadGroup, name); - this.nedt.setDaemon(true); // don't stop JVM from shutdown .. - } - } - - private final void startImpl() { - if(nedt.isAlive()) { - throw new RuntimeException("EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning()+", edt: "+nedt); - } - start_iter++; - nedt.setName(name+start_iter); - nedt.shouldStop = false; - if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT START - edt: "+nedt); - // Thread.dumpStack(); - } - nedt.start(); - } - - @Override - public boolean isCurrentThreadEDT() { - return swtDisplay.getThread() == Thread.currentThread(); - } - - @Override - public final boolean isCurrentThreadNEDT() { - return nedt == Thread.currentThread(); - } - - @Override - public final boolean isCurrentThreadEDTorNEDT() { - final Thread ct = Thread.currentThread(); - return ct == swtDisplay.getThread() || ct == nedt ; - } - - @Override - public boolean isRunning() { - return nedt.isRunning() ; // SWT is always running - } - - @Override - public final void invokeStop(Runnable task) { - invokeImpl(true, task, true); - } - - @Override - public final void invoke(boolean wait, Runnable task) { - invokeImpl(wait, task, false); - } - - private void invokeImpl(boolean wait, Runnable task, boolean stop) { - if(task == null) { - throw new RuntimeException("Null Runnable"); - } - synchronized(edtLock) { // lock the EDT status - if( nedt.shouldStop ) { - // drop task .. - if(DEBUG) { - System.err.println("Warning: EDT about (1) to stop, won't enqueue new task: "+nedt); - Thread.dumpStack(); - } - return; - } - // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); - // Thread.dumpStack(); - if(stop) { - nedt.shouldStop = true; - if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); - // Thread.dumpStack(); - } - } - - // start if should not stop && not started yet - if( !stop && !nedt.isRunning() ) { - startImpl(); - } - } - if(wait) { - swtDisplay.syncExec(task); - } else { - swtDisplay.asyncExec(task); - } - } - - @Override - final public void waitUntilIdle() { - final NewtEventDispatchThread _edt; - synchronized(edtLock) { - _edt = nedt; - } - if(!_edt.isRunning() || EventQueue.isDispatchThread() || _edt == Thread.currentThread()) { - return; - } - try { - swtDisplay.syncExec(new Runnable() { - public void run() { } - }); - } catch (Exception e) { } - } - - @Override - final public void waitUntilStopped() { - synchronized(edtLock) { - if(nedt.isRunning() && nedt != Thread.currentThread() ) { - while(nedt.isRunning()) { - try { - edtLock.wait(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - } - } - - class NewtEventDispatchThread extends Thread { - volatile boolean shouldStop = false; - volatile boolean isRunning = false; - Object sync = new Object(); - - public NewtEventDispatchThread(ThreadGroup tg, String name) { - super(tg, name); - } - - final public boolean isRunning() { - return isRunning; - } - - @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()+": EDT run() START "+ getName()); - } - RuntimeException error = null; - try { - do { - // event dispatch - if(!shouldStop) { - // FIXME: Determine whether we require to run the - // delivery of events (dispatch) on AWT-EDT. - // Since the WindowDriver itself delivers all Window related events, - // this shall not be required. - // AWTEDTExecutor.singleton.invoke(true, dispatchMessages); - 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 EDT", t); - } - } finally { - if(DEBUG) { - System.err.println(getName()+": EDT run() END "+ getName()+", "+error); - } - synchronized(edtLock) { - isRunning = !shouldStop; - if(!isRunning) { - edtLock.notifyAll(); - } - } - if(DEBUG) { - System.err.println(getName()+": EDT run() EXIT "+ getName()+", exception: "+error); - } - if(null!=error) { - throw error; - } - } // finally - } // run() - } // EventDispatchThread - -} diff --git a/src/newt/classes/com/jogamp/newt/util/EDTUtil.java b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java index 7e19d9de5..58aa67105 100644 --- a/src/newt/classes/com/jogamp/newt/util/EDTUtil.java +++ b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java @@ -63,25 +63,30 @@ 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 start via <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> * - * @see #invoke(boolean, java.lang.Runnable) - * @see #invokeStop(java.lang.Runnable) + * @throws IllegalStateException if EDT is running and not subject to be stopped, i.e. {@link #isRunning()} returns true + * @throws RuntimeException if EDT could not be started + * + * @see #invokeStop(boolean, java.lang.Runnable) + * @see #waitUntilStopped() */ - public void reset(); + public void start() throws IllegalStateException; /** * Returns true if the current thread is the event dispatch thread (EDT). * <p> - * The EDT is the platform specific thread dispatching toolkit-events + * 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()}, + * 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> */ @@ -90,60 +95,74 @@ public interface EDTUtil { /** * 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 + * 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()}, + * 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 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); - /** - * Shall start the thread if not running.<br> - * Append task to the EDT task queue.<br> - * Wait until execution is finished if <code>wait == true</code>.<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 5e79e9b49..80da1ce3b 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. @@ -45,6 +45,7 @@ import java.util.List; import javax.media.nativewindow.NativeWindowFactory; import com.jogamp.common.os.Platform; +import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.ReflectionUtil; import jogamp.newt.Debug; @@ -54,13 +55,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 +73,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 jogamp.newt.driver.macosx.WindowDriver NEWT Mac OSX Window} driver implementation.<br> - * <i>TODO</i>: Some hardcoded dependencies exist in this implementation, + * <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): @@ -96,36 +97,36 @@ public class MainThread { 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(); NEWTJNILibLoader.loadNEWT(); - HINT_USE_MAIN_THREAD = !NativeWindowFactory.isAWTAvailable() || - Debug.getBooleanProperty("newt.MainThread.force", true); + HINT_USE_MAIN_THREAD = !NativeWindowFactory.isAWTAvailable() || + PropertyAccess.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) { + + private static final Thread[] getAllThreads(final int[] count) { int tn; Thread[] threads = new Thread[ rootThreadGroup.activeCount() ]; while ( ( tn = rootThreadGroup.enumerate( threads, true ) ) == threads.length ) { @@ -135,9 +136,9 @@ public class MainThread { return threads; } private static final List<Thread> getNonDaemonThreads() { - List<Thread> res = new ArrayList<Thread>(); - int[] tn = { 0 }; - Thread[] threads = getAllThreads(tn); + final List<Thread> res = new ArrayList<Thread>(); + final int[] tn = { 0 }; + final Thread[] threads = getAllThreads(tn); for(int i = tn[0] - 1; i >= 0; i--) { final Thread thread = threads[i]; try { @@ -145,31 +146,31 @@ public class MainThread { res.add(thread); if(DEBUG) System.err.println("XXX0: "+thread.getName()+", "+thread); } - } catch (Throwable t) { + } catch (final Throwable t) { t.printStackTrace(); } } - return res; + return res; } - private static final int getNonDaemonThreadCount(List<Thread> ignoreThreads) { + private static final int getNonDaemonThreadCount(final List<Thread> ignoreThreads) { int res = 0; - int[] tn = { 0 }; - Thread[] threads = getAllThreads(tn); - + final int[] tn = { 0 }; + final 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); } - } catch (Throwable t) { + } catch (final Throwable t) { t.printStackTrace(); } } - return res; + return res; } - + static class UserApp extends Thread { private final String mainClassNameShort; private final String mainClassName; @@ -177,11 +178,11 @@ public class MainThread { private final Method mainClassMain; private List<Thread> nonDaemonThreadsAtStart; - public UserApp(String mainClassName, String[] mainClassArgs) throws SecurityException, NoSuchMethodException, ClassNotFoundException { + public UserApp(final String mainClassName, final String[] mainClassArgs) throws SecurityException, NoSuchMethodException, ClassNotFoundException { 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 +193,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()); } @@ -204,10 +205,10 @@ public class MainThread { try { if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" invoke "+mainClassName); mainClassMain.invoke(null, new Object[] { mainClassArgs } ); - } catch (InvocationTargetException ite) { + } catch (final InvocationTargetException ite) { ite.getTargetException().printStackTrace(); return; - } catch (Throwable t) { + } catch (final Throwable t) { t.printStackTrace(); return; } @@ -219,7 +220,7 @@ public class MainThread { if(DEBUG) System.err.println("MainAction.run(): post user app, non daemon threads alive: "+ndtr); try { Thread.sleep(1000); - } catch (InterruptedException e) { + } catch (final InterruptedException e) { e.printStackTrace(); } } @@ -230,32 +231,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) { + } catch (final 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 { + public static void main(final String[] args) throws SecurityException, NoSuchMethodException, ClassNotFoundException { final Thread cur = Thread.currentThread(); - + useMainThread = HINT_USE_MAIN_THREAD; if(DEBUG) { @@ -268,13 +269,13 @@ public class MainThread { if(!useMainThread && !NativeWindowFactory.isAWTAvailable()) { throw new RuntimeException("!USE_MAIN_THREAD and no AWT available"); } - + if(args.length==0) { return; } - String mainClassName=args[0]; - String[] mainClassArgs=new String[args.length-1]; + final String mainClassName=args[0]; + final String[] mainClassArgs=new String[args.length-1]; if(args.length>1) { System.arraycopy(args, 1, mainClassArgs, 0, args.length-1); } @@ -282,32 +283,32 @@ public class MainThread { mainAction = new UserApp(mainClassName, mainClassArgs); if(isMacOSX) { - ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "initSingleton", + ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "initSingleton", null, null, MainThread.class.getClassLoader()); } if ( useMainThread ) { try { cur.setName(cur.getName()+"-MainThread"); - } catch (Exception e) {} - + } catch (final 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) { + } catch (final 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..e5f8ff17c --- /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(final List<MonitorMode> monitorModes, final MonitorMode search) { + return monitorModes.indexOf(search); + } + + public static int getIndexByHashCode(final List<MonitorMode> monitorModes, final 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(final List<MonitorMode> monitorModes, final MonitorMode.SizeAndRRate sizeAndRate, final int modeId, final 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(final List<MonitorMode> monitorModes, final 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(final List<MonitorMode> monitorModes, final 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(final List<MonitorMode> monitorModes, final 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(final List<MonitorMode> monitorModes, final 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(final List<MonitorMode> monitorModes, final 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 in pixel units + * @return modes with nearest resolution, or matching ones. May return zero sized list for non. + */ + public static List<MonitorMode> filterByResolution(final List<MonitorMode> monitorModes, final 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(final List<MonitorMode> monitorModes, final 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); + final 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(final 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(final 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..9a3e79a8f --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/util/applet/JOGLNewtApplet3Run.java @@ -0,0 +1,360 @@ +/** + * 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.UpstreamWindowHookMutableSizePos; +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; + UpstreamWindowHookMutableSizePos upstreamSizePosHook; + PointImmutable upstreamLocOnScreen; + NativeWindow browserWin; + + final String getParameter(final 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 (final 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 UpstreamWindowHookMutableSizePos(upstreamWin.getX(), upstreamWin.getY(), + upstreamWin.getWidth(), upstreamWin.getHeight(), + upstreamWin.getWidth(), upstreamWin.getHeight()); // FIXME: pixel-dim == window-dim 'for now' ? + 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(final boolean v) { + if( null != glWindow ) { + glWindow.setVisible(v); + } + } + + @Override + public void setSize(final int width, final int height) { + upstreamSizePosHook.setWinSize(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(final NativeWindowUpstream nw) { + upstreamSizePosHook.setWinPos(nw.getX(), nw.getY()); + if( null != glWindow ) { + glWindow.setPosition(nw.getX(), nw.getY()); + } + } + }; + } + + @Override + public void init(final 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 (final 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 (final 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..d40e09d96 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; @@ -49,6 +54,7 @@ import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowListener; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.util.AnimatorBase; /** Shows how to deploy an applet using JOGL. This demo must be @@ -56,27 +62,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; - - public JOGLNewtAppletBase(String glEventListenerClazzName, - int glSwapInterval, - boolean noDefaultKeyListener, - boolean glClosable, - boolean glDebug, - boolean glTrace) { - + NativeWindow parentWin; + + public JOGLNewtAppletBase(final String glEventListenerClazzName, + final int glSwapInterval, + final boolean noDefaultKeyListener, + final boolean glClosable, + final boolean glDebug, + final boolean glTrace) { + this.glEventListenerClazzName=glEventListenerClazzName; this.glSwapInterval=glSwapInterval; this.noDefaultKeyListener = noDefaultKeyListener; @@ -90,19 +97,19 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { public Animator getGLAnimator() { return glAnimator; } public boolean isValid() { return isValid; } - public static boolean str2Bool(String str, boolean def) { + public static boolean str2Bool(final String str, final boolean def) { if(null==str) return def; try { return Boolean.valueOf(str).booleanValue(); - } catch (Exception ex) { ex.printStackTrace(); } + } catch (final Exception ex) { ex.printStackTrace(); } return def; } - public static int str2Int(String str, int def) { + public static int str2Int(final String str, final int def) { if(null==str) return def; try { return Integer.parseInt(str); - } catch (Exception ex) { ex.printStackTrace(); } + } catch (final Exception ex) { ex.printStackTrace(); } return def; } @@ -111,19 +118,20 @@ 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; try { clazz = Class.forName(clazzName, false, cl); - } catch (Throwable t) { + } catch (final Throwable t) { t.printStackTrace(); } return clazz; } }); instance = clazz.newInstance(); - } catch (Throwable t) { + } catch (final Throwable t) { t.printStackTrace(); throw new RuntimeException("Error while instantiating demo: "+clazzName); } @@ -136,46 +144,30 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { return (GLEventListener) instance; } - public static boolean setField(Object instance, String fieldName, Object value) { + public static boolean setField(final Object instance, final String fieldName, final Object value) { try { - Field f = instance.getClass().getField(fieldName); + final Field f = instance.getClass().getField(fieldName); if(f.getType().isInstance(value)) { f.set(instance, value); return true; } else { System.out.println(instance.getClass()+" '"+fieldName+"' field not assignable with "+value.getClass()+", it's a: "+f.getType()); } - } catch (NoSuchFieldException nsfe) { + } catch (final NoSuchFieldException nsfe) { System.out.println(instance.getClass()+" has no '"+fieldName+"' field"); - } catch (Throwable t) { + } catch (final Throwable t) { t.printStackTrace(); } return false; } - public void init(GLWindow glWindow) { + public void init(final GLWindow glWindow) { init(Thread.currentThread().getThreadGroup(), glWindow); } - public void init(ThreadGroup tg, final GLWindow glWindow) { + public void init(final 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 +192,7 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { if(glEventListener instanceof KeyListener) { glWindow.addKeyListener((KeyListener)glEventListener); } - + if(!noDefaultKeyListener) { glWindow.addKeyListener(this); } @@ -208,26 +200,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, AnimatorBase.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) { + + } catch (final Throwable t) { throw new RuntimeException(t); } isValid = true; } + private final WindowListener reparentHomeListener = new WindowAdapter() { + // Closing action: back to parent! + @Override + public void windowDestroyNotify(final 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 (final 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,58 +275,117 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { // *********************************************************************************** // *********************************************************************************** - public void init(GLAutoDrawable drawable) { + @Override + public void init(final 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 (final 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 (final Exception e) {e.printStackTrace();} } if(glSwapInterval>=0) { _gl.setSwapInterval(glSwapInterval); } } - public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { } - public void display(GLAutoDrawable drawable) { + @Override + public void display(final GLAutoDrawable drawable) { } - public void dispose(GLAutoDrawable drawable) { + @Override + public void dispose(final GLAutoDrawable drawable) { } // *********************************************************************************** // *********************************************************************************** // *********************************************************************************** - public void keyPressed(KeyEvent e) { - } - public void keyReleased(KeyEvent e) { - } - public void keyTyped(KeyEvent e) { + @Override + public void keyPressed(final 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.getSurfaceWidth()/2, glWindow.getSurfaceHeight()/2); + System.err.println("[set mouse pos post]"); + } }.start(); } } + + @Override + public void keyReleased(final 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..7ec790ee2 --- /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(final String[] args) { + final 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(final 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(final boolean v) { + if( null != canvas ) { + canvas.setVisible(v); + } + } + + @Override + public void setSize(final int width, final 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(final NativeWindowUpstream nw) { + if( null != canvas ) { + canvas.setPosition(nw.getX(), nw.getY()); + } + } + }; + } + + @Override + public void init(final 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); + + final GLDrawableFactory factory = GLDrawableFactory.getFactory(canvas.getGLProfile()); + final 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; + } + + static class GLInfo implements GLEventListener { + @Override + public void init(final GLAutoDrawable drawable) { + final GL gl = drawable.getGL(); + final String s = JoglVersion.getGLInfo(gl, null).toString(); + System.err.println(s); + } + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { + } + @Override + public void display(final GLAutoDrawable drawable) { + } + @Override + public void dispose(final GLAutoDrawable drawable) { + } + } +} diff --git a/src/newt/classes/jogamp/newt/Debug.java b/src/newt/classes/jogamp/newt/Debug.java index 3c83da4d9..a255cb755 100644 --- a/src/newt/classes/jogamp/newt/Debug.java +++ b/src/newt/classes/jogamp/newt/Debug.java @@ -1,44 +1,36 @@ -/* - * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. +/** + * Copyright 2014 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.security.AccessController; +import java.security.PrivilegedAction; + import com.jogamp.common.util.PropertyAccess; /** Helper routines for logging and debugging. */ @@ -47,40 +39,37 @@ 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) { - Package p = Package.getPackage("com.jogamp.newt"); + final Package p = Package.getPackage("com.jogamp.newt"); System.err.println("NEWT specification version " + p.getSpecificationVersion()); System.err.println("NEWT implementation version " + p.getImplementationVersion()); System.err.println("NEWT implementation vendor " + p.getImplementationVendor()); } } - 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(final 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 18418a8dc..ef8ea66b6 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,19 +50,24 @@ import com.jogamp.newt.util.EDTUtil; public class DefaultEDTUtil implements EDTUtil { public static final boolean DEBUG = Debug.debug("EDT"); + /** 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 */ ThreadGroup threadGroup; private final String name; private final Runnable dispatchMessages; - private EventDispatchThread edt = null; + private NEDT edt = null; private int start_iter=0; private static long pollPeriod = EDTUtil.defaultEDTPollPeriod; - public DefaultEDTUtil(ThreadGroup tg, String name, Runnable dispatchMessages) { + public DefaultEDTUtil(final ThreadGroup tg, final String name, final 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 .. } @@ -71,36 +77,45 @@ public class DefaultEDTUtil implements EDTUtil { } @Override - final public void setPollPeriod(long ms) { - pollPeriod = ms; + final public void setPollPeriod(final long ms) { + pollPeriod = ms; // writing to static field is intended } - + @Override - public final void reset() { - synchronized(edtLock) { - waitUntilStopped(); + public final void 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(); + } + if( !edt.isRunning() ) { + throw new RuntimeException("EDT could not be started: "+edt); } } private final void startImpl() { if(edt.isAlive()) { - throw new RuntimeException("EDT Thread.isAlive(): true, isRunning: "+edt.isRunning()+", edt: "+edt+", tasks: "+edt.tasks.size()); + 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); - edt.shouldStop = false; if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT START - edt: "+edt); - // Thread.dumpStack(); + System.err.println(Thread.currentThread()+": Default-EDT START - edt: "+edt); } edt.start(); } @@ -109,7 +124,7 @@ public class DefaultEDTUtil implements EDTUtil { public final boolean isCurrentThreadEDT() { return edt == Thread.currentThread(); // EDT == NEDT } - + @Override public final boolean isCurrentThreadNEDT() { return edt == Thread.currentThread(); // EDT == NEDT @@ -118,81 +133,109 @@ public class DefaultEDTUtil implements EDTUtil { @Override public final boolean isCurrentThreadEDTorNEDT() { return edt == Thread.currentThread(); // EDT == NEDT - } - + } + @Override public final boolean isRunning() { return edt.isRunning() ; } @Override - public final void invokeStop(Runnable task) { - invokeImpl(true, task, true); + public final boolean invokeStop(final boolean wait, final 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 boolean invokeAndWaitError(final Runnable task) { + if(DEBUG) { + System.err.println(Thread.currentThread()+": Default-EDT.invokeAndWaitError"); + Thread.dumpStack(); + } + return invokeImpl(true /* wait */, task, false /* stop */, true /* provokeError */); } @Override - public final void invoke(boolean wait, Runnable task) { - invokeImpl(wait, task, false); + public final boolean invoke(final boolean wait, final Runnable task) { + return invokeImpl(wait, task, false /* stop */, false /* provokeError */); } - private void invokeImpl(boolean wait, Runnable task, boolean stop) { - if(task == null) { - throw new RuntimeException("Null Runnable"); - } + private static Runnable nullTask = new Runnable() { + @Override + public void run() { } + }; + + private final boolean invokeImpl(boolean wait, Runnable task, final boolean stop, final 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 { - // start if should not stop && not started yet - if( !stop && !edt.isRunning() ) { - startImpl(); + 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(); + } + } + return false; + } else if( stop && null == task ) { + task = nullTask; // ensures execution triggering stop } - synchronized(edt.tasks) { - 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(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(); } - // append task .. - edt.tasks.add(rTask); - edt.tasks.notifyAll(); + } else { + wait = false; } } } if( wait ) { try { rTaskLock.wait(); // free lock, allow execution of rTask - } catch (InterruptedException ie) { + } catch (final InterruptedException ie) { throwable = ie; } if(null==throwable) { @@ -205,59 +248,66 @@ 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; } } @Override - final public void waitUntilIdle() { - final EventDispatchThread _edt; + 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(); - } catch (InterruptedException e) { + } catch (final InterruptedException e) { e.printStackTrace(); } } + return true; } } @Override - final public void waitUntilStopped() { + 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) { + } catch (final 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(final ThreadGroup tg, final String name) { super(tg, name); } final public boolean isRunning() { - return isRunning; + return isRunning && !shouldStop; } @Override @@ -267,24 +317,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 { @@ -299,7 +349,7 @@ public class DefaultEDTUtil implements EDTUtil { if(!shouldStop && tasks.size()==0) { try { tasks.wait(pollPeriod); - } catch (InterruptedException e) { + } catch (final InterruptedException e) { e.printStackTrace(); } } @@ -307,58 +357,58 @@ 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(): Caught exception occured on thread "+Thread.currentThread().getName()+": "+task.toString()); task.getThrowable().printStackTrace(); } } } while(!shouldStop) ; - } catch (Throwable t) { + } catch (final Throwable t) { // handle errors .. shouldStop = true; 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()+", exception: "+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 bca7f6e5b..84ce45238 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,44 +29,249 @@ * 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; } + + @Override + public final PointerIcon createPointerIcon(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) + throws IllegalArgumentException, IllegalStateException, IOException + { + 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 (final 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 (final 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(final PixelFormat pixelformat, final int width, final 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(final PixelFormat pixelformat, final int width, final int height, final ByteBuffer pixels, final int hotX, final int hotY) { + return 0; + } - private static Class<?> getDisplayClass(String type) - throws ClassNotFoundException + /** Executed from EDT! */ + protected void destroyPointerIconImpl(final long displayHandle, final long piHandle) { } + + /** Ensure static init has been run. */ + /* pp */static void initSingleton() { } + + private static Class<?> getDisplayClass(final String type) + throws ClassNotFoundException { final Class<?> displayClass = NewtFactory.getCustomClass(type, "DisplayDriver"); if(null==displayClass) { - throw new ClassNotFoundException("Failed to find NEWT Display Class <"+type+".DisplayDriver>"); + throw new ClassNotFoundException("Failed to find NEWT Display Class <"+type+".DisplayDriver>"); } return displayClass; } /** Make sure to reuse a Display with the same name */ - public static Display create(String type, String name, final long handle, boolean reuse) { + public static Display create(final String type, String name, final long handle, final 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()); @@ -74,29 +279,28 @@ 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); - } - if(null == display.edtUtil) { - display.setEDTUtil(null); // device's default if EDT is used, or null + display.setEDTUtil( display.edtUtil ); // device's default if EDT is used, or null + Display.addDisplay2List(display); } + if(DEBUG) { System.err.println("Display.create() NEW: "+display+" "+getThreadName()); } return display; - } catch (Exception e) { + } catch (final Exception e) { throw new RuntimeException(e); } } @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (obj == null) { return false; } @@ -116,48 +320,47 @@ 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) { + } catch (final 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 EDTUtil createEDTUtil() { final EDTUtil def; if(NewtFactory.useEDT()) { - def = 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: "+def.getClass().getName()); + System.err.println("Display.createEDTUtil("+getFQName()+"): "+def.getClass().getName()); } } else { def = null; @@ -166,26 +369,20 @@ public abstract class DisplayImpl extends Display { } @Override - public EDTUtil setEDTUtil(EDTUtil newEDTUtil) { - if(null == newEDTUtil) { - newEDTUtil = createEDTUtil(); - } else if( newEDTUtil == edtUtil ) { - if(DEBUG) { - System.err.println("Display.setEDTUtil: "+newEDTUtil+" - keep!"); + 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 null; // no change + return oldEDTUtil; } - final EDTUtil oldEDTUtil = edtUtil; if(DEBUG) { - System.err.println("Display.setEDTUtil: "+oldEDTUtil+" -> "+newEDTUtil); - } - if(null != oldEDTUtil) { - stopEDT( new Runnable() { public void run() {} } ); - // ready for restart .. - oldEDTUtil.waitUntilStopped(); - oldEDTUtil.reset(); + final String msg = ( null == usrEDTUtil ) ? "default" : "custom"; + System.err.println("Display.setEDTUtil("+msg+"): "+oldEDTUtil+" -> "+usrEDTUtil); } - edtUtil = newEDTUtil; + stopEDT( oldEDTUtil, null ); + edtUtil = ( null == usrEDTUtil ) ? createEDTUtil() : usrEDTUtil; return oldEDTUtil; } @@ -194,72 +391,143 @@ public abstract class DisplayImpl extends Display { 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(); + public void runOnEDTIfAvail(final boolean wait, final Runnable task) { + 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 (final InterruptedException e) { } + } else { + closeNativeTask.run(); + } + } + } + } + + @Override public synchronized final int addReference() { if(DEBUG) { - System.err.println("Display.addReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount+1)); + System.err.println("Display.addReference() ("+Display.getThreadName()+"): "+refCount+" -> "+(refCount+1)); } if ( 0 == refCount ) { createNative(); @@ -271,9 +539,10 @@ public abstract class DisplayImpl extends Display { } + @Override public synchronized final int removeReference() { if(DEBUG) { - System.err.println("Display.removeReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount-1)); + System.err.println("Display.removeReference() ("+Display.getThreadName()+"): "+refCount+" -> "+(refCount-1)); } refCount--; // could become < 0, in case of manual destruction without actual creation/addReference if(0>=refCount) { @@ -283,50 +552,61 @@ 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) { + public String validateDisplayName(String name, final long handle) { if(null==name && 0!=handle) { name="wrapping-"+toHexString(handle); } return ( null == name ) ? nilString : name ; } - private static String getFQName(String type, String name, int id) { + private static String getFQName(String type, String name, final int id) { if(null==type) type=nilString; if(null==name) name=nilString; - StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); sb.append(type); sb.append("_"); 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(); @@ -334,49 +614,51 @@ 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(); - } - } - protected DispatchMessagesRunnable dispatchMessagesRunnable = new DispatchMessagesRunnable(); + } }; - final void dispatchMessage(final NEWTEventTask eventTask) { - final NEWTEvent event = eventTask.get(); - try { - if(null == event) { - // Ooops ? - System.err.println("Warning: event of eventTask is NULL"); - Thread.dumpStack(); - return; - } - final Object source = event.getSource(); + 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)) { @@ -386,13 +668,28 @@ public abstract class DisplayImpl extends Display { } else { throw new RuntimeException("Event source not NEWT: "+source.getClass().getName()+", "+source); } - } catch (Throwable t) { + } catch (final Throwable t) { final RuntimeException re; if(t instanceof RuntimeException) { re = (RuntimeException) t; } else { re = new RuntimeException(t); } + throw re; + } + } + + final void dispatchMessage(final NEWTEventTask eventTask) { + final NEWTEvent event = eventTask.get(); + try { + if(null == event) { + // Ooops ? + System.err.println("Warning: event of eventTask is NULL"); + Thread.dumpStack(); + return; + } + dispatchMessage(event); + } catch (final RuntimeException re) { if( eventTask.isCallerWaiting() ) { // propagate exception to caller eventTask.setException(re); @@ -400,14 +697,15 @@ public abstract class DisplayImpl extends Display { throw re; } } - eventTask.notifyCaller(); + 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; } @@ -435,22 +733,23 @@ public abstract class DisplayImpl extends Display { dispatchMessagesNative(); } - public void enqueueEvent(boolean wait, NEWTEvent e) { - if(!isEDTRunning()) { + public void enqueueEvent(final boolean wait, final NEWTEvent e) { + 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 or NEDT -> consume right away - if(wait && edtUtil.isCurrentThreadEDTorNEDT() ) { - dispatchMessage(new NEWTEventTask(e, null)); + if(wait && _edtUtil.isCurrentThreadEDTorNEDT() ) { + dispatchMessage(e); return; } - + final Object lock = new Object(); final NEWTEventTask eTask = new NEWTEventTask(e, wait?lock:null); synchronized(lock) { @@ -462,42 +761,45 @@ public abstract class DisplayImpl extends Display { if( wait ) { try { lock.wait(); - } catch (InterruptedException ie) { + } catch (final 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(final AbstractGraphicsDevice device, final 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(final 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..72300740f --- /dev/null +++ b/src/newt/classes/jogamp/newt/MonitorDeviceImpl.java @@ -0,0 +1,146 @@ +/** + * 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(final ScreenImpl screen, final int nativeId, final DimensionImmutable sizeMM, final Rectangle viewportPU, final Rectangle viewportWU, final MonitorMode currentMode, final ArrayHashSet<MonitorMode> supportedModes) { + super(screen, nativeId, sizeMM, viewportPU, viewportWU, 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(final 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(final MonitorMode currentMode) { + this.currentMode = currentMode; + } + + /* pp */ final Rectangle getMutuableViewportPU() { return viewportPU; } + /* pp */ final Rectangle getMutuableViewportWU() { return viewportWU; } + + /* pp */ final 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..6e376ce48 --- /dev/null +++ b/src/newt/classes/jogamp/newt/MonitorModeProps.java @@ -0,0 +1,418 @@ +/** + * 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 { + /** + * {@value} Elements: width, height + * <p> + * WARNING: must be synchronized with ScreenMode.h, native implementation + * </p> + */ + public static final int NUM_RESOLUTION_PROPERTIES = 2; + + /** + * {@value} Element: bpp + * <p> + * WARNING: must be synchronized with ScreenMode.h, native implementation + * </p> + */ + public static final int NUM_SURFACE_SIZE_PROPERTIES = 1; + + /** + * {@value} Elements: refresh-rate (Hz*100), flags + * <p> + * WARNING: must be synchronized with ScreenMode.h, native implementation + * </p> + */ + public static final int NUM_SIZEANDRATE_PROPERTIES = 2; + + /** + * {@value} Elements: id, rotation + * <p> + * WARNING: must be synchronized with ScreenMode.h, native implementation + * </p> + */ + public static final int NUM_MONITOR_MODE_PROPERTIES = 2; + + /** + * {@value} Elements: + * <ul> + * <li>count</li> + * <li>{@link #NUM_RESOLUTION_PROPERTIES}</li> + * <li>{@link #NUM_SURFACE_SIZE_PROPERTIES}</li> + * <li>{@link #NUM_SIZEANDRATE_PROPERTIES}</li> + * <li>{@link #NUM_MONITOR_MODE_PROPERTIES}</li> + * <li>mode-id</li> + * </ul> + * <p> + * WARNING: must be synchronized with ScreenMode.h, native implementation + * </p> + */ + public static final int NUM_MONITOR_MODE_PROPERTIES_ALL = 8; + + /** + * {@value} Elements: count + {@link #NUM_RESOLUTION_PROPERTIES} + * <p> + * WARNING: must be synchronized with ScreenMode.h, native implementation + * </p> + */ + public static final int IDX_MONITOR_MODE_BPP = 1 // count + + MonitorModeProps.NUM_RESOLUTION_PROPERTIES + ; + /** + * {@value} Elements: + * <ul> + * <li>count</li> + * <li>{@link #NUM_RESOLUTION_PROPERTIES}</li> + * <li>{@link #NUM_SURFACE_SIZE_PROPERTIES}</li> + * <li>{@link #NUM_SIZEANDRATE_PROPERTIES}</li> + * <li>mode-id</li> + * </ul> + * <p> + * WARNING: must be synchronized with ScreenMode.h, native implementation + * </p> + */ + 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 + ; + + /** + * {@value} Elements: + * <ul> + * <li>count</li> + * <li>id</li> + * <li>ScreenSizeMM[width, height] (2 elements)</li> + * <li>Rotated Viewport pixel-units (4 elements)</li> + * <li>Rotated Viewport window-units (4 elements)</li> + * <li>CurrentMonitorModeId</li> + * <li>Rotation</li> + * <li>SupportedModeId+</li> + * </ul> + * <p> + * Viewport := [x, y, width, height] (4 elements) + * </p> + * <p> + * WARNING: must be synchronized with ScreenMode.h, native implementation + * </p> + */ + public static final int MIN_MONITOR_DEVICE_PROPERTIES = 15; + + 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(final int[] resolutionProperties, int offset) { + final Dimension resolution = new Dimension(resolutionProperties[offset++], resolutionProperties[offset++]); + return resolution; + } + + /** WARNING: must be synchronized with ScreenMode.h, native implementation */ + private static SurfaceSize streamInSurfaceSize(final DimensionImmutable resolution, final int[] sizeProperties, final int offset) { + return new SurfaceSize(resolution, sizeProperties[offset]); + } + + /** WARNING: must be synchronized with ScreenMode.h, native implementation */ + private static MonitorMode.SizeAndRRate streamInSizeAndRRate(final SurfaceSize surfaceSize, final 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(final MonitorMode.SizeAndRRate sizeAndRate, final 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(final int[] mode_idx, final Cache cache, + final 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) { + final 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 (final MonitorMode monitorMode) { + final 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(final int[] monitor_idx, final Cache cache, final ScreenImpl screen, final 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 viewportPU = new Rectangle(monitorProperties[offset++], monitorProperties[offset++], monitorProperties[offset++], monitorProperties[offset++]); + final Rectangle viewportWU = 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, viewportPU, viewportWU, currentMode, supportedModes); + if(null!=cache) { + monitorDevice = cache.monitorDevices.getOrAdd(monitorDevice); + } + if( null != monitor_idx ) { + final 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(final List<MonitorMode> monitorModes, final int modeId, final 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(final int[] monitor_idx, final Cache cache, final ScreenImpl screen, final ArrayHashSet<MonitorMode> supportedModes, final MonitorMode currentMode, final 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 viewportPU = new Rectangle(monitorProperties[offset++], monitorProperties[offset++], monitorProperties[offset++], monitorProperties[offset++]); + final Rectangle viewportWU = new Rectangle(monitorProperties[offset++], monitorProperties[offset++], monitorProperties[offset++], monitorProperties[offset++]); + MonitorDevice monitorDevice = new MonitorDeviceImpl(screen, id, sizeMM, viewportPU, viewportWU, currentMode, supportedModes); + if(null!=cache) { + monitorDevice = cache.monitorDevices.getOrAdd(monitorDevice); + } + if( null != monitor_idx ) { + final 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 (final MonitorDevice monitorDevice) { + // min 11: count, id, ScreenSizeMM[width, height], Viewport[x, y, width, height], currentMonitorModeId, rotation, supportedModeId+ + final int supportedModeCount = monitorDevice.getSupportedModes().size(); + if( 0 == supportedModeCount ) { + throw new RuntimeException("no supported modes: "+monitorDevice); + } + final 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.getViewportInWindowUnits().getX(); + data[idx++] = monitorDevice.getViewportInWindowUnits().getY(); + data[idx++] = monitorDevice.getViewportInWindowUnits().getWidth(); + data[idx++] = monitorDevice.getViewportInWindowUnits().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(final int rotation, final int[] pairs, int offset, final 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 bc0eb7531..793fbf9b0 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,29 +42,22 @@ 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)) { - // either: [jogl-all.jar, jogl-all-noawt.jar, jogl-all-mobile.jar] -> jogl-all-natives-<os.and.arch>.jar - // or: nativewindow-core.jar -> nativewindow-natives-<os.and.arch>.jar, - // newt-core.jar -> newt-natives-<os.and.arch>.jar - JNILibLoaderBase.addNativeJarLibs(new Class<?>[] { NWJNILibLoader.class, NEWTJNILibLoader.class }, "-all", new String[] { "-noawt", "-mobile", "-core" } ); - } - 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<?>[] { jogamp.nativewindow.Debug.class, jogamp.newt.Debug.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 c6c1814f6..749391f1f 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,11 +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; +import java.util.List; + import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.GraphicsConfigurationFactory; @@ -43,16 +45,19 @@ import javax.media.nativewindow.VisualIDHolder; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; +import com.jogamp.newt.MonitorDevice; + public class OffscreenWindow extends WindowImpl implements MutableSurface { 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"); @@ -68,9 +73,10 @@ public class OffscreenWindow extends WindowImpl implements MutableSurface { synchronized(OffscreenWindow.class) { setWindowHandle(nextWindowHandle++); } - visibleChanged(false, true); + visibleChanged(false, true); } + @Override protected void closeNativeImpl() { // nop } @@ -81,30 +87,38 @@ public class OffscreenWindow extends WindowImpl implements MutableSurface { surfaceHandle = 0; } - public void setSurfaceHandle(long handle) { + @Override + public void setSurfaceHandle(final long handle) { surfaceHandle = handle ; } @Override public long getSurfaceHandle() { return surfaceHandle; - } + } - protected void requestFocusImpl(boolean reparented) { + @Override + protected void requestFocusImpl(final boolean reparented) { } @Override - public void setPosition(int x, int y) { + public void setPosition(final int x, final int y) { // nop } - + @Override - public boolean setFullscreen(boolean fullscreen) { - // nop - return false; + public boolean setFullscreen(final boolean fullscreen) { + return false; // nop } - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { + @Override + public boolean setFullscreen(final List<MonitorDevice> monitors) { + return false; // nop + } + + + @Override + protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, final int flags) { sizeChanged(false, width, height, false); if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); @@ -121,21 +135,22 @@ public class OffscreenWindow extends WindowImpl implements MutableSurface { } @Override - public Point getLocationOnScreen(Point storage) { + public Point getLocationOnScreen(final Point storage) { if(null!=storage) { - storage.setX(0); - storage.setY(0); + storage.set(0, 0); return storage; } return new Point(0,0); } - - protected Point getLocationOnScreenImpl(int x, int y) { + + @Override + protected Point getLocationOnScreenImpl(final int x, final int y) { return new Point(x,y); } - - protected void updateInsetsImpl(Insets insets) { - // nop .. + + @Override + protected void updateInsetsImpl(final Insets insets) { + // 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..546a463de --- /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 (final 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(Display.DEBUG) { + System.err.println("PointerIcon.destroy: "+this+", "+display+", "+Display.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 (final 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 e2c0f746f..e73e153ad 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,84 +29,84 @@ * 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.common.util.PropertyAccess; 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 = PropertyAccess.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 virtViewportPU = new Rectangle(0, 0, 0, 0); // virtual rotated viewport in pixel units + protected Rectangle virtViewportWU = new Rectangle(0, 0, 0, 0); // virtual rotated viewport in window units 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 final ArrayList<MonitorModeListener> refMonitorModeListener = new ArrayList<MonitorModeListener>(); + private long tCreated; // creationTime - static { - AccessController.doPrivileged(new PrivilegedAction<Object>() { - public Object run() { - registerShutdownHook(); - return null; - } - }); - } - - private static Class<?> getScreenClass(String type) throws ClassNotFoundException + private static Class<?> getScreenClass(final String type) throws ClassNotFoundException { final Class<?> screenClass = NewtFactory.getCustomClass(type, "ScreenDriver"); if(null==screenClass) { - throw new ClassNotFoundException("Failed to find NEWT Screen Class <"+type+".ScreenDriver>"); + throw new ClassNotFoundException("Failed to find NEWT Screen Class <"+type+".ScreenDriver>"); } return screenClass; } - public static Screen create(Display display, int idx) { + public static Screen create(final Display display, int idx) { try { if(!usrSizeQueried) { synchronized (Screen.class) { 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 w = PropertyAccess.getIntProperty("newt.ws.swidth", true, 0); + final int h = PropertyAccess.getIntProperty("newt.ws.sheight", true, 0); if(w>0 && h>0) { usrSize = new Dimension(w, h); System.err.println("User screen size "+usrSize); @@ -115,12 +115,12 @@ public abstract class ScreenImpl extends Screen implements ScreenModeListener { } } synchronized(screenList) { - Class<?> screenClass = getScreenClass(display.getType()); + final Class<?> screenClass = getScreenClass(display.getType()); ScreenImpl screen = (ScreenImpl) screenClass.newInstance(); screen.display = (DisplayImpl) display; idx = screen.validateScreenIndex(idx); { - Screen screen0 = ScreenImpl.getLastScreenOf(display, idx, -1); + final Screen screen0 = Screen.getLastScreenOf(display, idx, -1); if(null != screen0) { if(DEBUG) { System.err.println("Screen.create() REUSE: "+screen0+" "+Display.getThreadName()); @@ -130,21 +130,30 @@ 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.instantiationFinished(); + Screen.addScreen2List(screen); if(DEBUG) { System.err.println("Screen.create() NEW: "+screen+" "+Display.getThreadName()); } return screen; - } - } catch (Exception e) { + } + } catch (final Exception e) { throw new RuntimeException(e); } } + /** + * Notifies the driver impl. that the instantiation is finished, + * ie. instance created and all fields set. + */ + protected void instantiationFinished() { + // nop + } + @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (obj == null) { return false; } @@ -161,52 +170,54 @@ 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(); - System.err.println("Screen.createNative() START ("+DisplayImpl.getThreadName()+", "+this+")"); + System.err.println("Screen.createNative() START ("+Display.getThreadName()+", "+this+")"); } 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 ("+Display.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() ("+Display.getThreadName()+"): active "+screensActive); + // Thread.dumpStack(); + } } if ( null != aScreen ) { + releaseMonitorState(); closeNativeImpl(); aScreen = null; } @@ -214,23 +225,24 @@ 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)); + System.err.println("Screen.addReference() ("+Display.getThreadName()+"): "+refCount+" -> "+(refCount+1)); // Thread.dumpStack(); } 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)); + System.err.println("Screen.removeReference() ("+Display.getThreadName()+"): "+refCount+" -> "+(refCount-1)); // Thread.dumpStack(); } refCount--; // could become < 0, in case of manual destruction without actual creation/addReference @@ -241,13 +253,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>. @@ -256,421 +269,433 @@ 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. + * Calculates the virtual rotated viewport in pixel- and window units. * <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()}, however driver impl. may chose a different methodology. + * </p> + * @param viewport storage for result in pixel units + * @param viewportInWindowUnits storage for result in window units */ - protected abstract void getVirtualScreenOriginAndSize(Point virtualOrigin, Dimension virtualSize); - + protected void calcVirtualScreenOriginAndSize(final Rectangle viewport, final Rectangle viewportInWindowUnits) { + unionOfMonitorViewports(viewport, viewportInWindowUnits); + } + + @Override public final String getFQName() { return fqname; } /** - * Updates the <b>rotated</b> virtual ScreenSize using the native impl. + * Updates the <b>rotated</b> virtual viewport, may use native impl. */ protected void updateVirtualScreenOriginAndSize() { - getVirtualScreenOriginAndSize(vOrigin, vSize); - if(DEBUG) { - System.err.println("Detected screen origin "+vOrigin+", size "+vSize); + if(null != usrSize ) { + virtViewportPU.set(0, 0, usrSize.getWidth(), usrSize.getHeight()); + virtViewportWU.set(0, 0, usrSize.getWidth(), usrSize.getHeight()); + if(DEBUG) { + System.err.println("Update user virtual screen viewport @ "+Thread.currentThread().getName()+": "+virtViewportPU); + } + } else { + calcVirtualScreenOriginAndSize(virtViewportPU, virtViewportWU); + if(DEBUG) { + System.err.println("Updated virtual screen viewport @ "+Thread.currentThread().getName()+": "+virtViewportPU+" [pixel], "+virtViewportWU+" [window]"); + } } } + @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 virtViewportPU.getX(); } + @Override + public final int getY() { return virtViewportPU.getY(); } + @Override + public final int getWidth() { return virtViewportPU.getWidth(); } + @Override + public final int getHeight() { return virtViewportPU.getHeight(); } + @Override + public final RectangleImmutable getViewport() { return virtViewportPU; } + @Override + public final RectangleImmutable getViewportInWindowUnits() { return virtViewportWU; } @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 "+virtViewportPU+" [pixels], "+virtViewportWU+" [window], "+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 + // + + /** + * 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 boolean updateNativeMonitorDeviceViewportImpl(final MonitorDevice monitor, + final Rectangle viewportPU, final Rectangle viewportWU) { + return false; } - 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> + * <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(final 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; - } - ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); - if(null == sms) { - throw new InternalError("ScreenModeStatus.getScreenModeStatus("+this.getFQName()+") == null"); + @Override + public void monitorModeChangeNotify(final MonitorEvent me) { + if(DEBUG) { + System.err.println("monitorModeChangeNotify @ "+Thread.currentThread().getName()+": "+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); - } - - sms.fireScreenModeChanged(smU, success); - if(DEBUG) { - System.err.println("Screen.setCurrentScreenMode ("+(System.nanoTime()-tStart)/1e6+"ms): X.X "+smU+", success: "+success); - } - } finally { - sms.unlock(); + for(int i=0; i<refMonitorModeListener.size(); i++) { + refMonitorModeListener.get(i).monitorModeChangeNotify(me); } - return success; } - public void screenModeChangeNotify(ScreenMode sm) { - for(int i=0; i<referencedScreenModeListener.size(); i++) { - ((ScreenModeListener)referencedScreenModeListener.get(i)).screenModeChangeNotify(sm); + 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 boolean viewportUpdated = updateNativeMonitorDeviceViewportImpl(monitor, monitor.getMutuableViewportPU(), monitor.getMutuableViewportWU()); + if( DEBUG ) { + System.err.println("Screen.updateMonitorViewport["+i+"] @ "+Thread.currentThread().getName()+": updated: "+viewportUpdated+", PU "+monitor.getViewport()+", WU "+monitor.getViewportInWindowUnits()); + } } } - public void screenModeChanged(ScreenMode sm, boolean success) { + @Override + public void monitorModeChanged(final MonitorEvent me, final 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++) { + 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(final 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(final 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(final MonitorModeProps.Cache cache, final 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(final MonitorModeProps.Cache cache, final int monitorId, final MonitorMode currentMode) { + final 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 pixel-units + props[i++] = 0; // rotated viewport y pixel-units + props[i++] = currentMode.getRotatedWidth(); // rotated viewport width pixel-units + props[i++] = currentMode.getRotatedHeight(); // rotated viewport height pixel-units + props[i++] = 0; // rotated viewport x window-units + props[i++] = 0; // rotated viewport y window-units + props[i++] = currentMode.getRotatedWidth(); // rotated viewport width window-units + props[i++] = currentMode.getRotatedHeight(); // rotated viewport height window-units + 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(final 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 ("+Display.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(final 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(final Iterator<MonitorMode> iMode=cache.monitorModes.iterator(); iMode.hasNext(); i++) { + System.err.println("All["+i+"]: "+iMode.next()); + } + i=0; + for(final Iterator<MonitorDevice> iMonitor=cache.monitorDevices.iterator(); iMonitor.hasNext(); i++) { + final MonitorDevice crt = iMonitor.next(); + System.err.println("["+i+"]: "+crt); + int j=0; + for(final 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(final 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 (final 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()); + final 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 (final 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..ae982414b --- /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 final 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(final String screenFQN, final ScreenMonitorState sms) { + screen2ScreenMonitorState.lock(); + try { + final 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(final String screenFQN) { + screen2ScreenMonitorState.lock(); + try { + unmapScreenMonitorStateUnlocked(screenFQN); + } finally { + screen2ScreenMonitorState.unlock(); + } + } + protected static void unmapScreenMonitorStateUnlocked(final String screenFQN) { + final ScreenMonitorState sms = screenFQN2ScreenMonitorState.remove(screenFQN); + if(DEBUG) { + System.err.println("ScreenMonitorState.unmap "+screenFQN+" -> "+sms); + } + } + + protected static ScreenMonitorState getScreenMonitorState(final String screenFQN) { + screen2ScreenMonitorState.lock(); + try { + return getScreenMonitorStateUnlocked(screenFQN); + } finally { + screen2ScreenMonitorState.unlock(); + } + } + protected static ScreenMonitorState getScreenMonitorStateUnlocked(final String screenFQN) { + return screenFQN2ScreenMonitorState.get(screenFQN); + } + + protected static void lockScreenMonitorState() { + screen2ScreenMonitorState.lock(); + } + + protected static void unlockScreenMonitorState() { + screen2ScreenMonitorState.unlock(); + } + + public ScreenMonitorState(final ArrayHashSet<MonitorDevice> allMonitors, + final 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(final 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(final 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(final MonitorDevice monitor) { + return allMonitors.get(monitor); + } + + protected final void validateMonitor(final MonitorDevice monitor) { + final MonitorDevice md = allMonitors.get(monitor); + if( null == md ) { + throw new InternalError("Monitor unknown: "+monitor); + } + } + + protected final void fireMonitorModeChangeNotify(final MonitorDevice monitor, final 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(final MonitorDevice monitor, final MonitorMode currentMode, final 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 c1ac87d38..b62628962 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,123 @@ 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.ScalableSurface; 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.SurfaceScaleUtils; import jogamp.nativewindow.SurfaceUpdatedHelper; +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.common.util.IntBitfield; +import com.jogamp.common.util.PropertyAccess; +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 = PropertyAccess.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(final 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 pixWidth = 128, pixHeight = 128; // client-area size w/o insets in pixel units, default: may be overwritten by user + private volatile int winWidth = 128, winHeight = 128; // client-area size w/o insets in window units, default: may be overwritten by user + protected final int[] nativePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + protected final int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + protected final int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + + private volatile int x = 64, y = 64; // client-area pos w/o insets in window units + 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,72 +171,118 @@ 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 insideSurface = 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 "+insideSurface+", 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(final int id) { + if( 0 <= id && id < movePositions.length ) { + return movePositions[id]; } + return null; } + public final String toString() { return "PState1[inside "+insideSurface+", 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 // - private static Class<?> getWindowClass(String type) + private static Class<?> getWindowClass(final String type) throws ClassNotFoundException { final Class<?> windowClass = NewtFactory.getCustomClass(type, "WindowDriver"); if(null==windowClass) { - throw new ClassNotFoundException("Failed to find NEWT Window Class <"+type+".WindowDriver>"); + throw new ClassNotFoundException("Failed to find NEWT Window Class <"+type+".WindowDriver>"); } return windowClass; } - public static WindowImpl create(NativeWindow parentWindow, long parentWindowHandle, Screen screen, CapabilitiesImmutable caps) { + public static WindowImpl create(final NativeWindow parentWindow, final long parentWindowHandle, final Screen screen, final CapabilitiesImmutable caps) { try { Class<?> windowClass; if(caps.isOnscreen()) { @@ -167,44 +290,60 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } else { windowClass = OffscreenWindow.class; } - WindowImpl window = (WindowImpl) windowClass.newInstance(); + final WindowImpl window = (WindowImpl) windowClass.newInstance(); window.parentWindow = parentWindow; 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) { + } catch (final Throwable t) { t.printStackTrace(); throw new NativeWindowException(t); } } - - public static WindowImpl create(Object[] cstrArguments, Screen screen, CapabilitiesImmutable caps) { + + public static WindowImpl create(final Object[] cstrArguments, final Screen screen, final CapabilitiesImmutable caps) { try { - Class<?> windowClass = getWindowClass(screen.getDisplay().getType()); - Class<?>[] cstrArgumentTypes = getCustomConstructorArgumentTypes(windowClass); + final Class<?> windowClass = getWindowClass(screen.getDisplay().getType()); + final Class<?>[] cstrArgumentTypes = getCustomConstructorArgumentTypes(windowClass); if(null==cstrArgumentTypes) { throw new NativeWindowException("WindowClass "+windowClass+" doesn't support custom arguments in constructor"); } - int argsChecked = verifyConstructorArgumentTypes(cstrArgumentTypes, cstrArguments); + final int argsChecked = verifyConstructorArgumentTypes(cstrArgumentTypes, cstrArguments); if ( argsChecked < cstrArguments.length ) { throw new NativeWindowException("WindowClass "+windowClass+" constructor mismatch at argument #"+argsChecked+"; Constructor: "+getTypeStrList(cstrArgumentTypes)+", arguments: "+getArgsStrList(cstrArguments)); } - WindowImpl window = (WindowImpl) ReflectionUtil.createInstance( windowClass, cstrArgumentTypes, cstrArguments ) ; + final 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) { + } catch (final Throwable t) { throw new NativeWindowException(t); } } - protected final void setGraphicsConfiguration(AbstractGraphicsConfiguration cfg) { + /** 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(final AbstractGraphicsConfiguration cfg) { config = cfg; } - + public static interface LifecycleHook { /** * Reset of internal state counter, ie totalFrames, etc. @@ -212,15 +351,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. @@ -236,7 +382,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. @@ -246,12 +392,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() { @@ -262,38 +416,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); setKeyboardVisible(keyboardVisible); - if(waitForVisible(true, false)) { + 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; } @@ -306,7 +481,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) { @@ -333,7 +508,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return true; } - private static long getNativeWindowHandle(NativeWindow nativeWindow) { + private static long getNativeWindowHandle(final NativeWindow nativeWindow) { long handle = 0; if(null!=nativeWindow) { boolean wasLocked = false; @@ -344,7 +519,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(0==handle) { throw new NativeWindowException("Parent native window handle is NULL, after succesful locking: "+nativeWindow); } - } catch (NativeWindowException nwe) { + } catch (final NativeWindowException nwe) { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.getNativeWindowHandle: not successful yet: "+nwe); } @@ -371,18 +546,20 @@ 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(final WindowClosingMode op) { synchronized (closingListenerLock) { - WindowClosingMode _op = defaultCloseOperation; + final WindowClosingMode _op = defaultCloseOperation; defaultCloseOperation = op; return _op; } @@ -394,28 +571,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) @@ -427,16 +604,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer protected abstract void closeNativeImpl(); - /** + /** * Async request which shall be performed within {@link #TIMEOUT_NATIVEWINDOW}. * <p> - * If if <code>force == false</code> the native implementation + * 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> - * + * 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); @@ -446,26 +623,27 @@ 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 - * @param height client-area size, or <=0 if unchanged + * + * @param x client-area position in window units, or <0 if unchanged + * @param y client-area position in window units, or <0 if unchanged + * @param width client-area size in window units, or <=0 if unchanged + * @param height client-area size in window units, or <=0 if unchanged * @param flags bitfield of change and status flags * * @see #sizeChanged(int,int) @@ -473,79 +651,99 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer */ protected abstract boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags); - protected int getReconfigureFlags(int changeFlags, boolean visible) { - return changeFlags |= ( ( 0 != getParentWindowHandle() ) ? FLAG_HAS_PARENT : 0 ) | - ( isUndecorated() ? FLAG_IS_UNDECORATED : 0 ) | - ( isFullscreen() ? FLAG_IS_FULLSCREEN : 0 ) | - ( isAlwaysOnTop() ? FLAG_IS_ALWAYSONTOP : 0 ) | - ( visible ? FLAG_IS_VISIBLE : 0 ) ; + /** + * Tests whether a single reconfigure flag is supported by implementation. + * <p> + * Default is all but {@link #FLAG_IS_FULLSCREEN_SPAN} + * </p> + */ + protected boolean isReconfigureFlagSupported(final int changeFlags) { + return 0 == ( changeFlags & FLAG_IS_FULLSCREEN_SPAN ); } - protected static String getReconfigureFlagsAsString(StringBuilder sb, int flags) { + + protected int getReconfigureFlags(final int changeFlags, final boolean visible) { + return changeFlags | ( ( 0 != getParentWindowHandle() ) ? FLAG_HAS_PARENT : 0 ) | + ( isUndecorated() ? FLAG_IS_UNDECORATED : 0 ) | + ( isFullscreen() ? FLAG_IS_FULLSCREEN : 0 ) | + ( isAlwaysOnTop() ? FLAG_IS_ALWAYSONTOP : 0 ) | + ( visible ? FLAG_IS_VISIBLE : 0 ) ; + } + protected static String getReconfigureFlagsAsString(StringBuilder sb, final 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) {} + + protected void setTitleImpl(final 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 in window units. + * <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> + + /** + * Triggered by user via {@link #getInsets()}.<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) */ protected abstract void updateInsetsImpl(Insets insets); - protected boolean setPointerVisibleImpl(boolean pointerVisible) { return false; } - protected boolean confinePointerImpl(boolean confine) { return false; } - protected void warpPointerImpl(int x, int y) { } - + protected boolean setPointerVisibleImpl(final boolean pointerVisible) { return false; } + protected boolean confinePointerImpl(final boolean confine) { return false; } + protected void warpPointerImpl(final int x, final int y) { } + protected void setPointerIconImpl(final PointerIconImpl pi) { } + //---------------------------------------------------------------------- // NativeSurface // @@ -553,10 +751,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer @Override public final int lockSurface() throws NativeWindowException, RuntimeException { final RecursiveLock _wlock = windowLock; - final RecursiveLock _slock = surfaceLock; _wlock.lock(); - _slock.lock(); - int res = _slock.getHoldCount() == 1 ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; // new lock ? + surfaceLockCount++; + int res = ( 1 == surfaceLockCount ) ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; // new lock ? if ( LOCK_SURFACE_NOT_READY == res ) { try { @@ -573,7 +770,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } finally { if (LOCK_SURFACE_NOT_READY >= res) { - _slock.unlock(); + surfaceLockCount--; _wlock.unlock(); } } @@ -583,12 +780,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer @Override public final void unlockSurface() { - final RecursiveLock _slock = surfaceLock; final RecursiveLock _wlock = windowLock; - _slock.validateLocked(); _wlock.validateLocked(); - if (_slock.getHoldCount() == 1) { + if ( 1 == surfaceLockCount ) { final AbstractGraphicsDevice adevice = getGraphicsConfiguration().getScreen().getDevice(); try { unlockSurfaceImpl(); @@ -596,42 +791,67 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer adevice.unlock(); } } - _slock.unlock(); + surfaceLockCount--; _wlock.unlock(); } @Override public final boolean isSurfaceLockedByOtherThread() { - return surfaceLock.isLockedByOtherThread(); + return windowLock.isLockedByOtherThread(); } @Override public final Thread getSurfaceLockOwner() { - return surfaceLock.getOwner(); + return windowLock.getOwner(); } public final RecursiveLock getLock() { return windowLock; } - + + @Override public long getSurfaceHandle() { return windowHandle; // default: return window handle } + @Override public boolean surfaceSwap() { return false; } + @Override + public final void addSurfaceUpdatedListener(final SurfaceUpdatedListener l) { + surfaceUpdatedHelper.addSurfaceUpdatedListener(l); + } + + @Override + public final void addSurfaceUpdatedListener(final int index, final SurfaceUpdatedListener l) throws IndexOutOfBoundsException { + surfaceUpdatedHelper.addSurfaceUpdatedListener(index, l); + } + + @Override + public final void removeSurfaceUpdatedListener(final SurfaceUpdatedListener l) { + surfaceUpdatedHelper.removeSurfaceUpdatedListener(l); + } + + @Override + public final void surfaceUpdated(final Object updater, final NativeSurface ns, final 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(); } //---------------------------------------------------------------------- @@ -640,14 +860,20 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // public final void destroy() - see below + @Override + public final NativeSurface getNativeSurface() { return this; } + + @Override public final NativeWindow getParent() { return parentWindow; } + @Override public final long getWindowHandle() { return windowHandle; } + @Override public Point getLocationOnScreen(Point storage) { if(isNativeValid()) { Point d; @@ -684,32 +910,47 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // Window // + @Override public final boolean isNativeValid() { return 0 != windowHandle ; } + @Override public final Screen getScreen() { return screen; } - protected final void setVisibleImpl(boolean visible, int x, int y, int width, int height) { - reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_VISIBILITY, visible)); - } - final void setVisibleActionImpl(boolean visible) { + protected void setScreen(final ScreenImpl newScreen) { // never null ! + removeScreenReference(); + screen = newScreen; + } + + @Override + public final MonitorDevice getMainMonitor() { + return screen.getMainMonitor( getBounds() ); + } + + /** + * @param visible + * @param x client-area position in window units, or <0 if unchanged + * @param y client-area position in window units, or <0 if unchanged + * @param width client-area size in window units, or <=0 if unchanged + * @param height client-area size in window units, or <=0 if unchanged + */ + protected final void setVisibleImpl(final boolean visible, final int x, final int y, final int width, final int height) { + reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_VISIBILITY, visible)); + } + final void setVisibleActionImpl(final boolean visible) { boolean nativeWindowCreated = false; boolean madeVisible = false; - + final RecursiveLock _lock = windowLock; _lock.lock(); try { - if(null!=lifecycleHook) { - lifecycleHook.resetCounter(); - } - if(!visible && null!=childWindows && childWindows.size()>0) { synchronized(childWindowsLock) { for(int i = 0; i < childWindows.size(); i++ ) { - NativeWindow nw = childWindows.get(i); + final NativeWindow nw = childWindows.get(i); if(nw instanceof WindowImpl) { ((WindowImpl)nw).setVisible(false); } @@ -722,12 +963,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; } } @@ -738,7 +981,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(isNativeValid() && visible && null!=childWindows && childWindows.size()>0) { synchronized(childWindowsLock) { for(int i = 0; i < childWindows.size(); i++ ) { - NativeWindow nw = childWindows.get(i); + final NativeWindow nw = childWindows.get(i); if(nw instanceof WindowImpl) { ((WindowImpl)nw).setVisible(true); } @@ -749,6 +992,9 @@ 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 { + if(null!=lifecycleHook) { + lifecycleHook.resetCounter(); + } _lock.unlock(); } if( nativeWindowCreated || madeVisible ) { @@ -758,54 +1004,62 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private class VisibleAction implements Runnable { boolean visible; - private VisibleAction(boolean visible) { + private VisibleAction(final boolean visible) { this.visible = visible; } + @Override public final void run() { setVisibleActionImpl(visible); } } - protected void setVisible(boolean wait, boolean visible) { + @Override + public final void setVisible(final boolean wait, final 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(wait, new VisibleAction(visible)); + runOnEDTIfAvail(wait, new VisibleAction(visible)); } - - public void setVisible(boolean visible) { + + @Override + public final void setVisible(final boolean visible) { setVisible(true, visible); } - + private class SetSizeAction implements Runnable { int width, height; + boolean force; - private SetSizeAction(int w, int h) { + private SetSizeAction(final int w, final int h, final boolean disregardFS) { this.width = w; this.height = h; + this.force = disregardFS; } + @Override public final void run() { final RecursiveLock _lock = windowLock; _lock.lock(); try { - if ( !isFullscreen() && ( getWidth() != width || getHeight() != height ) ) { + if ( force || ( !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); + System.err.println("Window setSize: START force "+force+", "+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); } @@ -823,14 +1077,25 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } - public void setSize(int width, int height) { - runOnEDTIfAvail(true, new SetSizeAction(width, height)); - } - public void setTopLevelSize(int width, int height) { + private void setSize(final int width, final int height, final boolean force) { + runOnEDTIfAvail(true, new SetSizeAction(width, height, force)); + } + @Override + public final void setSize(final int width, final int height) { + runOnEDTIfAvail(true, new SetSizeAction(width, height, false)); + } + @Override + public final void setSurfaceSize(final int pixelWidth, final int pixelHeight) { + // FIXME HiDPI: Shortcut, may need to adjust if we change scaling methodology + setSize(pixelWidth / getPixelScaleX(), pixelHeight / getPixelScaleY()); + } + @Override + public final void setTopLevelSize(final int width, final int height) { setSize(width - getInsets().getTotalWidth(), height - getInsets().getTotalHeight()); } - private class DestroyAction implements Runnable { + private final Runnable destroyAction = new Runnable() { + @Override public final void run() { boolean animatorPaused = false; if(null!=lifecycleHook) { @@ -839,23 +1104,27 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(null!=lifecycleHook) { lifecycleHook.destroyActionPreLock(); } + RuntimeException lifecycleCaughtInLock = null; 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) { // avoid ConcurrentModificationException: parent -> child -> parent.removeChild(this) @SuppressWarnings("unchecked") - ArrayList<NativeWindow> clonedChildWindows = (ArrayList<NativeWindow>) childWindows.clone(); + final ArrayList<NativeWindow> clonedChildWindows = (ArrayList<NativeWindow>) childWindows.clone(); while( clonedChildWindows.size() > 0 ) { - NativeWindow nw = clonedChildWindows.remove(0); + final 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(); } @@ -865,19 +1134,26 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(null!=lifecycleHook) { // send synced destroy notification for proper cleanup, eg GLWindow/OpenGL - lifecycleHook.destroyActionInLock(); + try { + lifecycleHook.destroyActionInLock(); + } catch (final RuntimeException re) { + lifecycleCaughtInLock = re; + } } - 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(); + final Display dpy = screen.getDisplay(); + if(null != dpy) { + dpy.validateEDTStopped(); } // send synced destroyed notification @@ -885,54 +1161,73 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(DEBUG_IMPLEMENTATION) { System.err.println("Window.destroy() END "+getThreadName()/*+", "+WindowImpl.this*/); + if( null != lifecycleCaughtInLock ) { + System.err.println("Window.destroy() caught: "+lifecycleCaughtInLock.getMessage()); + lifecycleCaughtInLock.printStackTrace(); + } + } + if( null != lifecycleCaughtInLock ) { + throw lifecycleCaughtInLock; } } finally { // update states before release window lock setWindowHandle(0); visible = false; fullscreen = false; + fullscreenMonitors = null; + fullscreenUseMainMonitor = true; hasFocus = false; parentWindowHandle = 0; + hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + nativePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + nativePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; _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(final 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) { + protected static boolean isOffscreenInstance(final NativeWindow cWin, final NativeWindow pWin) { boolean ofs = false; - final AbstractGraphicsConfiguration cWinCfg = cWin.getGraphicsConfiguration(); + final AbstractGraphicsConfiguration cWinCfg = cWin.getGraphicsConfiguration(); if( null != cWinCfg ) { ofs = !cWinCfg.getChosenCapabilities().isOnscreen(); } @@ -944,15 +1239,21 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } 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(final NativeWindow newParentWindow, final int topLevelX, final 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 } @@ -960,12 +1261,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return operation; } - private void setScreen(ScreenImpl newScreen) { // never null ! - 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(); @@ -975,24 +1280,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; - + 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) { @@ -1001,15 +1317,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; @@ -1031,7 +1353,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()) { @@ -1042,25 +1364,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 { @@ -1072,12 +1393,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 @@ -1086,8 +1414,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; @@ -1103,15 +1432,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 @@ -1123,23 +1455,17 @@ 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) { } + try { Thread.sleep(100); } catch (final InterruptedException e) { } display.dispatchMessagesNative(); // status up2date } @@ -1163,45 +1489,74 @@ 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 { + if(null!=lifecycleHook) { + lifecycleHook.resetCounter(); + } _lock.unlock(); } if(wasVisible) { @@ -1215,53 +1570,55 @@ 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 { + private final Runnable reparentActionRecreate = new Runnable() { + @Override public final void run() { 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 { _lock.unlock(); } - } - } - private final ReparentActionRecreate reparentActionRecreate = new ReparentActionRecreate(); + } }; - public final ReparentOperation reparentWindow(NativeWindow newParent) { - return reparentWindow(newParent, false); - } - - public ReparentOperation reparentWindow(NativeWindow newParent, boolean forceDestroyCreate) { - final ReparentAction reparentAction = new ReparentAction(newParent, forceDestroyCreate); + @Override + public final ReparentOperation reparentWindow(final NativeWindow newParent, final int x, final int y, final int hints) { + final ReparentAction reparentAction = new ReparentAction(newParent, x, y, hints); runOnEDTIfAvail(true, reparentAction); return reparentAction.getOp(); } - public CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) { - CapabilitiesChooser old = this.capabilitiesChooser; + @Override + public final CapabilitiesChooser setCapabilitiesChooser(final CapabilitiesChooser chooser) { + final CapabilitiesChooser old = this.capabilitiesChooser; this.capabilitiesChooser = chooser; return old; } + @Override public final CapabilitiesImmutable getChosenCapabilities() { return getGraphicsConfiguration().getChosenCapabilities(); } + @Override public final CapabilitiesImmutable getRequestedCapabilities() { return capsRequested; } @@ -1269,10 +1626,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private class DecorationAction implements Runnable { boolean undecorated; - private DecorationAction(boolean undecorated) { + private DecorationAction(final boolean undecorated) { this.undecorated = undecorated; } + @Override public final void run() { final RecursiveLock _lock = windowLock; _lock.lock(); @@ -1288,7 +1646,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer final int width = getWidth(); final int height = getHeight(); - DisplayImpl display = (DisplayImpl) screen.getDisplay(); + final DisplayImpl display = (DisplayImpl) screen.getDisplay(); display.dispatchMessagesNative(); // status up2date reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_DECORATION, isVisible())); display.dispatchMessagesNative(); // status up2date @@ -1301,10 +1659,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } - public void setUndecorated(boolean value) { + @Override + public final void setUndecorated(final boolean value) { runOnEDTIfAvail(true, new DecorationAction(value)); } + @Override public final boolean isUndecorated() { return 0 != parentWindowHandle || undecorated || fullscreen ; } @@ -1312,10 +1672,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private class AlwaysOnTopAction implements Runnable { boolean alwaysOnTop; - private AlwaysOnTopAction(boolean alwaysOnTop) { + private AlwaysOnTopAction(final boolean alwaysOnTop) { this.alwaysOnTop = alwaysOnTop; } + @Override public final void run() { final RecursiveLock _lock = windowLock; _lock.lock(); @@ -1323,7 +1684,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer 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(); @@ -1331,7 +1692,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer final int width = getWidth(); final int height = getHeight(); - DisplayImpl display = (DisplayImpl) screen.getDisplay(); + final DisplayImpl display = (DisplayImpl) screen.getDisplay(); display.dispatchMessagesNative(); // status up2date reconfigureWindowImpl(x, y, width, height, getReconfigureFlags(FLAG_CHANGE_ALWAYSONTOP, isVisible())); display.dispatchMessagesNative(); // status up2date @@ -1344,18 +1705,26 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } - public final void setAlwaysOnTop(boolean value) { - runOnEDTIfAvail(true, new AlwaysOnTopAction(value)); + @Override + public final void setAlwaysOnTop(final boolean 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 = ""; } @@ -1365,31 +1734,128 @@ 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; } } } - public boolean isPointerConfined() { + private boolean setPointerVisibleIntern(final boolean pointerVisible) { + final 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 (final 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 (final Exception e) { + e.printStackTrace(); + } + } + return false; + } + + @Override + public final boolean isPointerConfined() { return pointerConfined; } - - public void confinePointer(boolean confine) { + @Override + public final void confinePointer(final boolean confine) { if(this.pointerConfined != confine) { boolean setVal = 0 == getWindowHandle(); if(!setVal) { if(confine) { requestFocus(); - warpPointer(getWidth()/2, getHeight()/2); + warpPointer(getSurfaceWidth()/2, getSurfaceHeight()/2); } setVal = confinePointerImpl(confine); if(confine) { @@ -1397,21 +1863,23 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // this allows user listener to sync previous position value to the new centered position try { Thread.sleep(3 * screen.getDisplay().getEDTUtil().getPollPeriod()); - } catch (InterruptedException e) { } + } catch (final InterruptedException e) { } } } if(setVal) { - this.pointerConfined = confine; + this.pointerConfined = confine; } - } + } } - - public void warpPointer(int x, int y) { + + @Override + public final void warpPointer(final int x, final int y) { if(0 != getWindowHandle()) { warpPointerImpl(x, y); } } - + + @Override public final InsetsImmutable getInsets() { if(isUndecorated()) { return Insets.getZero(); @@ -1419,27 +1887,109 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer updateInsetsImpl(insets); return insets; } - + + @Override + public final int getX() { + return x; + } + + @Override + public final int getY() { + return y; + } + + @Override public final int getWidth() { - return width; + return winWidth; } + @Override public final int getHeight() { - return height; + return winHeight; } - public final int getX() { - return x; + @Override + public final Rectangle getBounds() { + return new Rectangle(x, y, winWidth, winHeight); } - public final int getY() { - return y; + @Override + public final int getSurfaceWidth() { + return pixWidth; + } + + @Override + public final int getSurfaceHeight() { + return pixHeight; + } + + @Override + public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) { + pixelUnitsAndResult[0] /= getPixelScaleX(); + pixelUnitsAndResult[1] /= getPixelScaleY(); + return pixelUnitsAndResult; + } + + @Override + public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) { + windowUnitsAndResult[0] *= getPixelScaleX(); + windowUnitsAndResult[1] *= getPixelScaleY(); + return windowUnitsAndResult; + } + + protected final Point convertToWindowUnits(final Point pixelUnitsAndResult) { + return pixelUnitsAndResult.scaleInv(getPixelScaleX(), getPixelScaleY()); + } + + protected final Point convertToPixelUnits(final Point windowUnitsAndResult) { + return windowUnitsAndResult.scale(getPixelScaleX(), getPixelScaleY()); + } + + /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */ + protected final int getPixelScaleX() { + return hasPixelScale[0]; + } + + /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */ + protected final int getPixelScaleY() { + return hasPixelScale[1]; + } + + @Override + public void setSurfaceScale(final int[] pixelScale) { + SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG_IMPLEMENTATION ? getClass().getSimpleName() : null); + } + + @Override + public final int[] getRequestedSurfaceScale(final int[] result) { + System.arraycopy(reqPixelScale, 0, result, 0, 2); + return result; + } + + @Override + public final int[] getCurrentSurfaceScale(final int[] result) { + System.arraycopy(hasPixelScale, 0, result, 0, 2); + return result; + } + + @Override + public final int[] getNativeSurfaceScale(final int[] result) { + System.arraycopy(nativePixelScale, 0, result, 0, 2); + return result; + } + + @Override + public final float[] getPixelsPerMM(final float[] ppmmStore) { + getMainMonitor().getPixelsPerMM(ppmmStore); + ppmmStore[0] *= (float)hasPixelScale[0] / (float)nativePixelScale[0]; + ppmmStore[1] *= (float)hasPixelScale[1] / (float)nativePixelScale[1]; + return ppmmStore; } protected final boolean autoPosition() { return autoPosition; } - - /** 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) { + + /** Sets the position fields {@link #x} and {@link #y} in window units to the given values and {@link #autoPosition} to false. */ + protected final void definePosition(final int x, final int y) { if(DEBUG_IMPLEMENTATION) { System.err.println("definePosition: "+this.x+"/"+this.y+" -> "+x+"/"+y); // Thread.dumpStack(); @@ -1448,19 +1998,28 @@ 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. */ - protected final void defineSize(int width, int height) { + /** + * Sets the size fields {@link #winWidth} and {@link #winHeight} in window units to the given values + * and {@link #pixWidth} and {@link #pixHeight} in pixel units according to {@link #convertToPixelUnits(int[])}. + */ + protected final void defineSize(final int winWidth, final int winHeight) { + final int pixWidth = winWidth * getPixelScaleX(); // FIXME HiDPI: Shortcut, may need to adjust if we change scaling methodology + final int pixHeight = winHeight * getPixelScaleY(); if(DEBUG_IMPLEMENTATION) { - System.err.println("defineSize: "+this.width+"x"+this.height+" -> "+width+"x"+height); + System.err.println("defineSize: win["+this.winWidth+"x"+this.winHeight+" -> "+winWidth+"x"+winHeight+ + "], pixel["+this.pixWidth+"x"+this.pixHeight+" -> "+pixWidth+"x"+pixHeight+"]"); // Thread.dumpStack(); } - this.width = width; this.height = height; + this.winWidth = winWidth; this.winHeight = winHeight; + this.pixWidth = pixWidth; this.pixHeight = pixHeight; } - + + @Override public final boolean isVisible() { return visible; } + @Override public final boolean isFullscreen() { return fullscreen; } @@ -1469,6 +2028,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. @@ -1477,66 +2045,49 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return false; } - public LifecycleHook getLifecycleHook() { + public final LifecycleHook getLifecycleHook() { return lifecycleHook; } - public LifecycleHook setLifecycleHook(LifecycleHook hook) { - LifecycleHook old = lifecycleHook; + public final LifecycleHook setLifecycleHook(final LifecycleHook hook) { + final 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(final Runnable r) { + windowDestroyNotifyAction = r; } - + protected final long getParentWindowHandle() { return isFullscreen() ? 0 : parentWindowHandle; } @Override - public String toString() { - StringBuilder sb = new StringBuilder(); + public final String toString() { + final StringBuilder sb = new StringBuilder(); sb.append(getClass().getName()+"[Config "+config+ - "\n, "+screen+ - "\n, ParentWindow "+parentWindow+ - "\n, ParentWindowHandle "+toHexString(parentWindowHandle)+" ("+(0!=getParentWindowHandle())+")"+ - "\n, WindowHandle "+toHexString(getWindowHandle())+ - "\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, ChildWindows "+childWindows.size()); + ",\n "+screen+ + ",\n ParentWindow "+parentWindow+ + ",\n ParentWindowHandle "+toHexString(parentWindowHandle)+" ("+(0!=getParentWindowHandle())+")"+ + ",\n WindowHandle "+toHexString(getWindowHandle())+ + ",\n SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+windowLock.isLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+ + ",\n window["+getX()+"/"+getY()+" (auto "+autoPosition()+") "+getWidth()+"x"+getHeight()+"], pixel["+getSurfaceWidth()+"x"+getSurfaceHeight()+ + "],\n Visible "+isVisible()+", focus "+hasFocus()+ + ",\n Undecorated "+undecorated+" ("+isUndecorated()+")"+ + ",\n AlwaysOnTop "+alwaysOnTop+", Fullscreen "+fullscreen+ + ",\n WrappedSurface "+getWrappedSurface()+ + ",\n ChildWindows "+childWindows.size()); sb.append(", SurfaceUpdatedListeners num "+surfaceUpdatedHelper.size()+" ["); for (int i = 0; i < surfaceUpdatedHelper.size(); i++ ) { @@ -1550,33 +2101,33 @@ 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(); } - protected final void setWindowHandle(long handle) { + protected final void setWindowHandle(final long handle) { windowHandle = handle; } - public void runOnEDTIfAvail(boolean wait, final Runnable task) { - if(windowLock.isOwner(Thread.currentThread())) { + @Override + public final void runOnEDTIfAvail(final boolean wait, final Runnable task) { + if( windowLock.isOwner( Thread.currentThread() ) ) { task.run(); } else { - Screen scrn = getScreen(); - if(null==scrn) { - throw new RuntimeException("Null screen of inner class: "+this); - } - DisplayImpl d = (DisplayImpl) scrn.getDisplay(); - d.runOnEDTIfAvail(wait, task); + ( (DisplayImpl) screen.getDisplay() ).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)); @@ -1585,6 +2136,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)); @@ -1593,40 +2145,44 @@ 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(final boolean wait) { requestFocus(wait /* wait */, false /* skipFocusAction */, brokenFocusChange /* force */); } - - private void requestFocus(boolean wait, boolean skipFocusAction, boolean force) { + + private void requestFocus(final boolean wait, final boolean skipFocusAction, final boolean force) { if( isNativeValid() && ( force || !hasFocus() ) && ( skipFocusAction || !focusAction() ) ) { runOnEDTIfAvail(wait, force ? requestFocusActionForced : requestFocusAction); } } - + /** Internally forcing request focus on current thread */ - private void requestFocusInt(boolean skipFocusAction) { + private void requestFocusInt(final 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(final FocusRunnable focusAction) { this.focusAction = focusAction; } - + private boolean focusAction() { if(DEBUG_IMPLEMENTATION) { System.err.println("Window.focusAction() START - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle())); @@ -1642,23 +2198,25 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } return res; } - - protected void setBrokenFocusChange(boolean v) { + + protected final void setBrokenFocusChange(final boolean v) { brokenFocusChange = v; } - - public void setKeyboardFocusHandler(KeyListener l) { + + @Override + public final void setKeyboardFocusHandler(final KeyListener l) { keyboardFocusHandler = l; } - + private class SetPositionAction implements Runnable { int x, y; - private SetPositionAction(int x, int y) { + private SetPositionAction(final int x, final int y) { this.x = x; this.y = y; } + @Override public final void run() { final RecursiveLock _lock = windowLock; _lock.lock(); @@ -1666,10 +2224,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(DEBUG_IMPLEMENTATION) { System.err.println("Window setPosition: "+getX()+"/"+getY()+" -> "+x+"/"+y+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)); } - if ( !isFullscreen() && ( getX() != x || getY() != y ) ) { + // Let the window be positioned if !fullscreen and position changed or being a child window. + if ( !isFullscreen() && ( getX() != x || getY() != y || null != getParent()) ) { 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(..) } @@ -1680,58 +2243,96 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } - public void setPosition(int x, int y) { + @Override + public void setPosition(final int x, final int y) { autoPosition = false; runOnEDTIfAvail(true, new SetPositionAction(x, y)); } - - public void setTopLevelPosition(int x, int y) { + + @Override + public final void setTopLevelPosition(final int x, final 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(final 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() { 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.getViewportInWindowUnits(); // window units + final RectangleImmutable viewport; // window units + 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(); + } + } + { + final Rectangle viewportInWindowUnits = new Rectangle(); + MonitorDevice.unionOfViewports(null, viewportInWindowUnits, fullscreenMonitors); + viewport = viewportInWindowUnits; + } + 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(); @@ -1741,14 +2342,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+" [wu], monitorsViewport "+viewport+" [wu]"+ + ", 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 (final 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 ) { @@ -1760,28 +2377,51 @@ 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 (final 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 { + blockInsetsChange = false; _lock.unlock(); } sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener @@ -1789,86 +2429,170 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } private final FullScreenAction fullScreenAction = new FullScreenAction(); - public boolean setFullscreen(boolean fullscreen) { + @Override + public boolean setFullscreen(final boolean fullscreen) { + return setFullscreenImpl(fullscreen, true, null); + } + + @Override + public boolean setFullscreen(final List<MonitorDevice> monitors) { + return setFullscreenImpl(true, false, monitors); + } + + private boolean setFullscreenImpl(final boolean fullscreen, final boolean useMainMonitor, final 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 { - boolean animatorPaused = false; + /** Notify WindowDriver about the finished monitor mode change. */ + protected void monitorModeChanged(final MonitorEvent me, final boolean success) { + } - public void screenModeChangeNotify(ScreenMode sm) { + private class MonitorModeListenerImpl implements MonitorModeListener { + boolean animatorPaused = false; + boolean hidden = false; + boolean hadFocus = false; + boolean fullscreenPaused = false; + List<MonitorDevice> _fullscreenMonitors = null; + boolean _fullscreenUseMainMonitor = true; + + @Override + public void monitorModeChangeNotify(final MonitorEvent me) { + hadFocus = hasFocus(); + final boolean isOSX = NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(true); + final boolean quirkFSPause = fullscreen && isReconfigureFlagSupported(FLAG_IS_FULLSCREEN_SPAN); + final boolean quirkHide = !quirkFSPause && !fullscreen && isVisible() && isOSX; if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.screenModeChangeNotify: "+sm); + System.err.println("Window.monitorModeChangeNotify: hadFocus "+hadFocus+", qFSPause "+quirkFSPause+", qHide "+quirkHide+", "+me+" @ "+Thread.currentThread().getName()); } if(null!=lifecycleHook) { animatorPaused = lifecycleHook.pauseRenderingAction(); } + if( quirkFSPause ) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.monitorModeChangeNotify: FS Pause"); + } + fullscreenPaused = true; + _fullscreenMonitors = fullscreenMonitors; + _fullscreenUseMainMonitor = fullscreenUseMainMonitor; + setFullscreenImpl(false, true, null); + } + if( quirkHide ) { + // hiding & showing the window around mode-change solves issues w/ OSX, + // where the content would be black until a resize. + hidden = true; + WindowImpl.this.setVisible(false); + } } - public void screenModeChanged(ScreenMode sm, boolean success) { - if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.screenModeChanged: "+sm+", success: "+success); + @Override + public void monitorModeChanged(final MonitorEvent me, final boolean success) { + if(!animatorPaused && success && null!=lifecycleHook) { + // Didn't pass above notify method. probably detected screen change after it happened. + animatorPaused = lifecycleHook.pauseRenderingAction(); } - - if(success) { - if(!animatorPaused && null!=lifecycleHook) { - // Didn't pass above notify method. probably detected screen change after it happened. - animatorPaused = lifecycleHook.pauseRenderingAction(); + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.monitorModeChanged.0: success: "+success+", hadFocus "+hadFocus+", animPaused "+animatorPaused+ + ", hidden "+hidden+", FS "+fullscreen+", FS-paused "+fullscreenPaused+ + " @ "+Thread.currentThread().getName()); + System.err.println("Window.monitorModeChanged.0: "+getScreen()); + System.err.println("Window.monitorModeChanged.0: "+me); + } + WindowImpl.this.monitorModeChanged(me, success); + + if( success && !fullscreen && !fullscreenPaused ) { + // Simply move/resize window to fit in virtual screen if required + final RectangleImmutable viewport = screen.getViewportInWindowUnits(); + 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.1: Non-FS - 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(), true /* force */); + } } - DimensionImmutable screenSize = sm.getMonitorMode().getSurfaceSize().getResolution(); - if ( getHeight() > screenSize.getHeight() || - getWidth() > screenSize.getWidth() ) { - setSize(screenSize.getWidth(), screenSize.getHeight()); + } else if( fullscreenPaused ) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.monitorModeChanged.2: FS Restore"); + } + setFullscreenImpl(true, _fullscreenUseMainMonitor, _fullscreenMonitors); + fullscreenPaused = false; + _fullscreenMonitors = null; + _fullscreenUseMainMonitor = true; + } else if( success && fullscreen && null != fullscreenMonitors ) { + // If changed monitor is part of this fullscreen mode, reset size! (Bug 771) + final MonitorDevice md = me.getMonitor(); + if( fullscreenMonitors.contains(md) ) { + final Rectangle viewportInWindowUnits = new Rectangle(); + MonitorDevice.unionOfViewports(null, viewportInWindowUnits, fullscreenMonitors); + if(DEBUG_IMPLEMENTATION) { + final RectangleImmutable winBounds = WindowImpl.this.getBounds(); + System.err.println("Window.monitorModeChanged.3: FS Monitor Match: Fit window "+winBounds+" into new viewport union "+viewportInWindowUnits+" [window], provoked by "+md); + } + definePosition(viewportInWindowUnits.getX(), viewportInWindowUnits.getY()); // set pos for setVisible(..) or createNative(..) - reduce EDT roundtrip + setSize(viewportInWindowUnits.getWidth(), viewportInWindowUnits.getHeight(), true /* force */); } } - + if( hidden ) { + WindowImpl.this.setVisible(true); + hidden = false; + } + sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener if(animatorPaused) { lifecycleHook.resumeRenderingAction(); } - sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener + if( hadFocus ) { + requestFocus(true); + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.monitorModeChanged.X: @ "+Thread.currentThread().getName()+", this: "+WindowImpl.this); + } } } - private final ScreenModeListenerImpl screenModeListenerImpl = new ScreenModeListenerImpl(); - + private final MonitorModeListenerImpl monitorModeListenerImpl = new MonitorModeListenerImpl(); //---------------------------------------------------------------------- // Child Window Management - // + // - public final boolean removeChild(NativeWindow win) { + @Override + public final boolean removeChild(final NativeWindow win) { synchronized(childWindowsLock) { return childWindows.remove(win); } } - public final boolean addChild(NativeWindow win) { + @Override + public final boolean addChild(final NativeWindow win) { if (win == null) { return false; } @@ -1880,7 +2604,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer //---------------------------------------------------------------------- // Generic Event Support // - private void doEvent(boolean enqueue, boolean wait, com.jogamp.newt.event.NEWTEvent event) { + private void doEvent(final boolean enqueue, boolean wait, final com.jogamp.newt.event.NEWTEvent event) { boolean done = false; if(!enqueue) { @@ -1893,13 +2617,15 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } - public void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { + @Override + public final void enqueueEvent(final boolean wait, final 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(final NEWTEvent e) { switch(e.getEventType()) { // special repaint treatment case WindowEvent.EVENT_WINDOW_REPAINT: @@ -1910,9 +2636,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer 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; @@ -1926,7 +2652,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer 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 @@ -1940,7 +2666,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); } @@ -1948,189 +2674,736 @@ 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 final 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)0 }, button, + 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[], 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, + final 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]; + } + } + final short button = 0 < pCount ? (short) ( pIDs[0] + 1 ) : (short)0; + doPointerEvent(enqueue, wait, pTypes, eventType, modifiers, actionIdx, pIDs, button, + 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. + * @param button Corresponding mouse-button, a button of 0 denotes no 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 short buttonIn, 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 button; + { + // validate button + if( 0 <= buttonIn && buttonIn <= com.jogamp.newt.event.MouseEvent.BUTTON_COUNT ) { // we allow button==0 for no button, i.e. mouse-ptr move + button = buttonIn; + } 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 insideSurface = x >= 0 && y >= 0 && x < getSurfaceWidth() && y < getSurfaceHeight(); + final Point movePositionP0 = pState1.getMovePosition(pID[0]); + 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.insideSurface = true; + pState1.exitSent = false; + } else { + pState1.insideSurface = 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), getSurfaceWidth()-1); + y = Math.min(Math.max(y, 0), getSurfaceHeight()-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.insideSurface != insideSurface ) { + // ENTER/EXIT! + pState1.insideSurface = insideSurface; + if( insideSurface ) { + pState1.exitSent = false; + } + pState1.clearButton(); + } + } + + // + // Drop exterior events if not dragging pointer and not EXIT event + // Safeguard for non compliant implementations! + // + if( !pState1.dragging && !insideSurface && 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 "+insideSurface+", "+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(final int lower, final int edge, final 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[], 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) { + if(DEBUG_MOUSE_EVENT) { + System.err.println("consumePointerEvent.in: "+pe+", "+pState0+", pos "+pe.getX()+"/"+pe.getY()+", win["+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+ + "], pixel["+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"); + } + + // + // - 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 insideSurface; + 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 + // final int pe_x = Math.min(Math.max(pe.getX(), 0), getSurfaceWidth()-1); + // final int pe_y = Math.min(Math.max(pe.getY(), 0), getSurfaceHeight()-1); + pState0.clearButton(); + if( eventType == MouseEvent.EVENT_MOUSE_ENTERED ) { + insideSurface = true; + pState0.insideSurface = true; + pState0.exitSent = false; + pState0.dragging = false; + } else { + insideSurface = false; + pState0.insideSurface = 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: + final int pe_x = pe.getX(); + final int pe_y = pe.getY(); + insideSurface = pe_x >= 0 && pe_y >= 0 && pe_x < getSurfaceWidth() && pe_y < getSurfaceHeight(); + if( pe.getPointerType(0) == PointerType.Mouse ) { + if( !pState0.insideSurface && insideSurface ) { + // ENTER .. use clipped coordinates + eEntered = new MouseEvent(MouseEvent.EVENT_MOUSE_ENTERED, pe.getSource(), pe.getWhen(), pe.getModifiers(), + Math.min(Math.max(pe_x, 0), getSurfaceWidth()-1), + Math.min(Math.max(pe_y, 0), getSurfaceHeight()-1), + (short)0, (short)0, pe.getRotation(), pe.getRotationScale()); + pState0.exitSent = false; + } else if( !insideSurface && eExitAllowed ) { + // EXIT .. use clipped coordinates + eExited = new MouseEvent(MouseEvent.EVENT_MOUSE_EXITED, pe.getSource(), pe.getWhen(), pe.getModifiers(), + Math.min(Math.max(pe_x, 0), getSurfaceWidth()-1), + Math.min(Math.max(pe_y, 0), getSurfaceHeight()-1), + (short)0, (short)0, pe.getRotation(), pe.getRotationScale()); + pState0.exitSent = true; + } + } + if( pState0.insideSurface != insideSurface || null != eEntered || null != eExited) { + pState0.clearButton(); + } + pState0.insideSurface = insideSurface; + } + if( null != eEntered ) { + if(DEBUG_MOUSE_EVENT) { + System.err.println("consumePointerEvent.send.0: "+eEntered+", "+pState0); + } + dispatchMouseEvent(eEntered); + } else if( DEBUG_MOUSE_EVENT && !insideSurface ) { + 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(final MouseListener l) { addMouseListener(-1, l); } - public void addMouseListener(int index, MouseListener l) { + @Override + public final void addMouseListener(int index, final MouseListener l) { if(l == null) { return; } @SuppressWarnings("unchecked") + final 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(final MouseListener l) { if (l == null) { return; } @SuppressWarnings("unchecked") + final ArrayList<MouseListener> clonedListeners = (ArrayList<MouseListener>) mouseListeners.clone(); clonedListeners.remove(l); mouseListeners = clonedListeners; } - public MouseListener getMouseListener(int index) { + @Override + public final MouseListener getMouseListener(int index) { @SuppressWarnings("unchecked") + final 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(final boolean enable) { + defaultGestureHandlerEnabled = enable; + } + @Override + public final boolean areDefaultGesturesEnabled() { + return defaultGestureHandlerEnabled; + } + + @Override + public final void addGestureHandler(final GestureHandler gh) { + addGestureHandler(-1, gh); + } + @Override + public final void addGestureHandler(int index, final GestureHandler gh) { + if(gh == null) { + return; + } + @SuppressWarnings("unchecked") + final + ArrayList<GestureHandler> cloned = (ArrayList<GestureHandler>) pointerGestureHandler.clone(); + if(0>index) { + index = cloned.size(); + } + cloned.add(index, gh); + pointerGestureHandler = cloned; + } + @Override + public final void removeGestureHandler(final GestureHandler gh) { + if (gh == null) { + return; + } + @SuppressWarnings("unchecked") + final + ArrayList<GestureHandler> cloned = (ArrayList<GestureHandler>) pointerGestureHandler.clone(); + cloned.remove(gh); + pointerGestureHandler = cloned; + } + @Override + public final void addGestureListener(final GestureHandler.GestureListener gl) { + addGestureListener(-1, gl); + } + @Override + public final void addGestureListener(int index, final GestureHandler.GestureListener gl) { + if(gl == null) { + return; + } + @SuppressWarnings("unchecked") + final + ArrayList<GestureHandler.GestureListener> cloned = (ArrayList<GestureHandler.GestureListener>) gestureListeners.clone(); + if(0>index) { + index = cloned.size(); } - boolean consumed = false; - for(int i = 0; !consumed && i < mouseListeners.size(); i++ ) { - MouseListener l = mouseListeners.get(i); + cloned.add(index, gl); + gestureListeners = cloned; + } + @Override + public final void removeGestureListener(final GestureHandler.GestureListener gl) { + if (gl == null) { + return; + } + @SuppressWarnings("unchecked") + final + ArrayList<GestureHandler.GestureListener> cloned = (ArrayList<GestureHandler.GestureListener>) gestureListeners.clone(); + cloned.remove(gl); + gestureListeners= cloned; + } + + private final void dispatchMouseEvent(final MouseEvent e) { + for(int i = 0; !e.isConsumed() && i < mouseListeners.size(); i++ ) { + final MouseListener l = mouseListeners.get(i); switch(e.getEventType()) { case MouseEvent.EVENT_MOUSE_CLICKED: l.mouseClicked(e); @@ -2159,46 +3432,83 @@ 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(final short keyCode, final 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(final short keyCode) { + final int v = 0xFFFF & keyCode; + if( v <= keyTrackingRange ) { + return keyPressedState.get(v); + } + return false; } - public void addKeyListener(KeyListener l) { - addKeyListener(-1, l); + public void sendKeyEvent(final short eventType, final int modifiers, final short keyCode, final short keySym, final 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(final boolean wait, final short eventType, final int modifiers, final short keyCode, final short keySym, final 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) ); } - public final void setKeyboardVisible(boolean visible) { + @Override + public final void setKeyboardVisible(final 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 n = setKeyboardVisibleImpl(visible); + // 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+" -> "+visible +" -> "+n); + System.err.println("setKeyboardVisible(native): visible "+keyboardVisible+" -- op[visible:"+visible +", ok "+ok+"] -> "+(visible && ok)); } - keyboardVisible = n; + keyboardVisibilityChanged( visible && ok ); } else { - keyboardVisible = visible; // earmark for creation + keyboardVisibilityChanged( visible ); // earmark for creation } } + @Override public final boolean isKeyboardVisible() { return keyboardVisible; - } - protected boolean setKeyboardVisibleImpl(boolean visible) { + } + /** + * 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(final 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) { + protected void keyboardVisibilityChanged(final boolean visible) { if(keyboardVisible != visible) { if(DEBUG_IMPLEMENTATION || DEBUG_KEY_EVENT) { System.err.println("keyboardVisibilityChanged: "+keyboardVisible+" -> "+visible); @@ -2207,44 +3517,56 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } protected boolean keyboardVisible = false; - - public void addKeyListener(int index, KeyListener l) { + + @Override + public final void addKeyListener(final KeyListener l) { + addKeyListener(-1, l); + } + + @Override + public final void addKeyListener(int index, final KeyListener l) { if(l == null) { return; } @SuppressWarnings("unchecked") + final 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(final KeyListener l) { if (l == null) { return; } @SuppressWarnings("unchecked") + final ArrayList<KeyListener> clonedListeners = (ArrayList<KeyListener>) keyListeners.clone(); clonedListeners.remove(l); keyListeners = clonedListeners; } - public KeyListener getKeyListener(int index) { + @Override + public final KeyListener getKeyListener(int index) { @SuppressWarnings("unchecked") + final 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()]); } - private final boolean propagateKeyEvent(KeyEvent e, KeyListener l) { + private final boolean propagateKeyEvent(final KeyEvent e, final KeyListener l) { switch(e.getEventType()) { case KeyEvent.EVENT_KEY_PRESSED: l.keyPressed(e); @@ -2252,92 +3574,101 @@ 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); + + protected void consumeKeyEvent(final KeyEvent e) { + 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(final 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(final boolean wait, final int eventType) { + enqueueEvent( wait, new WindowEvent((short)eventType, this, System.currentTimeMillis()) ); } - public void addWindowListener(WindowListener l) { + @Override + public final void addWindowListener(final WindowListener l) { addWindowListener(-1, l); } - public void addWindowListener(int index, WindowListener l) + @Override + public final void addWindowListener(int index, final WindowListener l) throws IndexOutOfBoundsException { if(l == null) { return; } @SuppressWarnings("unchecked") + final 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; } - public final void removeWindowListener(WindowListener l) { + @Override + public final void removeWindowListener(final WindowListener l) { if (l == null) { return; } @SuppressWarnings("unchecked") + final ArrayList<WindowListener> clonedListeners = (ArrayList<WindowListener>) windowListeners.clone(); clonedListeners.remove(l); windowListeners = clonedListeners; } - public WindowListener getWindowListener(int index) { + @Override + public final WindowListener getWindowListener(int index) { @SuppressWarnings("unchecked") + final 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()]); } - protected void consumeWindowEvent(WindowEvent e) { + protected void consumeWindowEvent(final WindowEvent e) { if(DEBUG_IMPLEMENTATION) { - System.err.println("consumeWindowEvent: "+e+", visible "+isVisible()+" "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); + System.err.println("consumeWindowEvent: "+e+", visible "+isVisible()+" "+getX()+"/"+getY()+", win["+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+ + "], pixel["+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"); } - for(int i = 0; i < windowListeners.size(); i++ ) { - WindowListener l = windowListeners.get(i); + for(int i = 0; !e.isConsumed() && i < windowListeners.size(); i++ ) { + final WindowListener l = windowListeners.get(i); switch(e.getEventType()) { case WindowEvent.EVENT_WINDOW_RESIZED: l.windowResized(e); @@ -2361,7 +3692,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer l.windowRepaint((WindowUpdateEvent)e); break; default: - throw + throw new NativeWindowException("Unexpected window event type " + e.getEventType()); } @@ -2369,13 +3700,13 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } /** Triggered by implementation's WM events to update the focus state. */ - protected void focusChanged(boolean defer, boolean focusGained) { + protected void focusChanged(final boolean defer, final boolean focusGained) { if(brokenFocusChange || hasFocus != focusGained) { if(DEBUG_IMPLEMENTATION) { 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 { @@ -2383,9 +3714,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } - + /** Triggered by implementation's WM events to update the visibility state. */ - protected void visibleChanged(boolean defer, boolean visible) { + protected final void visibleChanged(final boolean defer, final 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)); @@ -2394,32 +3725,43 @@ 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(final boolean visible, final 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 ) { + /** Returns -1 if failed, otherwise remaining time until <code>timeOut</code>, maybe zero. */ + private long waitForVisible(final boolean visible, final boolean failFast, final 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 (final InterruptedException ie) {} display.dispatchMessagesNative(); // status up2date - try { Thread.sleep(10); } catch (InterruptedException ie) {} } 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. */ - protected void sizeChanged(boolean defer, int newWidth, int newHeight, boolean force) { + /** Triggered by implementation's WM events to update the client-area size in window units w/o insets/decorations. */ + protected void sizeChanged(final boolean defer, final int newWidth, final int newHeight, final boolean force) { if(force || getWidth() != newWidth || getHeight() != newHeight) { if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.sizeChanged: ("+getThreadName()+"): (defer: "+defer+") force "+force+", "+getWidth()+"x"+getHeight()+" -> "+newWidth+"x"+newHeight+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + System.err.println("Window.sizeChanged: ("+getThreadName()+"): (defer: "+defer+") force "+force+", "+ + getWidth()+"x"+getHeight()+" -> "+newWidth+"x"+newHeight+ + " - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); } if(0>newWidth || 0>newHeight) { throw new NativeWindowException("Illegal width or height "+newWidth+"x"+newHeight+" (must be >= 0)"); @@ -2434,20 +3776,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) {} - } + + private boolean waitForSize(final int w, final int h, final boolean failFast, final long timeOut) { + 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 (final 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); @@ -2455,12 +3793,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(final boolean defer, final int newX, final 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)); @@ -2472,82 +3812,146 @@ 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 + } + } + + /** + * 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(final boolean useCustomPosition, final int x, final int y, final 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 (final 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. - * + * 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) { + protected void insetsChanged(final boolean defer, final int left, final int right, final int top, final 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(final 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(final int x, final int y, final int width, final 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) { - width = ( 0 >= width ) ? getWidth() : width; - height = ( 0 >= height ) ? getHeight() : height; + * @param defer if true sent event later, otherwise wait until processed. + * @param x dirty-region y-pos in pixel units + * @param y dirty-region x-pos in pixel units + * @param width dirty-region width in pixel units + * @param height dirty-region height in pixel units + */ + protected final void windowRepaint(final boolean defer, final int x, final int y, int width, int height) { + width = ( 0 >= width ) ? getSurfaceWidth() : width; + height = ( 0 >= height ) ? getSurfaceHeight() : height; if(DEBUG_IMPLEMENTATION) { System.err.println("Window.windowRepaint "+getThreadName()+" (defer: "+defer+") "+x+"/"+y+" "+width+"x"+height); } if(isNativeValid()) { - NEWTEvent e = new WindowUpdateEvent(WindowEvent.EVENT_WINDOW_REPAINT, this, System.currentTimeMillis(), + final NEWTEvent e = new WindowUpdateEvent(WindowEvent.EVENT_WINDOW_REPAINT, this, System.currentTimeMillis(), new Rectangle(x, y, width, height)); doEvent(defer, false, e); } @@ -2557,16 +3961,16 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer // Reflection helper .. // - private static Class<?>[] getCustomConstructorArgumentTypes(Class<?> windowClass) { + private static Class<?>[] getCustomConstructorArgumentTypes(final Class<?> windowClass) { Class<?>[] argTypes = null; try { - Method m = windowClass.getDeclaredMethod("getCustomConstructorArgumentTypes"); + final Method m = windowClass.getDeclaredMethod("getCustomConstructorArgumentTypes"); argTypes = (Class[]) m.invoke(null, (Object[])null); - } catch (Throwable t) {} + } catch (final Throwable t) {} return argTypes; } - private static int verifyConstructorArgumentTypes(Class<?>[] types, Object[] args) { + private static int verifyConstructorArgumentTypes(final Class<?>[] types, final Object[] args) { if(types.length != args.length) { return -1; } @@ -2578,8 +3982,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return args.length; } - private static String getArgsStrList(Object[] args) { - StringBuilder sb = new StringBuilder(); + private static String getArgsStrList(final Object[] args) { + final StringBuilder sb = new StringBuilder(); for(int i=0; i<args.length; i++) { sb.append(args[i].getClass()); if(i<args.length) { @@ -2589,8 +3993,8 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer return sb.toString(); } - private static String getTypeStrList(Class<?>[] types) { - StringBuilder sb = new StringBuilder(); + private static String getTypeStrList(final Class<?>[] types) { + final StringBuilder sb = new StringBuilder(); for(int i=0; i<types.length; i++) { sb.append(types[i]); if(i<types.length) { @@ -2600,19 +4004,15 @@ 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(); } - public static String toHexString(int hex) { + public static String toHexString(final int hex) { return Display.toHexString(hex); } - public static String toHexString(long hex) { + public static String toHexString(final long hex) { return Display.toHexString(hex); } } diff --git a/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java b/src/newt/classes/jogamp/newt/awt/NewtFactoryAWT.java index 861a6d6be..682313ef4 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; @@ -55,7 +55,7 @@ public class NewtFactoryAWT extends NewtFactory { * * @param awtCompObject must be of type java.awt.Component */ - public static JAWTWindow getNativeWindow(Object awtCompObject, CapabilitiesImmutable capsRequested) { + public static JAWTWindow getNativeWindow(final Object awtCompObject, final CapabilitiesImmutable capsRequested) { if(null==awtCompObject) { throw new NativeWindowException("Null AWT Component"); } @@ -65,9 +65,9 @@ public class NewtFactoryAWT extends NewtFactory { return getNativeWindow( (java.awt.Component) awtCompObject, capsRequested ); } - public static JAWTWindow getNativeWindow(java.awt.Component awtComp, CapabilitiesImmutable capsRequested) { - AWTGraphicsConfiguration config = AWTGraphicsConfiguration.create(awtComp, null, capsRequested); - NativeWindow nw = NativeWindowFactory.getNativeWindow(awtComp, config); // a JAWTWindow + public static JAWTWindow getNativeWindow(final java.awt.Component awtComp, final CapabilitiesImmutable capsRequested) { + final AWTGraphicsConfiguration config = AWTGraphicsConfiguration.create(awtComp, null, capsRequested); + final NativeWindow nw = NativeWindowFactory.getNativeWindow(awtComp, config); // a JAWTWindow if(! ( nw instanceof JAWTWindow ) ) { throw new NativeWindowException("Not an AWT NativeWindow: "+nw); } @@ -76,12 +76,12 @@ public class NewtFactoryAWT extends NewtFactory { } return (JAWTWindow)nw; } - - public static void destroyNativeWindow(JAWTWindow jawtWindow) { + + public static void destroyNativeWindow(final 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..31ee92ca4 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,653 @@ * 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 javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeSurfaceHolder; + +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 short eventTypeAWT2NEWT(final 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(final 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(final int button) { + if( 0 < button && button <= awtButtonDownMasks.length ) { + return awtButtonDownMasks[button-1]; + } else { + return 0; + } + } + + public static final short awtButton2Newt(final int awtButton) { + if( 0 < awtButton && awtButton <= com.jogamp.newt.event.MouseEvent.BUTTON_COUNT ) { + return (short)awtButton; + } else { + return (short)0; + } } - public static final int awtModifiers2Newt(int awtMods, boolean mouseHint) { + /** + * 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 & 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()); + public static final com.jogamp.newt.event.WindowEvent createWindowEvent(final java.awt.event.WindowEvent event, final NativeSurfaceHolder sourceHolder) { + final short newtType = eventTypeAWT2NEWT(event.getID()); + if( (short)0 != newtType ) { + return new com.jogamp.newt.event.WindowEvent(newtType, sourceHolder, 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()); + public static final com.jogamp.newt.event.WindowEvent createWindowEvent(final java.awt.event.ComponentEvent event, final NativeSurfaceHolder sourceHolder) { + final short newtType = eventTypeAWT2NEWT(event.getID()); + if( (short)0 != newtType ) { + return new com.jogamp.newt.event.WindowEvent(newtType, sourceHolder, 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()); + public static final com.jogamp.newt.event.WindowEvent createWindowEvent(final java.awt.event.FocusEvent event, final NativeSurfaceHolder sourceHolder) { + final short newtType = eventTypeAWT2NEWT(event.getID()); + if( (short)0 != newtType ) { + return new com.jogamp.newt.event.WindowEvent(newtType, sourceHolder, 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; + public static final com.jogamp.newt.event.MouseEvent createMouseEvent(final java.awt.event.MouseEvent event, final NativeSurfaceHolder sourceHolder) { + 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); - if(null!=newtSource) { - if(newtSource.isPointerConfined()) { - mods |= InputEvent.CONFINED_MASK; - } - if(!newtSource.isPointerVisible()) { - mods |= InputEvent.INVISIBLE_MASK; + 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 + final NativeSurface source = sourceHolder.getNativeSurface(); + final int[] pixelPos; + if( null != source ) { + if( source instanceof com.jogamp.newt.Window ) { + final com.jogamp.newt.Window newtSource = (com.jogamp.newt.Window) source; + if(newtSource.isPointerConfined()) { + mods |= com.jogamp.newt.event.InputEvent.CONFINED_MASK; + } + if(!newtSource.isPointerVisible()) { + mods |= com.jogamp.newt.event.InputEvent.INVISIBLE_MASK; + } } + pixelPos = source.convertToPixelUnits(new int[] { event.getX(), event.getY() }); + } else { + pixelPos = new int[] { event.getX(), event.getY() }; } - + 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, sourceHolder, event.getWhen(), + mods, pixelPos[0], pixelPos[1], (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()); + public static final com.jogamp.newt.event.KeyEvent createKeyEvent(final java.awt.event.KeyEvent event, final NativeSurfaceHolder sourceHolder) { + return createKeyEvent(eventTypeAWT2NEWT(event.getID()), event, sourceHolder); + } + + public static final com.jogamp.newt.event.KeyEvent createKeyEvent(final short newtType, final java.awt.event.KeyEvent event, final NativeSurfaceHolder sourceHolder) { + if( (short)0 != newtType ) { + final short newtKeyCode = awtKeyCode2NewtKeyCode( event.getKeyCode() ); + return com.jogamp.newt.event.KeyEvent.create( + newtType, sourceHolder, 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 747e2368e..93cdd7e56 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; @@ -42,113 +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) { + + public AWTParentWindowAdapter(final NativeWindow downstreamParent, final com.jogamp.newt.Window downstream) { super(downstream); this.downstreamParent = downstreamParent; } + public AWTParentWindowAdapter() { + super(); + } + public AWTParentWindowAdapter setDownstream(final NativeWindow downstreamParent, final 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(final java.awt.Component awtComponent) { awtComponent.addHierarchyListener(this); return super.addTo(awtComponent); } - public AWTAdapter removeFrom(java.awt.Component awtComponent) { + @Override + public synchronized AWTAdapter removeFrom(final java.awt.Component awtComponent) { awtComponent.removeHierarchyListener(this); return super.removeFrom(awtComponent); } - public void focusGained(java.awt.event.FocusEvent e) { + @Override + public synchronized void focusGained(final 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(final 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(final 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); } - final Window newtWindow = getNewtWindow(); - newtWindow.runOnEDTIfAvail(false, new Runnable() { - public void run() { - int cw = comp.getWidth(); - int ch = comp.getHeight(); - if( 0 < cw * ch ) { - if( newtWindow.getWidth() != cw || newtWindow.getHeight() != ch ) { - newtWindow.setSize(cw, ch); - if(comp.isVisible() != newtWindow.isVisible()) { - newtWindow.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(newtWindow.isVisible()) { - newtWindow.setVisible(false); - } - }}); + }}); + } } - public void componentMoved(java.awt.event.ComponentEvent e) { + @Override + public synchronized void componentMoved(final 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); } - final Window newtWindow = getNewtWindow(); - if(newtWindow.getDelegatedWindow() instanceof DriverUpdatePosition) { - ((DriverUpdatePosition)newtWindow.getDelegatedWindow()).updatePosition(); - } } - public void windowActivated(java.awt.event.WindowEvent e) { + @Override + public synchronized void windowActivated(final java.awt.event.WindowEvent e) { // no propagation to NEWT child window } - public void windowDeactivated(java.awt.event.WindowEvent e) { + @Override + public synchronized void windowDeactivated(final 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(final 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..43cfa9b25 --- /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(final IOUtil.ClassResources resources, final int[] data_size, final 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/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/android/DisplayDriver.java index a367462c4..a5f4fc769 100644 --- a/src/newt/classes/jogamp/newt/driver/android/DisplayDriver.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. @@ -53,14 +53,15 @@ public class DisplayDriver extends jogamp.newt.DisplayImpl { protected void createNativeImpl() { // EGL Device aDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); + aDevice.open(); } - protected void closeNativeImpl() { + protected void closeNativeImpl(final 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 f2f30937b..e6316004b 100644 --- a/src/newt/classes/jogamp/newt/driver/android/MD.java +++ b/src/newt/classes/jogamp/newt/driver/android/MD.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,13 +20,15 @@ * 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.PlatformPropsImpl; + import com.jogamp.common.GlueGenVersion; import com.jogamp.common.os.Platform; import com.jogamp.common.util.VersionUtil; @@ -34,22 +36,22 @@ import com.jogamp.opengl.JoglVersion; public class MD { public static final String TAG = "JogAmp.NEWT"; - - public static String getInfo() { - StringBuilder sb = new StringBuilder(); - - sb.append(VersionUtil.getPlatformInfo()).append(Platform.NEWLINE) - .append(GlueGenVersion.getInstance()).append(Platform.NEWLINE) - .append(JoglVersion.getInstance()).append(Platform.NEWLINE) - .append(Platform.NEWLINE); - + + public static String getInfo() { + final StringBuilder sb = new StringBuilder(); + + sb.append(VersionUtil.getPlatformInfo()).append(PlatformPropsImpl.NEWLINE) + .append(GlueGenVersion.getInstance()).append(PlatformPropsImpl.NEWLINE) + .append(JoglVersion.getInstance()).append(PlatformPropsImpl.NEWLINE) + .append(PlatformPropsImpl.NEWLINE); + JoglVersion.getDefaultOpenGLInfo(null, sb, true); - return sb.toString(); + return sb.toString(); } - - public static void main(String args[]) { - + + public static void main(final String args[]) { + System.err.println(getInfo()); } } diff --git a/src/newt/classes/jogamp/newt/driver/android/NewtBaseActivity.java b/src/newt/classes/jogamp/newt/driver/android/NewtBaseActivity.java index 28c4da72f..68d43ac75 100644 --- a/src/newt/classes/jogamp/newt/driver/android/NewtBaseActivity.java +++ b/src/newt/classes/jogamp/newt/driver/android/NewtBaseActivity.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,9 +32,12 @@ 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.WindowDriver; @@ -46,102 +49,174 @@ 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(final 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; rootActivity = this; } - - public void setRootActivity(Activity rootActivity) { - this.isDelegatedActivity = true; + + public void setRootActivity(final Activity rootActivity) { this.rootActivity = rootActivity; + this.isDelegatedActivity = this != rootActivity; } - + public final boolean isDelegatedActivity() { return isDelegatedActivity; } - + public final Activity getActivity() { return rootActivity; - } - + } + /** * This is one of the three registration methods (see below). * <p> * This methods issues {@link android.view.Window#setContentView(android.view.View, android.view.ViewGroup.LayoutParams) androidWindow.setContenView(newtWindow.getAndroidView())} * and finally calls {@link #registerNEWTWindow(Window)}. - * </p> + * </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 WindowDriver) { - adaptTheme4Transparency(newtWindow.getRequestedCapabilities()); - layoutForNEWTWindow(androidWindow, newtWindow); - WindowDriver newtAWindow = (WindowDriver)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). * <p> * This methods issues {@link android.view.Window#addContentView(android.view.View, android.view.ViewGroup.LayoutParams) androidWindow.addContenView(newtWindow.getAndroidView(), params)} * and finally calls {@link #registerNEWTWindow(Window)}. - * </p> + * </p> * @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 WindowDriver) { - WindowDriver newtAWindow = (WindowDriver)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(final GLStateKeeper glsk) { + Log.d(MD.TAG, "GLStateKeeper Preserving: 0x"+Integer.toHexString(glsk.hashCode())); + } + @Override + public void glStateRestored(final 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. + * Convenient method to set the Android window's flags to fullscreen or size-layout depending on the given NEWT window. * <p> * Must be called before creating the view and adding any content, i.e. setContentView() ! * </p> * @param androidWindow * @param newtWindow */ - public void layoutForNEWTWindow(android.view.Window androidWindow, Window newtWindow) { + public void layoutForNEWTWindow(final android.view.Window androidWindow, final Window newtWindow) { if(null == androidWindow || null == newtWindow) { throw new IllegalArgumentException("Android or NEWT Window null"); } - + if( newtWindow.isFullscreen() || newtWindow.isUndecorated() ) { androidWindow.requestFeature(android.view.Window.FEATURE_NO_TITLE); } @@ -150,27 +225,27 @@ public class NewtBaseActivity extends Activity { androidWindow.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); } else { androidWindow.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - androidWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + androidWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } - - if(newtWindow.getWidth()>0 && newtWindow.getHeight()>0 && !newtWindow.isFullscreen()) { + + if(newtWindow.getWidth()>0 && newtWindow.getHeight()>0 && !newtWindow.isFullscreen()) { androidWindow.setLayout(newtWindow.getWidth(), newtWindow.getHeight()); - } + } } /** - * Convenient method to set the Android window's flags to fullscreen or size-layout depending on the given NEWT window. + * Convenient method to set the Android window's flags to fullscreen or size-layout depending on the given NEWT window. * <p> * Must be called before creating the view and adding any content, i.e. setContentView() ! * </p> * @param androidWindow * @param newtWindow */ - public void setFullscreenFeature(android.view.Window androidWindow, boolean fullscreen) { + public void setFullscreenFeature(final android.view.Window androidWindow, final boolean fullscreen) { if(null == androidWindow) { throw new IllegalArgumentException("Android or Window null"); } - + if( fullscreen ) { androidWindow.requestFeature(android.view.Window.FEATURE_NO_TITLE); androidWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); @@ -180,19 +255,19 @@ public class NewtBaseActivity extends Activity { androidWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } } - + /** - * Convenient method to set this context's theme to transparency depending on {@link CapabilitiesImmutable#isBackgroundOpaque()}. + * Convenient method to set this context's theme to transparency depending on {@link CapabilitiesImmutable#isBackgroundOpaque()}. * <p> * Must be called before creating the view and adding any content, i.e. setContentView() ! * </p> */ - protected void adaptTheme4Transparency(CapabilitiesImmutable caps) { + protected void adaptTheme4Transparency(final CapabilitiesImmutable caps) { if(!caps.isBackgroundOpaque()) { setTransparencyTheme(); } } - + /** * Convenient method to set this context's theme to transparency. * <p> @@ -204,12 +279,12 @@ public class NewtBaseActivity extends Activity { * </p> * <p> * Can be called only once. - * </p> + * </p> */ public void setTransparencyTheme() { if(!setThemeCalled) { setThemeCalled = true; - final Context ctx = getActivity().getApplicationContext(); + final Context ctx = getActivity().getApplicationContext(); final String frn = ctx.getPackageName()+":style/Theme.Transparent"; final int resID = ctx.getResources().getIdentifier("Theme.Transparent", "style", ctx.getPackageName()); if(0 == resID) { @@ -220,48 +295,72 @@ 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(final GLAnimatorControl animator) { this.animator = animator; if(!animator.isStarted()) { animator.start(); } animator.pause(); } - + @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"); + public void onCreate(final Bundle savedInstanceState) { + 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(); } @@ -272,49 +371,93 @@ public class NewtBaseActivity extends Activity { ((FPSCounter)win).resetFPSCounter(); } } - if(null != animator) { - animator.resume(); - animator.resetFPSCounter(); - } + startAnimation(true); + Log.d(MD.TAG, "onResume.X"); } @Override public void onPause() { - Log.d(MD.TAG, "onPause"); - if(null != animator) { - animator.pause(); - } - for(int i=0; i<newtWindows.size(); i++) { - final Window win = newtWindows.get(i); - win.setVisible(false); + 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()) { - super.onStop(); + 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(); + 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 a49f1648c..4dd96c9a0 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. @@ -33,6 +33,8 @@ import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLProfile; +import jogamp.common.os.PlatformPropsImpl; + import com.jogamp.common.GlueGenVersion; import com.jogamp.common.os.Platform; import com.jogamp.common.util.VersionUtil; @@ -47,58 +49,75 @@ import android.widget.ScrollView; import android.widget.TextView; public class NewtVersionActivity extends NewtBaseActivity { + @Override - public void onCreate(Bundle savedInstanceState) { + public void onCreate(final 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 = ((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 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"+PlatformPropsImpl.NEWLINE+VersionUtil.getPlatformInfo()+PlatformPropsImpl.NEWLINE+GlueGenVersion.getInstance()+PlatformPropsImpl.NEWLINE+JoglVersion.getInstance()+PlatformPropsImpl.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) + final GLCapabilities caps = new GLCapabilities(glp); + final 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.getSurfaceWidth(), glWindow.getSurfaceHeight(), Gravity.BOTTOM|Gravity.RIGHT)); + registerNEWTWindow(glWindow); + + glWindow.addGLEventListener(new GLEventListener() { + public void init(final GLAutoDrawable drawable) { + final GL gl = drawable.getGL(); + final StringBuilder sb = new StringBuilder(); + sb.append(JoglVersion.getGLInfo(gl, null, true)).append(PlatformPropsImpl.NEWLINE); + sb.append("Requested: ").append(PlatformPropsImpl.NEWLINE); + sb.append(drawable.getNativeSurface().getGraphicsConfiguration().getRequestedCapabilities()).append(PlatformPropsImpl.NEWLINE).append(PlatformPropsImpl.NEWLINE); + sb.append("Chosen: ").append(PlatformPropsImpl.NEWLINE); + sb.append(drawable.getChosenGLCapabilities()).append(PlatformPropsImpl.NEWLINE).append(PlatformPropsImpl.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(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { + } - public void display(GLAutoDrawable drawable) { - } + public void display(final GLAutoDrawable drawable) { + } - public void dispose(GLAutoDrawable drawable) { - } - }); - glWindow.setVisible(true); + public void dispose(final 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..190f55e71 100644 --- a/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivityLauncher.java +++ b/src/newt/classes/jogamp/newt/driver/android/NewtVersionActivityLauncher.java @@ -8,14 +8,14 @@ import android.util.Log; public class NewtVersionActivityLauncher extends Activity { @Override - public void onCreate(Bundle savedInstanceState) { + public void onCreate(final 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 index 795aac5fb..b28cdcbed 100644 --- a/src/newt/classes/jogamp/newt/driver/android/ScreenDriver.java +++ b/src/newt/classes/jogamp/newt/driver/android/ScreenDriver.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. @@ -28,12 +28,10 @@ package jogamp.newt.driver.android; -import javax.media.nativewindow.*; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.DefaultGraphicsScreen; -import com.jogamp.newt.ScreenMode; -import com.jogamp.newt.util.ScreenModeUtil; +import jogamp.newt.MonitorModeProps; +import jogamp.newt.MonitorModeProps.Cache; import android.content.Context; import android.graphics.PixelFormat; @@ -41,6 +39,9 @@ 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 { @@ -50,59 +51,103 @@ public class ScreenDriver extends jogamp.newt.ScreenImpl { public ScreenDriver() { } + @Override protected void createNativeImpl() { aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); } + @Override protected void closeNativeImpl() { } - protected ScreenMode getCurrentScreenModeImpl() { + @Override + protected int validateScreenIndex(final int idx) { + return 0; // FIXME: only one screen available ? + } + + private final MonitorMode getModeImpl(final Cache cache, final android.view.Display aDisplay, final DisplayMetrics outMetrics, final int modeIdx, final int screenSizeNRot, final 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(final 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); - 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); + + 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; + final 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 pixel-units + props[i++] = 0; // rotated viewport y pixel-units + props[i++] = outMetrics.widthPixels; // rotated viewport width pixel-units + props[i++] = outMetrics.heightPixels; // rotated viewport height pixel-units + props[i++] = 0; // rotated viewport x window-units + props[i++] = 0; // rotated viewport y window-units + props[i++] = outMetrics.widthPixels; // rotated viewport width window-units + props[i++] = outMetrics.heightPixels; // rotated viewport height window-units + MonitorModeProps.streamInMonitorDevice(null, cache, this, cache.monitorModes, currentMode, props, 0); } - - protected int validateScreenIndex(int idx) { - return 0; // FIXME: only one screen available ? + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(final 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); } - - 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()); + + @Override + protected boolean setCurrentMonitorModeImpl(final MonitorDevice monitor, final MonitorMode mode) { + return false; } - + //---------------------------------------------------------------------- // Internals only // - static int androidRotation2NewtRotation(int arot) { + static int androidRotation2NewtRotation(final 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_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 ScreenMode.ROTATE_0; + return MonitorMode.ROTATE_0; } - static int getScreenSize(DisplayMetrics outMetrics, int nrot, int[] props, int offset) { + static int getScreenSize(final DisplayMetrics outMetrics, final int nrot, final 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) { + if (MonitorMode.ROTATE_90 == nrot || MonitorMode.ROTATE_270 == nrot) { props[offset++] = outMetrics.heightPixels; props[offset++] = outMetrics.widthPixels; } else { @@ -110,8 +155,8 @@ public class ScreenDriver extends jogamp.newt.ScreenImpl { props[offset++] = outMetrics.heightPixels; } return offset; - } - static int getBpp(android.view.Display aDisplay, int[] props, int offset) { + } + static int getBpp(final android.view.Display aDisplay, final int[] props, int offset) { int bpp; switch(aDisplay.getPixelFormat()) { case PixelFormat.RGBA_8888: bpp=32; break; @@ -121,18 +166,28 @@ public class ScreenDriver extends jogamp.newt.ScreenImpl { 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; + 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; + static int getScreenSizeMM(final DisplayMetrics outMetrics, final int[] props, int offset) { + final float inW = outMetrics.widthPixels / outMetrics.xdpi; + final float inH = outMetrics.heightPixels / outMetrics.ydpi; final float mmpi = 25.4f; - props[offset++] = (int) ((iw * mmpi)+0.5); - props[offset++] = (int) ((ih * mmpi)+0.5); + 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 index f18520630..786ebb31c 100644 --- a/src/newt/classes/jogamp/newt/driver/android/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/android/WindowDriver.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. @@ -29,53 +29,144 @@ 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.Rectangle; +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 com.jogamp.newt.Window; 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.View; +import android.view.KeyEvent; -public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { + +public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { static { DisplayDriver.initSingleton(); } - public static CapabilitiesImmutable fixCaps(boolean matchFormatPrecise, int format, CapabilitiesImmutable rCaps) { - PixelFormat pf = new PixelFormat(); + /** + * 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(final 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(final 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(final boolean matchFormatPrecise, final int format, final CapabilitiesImmutable rCaps) { + final PixelFormat pf = new PixelFormat(); PixelFormat.getPixelFormatInfo(format, pf); - final CapabilitiesImmutable res; + 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.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; + 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; @@ -86,51 +177,25 @@ public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { rCaps.getGreenBits() > g && rCaps.getBlueBits() > b && rCaps.getAlphaBits() > a ; - + if(change) { - Capabilities nCaps = (Capabilities) rCaps.cloneMutable(); + final Capabilities nCaps = (Capabilities) rCaps.cloneMutable(); nCaps.setRedBits(r); nCaps.setGreenBits(g); nCaps.setBlueBits(b); nCaps.setAlphaBits(a); - res = nCaps; + 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) { + + public static final boolean isAndroidFormatTransparent(final int aFormat) { switch (aFormat) { case PixelFormat.TRANSLUCENT: case PixelFormat.TRANSPARENT: @@ -138,53 +203,22 @@ public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { } 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, WindowDriver.this); - if(null != newtEvents) { - focusChanged(false, true); - for(int i=0; i<newtEvents.length; i++) { - WindowDriver.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, WindowDriver.this); - if(null != newtEvents) { - for(int i=0; i<newtEvents.length; i++) { - WindowDriver.this.enqueueEvent(false, newtEvents[i]); - } - return true; - } - return false; - } - - @Override - public void onFocusChange(View v, boolean hasFocus) { - WindowDriver.this.focusChanged(false, hasFocus); - } - - } public static Class<?>[] getCustomConstructorArgumentTypes() { return new Class<?>[] { Context.class } ; } - + public WindowDriver() { reset(); } - private void reset() { - ownAndroidWindow = false; + public void registerActivity(final 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; @@ -193,93 +227,200 @@ public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { 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(final 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 void instantiationFinished() { - final Context ctx = StaticContext.getContext(); + 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."); } - 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(WindowDriver.this); - sh.setFormat(getFormat(getRequestedCapabilities())); - - // default size -> TBD ! - defineSize(0, 0); - } - - public SurfaceView getAndroidView() { return androidView; } - + + if( null != Looper.myLooper() ) { + setupAndroidView(ctx); + } + } + @Override - protected boolean canCreateNativeImpl() { - final boolean b = 0 != surfaceHandle; - Log.d(MD.TAG, "canCreateNativeImpl: "+b); + protected final boolean canCreateNativeImpl() { + Log.d(MD.TAG, "canCreateNativeImpl.0: surfaceHandle ready "+(0!=surfaceHandle)+" - on thread "+Thread.currentThread().getName()); + if(Window.DEBUG_IMPLEMENTATION) { + Thread.dumpStack(); + } + + if( isFullscreen() ) { + final MonitorDevice mainMonitor = getMainMonitor(); + final RectangleImmutable winRect = mainMonitor.getViewportInWindowUnits(); + definePosition(winRect.getX(), winRect.getY()); + defineSize(winRect.getWidth(), winRect.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 (final 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 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()); + 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+"], win["+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+ + "], pixel["+getSurfaceWidth()+"x"+getSurfaceHeight()+ + "] - on thread "+Thread.currentThread().getName()); if(0!=getParentWindowHandle()) { throw new NativeWindowException("Window parenting not supported (yet)"); } if(0==surfaceHandle) { - throw new InternalError("XXX"); + throw new InternalError("surfaceHandle null"); } - - final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) getScreen().getDisplay().getGraphicsDevice(); + final EGLGraphicsConfiguration eglConfig = EGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic( - capsByFormat, (GLCapabilitiesImmutable) getRequestedCapabilities(), - (GLCapabilitiesChooser)capabilitiesChooser, getScreen().getGraphicsScreen(), nativeFormat, - isAndroidFormatTransparent(androidFormat)); + 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); - Log.d(MD.TAG, "createNativeImpl X: eglSurfaceHandle 0x"+Long.toHexString(eglSurface)); + + setupInputListener(true); + + Log.d(MD.TAG, "createNativeImpl X: eglDevice 0x"+Integer.toHexString(eglDevice.hashCode())+", "+eglDevice+", eglSurfaceHandle 0x"+Long.toHexString(eglSurface)); } @Override - protected void closeNativeImpl() { - Log.d(MD.TAG, "closeNativeImpl 0 - surfaceHandle 0x"+Long.toHexString(surfaceHandle)+ + 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()+" - "+Thread.currentThread().getName()); + ", format [a "+androidFormat+", n "+nativeFormat+"], win["+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+ + "], pixel["+getSurfaceWidth()+"x"+getSurfaceHeight()+"],"+ + " - on thread "+Thread.currentThread().getName()); + if(Window.DEBUG_IMPLEMENTATION) { + Thread.dumpStack(); + } + + setupInputListener(false); + if(0 != eglSurface) { - final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) getScreen().getDisplay().getGraphicsDevice(); - if (!EGL.eglDestroySurface(eglDevice.getHandle(), eglSurface)) { - throw new GLException("Error destroying window surface (eglDestroySurface)"); + try { + if (!EGL.eglDestroySurface(eglDevice.getHandle(), eglSurface)) { + throw new GLException("Error destroying window surface (eglDestroySurface)"); + } + } catch (final Throwable t) { + Log.d(MD.TAG, "closeNativeImpl: Catch exception "+t.getMessage()); + t.printStackTrace(); + } finally { + eglSurface = 0; } - 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; } @@ -288,8 +429,21 @@ public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { public final long getSurfaceHandle() { return eglSurface; } - - protected void requestFocusImpl(boolean reparented) { + + /** + * <p> + * Accessible protected method! + * </p> + * + * {@inheritDoc} + */ + @Override + public final void focusChanged(final boolean defer, final boolean focusGained) { + super.focusChanged(defer, focusGained); + } + + @Override + protected final void requestFocusImpl(final boolean reparented) { if(null != androidView) { Log.d(MD.TAG, "requestFocusImpl: reparented "+reparented); androidView.post(new Runnable() { @@ -301,9 +455,10 @@ public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { } } - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { + @Override + protected final boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, final int flags) { boolean res = true; - + if( 0 != ( FLAG_CHANGE_FULLSCREEN & flags) ) { Log.d(MD.TAG, "reconfigureWindowImpl.setFullscreen post creation (setContentView()) n/a"); return false; @@ -325,32 +480,34 @@ public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { } } if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { - visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); + visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); } return res; } - protected Point getLocationOnScreenImpl(int x, int y) { + @Override + protected final Point getLocationOnScreenImpl(final int x, final int y) { return new Point(x,y); } - protected void updateInsetsImpl(Insets insets) { - // nop .. + @Override + protected final void updateInsetsImpl(final Insets insets) { + // nop .. } - + //---------------------------------------------------------------------- - // Virtual On-Screen Keyboard / SoftInput + // Virtual On-Screen Keyboard / SoftInput // - + private class KeyboardVisibleReceiver extends ResultReceiver { public KeyboardVisibleReceiver() { super(null); } - - @Override - public void onReceiveResult(int r, Bundle data) { + + @Override + public void onReceiveResult(final int r, final Bundle data) { boolean v = false; - + switch(r) { case InputMethodManager.RESULT_UNCHANGED_SHOWN: case InputMethodManager.RESULT_SHOWN: @@ -359,41 +516,49 @@ public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { case InputMethodManager.RESULT_HIDDEN: case InputMethodManager.RESULT_UNCHANGED_HIDDEN: v = false; - break; + break; } Log.d(MD.TAG, "keyboardVisible: "+v); keyboardVisibilityChanged(v); } } - private KeyboardVisibleReceiver keyboardVisibleReceiver = new KeyboardVisibleReceiver(); - - protected final boolean setKeyboardVisibleImpl(boolean visible) { + private final KeyboardVisibleReceiver keyboardVisibleReceiver = new KeyboardVisibleReceiver(); + + @Override + protected final boolean setKeyboardVisibleImpl(final 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: - imm.showSoftInput(androidView, 0, keyboardVisibleReceiver); + result = imm.showSoftInput(androidView, 0, keyboardVisibleReceiver); } else { // hide keyboard : - imm.hideSoftInputFromWindow(winid, 0, keyboardVisibleReceiver); + result = imm.hideSoftInputFromWindow(winid, 0, keyboardVisibleReceiver); } - return visible; + return result; } else { return false; // nop } } - + //---------------------------------------------------------------------- - // Surface Callbacks + // Surface Callbacks // - - public void surfaceCreated(SurfaceHolder holder) { - Log.d(MD.TAG, "surfaceCreated: "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); + + @Override + public final void surfaceCreated(final SurfaceHolder holder) { + Log.d(MD.TAG, "surfaceCreated: win["+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+ + "], pixels["+" "+getSurfaceWidth()+"x"+getSurfaceHeight()+"] - on thread "+Thread.currentThread().getName()); } - 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)); + @Override + public final void surfaceChanged(final SurfaceHolder aHolder, final int aFormat, final int aWidth, final 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(Window.DEBUG_IMPLEMENTATION) { + Thread.dumpStack(); + } if(0!=surfaceHandle && androidFormat != aFormat ) { // re-create Log.d(MD.TAG, "surfaceChanged (destroy old)"); @@ -404,29 +569,35 @@ public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { surface=null; } if(getScreen().isNativeValid()) { - getScreen().getCurrentScreenMode(); // if ScreenMode changed .. trigger ScreenMode event + // 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); - final int nWidth = getWidth0(surfaceHandle); - final int nHeight = getHeight0(surfaceHandle); + Log.d(MD.TAG, "surfaceChanged: androidFormat "+androidFormat+" -- (set-native "+aNativeWindowFormat+") --> nativeFormat "+nativeFormat); + + final int[] newSurfSize = { getWidth0(surfaceHandle), getHeight0(surfaceHandle) }; + final int[] newWinSize = convertToWindowUnits(new int[]{ newSurfSize[0], newSurfSize[1] }); // HiDPI: Not necessary yet .. capsByFormat = (GLCapabilitiesImmutable) fixCaps(true /* matchFormatPrecise */, nativeFormat, getRequestedCapabilities()); - sizeChanged(false, nWidth, nHeight, false); - + sizeChanged(false, newWinSize[0], newWinSize[1], 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()); - + ", format [a "+androidFormat+"/n "+nativeFormat+"], win["+ + getX()+"/"+getY()+" "+newWinSize[0]+"x"+newWinSize[1]+"], pixel["+newSurfSize[0]+"x"+newSurfSize[1]+"], visible: "+isVisible()); + if(isVisible()) { setVisible(false, true); } @@ -435,18 +606,78 @@ public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { windowRepaint(0, 0, aWidth, aHeight); Log.d(MD.TAG, "surfaceChanged: X"); } - - public void surfaceDestroyed(SurfaceHolder holder) { - Log.d(MD.TAG, "surfaceDestroyed"); + + @Override + public final void surfaceDestroyed(final SurfaceHolder holder) { + Log.d(MD.TAG, "surfaceDestroyed - on thread "+Thread.currentThread().getName()); windowDestroyNotify(true); // actually too late .. however .. + Thread.dumpStack(); } - public void surfaceRedrawNeeded(SurfaceHolder holder) { - Log.d(MD.TAG, "surfaceRedrawNeeded"); - windowRepaint(0, 0, getWidth(), getHeight()); + @Override + public final void surfaceRedrawNeeded(final SurfaceHolder holder) { + Log.d(MD.TAG, "surfaceRedrawNeeded - on thread "+Thread.currentThread().getName()); + windowRepaint(0, 0, getSurfaceWidth(), getSurfaceHeight()); } - - private boolean ownAndroidWindow; + + protected boolean handleKeyCodeBack(final KeyEvent.DispatcherState state, final 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(final android.view.KeyEvent aEvent, final 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(final 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() { + final Context ctx = StaticContext.getContext(); + if(null == ctx) { + throw new NativeWindowException("No static [Application] Context has been set. Call StaticContext.setContext(Context) first."); + } + final 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 ..) @@ -454,20 +685,34 @@ public class WindowDriver extends jogamp.newt.WindowImpl implements Callback2 { private Surface surface; private volatile long surfaceHandle; private long eglSurface; - + class MSurfaceView extends SurfaceView { - public MSurfaceView (Context ctx) { + public MSurfaceView (final Context ctx) { super(ctx); setBackgroundDrawable(null); // setBackgroundColor(Color.TRANSPARENT); } + + @Override + public boolean onKeyPreIme(final int keyCode, final 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); 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..579edc590 100644 --- a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java +++ b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.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,60 +20,99 @@ * 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.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 { + private static final boolean DEBUG_MOUSE_EVENT = Debug.debug("Android.MouseEvent"); + private static final boolean DEBUG_KEY_EVENT = Debug.debug("Android.KeyEvent"); - protected static final IntIntHashMap eventTypeANDROID2NEWT; - - private static final String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" , - "POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" }; - - 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); - - eventTypeANDROID2NEWT = map; + /** API Level 12: {@link android.view.MotionEvent#ACTION_SCROLL} = {@value} */ + private static final int ACTION_SCROLL = 8; + + private static final com.jogamp.newt.event.MouseEvent.PointerType aToolType2PointerType(final 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(final 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(final 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; + } + + private static final short aKeyEventType2NewtEventType(final 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; } - static final int androidKeyCode2Newt(int androidKeyCode) { + private static final short aKeyCode2NewtKeyCode(final int androidKeyCode, final 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; + case android.view.KeyEvent.KEYCODE_COMMA: return com.jogamp.newt.event.KeyEvent.VK_COMMA; case android.view.KeyEvent.KEYCODE_PERIOD: return com.jogamp.newt.event.KeyEvent.VK_PERIOD; case android.view.KeyEvent.KEYCODE_ALT_LEFT: return com.jogamp.newt.event.KeyEvent.VK_ALT; case android.view.KeyEvent.KEYCODE_ALT_RIGHT: return com.jogamp.newt.event.KeyEvent.VK_ALT_GRAPH; @@ -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,149 +134,231 @@ 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; // ?? - } - return 0; - } - - 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()); + 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 null; // no mapping .. + return com.jogamp.newt.event.KeyEvent.VK_UNDEFINED; } - static final int androidKeyModifiers2Newt(int androidMods) { + private static final int aKeyModifiers2Newt(final 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; if ((androidMods & android.view.KeyEvent.META_ALT_ON) != 0) newtMods |= com.jogamp.newt.event.InputEvent.ALT_MASK; - + 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(final android.view.accessibility.AccessibilityEvent event, final 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(final android.view.KeyEvent aEvent, final com.jogamp.newt.Window newtSource, final 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(final android.view.KeyEvent aEvent, final short newtType, final com.jogamp.newt.Window newtSource, final 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(final android.view.KeyEvent aEvent, final short newtKeyCode, final short newtType, final 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(final android.view.KeyEvent aEvent, final short newtType, final short newtKeyCode, final 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; } - 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)); + 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(final android.content.Context context, final 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); } - int type = eventTypeANDROID2NEWT.get(event.getAction()); - if(0xFFFFFFFF != type) { - int rotation = 0; - int clickCount = 1; + } + + private static void collectPointerData(final MotionEvent e, final int count, final int[] x, final int[] y, final float[] pressure, + final short[] pointerIds, final MouseEvent.PointerType[] pointerTypes) { + for(int i=0; i < count; i++) { + x[i] = (int)e.getX(i); + y[i] = (int)e.getY(i); + pressure[i] = e.getPressure(i); + pointerIds[i] = (short)e.getPointerId(i); + if( pressure[i] > maxPressure ) { + maxPressure = pressure[i]; + } + pointerTypes[i] = aToolType2PointerType( e.getToolType(i) ); + if(DEBUG_MOUSE_EVENT) { + System.err.println("createMouseEvent: ptr-data["+i+"] "+x[i]+"/"+y[i]+", pressure "+pressure[i]+", id "+pointerIds[i]+", type "+pointerTypes[i]); + } + } + } + + public boolean sendPointerEvent(final boolean enqueue, final boolean wait, final boolean setFocusOnDown, final boolean isOnTouchEvent, + final android.view.MotionEvent event, final jogamp.newt.driver.android.WindowDriver newtSource) { + if(DEBUG_MOUSE_EVENT) { + System.err.println("createMouseEvent: isOnTouchEvent "+isOnTouchEvent+", "+event); + } + + if( event.getPressure() > maxPressure ) { + maxPressure = event.getPressure(); // write to static field intended + } + + // + // 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 - if(null!=newtSource) { - if(newtSource.isPointerConfined()) { - modifiers |= InputEvent.CONFINED_MASK; - } - if(!newtSource.isPointerVisible()) { - modifiers |= InputEvent.INVISIBLE_MASK; - } + switch( aType ) { + case android.view.MotionEvent.ACTION_DOWN: + case android.view.MotionEvent.ACTION_POINTER_DOWN: + // modifier button-mask will be set on doPointerEvent(..) + if( setFocusOnDown ) { + newtSource.focusChanged(false, true); + } } - - 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 }; + + // + // 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); + } + collectPointerData(event, pCount, x, y, pressure, pointerIds, pointerTypes); } - } - return null; // no mapping .. + newtSource.doPointerEvent(enqueue, wait, pointerTypes, nType, modifiers, + pIndex, pointerIds, button, 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..8d20ef448 --- /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(final WindowDriver newtWindow, final android.content.Context context, final android.os.Handler handler) { + this.newtWindow = newtWindow; + this.factory = new AndroidNewtEventFactory(context, handler); + } + + private final boolean processTouchMotionEvents(final View v, final android.view.MotionEvent event, final 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(final InterruptedException e) { } + return true; // consumed/handled, further interest in events + } + return false; // no mapping, no further interest in the event! + } + + @Override + public boolean onTouch(final View v, final android.view.MotionEvent event) { + return processTouchMotionEvents(v, event, true); + } + + @Override + public boolean onGenericMotion(final View v, final android.view.MotionEvent event) { + return processTouchMotionEvents(v, event, false); + } + + @Override + public boolean onKey(final View v, final int keyCode, final 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(final View v, final 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 17eb6a2fb..eccdd63cf 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.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 jogamp.newt.driver.awt; @@ -47,13 +47,15 @@ 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") @@ -61,22 +63,27 @@ public class AWTCanvas extends Canvas { private GraphicsDevice device; private GraphicsConfiguration chosen; private AWTGraphicsConfiguration awtConfig; - - private WindowDriver newtWindowImpl; + private volatile JAWTWindow jawtWindow=null; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle private CapabilitiesChooser chooser=null; - private CapabilitiesImmutable capabilities; + private final CapabilitiesImmutable capabilities; + private final UpstreamScalable upstreamScale; + + public static interface UpstreamScalable { + int[] getReqPixelScale(); + void setHasPixelScale(final int[] pixelScale); + } private boolean displayConfigChanged=false; - public AWTCanvas(WindowDriver newtWindowImpl, CapabilitiesImmutable capabilities, CapabilitiesChooser chooser) { + public AWTCanvas(final CapabilitiesImmutable capabilities, final CapabilitiesChooser chooser, final UpstreamScalable upstreamScale) { super(); - this.newtWindowImpl = newtWindowImpl; if(null==capabilities) { throw new NativeWindowException("Capabilities null"); } this.capabilities=capabilities; this.chooser=chooser; + this.upstreamScale = upstreamScale; } public AWTGraphicsConfiguration getAWTGraphicsConfiguration() { @@ -88,8 +95,8 @@ public class AWTCanvas extends Canvas { * canvas from interfering with the OpenGL rendering. */ @Override - public void update(Graphics g) { - paint(g); + public void update(final Graphics g) { + // paint(g); } /** Overridden to cause OpenGL rendering to be performed during @@ -98,16 +105,16 @@ public class AWTCanvas extends Canvas { properly. */ @Override - public void paint(Graphics g) { - newtWindowImpl.windowRepaint(0, 0, getWidth(), getHeight()); + public void paint(final Graphics g) { } - + public boolean hasDeviceChanged() { - boolean res = displayConfigChanged; + final boolean res = displayConfigChanged; displayConfigChanged=false; return res; } + @Override public void addNotify() { /** @@ -120,8 +127,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"); @@ -137,12 +143,34 @@ public class AWTCanvas extends Canvas { // after native peer is valid: Windows disableBackgroundErase(); - GraphicsConfiguration gc = super.getGraphicsConfiguration(); + { + jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); + // trigger initialization cycle + jawtWindow.setSurfaceScale(upstreamScale.getReqPixelScale() ); + jawtWindow.lockSurface(); + upstreamScale.setHasPixelScale(jawtWindow.getCurrentSurfaceScale(new int[2])); + jawtWindow.unlockSurface(); + } + + final 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(); @@ -152,29 +180,39 @@ 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(); + final AbstractGraphicsDevice adevice = awtConfig.getNativeGraphicsConfiguration().getScreen().getDevice(); String adeviceMsg=null; if(Window.DEBUG_IMPLEMENTATION) { adeviceMsg = adevice.toString(); } - boolean closed = adevice.close(); + final 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. @@ -188,7 +226,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. @@ -198,30 +236,30 @@ 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. */ - AWTGraphicsConfiguration config = chooseGraphicsConfiguration( + final AWTGraphicsConfiguration config = chooseGraphicsConfiguration( awtConfig.getChosenCapabilities(), awtConfig.getRequestedCapabilities(), chooser, gc.getDevice()); final GraphicsConfiguration compatible = (null!=config)?config.getAWTGraphicsConfiguration():null; if(Window.DEBUG_IMPLEMENTATION) { - Exception e = new Exception("Info: Call Stack: "+Thread.currentThread().getName()); + final Exception e = new Exception("Info: Call Stack: "+Thread.currentThread().getName()); e.printStackTrace(); System.err.println("Created Config (n): HAVE GC "+chosen); System.err.println("Created Config (n): THIS GC "+gc); @@ -239,7 +277,7 @@ public class AWTCanvas extends Canvas { chosen = compatible; if( !config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities())) { displayConfigChanged=true; - } + } awtConfig = config; } } @@ -249,7 +287,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) { @@ -269,14 +307,14 @@ public class AWTCanvas extends Canvas { return gc; } - private static AWTGraphicsConfiguration chooseGraphicsConfiguration(CapabilitiesImmutable capsChosen, - CapabilitiesImmutable capsRequested, - CapabilitiesChooser chooser, - GraphicsDevice device) { - final AbstractGraphicsScreen aScreen = null != device ? + private static AWTGraphicsConfiguration chooseGraphicsConfiguration(final CapabilitiesImmutable capsChosen, + final CapabilitiesImmutable capsRequested, + final CapabilitiesChooser chooser, + final GraphicsDevice device) { + final AbstractGraphicsScreen aScreen = null != device ? AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT): AWTGraphicsScreen.createDefault(); - AWTGraphicsConfiguration config = (AWTGraphicsConfiguration) + final AWTGraphicsConfiguration config = (AWTGraphicsConfiguration) GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, capsChosen.getClass()).chooseGraphicsConfiguration(capsChosen, capsRequested, chooser, aScreen, VisualIDHolder.VID_UNDEFINED); @@ -298,6 +336,7 @@ public class AWTCanvas extends Canvas { if (!disableBackgroundEraseInitialized) { try { AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override public Object run() { try { Class<?> clazz = getToolkit().getClass(); @@ -307,16 +346,16 @@ public class AWTCanvas extends Canvas { clazz.getDeclaredMethod("disableBackgroundErase", new Class[] { Canvas.class }); disableBackgroundEraseMethod.setAccessible(true); - } catch (Exception e) { + } catch (final Exception e) { clazz = clazz.getSuperclass(); } } - } catch (Exception e) { + } catch (final Exception e) { } return null; } }); - } catch (Exception e) { + } catch (final Exception e) { } disableBackgroundEraseInitialized = true; if(Window.DEBUG_IMPLEMENTATION) { @@ -328,7 +367,7 @@ public class AWTCanvas extends Canvas { Throwable t=null; try { disableBackgroundEraseMethod.invoke(getToolkit(), new Object[] { this }); - } catch (Exception e) { + } catch (final Exception e) { // FIXME: workaround for 6504460 (incorrect backport of 6333613 in 5.0u10) // throw new GLException(e); t = e; diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java index 8771f5c74..39e68e48c 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java @@ -30,27 +30,30 @@ package jogamp.newt.driver.awt; 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.common.awt.AWTEDTExecutor; import jogamp.newt.Debug; public class AWTEDTUtil 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 ThreadGroup threadGroup; private final String name; private final Runnable dispatchMessages; - private NewtEventDispatchThread nedt = null; + private NEDT nedt = null; private int start_iter=0; private static long pollPeriod = EDTUtil.defaultEDTPollPeriod; - public AWTEDTUtil(ThreadGroup tg, String name, Runnable dispatchMessages) { + public AWTEDTUtil(final ThreadGroup tg, final String name, final Runnable dispatchMessages) { this.threadGroup = tg; this.name=Thread.currentThread().getName()+"-"+name+"-EDT-"; this.dispatchMessages=dispatchMessages; - this.nedt = new NewtEventDispatchThread(threadGroup, name); + this.nedt = new NEDT(threadGroup, name); this.nedt.setDaemon(true); // don't stop JVM from shutdown .. } @@ -60,31 +63,38 @@ public class AWTEDTUtil implements EDTUtil { } @Override - final public void setPollPeriod(long ms) { - pollPeriod = ms; + final public void setPollPeriod(final long ms) { + pollPeriod = ms; // writing to static field is intended } - + @Override - final public void reset() { - synchronized(edtLock) { - waitUntilStopped(); + public final void 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()+": EDT reset - edt: "+nedt); + System.err.println(Thread.currentThread()+": AWT-EDT reset - edt: "+nedt); } - this.nedt = new NewtEventDispatchThread(threadGroup, name); - this.nedt.setDaemon(true); // don't stop JVM from shutdown .. + if( nedt.getState() != Thread.State.NEW ) { + nedt = new NEDT(threadGroup, name); + nedt.setDaemon(true); // don't stop JVM from shutdown .. + } + startImpl(); + } + if( !nedt.isRunning() ) { + throw new RuntimeException("EDT could not be started: "+nedt); } } private final void startImpl() { if(nedt.isAlive()) { - throw new RuntimeException("EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning()+", edt: "+nedt); + throw new RuntimeException("AWT-EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", edt: "+nedt); } start_iter++; nedt.setName(name+start_iter); - nedt.shouldStop = false; if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT START - edt: "+nedt); + System.err.println(Thread.currentThread()+": AWT-EDT START - edt: "+nedt); // Thread.dumpStack(); } nedt.start(); @@ -99,100 +109,148 @@ public class AWTEDTUtil implements EDTUtil { public final boolean isCurrentThreadNEDT() { return nedt == Thread.currentThread(); } - + @Override public final boolean isCurrentThreadEDTorNEDT() { - return EventQueue.isDispatchThread() || nedt == Thread.currentThread(); + return EventQueue.isDispatchThread() || nedt == Thread.currentThread(); } - + @Override final public boolean isRunning() { - return nedt.isRunning() ; // AWT is always running + return nedt.isRunning() ; } @Override - public final void invokeStop(Runnable task) { - invokeImpl(true, task, true); + public final boolean invokeStop(final boolean wait, final Runnable task) { + return invokeImpl(wait, task, true); } @Override - public final void invoke(boolean wait, Runnable task) { - invokeImpl(wait, task, false); + public final boolean invoke(final boolean wait, final Runnable task) { + return invokeImpl(wait, task, false); } - - private void invokeImpl(boolean wait, Runnable task, boolean stop) { - if(task == null) { - throw new RuntimeException("Null Runnable"); - } - synchronized(edtLock) { // lock the EDT status - if( nedt.shouldStop ) { - // drop task .. - if(DEBUG) { - System.err.println("Warning: EDT about (1) to stop, won't enqueue new task: "+nedt); - Thread.dumpStack(); + + private final boolean invokeImpl(boolean wait, final Runnable task, final 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; } - return; - } - // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); - // Thread.dumpStack(); - if(stop) { - nedt.shouldStop = true; - if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); - // Thread.dumpStack(); + 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); + } } } - - // start if should not stop && not started yet - if( !stop && !nedt.isRunning() ) { - startImpl(); + if( wait ) { + try { + rTaskLock.wait(); // free lock, allow execution of rTask + } catch (final 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; } - AWTEDTExecutor.singleton.invoke(wait, task); - } + } @Override - final public void waitUntilIdle() { - final NewtEventDispatchThread _edt; + final public boolean waitUntilIdle() { + final NEDT _edt; synchronized(edtLock) { _edt = nedt; } - if(!_edt.isRunning() || EventQueue.isDispatchThread() || _edt == Thread.currentThread()) { - return; + if(!_edt.isRunning || _edt == Thread.currentThread() || EventQueue.isDispatchThread()) { + return false; } try { AWTEDTExecutor.singleton.invoke(true, new Runnable() { + @Override public void run() { } }); - } catch (Exception e) { } + } catch (final Exception e) { } + return true; } @Override - final public void waitUntilStopped() { + final public boolean waitUntilStopped() { synchronized(edtLock) { - if(nedt.isRunning() && nedt != Thread.currentThread() ) { - while(nedt.isRunning()) { + if( nedt.isRunning && nedt != Thread.currentThread() && !EventQueue.isDispatchThread() ) { + while( nedt.isRunning ) { try { edtLock.wait(); - } catch (InterruptedException e) { + } catch (final InterruptedException e) { e.printStackTrace(); } } + return true; + } else { + return false; } } } - - class NewtEventDispatchThread extends Thread { + + class NEDT extends Thread { volatile boolean shouldStop = false; volatile boolean isRunning = false; Object sync = new Object(); - public NewtEventDispatchThread(ThreadGroup tg, String name) { + public NEDT(final ThreadGroup tg, final String name) { super(tg, name); } final public boolean isRunning() { - return isRunning; + return isRunning && !shouldStop; } @Override @@ -201,58 +259,55 @@ public class AWTEDTUtil implements EDTUtil { 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()+": EDT run() START "+ getName()); + System.err.println(getName()+": AWT-EDT run() START "+ getName()); } RuntimeException error = null; try { do { // event dispatch if(!shouldStop) { - // FIXME: Determine whether we require to run the - // delivery of events (dispatch) on AWT-EDT. - // Since the WindowDriver itself delivers all Window related events, - // this shall not be required. - // AWTEDTExecutor.singleton.invoke(true, dispatchMessages); - dispatchMessages.run(); + // 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) { + } catch (final InterruptedException e) { e.printStackTrace(); } } } } while(!shouldStop) ; - } catch (Throwable t) { + } catch (final Throwable t) { // handle errors .. shouldStop = true; if(t instanceof RuntimeException) { error = (RuntimeException) t; } else { - error = new RuntimeException("Within EDT", t); + error = new RuntimeException("Within AWT-EDT", t); } } finally { if(DEBUG) { - System.err.println(getName()+": EDT run() END "+ getName()+", "+error); + System.err.println(getName()+": AWT-EDT run() END "+ getName()+", "+error); } synchronized(edtLock) { - isRunning = !shouldStop; - if(!isRunning) { - edtLock.notifyAll(); - } + isRunning = false; + edtLock.notifyAll(); } if(DEBUG) { - System.err.println(getName()+": EDT run() EXIT "+ getName()+", exception: "+error); + System.err.println(getName()+": AWT-EDT run() EXIT "+ getName()+", exception: "+error); } if(null!=error) { throw error; @@ -260,7 +315,7 @@ public class AWTEDTUtil implements EDTUtil { } // finally } // run() } // EventDispatchThread - + } diff --git a/src/newt/classes/jogamp/newt/driver/awt/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/awt/DisplayDriver.java index 70e9ba570..23da69dba 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/DisplayDriver.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,34 +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 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.DefaultEDTUtil; import jogamp.newt.DisplayImpl; public class DisplayDriver extends DisplayImpl { public DisplayDriver() { } + @Override protected void createNativeImpl() { aDevice = AWTGraphicsDevice.createDefault(); } - protected void setAWTGraphicsDevice(AWTGraphicsDevice d) { + protected void setAWTGraphicsDevice(final AWTGraphicsDevice d) { aDevice = d; } + @Override protected EDTUtil createEDTUtil() { final EDTUtil def; if(NewtFactory.useEDT()) { - def = new AWTEDTUtil(Thread.currentThread().getThreadGroup(), "AWTDisplay-"+getFQName(), dispatchMessagesRunnable); + def = new AWTEDTUtil(Thread.currentThread().getThreadGroup(), "AWTDisplay-"+getFQName(), dispatchMessagesRunnable); if(DEBUG) { System.err.println("Display.createNative("+getFQName()+") Create EDTUtil: "+def.getClass().getName()); } @@ -66,8 +69,12 @@ public class DisplayDriver extends DisplayImpl { return def; } - protected void closeNativeImpl() { } + @Override + protected void closeNativeImpl(final 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 index 6b1283a00..57948cfc3 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/ScreenDriver.java +++ b/src/newt/classes/jogamp/newt/driver/awt/ScreenDriver.java @@ -1,21 +1,21 @@ /* * 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 @@ -28,29 +28,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 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 javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; 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) { + protected void setAWTGraphicsScreen(final AWTGraphicsScreen s) { aScreen = s; } @@ -62,20 +66,70 @@ public class ScreenDriver extends ScreenImpl { super.updateVirtualScreenOriginAndSize(); } + @Override 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()); + + @Override + protected int validateScreenIndex(final int idx) { + return idx; // pass through ... + } + + private static MonitorMode getModeProps(final Cache cache, final 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; } + final 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(final 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()); + + 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 + props[i++] = ScreenImpl.default_sm_widthmm; // FIXME + props[i++] = ScreenImpl.default_sm_heightmm; // FIXME + props[i++] = 0; // rotated viewport x pixel-units + props[i++] = 0; // rotated viewport y pixel-units + props[i++] = currentMode.getRotatedWidth(); // rotated viewport width pixel-units + props[i++] = currentMode.getRotatedHeight(); // rotated viewport height pixel-units + props[i++] = 0; // rotated viewport x window-units + props[i++] = 0; // rotated viewport y window-units + props[i++] = currentMode.getRotatedWidth(); // rotated viewport width window-units + props[i++] = currentMode.getRotatedHeight(); // rotated viewport height window-units + MonitorModeProps.streamInMonitorDevice(null, cache, this, cache.monitorModes, currentMode, props, 0); + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(final MonitorDevice monitor) { + return null; + } + + @Override + protected boolean setCurrentMonitorModeImpl(final MonitorDevice monitor, final 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 index bee43a95e..06dcb8ff5 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/awt/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,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.awt; @@ -40,11 +40,15 @@ 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; @@ -68,146 +72,205 @@ public class WindowDriver extends WindowImpl { return new Class<?>[] { Container.class } ; } - public WindowDriver(Container container) { + public WindowDriver(final Container container) { super(); - this.container = container; + this.awtContainer = container; if(container instanceof Frame) { - frame = (Frame) container; + awtFrame = (Frame) container; } } private boolean owningFrame; - private Container container = null; - private Frame frame = null; // same instance as container, just for impl. convenience - private AWTCanvas canvas; + private Container awtContainer = null; + /** same instance as container, just for impl. convenience */ + private Frame awtFrame = null; + private AWTCanvas awtCanvas; - protected void requestFocusImpl(boolean reparented) { - container.requestFocus(); + @Override + protected void requestFocusImpl(final boolean reparented) { + awtContainer.requestFocus(); } @Override protected void setTitleImpl(final String title) { - if (frame != null) { - frame.setTitle(title); + if (awtFrame != null) { + awtFrame.setTitle(title); } } + private final AWTCanvas.UpstreamScalable upstreamScalable = new AWTCanvas.UpstreamScalable() { + @Override + public int[] getReqPixelScale() { + return WindowDriver.this.reqPixelScale; + } + + @Override + public void setHasPixelScale(final int[] pixelScale) { + System.arraycopy(pixelScale, 0, WindowDriver.this.hasPixelScale, 0, 2); + } + }; + + @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==container) { - frame = new Frame(); - container = frame; + if(null==awtContainer) { + awtFrame = new Frame(); + awtContainer = awtFrame; owningFrame=true; } else { owningFrame=false; - defineSize(container.getWidth(), container.getHeight()); - definePosition(container.getX(), container.getY()); + defineSize(awtContainer.getWidth(), awtContainer.getHeight()); + definePosition(awtContainer.getX(), awtContainer.getY()); } - if(null!=frame) { - frame.setTitle(getTitle()); + if(null!=awtFrame) { + awtFrame.setTitle(getTitle()); + } + awtContainer.setLayout(new BorderLayout()); + + if( null == awtCanvas ) { + awtCanvas = new AWTCanvas(capsRequested, WindowDriver.this.capabilitiesChooser, upstreamScalable); + + // 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 } - container.setLayout(new BorderLayout()); - - canvas = new AWTCanvas(this, capsRequested, WindowDriver.this.capabilitiesChooser); - - // canvas.addComponentListener(listener); - container.add(canvas, BorderLayout.CENTER); - - // via EDT .. - new AWTMouseAdapter(this).addTo(canvas); // fwd all AWT Mouse events to here - new AWTKeyAdapter(this).addTo(canvas); // fwd all AWT Key events to here - - // direct w/o EDT - new AWTWindowAdapter(new LocalWindowListener(), this).addTo(canvas); // 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 .. + + final NativeWindow nw = awtCanvas.getNativeWindow(); + if( null != nw ) { + setGraphicsConfiguration( awtCanvas.getAWTGraphicsConfiguration() ); + setWindowHandle( nw.getWindowHandle() ); + } } + @Override protected void closeNativeImpl() { - setWindowHandle(0); // just a marker .. - if(null!=container) { - container.setVisible(false); - container.remove(canvas); - container.setEnabled(false); - canvas.setEnabled(false); + setWindowHandle(0); + if(null!=awtContainer) { + awtContainer.setVisible(false); + awtContainer.remove(awtCanvas); + awtContainer.setEnabled(false); + awtCanvas.setEnabled(false); } - if(owningFrame && null!=frame) { - frame.dispose(); + if(owningFrame && null!=awtFrame) { + awtFrame.dispose(); owningFrame=false; - frame = null; } + awtCanvas = null; + awtFrame = null; + awtContainer = null; } @Override public boolean hasDeviceChanged() { - boolean res = canvas.hasDeviceChanged(); + final boolean res = awtCanvas.hasDeviceChanged(); if(res) { - final AWTGraphicsConfiguration cfg = canvas.getAWTGraphicsConfiguration(); + 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; } - 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); + @Override + protected void updateInsetsImpl(final javax.media.nativewindow.util.Insets insets) { + final Insets contInsets = awtContainer.getInsets(); + insets.set(contInsets.left, contInsets.right, contInsets.top, 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()); + private void setCanvasSizeImpl(final int width, final 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(final int width, final 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(final int x, final int y, final int width, final int height, final 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"); } } } - - final Dimension szClient = new Dimension(width, height); - canvas.setMinimumSize(szClient); - canvas.setPreferredSize(szClient); - canvas.setSize(szClient); - if(DEBUG_IMPLEMENTATION) { - final Insets insets = container.getInsets(); - final Dimension szContainer = new Dimension(width + insets.left + insets.right, - height + insets.top + insets.bottom); - System.err.println(getThreadName()+": AWTWindow new size: szClient "+szClient+", szCont "+szContainer+", insets "+insets); - } - + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { - if(null != frame) { - frame.pack(); + 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); } - container.validate(); - container.setVisible(0 != ( FLAG_IS_VISIBLE & flags)); } - - container.setLocation(x, y); - + 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 = canvas.getAWTGraphicsConfiguration(); + // oops ?? + final AWTGraphicsConfiguration cfg = awtCanvas.getAWTGraphicsConfiguration(); if(null == cfg) { throw new NativeWindowException("Error: !hasDeviceChanged && null == GraphicsConfiguration: "+this); } @@ -215,63 +278,64 @@ public class WindowDriver extends WindowImpl { } } visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); - } else { - container.invalidate(); - if(null != frame) { - frame.pack(); - } - container.validate(); } - + return true; } - protected Point getLocationOnScreenImpl(int x, int y) { - java.awt.Point ap = canvas.getLocationOnScreen(); + @Override + protected Point getLocationOnScreenImpl(final int x, final int y) { + final 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 Object getWrappedWindow() { - return canvas; + public NativeSurface getWrappedSurface() { + return ( null != awtCanvas ) ? awtCanvas.getNativeWindow() : null; } - class LocalWindowListener implements com.jogamp.newt.event.WindowListener { + class LocalWindowListener implements com.jogamp.newt.event.WindowListener { @Override - public void windowMoved(com.jogamp.newt.event.WindowEvent e) { - if(null!=container) { - WindowDriver.this.positionChanged(false, container.getX(), container.getY()); + public void windowMoved(final 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!=canvas) { - WindowDriver.this.sizeChanged(false, canvas.getWidth(), canvas.getHeight(), false); + public void windowResized(final 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, getSurfaceWidth(), getSurfaceHeight()); } } @Override - public void windowDestroyNotify(WindowEvent e) { + public void windowDestroyNotify(final WindowEvent e) { WindowDriver.this.windowDestroyNotify(false); } @Override - public void windowDestroyed(WindowEvent e) { - if(isNativeValid()) { - WindowDriver.this.windowDestroyNotify(true); - } - + public void windowDestroyed(final WindowEvent e) { + // Not fwd by AWTWindowAdapter, synthesized by NEWT } @Override - public void windowGainedFocus(WindowEvent e) { - WindowDriver.this.focusChanged(false, true); + public void windowGainedFocus(final WindowEvent e) { + WindowDriver.this.focusChanged(false, true); } @Override - public void windowLostFocus(WindowEvent e) { - WindowDriver.this.focusChanged(false, false); + public void windowLostFocus(final WindowEvent e) { + WindowDriver.this.focusChanged(false, false); } @Override - public void windowRepaint(WindowUpdateEvent e) { - WindowDriver.this.windowRepaint(false, 0, 0, getWidth(), getHeight()); + public void windowRepaint(final WindowUpdateEvent e) { + if(null!=awtCanvas) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window Repaint: "+awtCanvas); + } + WindowDriver.this.windowRepaint(false, 0, 0, getSurfaceWidth(), getSurfaceHeight()); + } } } } diff --git a/src/newt/classes/jogamp/newt/driver/bcm/egl/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/egl/DisplayDriver.java index 65ca63eec..ceb337150 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/egl/DisplayDriver.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,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.bcm.egl; @@ -60,6 +60,7 @@ public class DisplayDriver extends jogamp.newt.DisplayImpl { public DisplayDriver() { } + @Override protected void createNativeImpl() { final long handle = CreateDisplay(ScreenDriver.fixedWidth, ScreenDriver.fixedHeight); if (handle == EGL.EGL_NO_DISPLAY) { @@ -68,12 +69,15 @@ public class DisplayDriver extends jogamp.newt.DisplayImpl { aDevice = new EGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, handle, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT, null); } - protected void closeNativeImpl() { + @Override + protected void closeNativeImpl(final 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 index deb2a534b..2cd47313a 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/egl/ScreenDriver.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/egl/ScreenDriver.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,14 +29,19 @@ * 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.Dimension; -import javax.media.nativewindow.util.Point; +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 { @@ -48,23 +53,66 @@ public class ScreenDriver extends jogamp.newt.ScreenImpl { public ScreenDriver() { } + @Override protected void createNativeImpl() { aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); } + @Override protected void closeNativeImpl() { } - protected int validateScreenIndex(int idx) { - return 0; // only one screen available + @Override + protected int validateScreenIndex(final int idx) { + return 0; // only one screen available + } + + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(final 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 pixel-units + props[i++] = 0; // rotated viewport y pixel-units + props[i++] = fixedWidth; // FIXME rotated viewport width pixel-units + props[i++] = fixedHeight; // FIXME rotated viewport height pixel-units + props[i++] = 0; // rotated viewport x window-units + props[i++] = 0; // rotated viewport y window-units + props[i++] = fixedWidth; // FIXME rotated viewport width window-units + props[i++] = fixedHeight; // FIXME rotated viewport height window-units + MonitorModeProps.streamInMonitorDevice(null, cache, this, cache.monitorModes, currentMode, props, 0); } - - protected void getVirtualScreenOriginAndSize(Point virtualOrigin, Dimension virtualSize) { - virtualOrigin.setX(0); - virtualOrigin.setY(0); - virtualSize.setWidth(fixedWidth); // FIXME - virtualSize.setHeight(fixedHeight); // FIXME + + @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(final Rectangle viewport, final Rectangle viewportInWindowUnits) { + viewport.set(0, 0, fixedWidth, fixedHeight); // FIXME + viewportInWindowUnits.set(viewport); + } + //---------------------------------------------------------------------- // Internals only // diff --git a/src/newt/classes/jogamp/newt/driver/bcm/egl/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/bcm/egl/WindowDriver.java index 49d3d98ba..aec85f875 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/egl/WindowDriver.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,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.bcm.egl; @@ -52,11 +52,12 @@ public class WindowDriver extends jogamp.newt.WindowImpl { 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(), capsRequested).chooseGraphicsConfiguration( capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen(), VisualIDHolder.VID_UNDEFINED); @@ -64,7 +65,8 @@ public class WindowDriver extends jogamp.newt.WindowImpl { throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); } setGraphicsConfiguration(cfg); - setSizeImpl(getScreen().getWidth(), getScreen().getHeight()); + final int[] winSize = convertToWindowUnits(new int[] {getScreen().getWidth(), getScreen().getHeight()}); + setSizeImpl(winSize[0], winSize[1]); setWindowHandle(realizeWindow(true, getWidth(), getHeight())); if (0 == getWindowHandle()) { @@ -72,15 +74,17 @@ public class WindowDriver extends jogamp.newt.WindowImpl { } } + @Override protected void closeNativeImpl() { if(0!=windowHandleClose) { CloseWindow(getDisplayHandle(), windowHandleClose); } } - protected void requestFocusImpl(boolean reparented) { } + @Override + protected void requestFocusImpl(final boolean reparented) { } - protected void setSizeImpl(int width, int height) { + protected void setSizeImpl(final int width, final int height) { if(0!=getWindowHandle()) { // n/a in BroadcomEGL System.err.println("BCEGL Window.setSizeImpl n/a in BroadcomEGL with realized window"); @@ -89,8 +93,9 @@ public class WindowDriver extends jogamp.newt.WindowImpl { } } - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { - if(0!=getWindowHandle()) { + @Override + protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, final int flags) { + if(0!=getWindowHandle()) { if(0 != ( FLAG_CHANGE_FULLSCREEN & flags)) { if( 0 != ( FLAG_IS_FULLSCREEN & flags) ) { // n/a in BroadcomEGL @@ -110,19 +115,21 @@ public class WindowDriver 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; } - protected Point getLocationOnScreenImpl(int x, int y) { + @Override + protected Point getLocationOnScreenImpl(final int x, final int y) { return new Point(x,y); } - protected void updateInsetsImpl(Insets insets) { - // nop .. + @Override + protected void updateInsetsImpl(final Insets insets) { + // nop .. } @Override @@ -141,11 +148,11 @@ public class WindowDriver extends jogamp.newt.WindowImpl { private native void SwapWindow(long eglDisplayHandle, long eglWindowHandle); - private long realizeWindow(boolean chromaKey, int width, int height) { + private long realizeWindow(final boolean chromaKey, final int width, final int height) { if(DEBUG_IMPLEMENTATION) { System.err.println("BCEGL Window.realizeWindow() with: chroma "+chromaKey+", "+width+"x"+height+", "+getGraphicsConfiguration()); } - long handle = CreateWindow(getDisplayHandle(), chromaKey, width, height); + final long handle = CreateWindow(getDisplayHandle(), chromaKey, width, height); if (0 == handle) { throw new NativeWindowException("Error native Window Handle is null"); } @@ -153,9 +160,9 @@ public class WindowDriver extends jogamp.newt.WindowImpl { return handle; } - private void windowCreated(int cfgID, int width, int height) { + private void windowCreated(final int cfgID, final int width, final int height) { defineSize(width, height); - GLCapabilitiesImmutable capsReq = (GLCapabilitiesImmutable) getGraphicsConfiguration().getRequestedCapabilities(); + final GLCapabilitiesImmutable capsReq = (GLCapabilitiesImmutable) getGraphicsConfiguration().getRequestedCapabilities(); final AbstractGraphicsConfiguration cfg = EGLGraphicsConfiguration.create(capsReq, getScreen().getGraphicsScreen(), cfgID); if (null == cfg) { throw new NativeWindowException("Error creating EGLGraphicsConfiguration from id: "+cfgID+", "+this); 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 index 08c5c573c..1b67fa755 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/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. @@ -28,15 +28,28 @@ 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(); @@ -49,6 +62,18 @@ public class DisplayDriver extends DisplayImpl { 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 (final Exception e) { + e.printStackTrace(); + } + } + defaultPointerIconImage = image; } public static void initSingleton() { @@ -57,22 +82,128 @@ public class DisplayDriver extends DisplayImpl { 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; - protected void closeNativeImpl() { + @Override + protected void closeNativeImpl(final AbstractGraphicsDevice aDevice) { aDevice.close(); + CloseBCMDisplay0(bcmHandle); + bcmHandle = 0; } + /* pp */ final long getBCMHandle() { return bcmHandle; } + + @Override protected void dispatchMessagesNative() { - DispatchMessages(); + DispatchMessages0(); + } + + // @Override + // public final PixelFormat getNativePointerIconPixelFormat() { return PixelFormat.BGRA8888; } + + @Override + protected final long createPointerIconImpl(final PixelFormat pixelformat, final int width, final 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, final 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(final long bcmHandle, final Buffer pixels, final int width, final int height, final int hotX, final 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(); - protected static native boolean initIDs(); - private native void DispatchMessages(); + 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 index 787d1a1b4..64cae75f6 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/ScreenDriver.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/ScreenDriver.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. @@ -29,9 +29,12 @@ package jogamp.newt.driver.bcm.vc.iv; import javax.media.nativewindow.DefaultGraphicsScreen; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; +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 { @@ -42,31 +45,75 @@ public class ScreenDriver extends ScreenImpl { public ScreenDriver() { } + @Override protected void createNativeImpl() { aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); initNative(); } + @Override 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); + @Override + protected int validateScreenIndex(final int idx) { + return 0; // only one screen available + } + + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(final 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 pixel-units + props[i++] = 0; // rotated viewport y pixel-units + props[i++] = cachedWidth; // rotated viewport width pixel-units + props[i++] = cachedWidth; // rotated viewport height pixel-units + props[i++] = 0; // rotated viewport x window-units + props[i++] = 0; // rotated viewport y window-units + props[i++] = cachedWidth; // rotated viewport width window-units + props[i++] = cachedWidth; // rotated viewport height window-units + MonitorModeProps.streamInMonitorDevice(null, cache, this, cache.monitorModes, currentMode, props, 0); } - - protected void setScreenSize(int width, int height) { - cachedWidth = width; - cachedHeight = height; + + @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(final Rectangle viewport, final Rectangle viewportInWindowUnits) { + viewport.set(0, 0, cachedWidth, cachedHeight); + viewportInWindowUnits.set(viewport); + } + + /** Called from {@link #initNative()}. */ + protected void setScreenSize(final int width, final int height) { + cachedWidth = width; // write to static field intended + cachedHeight = height; // write to static field intended } - + private static int cachedWidth = 0; - private static int cachedHeight = 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 index 7b8d2e958..ad6d0b688 100644 --- a/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/bcm/vc/iv/WindowDriver.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. @@ -29,15 +29,24 @@ 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"; @@ -47,110 +56,160 @@ public class WindowDriver extends WindowImpl { } 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)"); } - // FIXME: Hack - Native BCM_VC_IV code CreateWindow() uses the default alpha value setting, - // which is alpha:8 ! Hence we require to chose alpha from the egl configurations. - // TODO: Properly select the alpha mode in CreateWindow()! This will allow this hack. - final Capabilities capsChosen = (Capabilities) capsRequested.cloneMutable(); - capsChosen.setAlphaBits(1); - + 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( - capsChosen, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen(), VisualIDHolder.VID_UNDEFINED); + 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 = CreateWindow(getWidth(), getHeight()); + nativeWindowHandle = CreateWindow0(display.getBCMHandle(), layer, getX(), getY(), getWidth(), getHeight(), + chosenCaps.isBackgroundOpaque(), chosenCaps.getAlphaBits()); if (nativeWindowHandle == 0) { throw new NativeWindowException("Error creating egl window: "+cfg); } - setVisible0(nativeWindowHandle, false); setWindowHandle(nativeWindowHandle); if (0 == getWindowHandle()) { throw new NativeWindowException("Error native Window Handle is null"); } windowHandleClose = nativeWindowHandle; - addWindowListener(LinuxMouseTracker.getSingleton()); - focusChanged(false, true); + + addWindowListener(linuxEventDeviceTracker); + addWindowListener(linuxMouseTracker); + focusChanged(false, true); } + @Override protected void closeNativeImpl() { - removeWindowListener(LinuxMouseTracker.getSingleton()); - + final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); + final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) getGraphicsConfiguration().getScreen().getDevice(); + + removeWindowListener(linuxMouseTracker); + removeWindowListener(linuxEventDeviceTracker); + if(0!=windowHandleClose) { - CloseWindow(windowHandleClose, windowUserData); - windowUserData=0; + 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()); } } - protected void requestFocusImpl(boolean reparented) { + @Override + protected void requestFocusImpl(final boolean reparented) { focusChanged(false, true); } - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { - if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { - setVisible0(nativeWindowHandle, 0 != ( FLAG_IS_VISIBLE & flags)); - visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); - } - - if(0!=nativeWindowHandle) { - if(0 != ( FLAG_CHANGE_FULLSCREEN & flags)) { - final boolean fs = 0 != ( FLAG_IS_FULLSCREEN & flags) ; - setFullScreen0(nativeWindowHandle, fs); - if(fs) { - return true; - } - } - // int _x=(x>=0)?x:this.x; - // int _y=(x>=0)?y:this.y; - width=(width>0)?width:getWidth(); - height=(height>0)?height:getHeight(); - if(width>0 || height>0) { - setSize0(nativeWindowHandle, width, height); - } - if(x>=0 || y>=0) { - System.err.println("setPosition n/a in KD"); - } - } - - if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) ) { - visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); - } - + @Override + protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, final int flags) { + reconfigure0(nativeWindowHandle, x, y, width, height, flags); return true; } - protected Point getLocationOnScreenImpl(int x, int y) { + @Override + protected Point getLocationOnScreenImpl(final int x, final int y) { return new Point(x,y); } - protected void updateInsetsImpl(Insets insets) { - // nop .. + @Override + protected void updateInsetsImpl(final Insets insets) { + // nop .. + } + + @Override + 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[] rotationXYZ, final float rotationScale) { + if( MouseEvent.EVENT_MOUSE_MOVED == eventType ) { + final DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); + display.moveActivePointerIcon(x, y); + } + super.doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, rotationXYZ, rotationScale); + } + + @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 CreateWindow(int width, int height); - private native long RealizeWindow(long eglWindowHandle); - private native int CloseWindow(long eglWindowHandle, long userData); - private native void setVisible0(long eglWindowHandle, boolean visible); - private native void setSize0(long eglWindowHandle, int width, int height); - private native void setFullScreen0(long eglWindowHandle, boolean fullscreen); - - private void windowCreated(long userData) { - windowUserData=userData; - } + 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 long windowUserData; + + 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/intel/gdl/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/intel/gdl/DisplayDriver.java index 97c384d33..1d43017c3 100644 --- a/src/newt/classes/jogamp/newt/driver/intel/gdl/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/intel/gdl/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.intel.gdl; @@ -59,6 +59,7 @@ public class DisplayDriver extends jogamp.newt.DisplayImpl { public DisplayDriver() { } + @Override protected void createNativeImpl() { synchronized(DisplayDriver.class) { if(0==initCounter) { @@ -72,7 +73,8 @@ public class DisplayDriver extends jogamp.newt.DisplayImpl { aDevice = new DefaultGraphicsDevice(NativeWindowFactory.TYPE_DEFAULT, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT, displayHandle); } - protected void closeNativeImpl() { + @Override + protected void closeNativeImpl(final AbstractGraphicsDevice aDevice) { if(0==displayHandle) { throw new NativeWindowException("displayHandle null; initCnt "+initCounter); } @@ -84,15 +86,17 @@ public class DisplayDriver extends jogamp.newt.DisplayImpl { } } } + aDevice.close(); } + @Override protected void dispatchMessagesNative() { if(0!=displayHandle) { DispatchMessages(displayHandle, focusedWindow); } } - protected void setFocus(WindowDriver focus) { + protected void setFocus(final WindowDriver focus) { focusedWindow = focus; } diff --git a/src/newt/classes/jogamp/newt/driver/intel/gdl/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/intel/gdl/ScreenDriver.java index 8eed14dde..2ce835c52 100644 --- a/src/newt/classes/jogamp/newt/driver/intel/gdl/ScreenDriver.java +++ b/src/newt/classes/jogamp/newt/driver/intel/gdl/ScreenDriver.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,15 +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. - * + * */ 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; + +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 { @@ -48,25 +53,68 @@ public class ScreenDriver extends jogamp.newt.ScreenImpl { public ScreenDriver() { } + @Override protected void createNativeImpl() { - AbstractGraphicsDevice adevice = getDisplay().getGraphicsDevice(); + final AbstractGraphicsDevice adevice = getDisplay().getGraphicsDevice(); GetScreenInfo(adevice.getHandle(), screen_idx); aScreen = new DefaultGraphicsScreen(adevice, screen_idx); } + @Override protected void closeNativeImpl() { } - protected int validateScreenIndex(int idx) { - return 0; // only one screen available + @Override + protected int validateScreenIndex(final int idx) { + return 0; // only one screen available + } + + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(final 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 pixel-units + props[i++] = 0; // rotated viewport y pixel-units + props[i++] = cachedWidth; // rotated viewport width pixel-units + props[i++] = cachedWidth; // rotated viewport height pixel-units + props[i++] = 0; // rotated viewport x window-units + props[i++] = 0; // rotated viewport y window-units + props[i++] = cachedWidth; // rotated viewport width window-units + props[i++] = cachedWidth; // rotated viewport height window-units + 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(final Rectangle viewport, final Rectangle viewportInWindowUnits) { + viewport.set(0, 0, cachedWidth, cachedHeight); + viewportInWindowUnits.set(viewport); } - + //---------------------------------------------------------------------- // Internals only // @@ -75,11 +123,11 @@ public class ScreenDriver extends jogamp.newt.ScreenImpl { private native void GetScreenInfo(long displayHandle, int screen_idx); // called by GetScreenInfo() .. - private void screenCreated(int width, int height) { + private void screenCreated(final int width, final int height) { 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/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/intel/gdl/WindowDriver.java index 98335f192..d7bd17e42 100644 --- a/src/newt/classes/jogamp/newt/driver/intel/gdl/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/intel/gdl/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.intel.gdl; @@ -48,6 +48,7 @@ public class WindowDriver extends jogamp.newt.WindowImpl { static long nextWindowHandle = 1; + @Override protected void createNativeImpl() { if(0!=getParentWindowHandle()) { throw new NativeWindowException("GDL Window does not support window parenting"); @@ -65,13 +66,15 @@ public class WindowDriver extends jogamp.newt.WindowImpl { synchronized(WindowDriver.class) { setWindowHandle(nextWindowHandle++); // just a marker - surfaceHandle = CreateSurface(aDevice.getHandle(), getScreen().getWidth(), getScreen().getHeight(), getX(), getY(), getWidth(), getHeight()); + surfaceHandle = CreateSurface(aDevice.getHandle(), getScreen().getWidth(), getScreen().getHeight(), + getX(), getY(), getSurfaceWidth(), getSurfaceHeight()); if (surfaceHandle == 0) { throw new NativeWindowException("Error creating window"); } } } + @Override protected void closeNativeImpl() { if(0!=surfaceHandle) { synchronized(WindowDriver.class) { @@ -82,9 +85,11 @@ public class WindowDriver extends jogamp.newt.WindowImpl { } } - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { - ScreenDriver screen = (ScreenDriver) getScreen(); + @Override + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, final int flags) { + final ScreenDriver screen = (ScreenDriver) getScreen(); + // Note for GDL: window units == pixel units if(width>screen.getWidth()) { width=screen.getWidth(); } @@ -108,11 +113,12 @@ public class WindowDriver extends jogamp.newt.WindowImpl { } visibleChanged(false, 0 != ( FLAG_IS_VISIBLE & flags)); } - + return true; } - protected void requestFocusImpl(boolean reparented) { + @Override + protected void requestFocusImpl(final boolean reparented) { ((DisplayDriver)getScreen().getDisplay()).setFocus(this); } @@ -121,14 +127,16 @@ public class WindowDriver extends jogamp.newt.WindowImpl { return surfaceHandle; } - protected Point getLocationOnScreenImpl(int x, int y) { + @Override + protected Point getLocationOnScreenImpl(final int x, final int y) { return new Point(x,y); } - protected void updateInsetsImpl(Insets insets) { - // nop .. + @Override + protected void updateInsetsImpl(final Insets insets) { + // nop .. } - + //---------------------------------------------------------------------- // Internals only // @@ -138,7 +146,7 @@ public class WindowDriver extends jogamp.newt.WindowImpl { private native void CloseSurface(long displayHandle, long surfaceHandle); private native void SetBounds0(long surfaceHandle, int scrn_width, int scrn_height, int x, int y, int width, int height); - private void updateBounds(int x, int y, int width, int height) { + private void updateBounds(final int x, final int y, final int width, final int height) { definePosition(x, y); defineSize(width, height); } diff --git a/src/newt/classes/jogamp/newt/driver/kd/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/kd/DisplayDriver.java index 745be5dae..e72ddbc11 100644 --- a/src/newt/classes/jogamp/newt/driver/kd/DisplayDriver.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; @@ -59,15 +59,19 @@ public class DisplayDriver extends DisplayImpl { public DisplayDriver() { } + @Override protected void createNativeImpl() { // FIXME: map name to EGL_*_DISPLAY aDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); + aDevice.open(); } - protected void closeNativeImpl() { + @Override + protected void closeNativeImpl(final AbstractGraphicsDevice aDevice) { aDevice.close(); } + @Override protected void dispatchMessagesNative() { DispatchMessages(); } diff --git a/src/newt/classes/jogamp/newt/driver/kd/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/kd/ScreenDriver.java index 656bcf5c9..d4113561a 100644 --- a/src/newt/classes/jogamp/newt/driver/kd/ScreenDriver.java +++ b/src/newt/classes/jogamp/newt/driver/kd/ScreenDriver.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,15 +29,18 @@ * 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 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 { @@ -48,28 +51,71 @@ public class ScreenDriver extends ScreenImpl { public ScreenDriver() { } + @Override protected void createNativeImpl() { aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); } + @Override 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); + @Override + protected int validateScreenIndex(final int idx) { + return 0; // only one screen available + } + + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(final 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 pixel-units + props[i++] = 0; // rotated viewport y pixel-units + props[i++] = cachedWidth; // rotated viewport width pixel-units + props[i++] = cachedWidth; // rotated viewport height pixel-units + props[i++] = 0; // rotated viewport x window-units + props[i++] = 0; // rotated viewport y window-units + props[i++] = cachedWidth; // rotated viewport width window-units + props[i++] = cachedWidth; // rotated viewport height window-units + MonitorModeProps.streamInMonitorDevice(null, cache, this, cache.monitorModes, currentMode, props, 0); + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(final MonitorDevice monitor) { + return monitor.getSupportedModes().get(0); } - - protected void sizeChanged(int w, int h) { - cachedWidth = w; - cachedHeight = h; + + @Override + protected boolean setCurrentMonitorModeImpl(final MonitorDevice monitor, final MonitorMode mode) { + return false; + } + + @Override + protected void calcVirtualScreenOriginAndSize(final Rectangle viewport, final Rectangle viewportInWindowUnits) { + viewport.set(0, 0, cachedWidth, cachedHeight); + viewportInWindowUnits.set(viewport); } - + + protected void sizeChanged(final int w, final int h) { + cachedWidth = w; // write to static field intended + cachedHeight = h; // write to static field intended + } + private static int cachedWidth = 0; - private static int cachedHeight = 0; + private static int cachedHeight = 0; } diff --git a/src/newt/classes/jogamp/newt/driver/kd/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/kd/WindowDriver.java index c733a3e94..d53ef00cd 100644 --- a/src/newt/classes/jogamp/newt/driver/kd/WindowDriver.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; @@ -56,6 +56,7 @@ public class WindowDriver extends WindowImpl { public WindowDriver() { } + @Override protected void createNativeImpl() { if(0!=getParentWindowHandle()) { throw new RuntimeException("Window parenting not supported (yet)"); @@ -67,9 +68,9 @@ public class WindowDriver extends WindowImpl { } setGraphicsConfiguration(cfg); - GLCapabilitiesImmutable eglCaps = (GLCapabilitiesImmutable) cfg.getChosenCapabilities(); - int eglConfigID = eglCaps.getVisualID(VIDType.EGL_CONFIG); - long eglConfig = EGLGraphicsConfiguration.EGLConfigId2EGLConfig(getDisplayHandle(), eglConfigID); + final GLCapabilitiesImmutable eglCaps = (GLCapabilitiesImmutable) cfg.getChosenCapabilities(); + final int eglConfigID = eglCaps.getVisualID(VIDType.EGL_CONFIG); + final long eglConfig = EGLGraphicsConfiguration.EGLConfigId2EGLConfig(getDisplayHandle(), eglConfigID); eglWindowHandle = CreateWindow(getDisplayHandle(), eglConfig); if (eglWindowHandle == 0) { @@ -83,6 +84,7 @@ public class WindowDriver extends WindowImpl { windowHandleClose = eglWindowHandle; } + @Override protected void closeNativeImpl() { if(0!=windowHandleClose) { CloseWindow(windowHandleClose, windowUserData); @@ -90,14 +92,16 @@ public class WindowDriver extends WindowImpl { } } - protected void requestFocusImpl(boolean reparented) { } + @Override + protected void requestFocusImpl(final boolean reparented) { } - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { + @Override + protected boolean reconfigureWindowImpl(final int x, final int y, int width, int height, final 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) ; @@ -117,22 +121,24 @@ public class WindowDriver 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; } - protected Point getLocationOnScreenImpl(int x, int y) { + @Override + protected Point getLocationOnScreenImpl(final int x, final int y) { return new Point(x,y); } - protected void updateInsetsImpl(Insets insets) { - // nop .. + @Override + protected void updateInsetsImpl(final Insets insets) { + // nop .. } - + //---------------------------------------------------------------------- // Internals only // @@ -145,12 +151,12 @@ public class WindowDriver extends WindowImpl { private native void setSize0(long eglWindowHandle, int width, int height); private native void setFullScreen0(long eglWindowHandle, boolean fullscreen); - private void windowCreated(long userData) { + private void windowCreated(final long userData) { windowUserData=userData; } @Override - protected void sizeChanged(boolean defer, int newWidth, int newHeight, boolean force) { + protected void sizeChanged(final boolean defer, final int newWidth, final int newHeight, final boolean force) { if(isFullscreen()) { ((ScreenDriver)getScreen()).sizeChanged(getWidth(), getHeight()); } 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..49a815cb6 --- /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 final 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 final EventDevicePoller[] eventDevicePollers = new EventDevicePoller[32]; + + @Override + public void windowResized(final WindowEvent e) { } + + @Override + public void windowMoved(final WindowEvent e) { } + + @Override + public void windowDestroyNotify(final WindowEvent e) { + final Object s = e.getSource(); + if(focusedWindow == s) { + focusedWindow = null; + } + } + + @Override + public void windowDestroyed(final WindowEvent e) { } + + @Override + public void windowGainedFocus(final WindowEvent e) { + final Object s = e.getSource(); + if(s instanceof WindowImpl) { + focusedWindow = (WindowImpl) s; + } + } + + @Override + public void windowLostFocus(final WindowEvent e) { + final Object s = e.getSource(); + if(focusedWindow == s) { + focusedWindow = null; + } + } + + public static void main(final String[] args ){ + System.setProperty("newt.debug.Window.KeyEvent", "true"); + LinuxEventDeviceTracker.getSingleton(); + try { + while(true) { + Thread.sleep(1000); + } + } catch (final InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + public void windowRepaint(final WindowUpdateEvent e) { } + + class EventDeviceManager implements Runnable { + + private volatile boolean stop = false; + + @Override + public void run() { + final File f = new File("/dev/input/"); + int number; + while(!stop){ + for(final String path:f.list()){ + if(path.startsWith("event")) { + final String stringNumber = path.substring(5); + number = Integer.parseInt(stringNumber); + if(number<32&&number>=0) { + if(eventDevicePollers[number]==null){ + eventDevicePollers[number] = new EventDevicePoller(number); + final 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 (final InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } + + class EventDevicePoller implements Runnable { + + private volatile boolean stop = false; + private final String eventDeviceName; + + public EventDevicePoller(final 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; + * }; + */ + final ByteBuffer bb = ByteBuffer.wrap(b); + final StructAccessor s = new StructAccessor(bb); + final File f = new File(eventDeviceName); + f.setReadOnly(); + InputStream fis; + try { + fis = new FileInputStream(f); + } catch (final 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 (final 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 (final IOException e) { + } + } + stop=true; + } + + private char NewtVKey2Unicode(final short VK, final 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(final 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(final 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 index 885649d17..1d9377563 100644 --- a/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.java +++ b/src/newt/classes/jogamp/newt/driver/linux/LinuxMouseTracker.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. @@ -36,6 +36,7 @@ import java.io.InputStream; import jogamp.newt.WindowImpl; +import com.jogamp.newt.Screen; import com.jogamp.newt.Window; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.WindowEvent; @@ -48,16 +49,16 @@ import com.jogamp.newt.event.WindowUpdateEvent; * 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; } @@ -65,49 +66,54 @@ public class LinuxMouseTracker implements WindowListener { private volatile boolean stop = false; private int x = 0; private int y = 0; - private int buttonDown = 0; + private short buttonDown = 0; private int old_x = 0; private int old_y = 0; - private int old_buttonDown = 0; + private volatile int lastFocusedX = 0; + private volatile int lastFocusedY = 0; + private short old_buttonDown = 0; private WindowImpl focusedWindow = null; - private MouseDevicePoller mouseDevicePoller = new MouseDevicePoller(); - + private final MouseDevicePoller mouseDevicePoller = new MouseDevicePoller(); + + public final int getLastX() { return lastFocusedX; } + public final int getLastY() { return lastFocusedY; } + @Override - public void windowResized(WindowEvent e) { } + public void windowResized(final WindowEvent e) { } @Override - public void windowMoved(WindowEvent e) { } + public void windowMoved(final WindowEvent e) { } @Override - public void windowDestroyNotify(WindowEvent e) { - Object s = e.getSource(); + public void windowDestroyNotify(final WindowEvent e) { + final Object s = e.getSource(); if(focusedWindow == s) { focusedWindow = null; } } @Override - public void windowDestroyed(WindowEvent e) { } + public void windowDestroyed(final WindowEvent e) { } @Override - public void windowGainedFocus(WindowEvent e) { - Object s = e.getSource(); + public void windowGainedFocus(final WindowEvent e) { + final Object s = e.getSource(); if(s instanceof WindowImpl) { focusedWindow = (WindowImpl) s; } } @Override - public void windowLostFocus(WindowEvent e) { - Object s = e.getSource(); + public void windowLostFocus(final WindowEvent e) { + final Object s = e.getSource(); if(focusedWindow == s) { focusedWindow = null; } } @Override - public void windowRepaint(WindowUpdateEvent e) { } - + public void windowRepaint(final WindowUpdateEvent e) { } + class MouseDevicePoller implements Runnable { @Override public void run() { @@ -117,7 +123,7 @@ public class LinuxMouseTracker implements WindowListener { InputStream fis; try { fis = new FileInputStream(f); - } catch (FileNotFoundException e) { + } catch (final FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); return; @@ -131,7 +137,7 @@ public class LinuxMouseTracker implements WindowListener { int read = 0; try { read = fis.read(b, 0, remaining); - } catch (IOException e) { + } catch (final IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } @@ -150,17 +156,18 @@ public class LinuxMouseTracker implements WindowListener { yo=(b[0]&128)>0; xd=b[1]; yd=b[2]; - + x+=xd; - y+=yd; - + y-=yd; + if(x<0) { x=0; } if(y<0) { y=0; } - + + buttonDown = 0; if(lb) { buttonDown = MouseEvent.BUTTON1; } @@ -170,52 +177,56 @@ public class LinuxMouseTracker implements WindowListener { 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; + // Clip to Screen Size + { + final Screen focusedScreen = focusedWindow.getScreen(); + final int sw = focusedScreen.getWidth(); + final int sh = focusedScreen.getHeight(); + if( x >= sw ) { + x = sw - 1; + } + if( y >= sh ) { + y = sh - 1; + } } - int wx = x - focusedWindow.getX(), wy = y - focusedWindow.getY(); - + final int[] winScreenPos = focusedWindow.convertToPixelUnits(new int[] { focusedWindow.getX(), focusedWindow.getY() }); + final int wx = x - winScreenPos[0], wy = y - winScreenPos[1]; if(old_x != x || old_y != y) { // mouse moved - focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_MOVED, 0, wx, wy, 0, 0 ); + 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 ); + focusedWindow.sendMouseEvent(MouseEvent.EVENT_MOUSE_PRESSED, 0, wx, wy, buttonDown, 0 ); } else { - focusedWindow.sendMouseEvent( - MouseEvent.EVENT_MOUSE_RELEASED, - 0, wx, wy, old_buttonDown, 0 ); + 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) { + } catch (final IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } - } + } } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java index 1a3f14859..051294dce 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/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,20 +29,31 @@ * 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 DisplayDriver extends DisplayImpl { + private static final PNGPixelRect defaultIconData; + static { NEWTJNILibLoader.loadNEWT(); @@ -52,6 +63,27 @@ public class DisplayDriver extends DisplayImpl { 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.RGBA8888, true /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } catch (final 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()); } @@ -60,19 +92,48 @@ public class DisplayDriver extends DisplayImpl { public static void initSingleton() { // just exist to ensure static init has been run } - + public DisplayDriver() { } + @Override + public PixelFormat getNativePointerIconPixelFormat() { return PixelFormat.RGBA8888; } + + @Override protected void dispatchMessagesNative() { // nop } - + + @Override protected void createNativeImpl() { aDevice = new MacOSXGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); } - protected void closeNativeImpl() { } + @Override + protected void closeNativeImpl(final 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(final PixelFormat pixelformat, final int width, final 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, final long piHandle) { + destroyPointerIcon0(piHandle); + } public static void runNSApplication() { runNSApplication0(); @@ -84,5 +145,9 @@ public class DisplayDriver 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 d39e0027b..d80d43e8f 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/MacKeyUtil.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. @@ -30,57 +30,130 @@ 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; @@ -109,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; @@ -120,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; @@ -130,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; @@ -153,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(final short keyCode, final 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; @@ -205,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/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/ScreenDriver.java index 24e60ba0a..5f458e2c9 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/ScreenDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/ScreenDriver.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,115 +29,179 @@ * 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 javax.media.nativewindow.util.Rectangle; +import jogamp.nativewindow.macosx.OSXUtil; +import jogamp.newt.MonitorModeProps; import jogamp.newt.ScreenImpl; -import com.jogamp.common.util.IntObjectHashMap; -import com.jogamp.newt.ScreenMode; -import com.jogamp.newt.util.ScreenModeUtil; +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; +import com.jogamp.opengl.math.FloatUtil; public class ScreenDriver extends ScreenImpl { - - // caching native CGDisplayScreenSize() results, since it's ridiculous slow (~6 ms each call) - private static IntObjectHashMap/*<int, DimensionImmutable>*/ scrnIdx2Dimension; - + static { DisplayDriver.initSingleton(); - scrnIdx2Dimension = new IntObjectHashMap(); - scrnIdx2Dimension.setKeyNotFoundValue(null); } public ScreenDriver() { } + @Override protected void createNativeImpl() { aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); } + @Override 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); + private MonitorMode getMonitorModeImpl(final MonitorModeProps.Cache cache, final int crt_idx, final 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; + } - 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); + private class CrtProps { + CrtProps() { + count = getMonitorCount0(); + pixelScaleArray = new int[count]; + propsOrigArray = new int[count][]; + propsFixedArray = new int[count][]; + + // + // Gather whole topology of monitors (NSScreens) + // + for(int crtIdx=0; crtIdx<count; crtIdx++) { + final float pixelScaleRaw = (float) OSXUtil.GetPixelScale(crtIdx); + pixelScaleArray[crtIdx] = FloatUtil.isZero(pixelScaleRaw, FloatUtil.EPSILON) ? 1 : (int)pixelScaleRaw; + propsOrigArray[crtIdx] = getMonitorProps0(crtIdx); + if ( null == propsOrigArray[crtIdx] ) { + throw new InternalError("Could not gather device props "+crtIdx+"/"+count); + } + // copy orig -> fixed + final int propsLen = propsOrigArray[crtIdx].length; + propsFixedArray[crtIdx] = new int[propsLen]; + System.arraycopy(propsOrigArray[crtIdx], 0, propsFixedArray[crtIdx], 0, propsLen); + } + + // + // Fix scaled viewport w/ pixelScale of each monitorProps, + // i.e. size by its own pixelScale and x/y offset by querying it's neighbors. + // + for(int crtIdx=0; crtIdx<count; crtIdx++) { + final int[] thisMonitorProps = propsFixedArray[crtIdx]; + final int x = thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+0]; + final int y = thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+1]; + final int thisPixelScale = pixelScaleArray[crtIdx]; + thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+2] *= thisPixelScale; // fix width + thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+3] *= thisPixelScale; // fix height + if( 0 != x ) { + // find matching viewport width for x-offset to apply it's pixelSize + for(int i=0; i<count; i++) { + if( i != crtIdx && x == propsOrigArray[i][MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+2] ) { + thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+0] *= pixelScaleArray[i]; + break; + } + } + } + if( 0 != y ) { + // find matching viewport height for y-offset to apply it's pixelSize + for(int i=0; i<count; i++) { + if( i != crtIdx && y == propsOrigArray[i][MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+3] ) { + thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+1] *= pixelScaleArray[i]; + break; + } + } + } + } } - return modeProps; + final int count; + final int[] pixelScaleArray; + final int[][] propsOrigArray; + final int[][] propsFixedArray; } - private int nativeModeIdx; - - protected int[] getScreenModeFirstImpl() { - nativeModeIdx = 0; - return getScreenModeNextImpl(); - } + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(final MonitorModeProps.Cache cache) { + final CrtProps crtProps = new CrtProps(); - protected int[] getScreenModeNextImpl() { - int[] modeProps = getScreenModeIdx(nativeModeIdx); - if (null != modeProps && 0 < modeProps.length) { - nativeModeIdx++; - return modeProps; + // + // Collect all monitorModes for all monitorDevices + // + for(int crtIdx=0; crtIdx<crtProps.count; crtIdx++) { + final ArrayHashSet<MonitorMode> supportedModes = new ArrayHashSet<MonitorMode>(); + int modeIdx = 0; + { + // Get all supported modes for this monitorDevice + MonitorMode mode; + while( true ) { + mode = getMonitorModeImpl(cache, crtIdx, modeIdx); + if( null != mode ) { + if( mode.getSurfaceSize().getBitsPerPixel() >= 24 ) { // drop otherwise + supportedModes.getOrAdd(mode); + } + modeIdx++; // next mode on same monitor + } else { + break; // done with modes on this monitor + } + } + } + if( 0 >= modeIdx ) { + throw new InternalError("Could not gather single mode of device "+crtIdx+"/"+crtProps.count); + } + final MonitorMode currentMode = getMonitorModeImpl(cache, crtIdx, -1); + if ( null == currentMode ) { + throw new InternalError("Could not gather current mode of device "+crtIdx+"/"+crtProps.count+", but gathered "+modeIdx+" modes"); + } + // merge monitor-props + supported modes + MonitorModeProps.streamInMonitorDevice(null, cache, this, supportedModes, currentMode, crtProps.propsFixedArray[crtIdx], 0); } - return null; } - protected ScreenMode getCurrentScreenModeImpl() { - int[] modeProps = getScreenModeIdx(-1); - if (null != modeProps && 0 < modeProps.length) { - return ScreenModeUtil.streamIn(modeProps, 0); + @Override + protected boolean updateNativeMonitorDeviceViewportImpl(final MonitorDevice monitor, final Rectangle viewportPU, final Rectangle viewportWU) { + final CrtProps crtProps = new CrtProps(); + final int crtIdx = monitor.getId(); + if( 0 > crtIdx || crtIdx >= crtProps.count ) { + throw new IndexOutOfBoundsException("monitor id "+crtIdx+" not withon [0.."+(crtProps.count-1)+"]"); } - return null; + final int[] fixedMonitorProps = crtProps.propsFixedArray[crtIdx]; + int offset = MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT; + viewportPU.set(fixedMonitorProps[offset++], fixedMonitorProps[offset++], fixedMonitorProps[offset++], fixedMonitorProps[offset++]); + viewportWU.set(fixedMonitorProps[offset++], fixedMonitorProps[offset++], fixedMonitorProps[offset++], fixedMonitorProps[offset++]); + return true; } - - 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); + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(final MonitorDevice monitor) { + return getMonitorModeImpl(null, monitor.getId(), -1); } - - protected int validateScreenIndex(int idx) { - return idx; + + @Override + protected boolean setCurrentMonitorModeImpl(final MonitorDevice monitor, final MonitorMode mode) { + return setMonitorMode0(monitor.getId(), mode.getId(), mode.getRotation()); } - - protected void getVirtualScreenOriginAndSize(Point virtualOrigin, Dimension virtualSize) { - virtualOrigin.setX(0); - virtualOrigin.setY(0); - virtualSize.setWidth(getWidthImpl0(screen_idx)); - virtualSize.setHeight(getHeightImpl0(screen_idx)); + + @Override + protected int validateScreenIndex(final int idx) { + return 0; // big-desktop w/ multiple monitor attached, only one screen available } - 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); + private native int getMonitorCount0(); + 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 index 4eeafb244..c0f7d3859 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/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,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.macosx; @@ -39,28 +39,146 @@ import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.MutableSurface; +import javax.media.nativewindow.ScalableSurface; import javax.media.nativewindow.VisualIDHolder; 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.nativewindow.SurfaceScaleUtils; +import jogamp.nativewindow.macosx.OSXUtil; +import jogamp.newt.PointerIconImpl; +import jogamp.newt.ScreenImpl; 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; +import com.jogamp.newt.event.MonitorEvent; +import com.jogamp.opengl.math.FloatUtil; public class WindowDriver extends WindowImpl implements MutableSurface, DriverClearFocus, DriverUpdatePosition { - + static { DisplayDriver.initSingleton(); } public WindowDriver() { } - + + private boolean updatePixelScale(final boolean sendEvent, final boolean defer, final float newPixelScaleRaw, final float nativePixelScaleRaw) { + final int[] newPixelScale = new int[2]; + { + final int _newPixelScale = FloatUtil.isZero(newPixelScaleRaw, FloatUtil.EPSILON) ? ScalableSurface.IDENTITY_PIXELSCALE : (int) newPixelScaleRaw; + newPixelScale[0]= _newPixelScale; + newPixelScale[1]= _newPixelScale; + final int _nativePixelScale = FloatUtil.isZero(nativePixelScaleRaw, FloatUtil.EPSILON) ? ScalableSurface.IDENTITY_PIXELSCALE : (int) nativePixelScaleRaw; + nativePixelScale[0]= _nativePixelScale; + nativePixelScale[1]= _nativePixelScale; + } + + if( SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, newPixelScale, DEBUG_IMPLEMENTATION ? getClass().getName() : null) ) { + if( sendEvent ) { + super.sizeChanged(defer, getWidth(), getHeight(), true); + } else { + defineSize(getWidth(), getHeight()); + } + return true; + } else { + return false; + } + } + + private boolean updatePixelScaleByScreenIdx(final boolean sendEvent) { + final float nativePixelScaleRaw = (float) OSXUtil.GetPixelScale(getScreen().getIndex()); + if( DEBUG_IMPLEMENTATION ) { + System.err.println("WindowDriver.updatePixelScale.1: "+hasPixelScale[0]+", "+nativePixelScaleRaw+" (native)"); + } + return updatePixelScale(sendEvent, true /* defer */, nativePixelScaleRaw, nativePixelScaleRaw); + } + + private boolean updatePixelScaleByWindowHandle(final boolean sendEvent) { + final long handle = getWindowHandle(); + if( 0 != handle ) { + final float nativePixelScaleRaw = (float)OSXUtil.GetPixelScale(handle); + if( DEBUG_IMPLEMENTATION ) { + System.err.println("WindowDriver.updatePixelScale.2: "+hasPixelScale[0]+", "+nativePixelScaleRaw+" (native)"); + } + return updatePixelScale(sendEvent, true /* defer */, nativePixelScaleRaw, nativePixelScaleRaw); + } else { + return false; + } + } + + /** Called from native code */ + protected void updatePixelScale(final boolean defer, final float newPixelScaleRaw, final float nativePixelScaleRaw) { + final long handle = getWindowHandle(); + if( DEBUG_IMPLEMENTATION ) { + System.err.println("WindowDriver.updatePixelScale.3: "+hasPixelScale[0]+" (has) -> "+newPixelScaleRaw+" (raw), "+nativePixelScaleRaw+" (native), drop "+(0==handle)); + } + if( 0 != handle ) { + updatePixelScale(true /* sendEvent*/, defer, newPixelScaleRaw, nativePixelScaleRaw); + } + } + + @Override + protected final void instantiationFinished() { + updatePixelScaleByScreenIdx(false /* sendEvent*/); + } + + @Override + protected void setScreen(final ScreenImpl newScreen) { // never null ! + super.setScreen(newScreen); + updatePixelScaleByScreenIdx(false /* sendEvent*/); // caller (reparent, ..) will send reshape event + } + + @Override + protected void monitorModeChanged(final MonitorEvent me, final boolean success) { + updatePixelScaleByWindowHandle(false /* sendEvent*/); // send reshape event itself + } + + @Override + public final void setSurfaceScale(final int[] pixelScale) { + SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG_IMPLEMENTATION ? getClass().getName() : null); + + final int[] resPixelScale; + if( isNativeValid() ) { + if( isOffscreenInstance ) { + final NativeWindow pWin = getParent(); + if( pWin instanceof ScalableSurface ) { + final ScalableSurface sSurf = (ScalableSurface)pWin; + sSurf.setSurfaceScale(reqPixelScale); + final int[] pPixelScale = sSurf.getCurrentSurfaceScale(new int[2]); + sSurf.getNativeSurfaceScale(nativePixelScale); + updatePixelScale(true /* sendEvent */, true /* defer */, pPixelScale[0], nativePixelScale[0]); // HiDPI: uniformPixelScale + } else { + // just notify updated pixelScale if offscreen + SurfaceScaleUtils.replaceAutoMaxWithPlatformMax(reqPixelScale); + updatePixelScale(true /* sendEvent */, true /* defer */, reqPixelScale[0], nativePixelScale[0]); // HiDPI: uniformPixelScale + } + } else { + // set pixelScale in native code, will issue an update PixelScale + OSXUtil.RunOnMainThread(true, false, new Runnable() { + @Override + public void run() { + setPixelScale0(getWindowHandle(), surfaceHandle, reqPixelScale[0]); // HiDPI: uniformPixelScale + } + } ); + } + resPixelScale = hasPixelScale; + } else { + hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + resPixelScale = reqPixelScale; + } + if( DEBUG_IMPLEMENTATION ) { + System.err.println("WindowDriver.setPixelScale: "+pixelScale[0]+"x"+pixelScale[1]+" (req) -> "+ + reqPixelScale[0]+"x"+reqPixelScale[1]+" (validated) -> "+ + resPixelScale[0]+"x"+resPixelScale[1]+" (result) - realized "+isNativeValid()); + } + } + @Override protected void createNativeImpl() { final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice(), capsRequested).chooseGraphicsConfiguration( @@ -69,7 +187,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); } setGraphicsConfiguration(cfg); - reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureFlags(FLAG_CHANGE_VISIBILITY, true)); + reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureFlags(FLAG_CHANGE_VISIBILITY, true)); if (0 == getWindowHandle()) { throw new NativeWindowException("Error creating window"); } @@ -79,373 +197,532 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl protected void closeNativeImpl() { try { if(DEBUG_IMPLEMENTATION) { System.err.println("MacWindow.CloseAction "+Thread.currentThread().getName()); } - final long handle = getWindowHandle(); + final long handle = getWindowHandle(); + visibleChanged(true, false); setWindowHandle(0); surfaceHandle = 0; sscSurfaceHandle = 0; - isOffscreenInstance = false; + isOffscreenInstance = false; if (0 != handle) { - close0(handle); + OSXUtil.RunOnMainThread(false, true /* kickNSApp */, 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); + } catch (final Throwable t) { + if(DEBUG_IMPLEMENTATION) { + final 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; + /** + * 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_SUCCESS; + return LOCK_SURFACE_NOT_READY; } @Override protected void unlockSurfaceImpl() { - if(!isOffscreenInstance) { - final long h = getWindowHandle(); - if(0 != h) { - unlockSurface0(h); + /** + * 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) { + public void setSurfaceHandle(final 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! + OSXUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + orderOut0( 0 != getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() ); + } } ); + } /** this is done by recreation! else if (isVisible()){ - orderFront0( 0!=getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() ); + OSXUtil.RunOnMainThread(false, new Runnable() { + public void run() { + orderFront0( 0!=getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() ); + } } ); } */ - } + } } @Override protected void setTitleImpl(final String title) { - setTitle0(getWindowHandle(), title); + OSXUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + setTitle0(getWindowHandle(), title); + } } ); } - protected void requestFocusImpl(boolean force) { - if(!isOffscreenInstance) { - requestFocus0(getWindowHandle(), force); + @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, 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() - requestFocusParent, isOffscreenInstance "+isOffscreenInstance); + System.err.println("MacWindow: clearFocus(), isOffscreenInstance "+isOffscreenInstance); } if(!isOffscreenInstance) { - resignFocus0(getWindowHandle()); + OSXUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + resignFocus0(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); + + private boolean useParent(final NativeWindow parent) { return null != parent && 0 != parent.getWindowHandle(); } + + @Override + public void updatePosition(final int x, final 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, 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(final boolean defer, final int newWidth, final int newHeight, final 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, false, new Runnable() { + @Override + public void run() { + setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY(), isVisible()); + } } ); + } } - if( !isOffscreenInstance ) { - setFrameTopLeftPoint0(getParentWindowHandle(), getWindowHandle(), pS.getX(), pS.getY()); - } // else no offscreen position - // no native event (fullscreen, some reparenting) - super.positionChanged(true, getX(), getY()); + super.sizeChanged(defer, newWidth, newHeight, force); } - - - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { + + @Override + protected boolean reconfigureWindowImpl(int x, int y, final int width, final int height, final int flags) { final boolean _isOffscreenInstance = isOffscreenInstance(this, this.getParent()); isOffscreenInstance = 0 != sscSurfaceHandle || _isOffscreenInstance; - final PointImmutable pS = isOffscreenInstance ? new Point(0, 0) : getTopLevelLocationOnScreen(x, y); - + final PointImmutable pClientLevelOnSreen; + if( isOffscreenInstance ) { + x = 0; y = 0; + 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); + } + } + + final boolean setVisible = 0 != ( FLAG_IS_VISIBLE & flags); + final boolean hasFocus = hasFocus(); + 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: "+x+"/"+y+" -> "+pS+" - "+width+"x"+height+ + 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)); + "\n\t, "+getReconfigureFlagsAsString(null, flags)+", setVisible "+setVisible+", hasFocus "+hasFocus); + // Thread.dumpStack(); } - - if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) && 0 == ( FLAG_IS_VISIBLE & flags) ) { + + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) && !setVisible ) { if ( !isOffscreenInstance ) { - orderOut0(getWindowHandle()); + OSXUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + orderOut0(getWindowHandle()); + visibleChanged(true, false); + } } ); + } else { + visibleChanged(true, false); } - // no native event .. - visibleChanged(true, false); } - if( 0 == getWindowHandle() && 0 != ( FLAG_IS_VISIBLE & flags) || + 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(), pS, 64, 64, false); + createWindow(true, 0 != getWindowHandle(), pClientLevelOnSreen, 64, 64, false, setVisible, false); } else { - createWindow(false, 0 != getWindowHandle(), pS, width, height, 0 != ( FLAG_IS_FULLSCREEN & flags)); + createWindow(false, 0 != getWindowHandle(), pClientLevelOnSreen, width, height, + 0 != ( FLAG_IS_FULLSCREEN & flags), setVisible, 0 != ( FLAG_IS_ALWAYSONTOP & 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()); + positionChanged(false, x, y); + updatePixelScaleByWindowHandle(false /* sendEvent */); + super.sizeChanged(false, width, height, true); + visibleChanged(false, setVisible); + if( hasFocus ) { + requestFocusImpl(true); + } + } else { + if( width>0 && height>0 ) { + if( !isOffscreenInstance ) { + OSXUtil.RunOnMainThread(false, 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); + super.sizeChanged(true, width, height, false); + } + if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) && setVisible ) { + if( !isOffscreenInstance ) { + OSXUtil.RunOnMainThread(false, 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)); } - // no native event .. - 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; } - 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)); - + @Override + protected Point getLocationOnScreenImpl(final int x, final int y) { final NativeWindow parent = getParent(); - if( null != parent && 0 != parent.getWindowHandle() ) { - p.translate(parent.getLocationOnScreen(null)); - } - return p; + final boolean useParent = useParent(parent); + return getLocationOnScreenImpl(x, y, parent, useParent); } - - 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()); + + private Point getLocationOnScreenImpl(final int x, final int y, final NativeWindow parent, final boolean useParent) { + if( !useParent && !isOffscreenInstance && 0 != surfaceHandle) { + return OSXUtil.GetLocationOnScreen(surfaceHandle, x, y); } - super.sizeChanged(defer, newWidth, newHeight, force); + + final Point p = new Point(x, y); + if( useParent ) { + p.translate( parent.getLocationOnScreen(null) ); + } + return p; } - + @Override - protected void positionChanged(boolean defer, int newX, int newY) { + protected void updateInsetsImpl(final Insets insets) { + // nop - using event driven insetsChange(..) + } + + /** Callback for native screen position change event of the client area. */ + protected void screenPositionChanged(final boolean defer, final int newX, final 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) ); + 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 + final Point absPos = new Point(newX, newY); + final 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()); } - super.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, false, new Runnable() { // waitUntildone due to PointerIconImpl's Lifecycle ! + @Override + public void run() { + setPointerIcon0(getWindowHandle(), piHandle); + } } ); + } + } + @Override protected boolean setPointerVisibleImpl(final boolean pointerVisible) { - if( !isOffscreenInstance ) { - return setPointerVisible0(getWindowHandle(), hasFocus(), pointerVisible); - } // else may need offscreen solution ? FIXME + if( !isOffscreenInstance ) { + OSXUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + setPointerVisible0(getWindowHandle(), hasFocus(), pointerVisible); + } } ); + return true; + } return false; } @Override protected boolean confinePointerImpl(final boolean confine) { - if( !isOffscreenInstance ) { - return confinePointer0(getWindowHandle(), 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); + if( !isOffscreenInstance ) { + warpPointer0(getWindowHandle(), x / getPixelScaleX(), y / getPixelScaleY()); } // 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) { - if(pressedKeyBalance > 1) { - // Auto-Repeat: OSX delivers only PRESSED - // inject auto-repeat RELEASE and TYPED keys _before_ - pressedKeyBalance--; - modifiers |= InputEvent.AUTOREPEAT_MASK; - super.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, (char)-1); // RELEASED - super.sendKeyEvent(KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, keyChar); // TYPED - } - // 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); - } - } - + 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[] rotationXYZ, final float rotationScale) { + super.doMouseEvent(enqueue, wait, eventType, modifiers, x * getPixelScaleX(), y * getPixelScaleY(), button, rotationXYZ, rotationScale); + } + @Override - public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { + public final void sendKeyEvent(final short eventType, final int modifiers, final short keyCode, final short keySym, final char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); + } + + @Override + public final void enqueueKeyEvent(final boolean wait, final short eventType, final int modifiers, final short _keyCode, final short _keySym, final char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); + } + + protected final void enqueueKeyEvent(final boolean wait, final short eventType, int modifiers, final short _keyCode, final char keyChar, final 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 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) { - if(pressedKeyBalance > 1) { - // Auto-Repeat: OSX delivers only PRESSED - // inject auto-repeat RELEASE and TYPED keys _before_ - pressedKeyBalance--; - modifiers |= InputEvent.AUTOREPEAT_MASK; - super.enqueueKeyEvent(wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, (char)-1); // RELEASED - super.enqueueKeyEvent(wait, KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, keyChar); // TYPED - } - // 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 int pressedKeyBalance = 0; - - private boolean validateKeyEvent(int eventType, int modifiers, int keyCode) { + final short keyCode = MacKeyUtil.validateKeyCode(_keyCode, keyChar); + final short keySym; + { + final 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_PRESSED: - pressedKeyBalance++; - keyDownModifiers = modifiers; - keyDownCode = keyCode; - return true; case KeyEvent.EVENT_KEY_RELEASED: - pressedKeyBalance--; - 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); + 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) { + // + + 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(0!=getWindowHandle() && !recreate) { - return; + 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!=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"); - } - close0(getWindowHandle()); + if( 0 != preWinHandle ) { setWindowHandle(0); + if( 0 == surfaceHandle ) { + throw new NativeWindowException("Internal Error - create w/ window, but no Newt NSView"); + } + OSXUtil.RunOnMainThread(false, false /* kickNSApp */, new Runnable() { + @Override + public void run() { + changeContentView0(parentWinHandle, preWinHandle, 0); + close0( preWinHandle ); + } }); } 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); + if( 0 != surfaceHandle ) { + throw new NativeWindowException("Internal Error - create w/o window, but has Newt NSView"); + } + surfaceHandle = createView0(pS.getX(), pS.getY(), width, height); + if( 0 == surfaceHandle ) { + throw new NativeWindowException("Could not create native view "+Thread.currentThread().getName()+" "+this); + } } - surfaceHandle = contentView0(getWindowHandle()); - if( offscreenInstance ) { - orderOut0(0!=getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle()); - } else { - setTitle0(getWindowHandle(), getTitle()); + + 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); } - } catch (Exception ie) { + setWindowHandle( newWin ); + + final boolean isOpaque = getGraphicsConfiguration().getChosenCapabilities().isBackgroundOpaque() && !offscreenInstance; + // Blocking initialization on main-thread! + OSXUtil.RunOnMainThread(true, false /* kickNSApp */, new Runnable() { + @Override + public void run() { + initWindow0( parentWinHandle, newWin, pS.getX(), pS.getY(), width, height, reqPixelScale[0] /* HiDPI uniformPixelScale */, + isOpaque, visible && !offscreenInstance, surfaceHandle); + if( offscreenInstance ) { + orderOut0(0!=parentWinHandle ? parentWinHandle : newWin); + } else { + setTitle0(newWin, getTitle()); + setAlwaysOnTop0(getWindowHandle(), alwaysOnTop); + } + } }); + } catch (final 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 long createView0(int x, int y, int w, int h); + 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, float reqPixelScale, + boolean opaque, boolean visible, long view); + private native void setPixelScale0(long window, long view, float reqPixelScale); + 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); - /** in case of a child window, it actually only issues orderBack(..) */ + /** 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); - 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); + /** 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 boolean setPointerVisible0(long windowHandle, boolean hasFocus, boolean visible); - private static native boolean confinePointer0(long windowHandle, boolean confine); + 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; @@ -461,5 +738,5 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl 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..5e703f690 --- /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(final IOUtil.ClassResources resources, final int[] data_size, final 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 index 579ec5be8..8973ae7ed 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/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,67 +29,128 @@ * 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(); - - if (!WindowDriver.initIDs0()) { + { + final 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 (final 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"); - } - sharedClassFactory = new RegisteredClassFactory(newtClassBaseName, WindowDriver.getNewtWndProc0()); + } } 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); } - protected void closeNativeImpl() { + @Override + protected void closeNativeImpl(final AbstractGraphicsDevice aDevice) { sharedClassFactory.releaseSharedClass(); + aDevice.close(); } + @Override protected void dispatchMessagesNative() { DispatchMessages0(); } - protected long getHInstance() { - return sharedClass.getHandle(); - } - protected String getWindowClassName() { return sharedClass.getName(); } + @Override + protected final long createPointerIconImpl(final PixelFormat pixelformat, final int width, final 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, final long piHandle) { + destroyIcon0(piHandle); + } + //---------------------------------------------------------------------- // Internals only // private static native void DispatchMessages0(); + + static long createBGRA8888Icon0(final Buffer pixels, final int width, final int height, final boolean isCursor, final int hotX, final 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 index 948b29460..38acd03ae 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/ScreenDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/ScreenDriver.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,96 +29,148 @@ * 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 javax.media.nativewindow.util.Rectangle; +import jogamp.newt.MonitorModeProps; import jogamp.newt.ScreenImpl; -import com.jogamp.newt.ScreenMode; -import com.jogamp.newt.util.ScreenModeUtil; +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 int[] getScreenModeIdx(int idx) { - int[] modeProps = getScreenMode0(screen_idx, idx); - if (null == modeProps || 0 == modeProps.length) { + private final String getAdapterName(final int crt_idx) { + return getAdapterName0(crt_idx); + } + private final String getActiveMonitorName(final String adapterName, final int monitor_idx) { + return getActiveMonitorName0(adapterName, monitor_idx); + } + + private final MonitorMode getMonitorModeImpl(final MonitorModeProps.Cache cache, final String adapterName, final int crtModeIdx) { + if( null == adapterName ) { 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); + final String activeMonitorName = getActiveMonitorName(adapterName, 0); + final int[] modeProps = null != activeMonitorName ? getMonitorMode0(adapterName, crtModeIdx) : null; + if ( null == modeProps || 0 >= modeProps.length) { + return null; } - return modeProps; + return MonitorModeProps.streamInMonitorMode(null, cache, modeProps, 0); } - private int nativeModeIdx; + @Override + protected void collectNativeMonitorModesAndDevicesImpl(final 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); - protected int[] getScreenModeFirstImpl() { - nativeModeIdx = 0; - return getScreenModeNextImpl(); + // next monitor, 1st mode + supportedModes= new ArrayHashSet<MonitorMode>(); + } + } + crtIdx++; + adapterName = getAdapterName(crtIdx); + } } - protected int[] getScreenModeNextImpl() { - int[] modeProps = getScreenModeIdx(nativeModeIdx); - if (null != modeProps && 0 < modeProps.length) { - nativeModeIdx++; - return modeProps; + @Override + protected boolean updateNativeMonitorDeviceViewportImpl(final MonitorDevice monitor, final Rectangle viewportPU, final Rectangle viewportWU) { + 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; + viewportPU.set(monitorProps[offset++], monitorProps[offset++], monitorProps[offset++], monitorProps[offset++]); + viewportWU.set(monitorProps[offset++], monitorProps[offset++], monitorProps[offset++], monitorProps[offset++]); + return true; + } } - return null; + return false; } - protected ScreenMode getCurrentScreenModeImpl() { - int[] modeProps = getScreenModeIdx(-1); - if (null != modeProps && 0 < modeProps.length) { - return ScreenModeUtil.streamIn(modeProps, 0); - } - return null; + @Override + protected MonitorMode queryCurrentMonitorModeImpl(final MonitorDevice monitor) { + return getMonitorModeImpl(null, getAdapterName(monitor.getId()), -1); } - 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()); + @Override + protected boolean setCurrentMonitorModeImpl(final MonitorDevice monitor, final 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()); } - protected int validateScreenIndex(int idx) { - return 0; // big-desktop, only one screen available + @Override + protected int validateScreenIndex(final int idx) { + return 0; // big-desktop w/ multiple monitor attached, 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)); + + @Override + protected void calcVirtualScreenOriginAndSize(final Rectangle viewport, final Rectangle viewportInWindowUnits) { + viewport.set(getVirtualOriginX0(), getVirtualOriginY0(), getVirtualWidthImpl0(), getVirtualHeightImpl0()); + viewportInWindowUnits.set(viewport); } - + // 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 getVirtualOriginX0(); + private native int getVirtualOriginY0(); + private native int getVirtualWidthImpl0(); + private native int getVirtualHeightImpl0(); - 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); + 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/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java index 71437c461..1c5c34457 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.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,13 +29,16 @@ * 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; @@ -46,22 +49,24 @@ 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 { + static { + DisplayDriver.initSingleton(); + } + private long hmon; private long hdc; private long hdc_old; private long windowHandleClose; - static { - DisplayDriver.initSingleton(); - } - public WindowDriver() { } @@ -70,21 +75,28 @@ public class WindowDriver 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 @@ -104,7 +116,7 @@ public class WindowDriver extends WindowImpl { @Override public boolean hasDeviceChanged() { if(0!=getWindowHandle()) { - long _hmon = MonitorFromWindow0(getWindowHandle()); + final long _hmon = MonitorFromWindow0(getWindowHandle()); if (hmon != _hmon) { if(DEBUG_IMPLEMENTATION) { System.err.println("Info: Window Device Changed "+Thread.currentThread().getName()+ @@ -118,6 +130,7 @@ public class WindowDriver extends WindowImpl { return false; } + @Override protected void createNativeImpl() { final ScreenDriver screen = (ScreenDriver) getScreen(); final DisplayDriver display = (DisplayDriver) screen.getDisplay(); @@ -127,70 +140,68 @@ public class WindowDriver extends WindowImpl { 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()+ + final 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) { - WindowDriver.trackPointerLeave0(WindowDriver.this.getWindowHandle()); - } - } + @Override protected void closeNativeImpl() { - if(windowHandleClose != 0) { - if (hdc != 0) { + if( 0 != windowHandleClose ) { + if ( 0 != hdc ) { try { GDI.ReleaseDC(windowHandleClose, hdc); - } catch (Throwable t) { - if(DEBUG_IMPLEMENTATION) { - Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); + } catch (final Throwable t) { + if(DEBUG_IMPLEMENTATION) { + final Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); e.printStackTrace(); } } } try { GDI.DestroyWindow(windowHandleClose); - } catch (Throwable t) { + } catch (final Throwable t) { if(DEBUG_IMPLEMENTATION) { - Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); + final Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); e.printStackTrace(); } - } finally { - windowHandleClose = 0; } } + windowHandleClose = 0; hdc = 0; hdc_old = 0; } - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { + @Override + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, final 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(); @@ -198,14 +209,15 @@ public class WindowDriver 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; } - protected void requestFocusImpl(boolean force) { + @Override + protected void requestFocusImpl(final boolean force) { requestFocus0(getWindowHandle(), force); } @@ -215,136 +227,150 @@ public class WindowDriver 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, - p0.getX(), p0.getY(), p0.getX()+getWidth(), p0.getY()+getHeight())); + final Point p0 = convertToPixelUnits( getLocationOnScreenImpl(0, 0) ); + res[0] = Boolean.valueOf(confinePointer0(getWindowHandle(), confine, + p0.getX(), p0.getY(), + p0.getX()+getSurfaceWidth(), p0.getY()+getSurfaceHeight())); } }); 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); + final Point sPos = convertToPixelUnits( getLocationOnScreenImpl(x, y) ); warpPointer0(getWindowHandle(), sPos.getX(), sPos.getY()); } }); return; } - - protected Point getLocationOnScreenImpl(int x, int y) { + + @Override + protected Point getLocationOnScreenImpl(final int x, final int y) { return GDIUtil.GetRelativeLocation( getWindowHandle(), 0 /*root win*/, x, y); } - protected void updateInsetsImpl(Insets insets) { - // nop - using event driven insetsChange(..) + @Override + protected void updateInsetsImpl(final Insets insets) { + // nop - using event driven insetsChange(..) } - - private final int validateKeyCode(int eventType, int modifiers, int keyCode, char keyChar) { - switch(eventType) { - case KeyEvent.EVENT_KEY_RELEASED: - pressedKeyBalance--; - lastPressedKeyCode = keyCode; - break; - case KeyEvent.EVENT_KEY_PRESSED: - pressedKeyBalance++; - lastPressedKeyCode = keyCode; - break; - case KeyEvent.EVENT_KEY_TYPED: - if(-1==keyCode) { - keyCode = lastPressedKeyCode; - } - lastPressedKeyCode = -1; - break; + + // + // 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(final short eventType, final int modifiers, + final int pActionIdx, final int[] pNames, + final int[] pX, final int[] pY, final float[] pPressure, final 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(final boolean isModifierKey, int modifiers, final short keyCode, final short keySym, final 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 keyCode; + return false; } - private int lastPressedKeyCode = 0; - private int pressedKeyBalance = 0; - private int autoRepeat = 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, modifiers, keyCode, keyChar); + public final void sendKeyEvent(final short eventType, final int modifiers, final short keyCode, final short keySym, final 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_RELEASED: - // reorder: WINDOWS delivery order is PRESSED, TYPED and RELEASED -> NEWT order: PRESSED, RELEASED and TYPED + 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_PRESSED: - if(pressedKeyBalance > 1) { - // Auto-Repeat: WINDOWS delivers only PRESSED and TYPED. - // Since reordering already injects RELEASE, we only need to set the AUTOREPEAT_MASK. - pressedKeyBalance--; - autoRepeat |= InputEvent.AUTOREPEAT_MASK; - } else { - autoRepeat &= ~InputEvent.AUTOREPEAT_MASK; + if( !handlePressTypedAutoRepeat(isModifierKey, modifiers, keyCode, keySym, keyChar) ) { + super.sendKeyEvent(KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keySym, keyChar); } - super.sendKeyEvent(eventType, modifiers | autoRepeat, keyCode, (char)-1); - break; - case KeyEvent.EVENT_KEY_TYPED: - modifiers |= autoRepeat; - super.sendKeyEvent(KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, (char)-1); - super.sendKeyEvent(eventType, modifiers, keyCode, keyChar); break; } } - + @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, modifiers, keyCode, keyChar); - switch(eventType) { - case KeyEvent.EVENT_KEY_RELEASED: - // reorder: WINDOWS delivery order is PRESSED, TYPED and RELEASED -> NEWT order: PRESSED, RELEASED and TYPED - break; - case KeyEvent.EVENT_KEY_PRESSED: - if(pressedKeyBalance > 1) { - // Auto-Repeat: WINDOWS delivers only PRESSED and TYPED. - // Since reordering already injects RELEASE, we only need to set the AUTOREPEAT_MASK. - pressedKeyBalance--; - autoRepeat |= InputEvent.AUTOREPEAT_MASK; - } else { - autoRepeat &= ~InputEvent.AUTOREPEAT_MASK; - } - super.enqueueKeyEvent(wait, eventType, modifiers | autoRepeat, keyCode, (char)-1); - break; - case KeyEvent.EVENT_KEY_TYPED: - modifiers |= autoRepeat; - super.enqueueKeyEvent(wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, (char)-1); - super.enqueueKeyEvent(wait, eventType, modifiers, keyCode, keyChar); - break; - } + public final void enqueueKeyEvent(final boolean wait, final short eventType, final int modifiers, final short keyCode, final short keySym, final 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); @@ -353,6 +379,8 @@ public class WindowDriver 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/x11/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java index 714324d63..e2b9a65d1 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.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,15 +29,19 @@ * 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; @@ -58,15 +62,14 @@ public class DisplayDriver extends DisplayImpl { } } - public static void initSingleton() { - // just exist to ensure static init has been run - } - + /** Ensure static init has been run. */ + /* pp */static void initSingleton() { } public DisplayDriver() { } - public String validateDisplayName(String name, long handle) { + @Override + public String validateDisplayName(final String name, final long handle) { return X11Util.validateDisplayName(name, handle); } @@ -74,98 +77,99 @@ public class DisplayDriver 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() { - long handle = X11Util.openDisplay(name); + X11Util.setX11ErrorHandler(true, DEBUG ? false : true); // make sure X11 error handler is set + final 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); - } catch(RuntimeException e) { - closeNativeImpl(); + CompleteDisplay0(aDevice.getHandle()); + } catch(final RuntimeException e) { + 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(final 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 + protected final long createPointerIconImpl(final PixelFormat pixelformat, final int width, final 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, final long piHandle) { + destroyPointerIcon0(displayHandle, piHandle); + } + //---------------------------------------------------------------------- // Internals only // + private static native boolean initIDs0(boolean debug); private native void CompleteDisplay0(long handle); - private void displayCompleted(long javaObjectAtom, long windowDeleteAtom) { + private void displayCompleted(final long javaObjectAtom, final 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(final long display, final Buffer pixels, final int width, final int height, final int hotX, final 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..4803852f9 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/x11/RandR11.java @@ -0,0 +1,370 @@ +/** + * 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; +import com.jogamp.newt.Screen; + +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(final long dpy, final 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(final long dpy, final 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); + } + } + + final 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 pixel-units + props[i++] = 0; // rotated viewport y pixel-units + props[i++] = currentMode.getRotatedWidth(); // rotated viewport width pixel-units + props[i++] = currentMode.getRotatedHeight(); // rotated viewport height pixel-units + props[i++] = 0; // rotated viewport x window-units + props[i++] = 0; // rotated viewport y window-units + props[i++] = currentMode.getRotatedWidth(); // rotated viewport width window-units + props[i++] = currentMode.getRotatedHeight(); // rotated viewport height window-units + 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(); + final long screenConfigHandle = getScreenConfiguration0(dpy, screen_idx); + if(0 == screenConfigHandle) { + return null; + } + int[] res; + final int nres_idx; + try { + final 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); + } + final 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(); + final long screenConfigHandle = getScreenConfiguration0(dpy, screen_idx); + if(0 == screenConfigHandle) { + return null; + } + int[] res; + int rate, rot; + final int nres_idx; + try { + final 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); + } + final 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, final MonitorDevice monitor, final MonitorMode mode) { + final long t0 = System.currentTimeMillis(); + boolean done = false; + final int screen_idx = screen.getIndex(); + final 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 < Screen.SCREEN_MODE_CHANGE_TIMEOUT) { + done = setCurrentScreenModePollEnd0(dpy, screen_idx, resId, f, r); + if(!done) { + try { Thread.sleep(10); } catch (final InterruptedException e) { } + } + } + } + } finally { + freeScreenConfiguration0(screenConfigHandle); + } + return done; + } + + @Override + public final void updateScreenViewport(final long dpy, final ScreenDriver screen, final 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..7a409bba1 --- /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) { + final 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(final long dpy, final 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(final long dpy, final ScreenDriver screen) { + if( null != crtInfoHandleMap ) { + for(final 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, final 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, final 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, final 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 index 10f6b84da..f7361740e 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/ScreenDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/ScreenDriver.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,290 +29,241 @@ * 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.util.Dimension; -import javax.media.nativewindow.util.Point; +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.PropertyAccess; +import com.jogamp.common.util.VersionNumber; import com.jogamp.nativewindow.x11.X11GraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsScreen; -import com.jogamp.newt.ScreenMode; -import com.jogamp.newt.util.ScreenModeUtil; +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 = PropertyAccess.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 = display.runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<Long>() { - public Long run(long dpy) { - return new Long(GetScreen0(dpy, screen_idx)); - } } ); + final Long handle = runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Long>() { + @Override + public Long run(final long dpy) { + return Long.valueOf(GetScreen0(dpy, screen_idx)); + } } ); if (handle.longValue() == 0) { throw new RuntimeException("Error creating screen: " + screen_idx); - } - aScreen = new X11GraphicsScreen((X11GraphicsDevice) getDisplay().getGraphicsDevice(), screen_idx); + } + final X11GraphicsDevice x11dev = (X11GraphicsDevice) getDisplay().getGraphicsDevice(); + final long dpy = x11dev.getHandle(); + aScreen = new X11GraphicsScreen(x11dev, screen_idx); + { + final 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 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; + private VersionNumber randrVersion; + private RandR rAndR; - 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(); - } } ); - } + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(final 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); - 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; + // 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); } - - 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; + // 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); } - rot = getCurrentScreenRotation0(screenConfigHandle); - if(0>rot) { - return null; + 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 { - freeScreenConfiguration0(screenConfigHandle); + rAndR.endInitialQuery(device.getHandle(), this); } - 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); - } } ); + } + } finally { + device.unlock(); + } } - 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); + @Override + protected boolean updateNativeMonitorDeviceViewportImpl(final MonitorDevice monitor, final Rectangle viewportPU, final Rectangle viewportWU) { + final AbstractGraphicsDevice device = getDisplay().getGraphicsDevice(); + device.lock(); + try { + final int[] viewportProps = rAndR.getMonitorDeviceViewport(device.getHandle(), this, monitor.getId()); + viewportPU.set(viewportProps[0], viewportProps[1], viewportProps[2], viewportProps[3]); + viewportWU.set(viewportProps[0], viewportProps[1], viewportProps[2], viewportProps[3]); // equal window-units and pixel-units + return true; + } 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(final 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 = 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); - } + final boolean done = runWithOptTempDisplayHandle( new DisplayImpl.DisplayRunnable<Boolean>() { + @Override + public Boolean run(final long dpy) { + return Boolean.valueOf( rAndR.setCurrentMonitorMode(dpy, ScreenDriver.this, monitor, mode) ); + } }).booleanValue(); - + if(DEBUG || !done) { - System.err.println("X11Screen.setCurrentScreenModeImpl: TO ("+SCREEN_MODE_CHANGE_TIMEOUT+") reached: "+ - (System.currentTimeMillis()-t0)+"ms; Current: "+getCurrentScreenMode()+"; Desired: "+screenMode); + System.err.println("X11Screen.setCurrentMonitorModeImpl: TO ("+SCREEN_MODE_CHANGE_TIMEOUT+") reached: "+ + (System.currentTimeMillis()-t0)+"ms; "+monitor.getCurrentMode()+" -> "+mode); } 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(); - + private final DisplayImpl.DisplayRunnable<Boolean> xineramaEnabledQueryWithTemp = new DisplayImpl.DisplayRunnable<Boolean>() { + @Override + public Boolean run(final long dpy) { + return Boolean.valueOf(X11Util.XineramaIsEnabled(dpy)); + } }; + + @Override protected int validateScreenIndex(final int idx) { - if(getDisplay().isNativeValid()) { - return runWithLockedDisplayHandle( xineramaEnabledQuery ).booleanValue() ? 0 : idx; + final DisplayDriver x11Display = (DisplayDriver) getDisplay(); + final Boolean r = x11Display.isXineramaEnabled(); + if( null != r ) { + return r.booleanValue() ? 0 : idx; } else { - return runWithTempDisplayHandle( xineramaEnabledQuery ).booleanValue() ? 0 : idx; + return runWithTempDisplayHandle( xineramaEnabledQueryWithTemp ).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; - } } ); - } - + + @Override + protected void calcVirtualScreenOriginAndSize(final Rectangle viewport, final Rectangle viewportInWindowUnits) { + final RectangleImmutable ov = DEBUG ? (RectangleImmutable) getViewport().cloneMutable() : null; + /** + 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(final long dpy) { + viewport.set(0, 0, getWidth0(dpy, screen_idx), getHeight0(dpy, screen_idx)); + viewportInWindowUnits.set(viewport); + return null; + } } ); + if( DEBUG ) { + System.err.println("X11Screen.calcVirtualScreenOriginAndSize: Querying X11: "+ov+" -> "+viewport); + } + } + } + //---------------------------------------------------------------------- // Internals only - // - private final <T> T runWithLockedDisplayHandle(DisplayRunnable<T> action) { - return display.runWithLockedDisplayHandle(action); - // return runWithTempDisplayHandle(action); - // return runWithoutLock(action); + // + private final <T> T runWithLockedDisplayDevice(final DisplayRunnable<T> action) { + return display.runWithLockedDisplayDevice(action); } - - private final <T> T runWithTempDisplayHandle(DisplayRunnable<T> action) { - final long displayHandle = X11Util.openDisplay(display.getName()); + + private final <T> T runWithTempDisplayHandle(final DisplayRunnable<T> action) { + final long displayHandle = X11Util.openDisplay(display.getName()); if(0 == displayHandle) { throw new RuntimeException("null device"); } @@ -324,34 +275,20 @@ public class ScreenDriver extends ScreenImpl { } return res; } - private final <T> T runWithoutLock(DisplayRunnable<T> action) { - return action.run(display.getHandle()); + + private final <T> T runWithOptTempDisplayHandle(final 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); - /** @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); + 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 index 97d1ae3db..afa9786e2 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/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,46 +29,91 @@ * 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_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_UP_BUTTON = 6; private static final int X11_WHEEL_TWO_DOWN_BUTTON = 7; - + + private static final int defaultIconDataSize; + private static final Buffer defaultIconData; + static { - DisplayDriver.initSingleton(); + 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 (final 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, screen.getGraphicsScreen(), VisualIDHolder.VID_UNDEFINED); + 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); } @@ -77,115 +122,231 @@ public class WindowDriver extends WindowImpl { 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)); + 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 DisplayDriver display = (DisplayDriver) getScreen().getDisplay(); + final AbstractGraphicsDevice edtDevice = display.getGraphicsDevice(); + edtDevice.lock(); 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); + CloseWindow0(edtDevice.getHandle(), windowHandleClose, + display.getJavaObjectAtom(), display.getWindowDeleteAtom() /* , display.getKbdHandle() */); // XKB disabled for now + } catch (final Throwable t) { + if(DEBUG_IMPLEMENTATION) { + final 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; + } } - protected boolean reconfigureWindowImpl(int x, int y, int width, int height, int flags) { + /** + * <p> + * X11 Window supports {@link #FLAG_IS_FULLSCREEN_SPAN} + * </p> + * {@inheritDoc} + */ + @Override + protected boolean isReconfigureFlagSupported(final 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+" "+width+"x"+height+", "+ - getReconfigureFlagsAsString(null, flags)); + System.err.println("X11Window reconfig: "+x+"/"+y+" -> "+_x+"/"+_y+" "+width+"x"+height+", insets "+_insets+", "+ 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 != ( 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(); - reconfigureWindow0( getDisplayEDTHandle(), getScreenIndex(), - getParentWindowHandle(), getWindowHandle(), display.getWindowDeleteAtom(), - x, y, width, height, flags); - + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(final 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(final boolean defer, final 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(final 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) { + protected void reparentNotify(final 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 requestFocusImpl(final boolean force) { + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(final long dpy) { + requestFocus0(dpy, getWindowHandle(), force); + return null; + } + }); } @Override protected void setTitleImpl(final String title) { - runWithLockedDisplayHandle( new DisplayImpl.DisplayRunnable<Object>() { - public Object run(long dpy) { + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(final long dpy) { setTitle0(dpy, getWindowHandle(), title); return null; } }); } - + + @Override + protected void setPointerIconImpl(final PointerIconImpl pi) { + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(final long dpy) { + try { + setPointerIcon0(dpy, getWindowHandle(), null != pi ? pi.validatedHandle() : 0); + } catch (final Exception e) { + e.printStackTrace(); + } + 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)); + return runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Boolean>() { + @Override + public Boolean run(final long dpy) { + final PointerIconImpl pi = (PointerIconImpl)getPointerIcon(); + final boolean res; + if( pointerVisible && null != pi ) { + setPointerIcon0(dpy, getWindowHandle(), pi.validatedHandle()); + res = true; + } else { + res = setPointerVisible0(dpy, getWindowHandle(), pointerVisible); + } + return Boolean.valueOf(res); } }).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)); + return runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Boolean>() { + @Override + public Boolean run(final long dpy) { + return Boolean.valueOf(confinePointer0(dpy, 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); + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(final long dpy) { + warpPointer0(dpy, getWindowHandle(), x, y); return null; } }); } - + + @Override 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); + return runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Point>() { + @Override + public Point run(final long dpy) { + return X11Lib.GetRelativeLocation(dpy, getScreenIndex(), getWindowHandle(), 0 /*root win*/, x, y); + } + } ); } - protected void updateInsetsImpl(Insets insets) { - // nop - using event driven insetsChange(..) + @Override + protected void updateInsetsImpl(final 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) { + + @Override + protected final void doMouseEvent(final boolean enqueue, final boolean wait, short eventType, int modifiers, + final int x, final int y, short button, final float[] rotationXYZ, final float rotationScale) { switch(eventType) { case MouseEvent.EVENT_MOUSE_PRESSED: switch(button) { @@ -198,64 +359,111 @@ public class WindowDriver extends WindowImpl { } break; case MouseEvent.EVENT_MOUSE_RELEASED: + final boolean shiftPressed = 0 != ( modifiers & InputEvent.SHIFT_MASK ); switch(button) { - case X11_WHEEL_ONE_UP_BUTTON: + case X11_WHEEL_ONE_UP_BUTTON: // vertical scroll up eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; button = 1; - rotation = 1; + rotationXYZ[shiftPressed ? 0 : 1] = 1; break; - case X11_WHEEL_ONE_DOWN_BUTTON: + case X11_WHEEL_ONE_DOWN_BUTTON: // vertical scroll down eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; button = 1; - rotation = -1; + rotationXYZ[shiftPressed ? 0 : 1] = -1; break; - case X11_WHEEL_TWO_UP_BUTTON: + case X11_WHEEL_TWO_UP_BUTTON: // horizontal scroll left eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; - button = 2; - rotation = 1; + button = 1; + rotationXYZ[0] = 1; + modifiers |= InputEvent.SHIFT_MASK; break; - case X11_WHEEL_TWO_DOWN_BUTTON: + case X11_WHEEL_TWO_DOWN_BUTTON: // horizontal scroll right eventType = MouseEvent.EVENT_MOUSE_WHEEL_MOVED; - button = 2; - rotation = -1; + button = 1; + rotationXYZ[0] = -1; + modifiers |= InputEvent.SHIFT_MASK; break; - } + } break; } - super.doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, rotation); + super.doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, rotationXYZ, rotationScale); } - - + + /** Called by native TK */ + protected final void sendKeyEvent(final short eventType, final int modifiers, final short keyCode, final short keySym, final char keyChar0, final String keyString) { + // handleKeyEvent(true, false, eventType, modifiers, keyCode, keyChar); + final boolean isModifierKey = KeyEvent.isModifierKey(keyCode); + final boolean isAutoRepeat = 0 != ( InputEvent.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(final short eventType, final int modifiers, final short keyCode, final short keySym, final char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); + } + @Override + public final void enqueueKeyEvent(final boolean wait, final short eventType, final int modifiers, final short keyCode, final short keySym, final 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 long getDisplayEDTHandle() { - return ((DisplayDriver) getScreen().getDisplay()).getEDTHandle(); - } - private final <T> T runWithLockedDisplayHandle(DisplayRunnable<T> action) { - return ((DisplayImpl) getScreen().getDisplay()).runWithLockedDisplayHandle(action); - // return runWithTempDisplayHandle(action); + + private final <T> T runWithLockedDisplayDevice(final DisplayRunnable<T> action) { + return ((DisplayDriver) getScreen().getDisplay()).runWithLockedDisplayDevice(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 long CreateWindow(final long parentWindowHandle, final long display, final int screen_index, + final int visualID, final long javaObjectAtom, final long windowDeleteAtom, + final int x, final int y, final int width, final int height, final boolean autoPosition, final int flags, + final int pixelDataSize, final 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); + 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/event/NEWTEventTask.java b/src/newt/classes/jogamp/newt/event/NEWTEventTask.java index 93d5e2fb9..2bdab2796 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,18 +35,18 @@ import com.jogamp.newt.event.NEWTEvent; * which notifies after sending the event for the <code>invokeAndWait()</code> semantics. */ public class NEWTEventTask { - private NEWTEvent event; - private Object notifyObject; + private final NEWTEvent event; + private final Object notifyObject; private RuntimeException exception; - public NEWTEventTask(NEWTEvent event, Object notifyObject) { + public NEWTEventTask(final NEWTEvent event, final Object notifyObject) { this.event = event ; this.notifyObject = notifyObject ; this.exception = null; } public final NEWTEvent get() { return event; } - public final void setException(RuntimeException e) { exception = e; } + public final void setException(final RuntimeException e) { exception = e; } public final RuntimeException getException() { return exception; } public final boolean isCallerWaiting() { return null != notifyObject; } 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..794e989a6 --- /dev/null +++ b/src/newt/classes/jogamp/newt/swt/SWTEDTUtil.java @@ -0,0 +1,370 @@ +/** + * 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, final 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(final long ms) { + pollPeriod = ms; // writing to static field is intended + } + + @Override + public final void start() throws IllegalStateException { + final boolean swtDisposed = swtDisplay.isDisposed(); + synchronized(edtLock) { + if( nedt.isRunning() ) { + final Thread curT = Thread.currentThread(); + final String swtTName; + final Thread swtT; + if( !swtDisposed ) { + swtT = swtDisplay.getThread(); + swtTName = swtT.getName(); + } else { + swtT = null; + swtTName = 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 "+swtTName+", 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 ) { + if( !nedt.isRunning() ) { + throw new RuntimeException("EDT could not be started: "+nedt); + } + } else { + // FIXME: Throw exception ? + } + } + + 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(final boolean wait, final Runnable task) { + return invokeImpl(wait, task, true); + } + + @Override + public final boolean invoke(final boolean wait, final Runnable task) { + return invokeImpl(wait, task, false); + } + + private final boolean invokeImpl(boolean wait, final 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 (final 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 (final 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 (final 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(final ThreadGroup tg, final 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 (final InterruptedException e) { + e.printStackTrace(); + } + } + } + } while(!shouldStop) ; + } catch (final 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..a989345dd --- /dev/null +++ b/src/newt/classes/jogamp/newt/swt/event/SWTNewtEventFactory.java @@ -0,0 +1,396 @@ +/** + * 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 javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeSurfaceHolder; + +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(final 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(final int awtMods, final 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 & 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(final org.eclipse.swt.widgets.Event event, final NativeSurfaceHolder sourceHolder) { + com.jogamp.newt.event.InputEvent res = createMouseEvent(event, sourceHolder); + if(null == res) { + res = createKeyEvent(event, sourceHolder); + } + return res; + } + + public static final com.jogamp.newt.event.MouseEvent createMouseEvent(final org.eclipse.swt.widgets.Event event, final NativeSurfaceHolder sourceHolder) { + 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); + + final NativeSurface source = sourceHolder.getNativeSurface(); + final int[] pixelPos; + if( null != source ) { + 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; + } + } + pixelPos = source.convertToPixelUnits(new int[] { event.x, event.y }); + } else { + pixelPos = new int[] { event.x, event.y }; + } + + return new com.jogamp.newt.event.MouseEvent( + type, sourceHolder, (0xFFFFFFFFL & event.time), + mods, pixelPos[0], pixelPos[1], (short)event.count, (short)event.button, MouseEvent.getRotationXYZ(rotation, mods), 1f); + } + return null; // no mapping .. + } + + public static final com.jogamp.newt.event.KeyEvent createKeyEvent(final org.eclipse.swt.widgets.Event event, final NativeSurfaceHolder sourceHolder) { + 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, sourceHolder, (0xFFFFFFFFL & 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(final org.eclipse.swt.widgets.Event event, final NativeSurfaceHolder sourceHolder, final com.jogamp.newt.event.MouseListener l) { + final com.jogamp.newt.event.MouseEvent res = createMouseEvent(event, sourceHolder); + 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(final org.eclipse.swt.widgets.Event event, final NativeSurfaceHolder sourceHolder, final com.jogamp.newt.event.KeyListener l) { + final com.jogamp.newt.event.KeyEvent res = createKeyEvent(event, sourceHolder); + 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 NativeSurfaceHolder sourceHolder, + final com.jogamp.newt.event.MouseListener ml, + final com.jogamp.newt.event.KeyListener kl) { + if(null==ctrl) { + throw new IllegalArgumentException("Argument ctrl is null"); + } + if(null==sourceHolder) { + throw new IllegalArgumentException("Argument source is null"); + } + + if( null != ml ) { + final Listener listener = new Listener () { + @Override + public void handleEvent (final Event event) { + dispatchMouseEvent( event, sourceHolder, ml ); + } }; + 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); + } + if( null != kl ) { + final Listener listener = new Listener () { + @Override + public void handleEvent (final Event event) { + dispatchKeyEvent( event, sourceHolder, kl ); + } }; + ctrl.addListener(SWT.KeyDown, listener); + ctrl.addListener(SWT.KeyUp, listener); + } + } +} + |