diff options
Diffstat (limited to 'src/newt')
86 files changed, 19672 insertions, 0 deletions
diff --git a/src/newt/classes/com/jogamp/newt/Display.java b/src/newt/classes/com/jogamp/newt/Display.java new file mode 100644 index 000000000..f879449b9 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/Display.java @@ -0,0 +1,230 @@ +/** + * 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 com.jogamp.newt.util.EDTUtil; +import jogamp.newt.Debug; +import jogamp.newt.DisplayImpl; + +import java.util.*; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeWindowException; + +public abstract class Display { + public static final boolean DEBUG = Debug.debug("Display"); + + /** return precomputed hashCode from FQN {@link #getFQName()} */ + public abstract int hashCode(); + + /** return true if obj is of type Display and both FQN {@link #getFQName()} equals */ + public boolean equals(Object obj) { + if (this == obj) { return true; } + if (obj instanceof Display) { + Display d = (Display)obj; + return d.getFQName().equals(getFQName()); + } + return false; + } + + /** + * 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> + * Otherwise the abstract device won't be available before the dependent components (Screen and Window) are realized. + * <p> + * This method is usually invoke by {@link #addReference()} + * </p> + * @throws NativeWindowException if the native creation failed. + */ + public abstract void createNative() throws NativeWindowException; + + /** + * Manually trigger the destruction, incl. native destruction.<br> + * <p> + * This method is usually invoke by {@link #removeReference()} + * </p> + */ + public abstract void destroy(); + + /** + * Validate EDT running state.<br> + * 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(); + + /** + * @return true if the native display handle is valid and ready to operate, + * otherwise false. + * + * @see #destroy() + */ + public abstract boolean isNativeValid(); + + /** + * @return number of references by Screen + */ + public abstract int getReferenceCount(); + + /** + * The 1st call will initiate native creation, + * since we follow the lazy creation pattern. + * + * @return number of references after adding one + * @throws NativeWindowException if the native creation failed. + * @see #removeReference() + */ + public abstract int addReference() throws NativeWindowException ; + + /** + * The last call may destroy this instance, + * if {@link #getDestroyWhenUnused()} returns <code>true</code>. + * + * @return number of references after removing one + * @see #addReference() + * @see #getDestroyWhenUnused() + * @see #setDestroyWhenUnused(boolean) + */ + public abstract int removeReference(); + + public abstract AbstractGraphicsDevice getGraphicsDevice(); + + /** + * @return the fully qualified Display name, + * which is a key of {@link #getType()} + {@link #getName()} + {@link #getId()} + */ + public abstract String getFQName(); + + public abstract long getHandle(); + + /** + * @return this display internal serial id + */ + public abstract int getId(); + + /** + * @return this display instance name as defined at creation time + */ + public abstract String getName(); + + /** + * @return the native display type, ie {@link javax.media.nativewindow.NativeWindowFactory#getNativeWindowType(boolean)} + */ + public abstract String getType(); + + public abstract EDTUtil getEDTUtil(); + + public abstract boolean isEDTRunning(); + + public abstract void dispatchMessages(); + + // Global Displays + protected static ArrayList displayList = new ArrayList(); + protected static int displaysActive = 0; + + public static void dumpDisplayList(String prefix) { + synchronized(displayList) { + Iterator i = displayList.iterator(); + System.err.println(prefix+" DisplayList[] entries: "+displayList.size()+" - "+getThreadName()); + for(int j=0; i.hasNext(); j++) { + DisplayImpl d = (DisplayImpl) i.next(); + System.err.println(" ["+j+"] : "+d); + } + } + } + + /** + * + * @param type + * @param name + * @param fromIndex start index, then increasing until found or end of list * + * @return + */ + public static Display getFirstDisplayOf(String type, String name, int fromIndex) { + return getDisplayOfImpl(type, name, fromIndex, 1); + } + + /** + * + * @param type + * @param name + * @param fromIndex start index, then decreasing until found or end of list. -1 is interpreted as size - 1. + * @return + */ + public static Display getLastDisplayOf(String type, String name, int fromIndex) { + return getDisplayOfImpl(type, name, fromIndex, -1); + } + + private static Display getDisplayOfImpl(String type, String name, int fromIndex, int incr) { + 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; + } + i+=incr; + } + } + return null; + } + + /** Returns the global display collection */ + public static Collection getAllDisplays() { + ArrayList list; + synchronized(displayList) { + list = (ArrayList) displayList.clone(); + } + return list; + } + + public static int getActiveDisplayNumber() { + synchronized(displayList) { + return displaysActive; + } + } + + public static String getThreadName() { + return Thread.currentThread().getName(); + } + + public static String toHexString(int hex) { + return "0x" + Integer.toHexString(hex); + } + + public static String toHexString(long hex) { + return "0x" + Long.toHexString(hex); + } + + public static int hashCodeNullSafe(Object o) { + return ( null != o ) ? o.hashCode() : 0; + } +} diff --git a/src/newt/classes/com/jogamp/newt/NewtFactory.java b/src/newt/classes/com/jogamp/newt/NewtFactory.java new file mode 100644 index 000000000..4b7eedca2 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/NewtFactory.java @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.newt; + +import javax.media.nativewindow.*; +import com.jogamp.common.jvm.JVMUtil; +import com.jogamp.newt.event.WindowEvent; +import jogamp.newt.DisplayImpl; +import jogamp.newt.ScreenImpl; +import jogamp.newt.WindowImpl; +import jogamp.newt.Debug; + +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 + static { + JVMUtil.initSingleton(); + NativeWindowFactory.initSingleton(false); // last resort .. + WindowImpl.init(NativeWindowFactory.getNativeWindowType(true)); + } + + public static Class getCustomClass(String packageName, String classBaseName) { + Class clazz = null; + if(packageName!=null || classBaseName!=null) { + String clazzName = packageName + "." + classBaseName ; + try { + clazz = Class.forName(clazzName); + } catch (Throwable t) {} + } + return clazz; + } + + private static boolean useEDT = true; + + /** + * Toggles the usage of an EventDispatchThread while creating a Display.<br> + * The default is enabled.<br> + * The EventDispatchThread is thread local to the Display instance.<br> + */ + public static synchronized void setUseEDT(boolean onoff) { + useEDT = onoff; + } + + /** @see #setUseEDT(boolean) */ + public static boolean useEDT() { return useEDT; } + + /** + * Create a Display entity, incl native creation + */ + public static Display createDisplay(String name) { + return createDisplay(name, true); + } + + public static Display createDisplay(String name, boolean reuse) { + return DisplayImpl.create(NativeWindowFactory.getNativeWindowType(true), name, 0, reuse); + } + + /** + * Create a Display entity using the given implementation type, incl native creation + */ + public static Display createDisplay(String type, String name) { + return createDisplay(type, name, true); + } + + public static Display createDisplay(String type, String name, boolean reuse) { + return DisplayImpl.create(type, name, 0, reuse); + } + + /** + * Create a Screen entity, incl native creation + */ + public static Screen createScreen(Display display, int index) { + return ScreenImpl.create(display, index); + } + + /** + * Create a top level Window entity, incl native creation.<br> + * The Display/Screen is created and owned, ie destructed atomatically.<br> + * A new Display is only created if no preexisting one could be found via {@link Display#getLastDisplayOf(java.lang.String, java.lang.String, int)}. + */ + public static Window createWindow(CapabilitiesImmutable caps) { + return createWindowImpl(NativeWindowFactory.getNativeWindowType(true), caps); + } + + /** + * Create a top level Window entity, incl native creation + */ + public static Window createWindow(Screen screen, CapabilitiesImmutable caps) { + return createWindowImpl(screen, caps); + } + + /** + * Create a child Window entity attached to the given parent, incl native creation.<br> + * The Screen and Display information is regenerated utilizing the parents information.<br> + * <p> + * In case <code>parentWindowObject</code> is a {@link com.jogamp.newt.Window} instance,<br> + * the new window is added to it's list of children.<br> + * This assures proper handling of visibility, creation and destruction.<br> + * {@link com.jogamp.newt.event.WindowEvent#EVENT_WINDOW_RESIZED} is not propagated to the child window for layout<br>, + * you have to add an appropriate {@link com.jogamp.newt.event.WindowListener} for this use case.<br> + * The parents visibility is passed to the new Window<br></p> + * <p> + * In case <code>parentWindowObject</code> is a different {@link javax.media.nativewindow.NativeWindow} implementation,<br> + * you have to handle all events appropriate.<br></p> + * <p> + * + * @param parentWindowObject either a NativeWindow instance + */ + public static Window createWindow(NativeWindow nParentWindow, CapabilitiesImmutable caps) { + final String type = NativeWindowFactory.getNativeWindowType(true); + + Screen screen = null; + Window parentWindow = null; + + if ( nParentWindow instanceof Window ) { + // use parent NEWT Windows Display/Screen + parentWindow = (Window) nParentWindow ; + screen = parentWindow.getScreen(); + } else { + // create a Display/Screen compatible to the NativeWindow + AbstractGraphicsConfiguration nParentConfig = nParentWindow.getGraphicsConfiguration(); + if(null!=nParentConfig) { + AbstractGraphicsScreen nParentScreen = nParentConfig.getScreen(); + AbstractGraphicsDevice nParentDevice = nParentScreen.getDevice(); + Display display = NewtFactory.createDisplay(type, nParentDevice.getHandle(), true); + screen = NewtFactory.createScreen(display, nParentScreen.getIndex()); + } else { + Display display = NewtFactory.createDisplay(type, null, true); // local display + screen = NewtFactory.createScreen(display, 0); // screen 0 + } + } + final Window win = createWindowImpl(nParentWindow, screen, caps); + + win.setSize(nParentWindow.getWidth(), nParentWindow.getHeight()); + if ( null != parentWindow ) { + parentWindow.addChild(win); + win.setVisible(parentWindow.isVisible()); + } + 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 + 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 + */ + public static Window createWindow(long parentWindowHandle, Screen screen, CapabilitiesImmutable caps) { + return createWindowImpl(parentWindowHandle, screen, caps); + } + + /** + * Ability to try a Window type with a constructor argument, if supported ..<p> + * Currently only valid is <code> AWTWindow(Frame frame) </code>, + * to support an external created AWT Frame, ie the browsers embedded frame. + * + * @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) { + 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); + } + + private static boolean instanceOf(Object obj, String clazzName) { + Class clazz = obj.getClass(); + do { + if(clazz.getName().equals(clazzName)) { + return true; + } + clazz = clazz.getSuperclass(); + } while (clazz!=null); + return false; + } + + public static boolean isScreenCompatible(NativeWindow parent, Screen childScreen) { + // Get parent's NativeWindow details + AbstractGraphicsConfiguration parentConfig = (AbstractGraphicsConfiguration) parent.getGraphicsConfiguration(); + AbstractGraphicsScreen parentScreen = (AbstractGraphicsScreen) parentConfig.getScreen(); + AbstractGraphicsDevice parentDevice = (AbstractGraphicsDevice) parentScreen.getDevice(); + + DisplayImpl childDisplay = (DisplayImpl) childScreen.getDisplay(); + String parentDisplayName = childDisplay.validateDisplayName(null, parentDevice.getHandle()); + String childDisplayName = childDisplay.getName(); + if( ! parentDisplayName.equals( childDisplayName ) ) { + return false; + } + + if( parentScreen.getIndex() != childScreen.getIndex() ) { + return false; + } + return true; + } + + public static Screen createCompatibleScreen(NativeWindow parent) { + return createCompatibleScreen(parent, null); + } + + public static Screen createCompatibleScreen(NativeWindow parent, Screen childScreen) { + // Get parent's NativeWindow details + AbstractGraphicsConfiguration parentConfig = (AbstractGraphicsConfiguration) parent.getGraphicsConfiguration(); + AbstractGraphicsScreen parentScreen = (AbstractGraphicsScreen) parentConfig.getScreen(); + AbstractGraphicsDevice parentDevice = (AbstractGraphicsDevice) parentScreen.getDevice(); + + 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(); + if(DEBUG_IMPLEMENTATION) { + System.err.println("NewtFactory.createCompatibleScreen: Display: "+ + parentDisplayName+" =? "+childDisplayName+" : "+displayEqual+"; Screen: "+ + parentScreen.getIndex()+" =? "+childScreen.getIndex()+" : "+screenEqual); + } + if( displayEqual && screenEqual ) { + // match: display/screen + return childScreen; + } + } + + // Prep NEWT's Display and Screen according to the parent + final String type = NativeWindowFactory.getNativeWindowType(true); + 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 new file mode 100644 index 000000000..961ffdf6a --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/NewtVersion.java @@ -0,0 +1,64 @@ +/** + * 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 com.jogamp.common.GlueGenVersion; +import com.jogamp.common.util.JogampVersion; +import com.jogamp.common.util.VersionUtil; +import com.jogamp.nativewindow.NativeWindowVersion; +import java.util.jar.Manifest; + +public class NewtVersion extends JogampVersion { + + protected static volatile NewtVersion jogampCommonVersionInfo; + + protected NewtVersion(String packageName, Manifest mf) { + super(packageName, mf); + } + + public static NewtVersion getInstance() { + 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); + } + } + } + return jogampCommonVersionInfo; + } + + public static void main(String args[]) { + System.err.println(VersionUtil.getPlatformInfo()); + System.err.println(GlueGenVersion.getInstance()); + System.err.println(NativeWindowVersion.getInstance()); + System.err.println(NewtVersion.getInstance()); + } +} diff --git a/src/newt/classes/com/jogamp/newt/Screen.java b/src/newt/classes/com/jogamp/newt/Screen.java new file mode 100644 index 000000000..fec3613a2 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/Screen.java @@ -0,0 +1,236 @@ +/** + * 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 com.jogamp.newt.event.ScreenModeListener; +import jogamp.newt.Debug; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.NativeWindowException; + +public abstract class Screen { + + /** + * A 10s timeout for screen mode change. It is observed, that some platforms + * need a notable amount of time for this task, especially in case of rotation change. + */ + public static final int SCREEN_MODE_CHANGE_TIMEOUT = 10000; + + public static final boolean DEBUG = Debug.debug("Screen"); + + /** return precomputed hashCode from FQN {@link #getFQName()} */ + public abstract int hashCode(); + + /** return true if obj is of type Display and both FQN {@link #getFQName()} equals */ + public boolean equals(Object obj) { + if (this == obj) { return true; } + if (obj instanceof Screen) { + Screen s = (Screen)obj; + return s.getFQName().equals(getFQName()); + } + return false; + } + + /** + * 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.AbstractGraphicsScreen}, via + * {@link #getGraphicsScreen()}.<br> + * Otherwise the abstract device won't be available before the dependent component (Window) is realized. + * <p> + * This method is usually invoke by {@link #addReference()} + * </p> + * <p> + * This method invokes {@link Display#addReference()} after creating the native peer,<br> + * which will issue {@link Display#createNative()} if the reference count was 0. + * </p> + * @throws NativeWindowException if the native creation failed. + */ + public abstract void createNative() throws NativeWindowException; + + /** + * Manually trigger the destruction, incl. native destruction.<br> + * <p> + * This method is usually invoke by {@link #removeReference()} + * </p> + * <p> + * This method invokes {@link Display#removeReference()} after it's own destruction,<br> + * which will issue {@link Display#destroy()} if the reference count becomes 0. + * </p> + */ + public abstract void destroy(); + + public abstract boolean isNativeValid(); + + /** + * @return number of references by Window + */ + public abstract int getReferenceCount(); + + /** + * See {@link Display#addReference()} + * + * @throws NativeWindowException if the native creation failed. + * @see #removeReference() + * @see #setDestroyWhenUnused(boolean) + * @see #getDestroyWhenUnused() + */ + public abstract int addReference() throws NativeWindowException; + + /** + * See {@link Display#removeReference()} + * + * @see #addReference() + * @see #setDestroyWhenUnused(boolean) + * @see #getDestroyWhenUnused() + */ + public abstract int removeReference(); + + public abstract AbstractGraphicsScreen getGraphicsScreen(); + + /** + * @return this Screen index of all Screens of {@link #getDisplay()}. + */ + public abstract int getIndex(); + + /** + * @return the current screen width + */ + public abstract int getWidth(); + + /** + * @return the current screen height + */ + public abstract int getHeight(); + + /** + * @return the associated Display + */ + public abstract Display getDisplay(); + + /** + * @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 + */ + public abstract void addScreenModeListener(ScreenModeListener sml); + + /** + * @param sml ScreenModeListener to be removed from ScreenMode change events + */ + public abstract void removeScreenModeListener(ScreenModeListener sml); + + /** + * Return a list of available {@link com.jogamp.newt.ScreenMode}s. + * @return a shallow copy of the internal immutable {@link com.jogamp.newt.ScreenMode}s, + * or null if not implemented for this native type {@link com.jogamp.newt.Display#getType()}. + */ + public abstract List/*<ScreenMode>*/ getScreenModes(); + + /** + * Return the original {@link com.jogamp.newt.ScreenMode}, as used at NEWT initialization. + * @return null if functionality not implemented, + * otherwise the original ScreenMode which is element of the list {@link #getScreenModes()}. + * + */ + public abstract ScreenMode getOriginalScreenMode(); + + /** + * Return the current {@link com.jogamp.newt.ScreenMode}. + * @return null if functionality not implemented, + * otherwise the current ScreenMode which is element of the list {@link #getScreenModes()}. + */ + public abstract ScreenMode getCurrentScreenMode(); + + /** + * 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 + */ + public abstract boolean setCurrentScreenMode(ScreenMode screenMode); + + // Global Screens + protected static ArrayList screenList = new ArrayList(); + protected static int screensActive = 0; + + /** + * + * @param type + * @param name + * @param fromIndex start index, then increasing until found or end of list * + * @return + */ + public static Screen getFirstScreenOf(Display display, int idx, int fromIndex) { + return getScreenOfImpl(display, idx, fromIndex, 1); + } + + /** + * + * @param type + * @param name + * @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) { + return getScreenOfImpl(display, idx, fromIndex, -1); + } + + private static Screen getScreenOfImpl(Display display, int idx, int fromIndex, 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; + } + i+=incr; + } + } + return null; + } + /** Returns the global display collection */ + public static Collection getAllScreens() { + ArrayList list; + synchronized(screenList) { + list = (ArrayList) screenList.clone(); + } + return list; + } + + public static int getActiveScreenNumber() { + synchronized(screenList) { + return screensActive; + } + } +} diff --git a/src/newt/classes/com/jogamp/newt/ScreenMode.java b/src/newt/classes/com/jogamp/newt/ScreenMode.java new file mode 100644 index 000000000..81ce70249 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/ScreenMode.java @@ -0,0 +1,189 @@ +/** + * 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 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 implements Cloneable { + /** 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; + } + + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException ex) { + throw new InternalError(); + } + } + + /** 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; + } + + 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; + } +} diff --git a/src/newt/classes/com/jogamp/newt/Window.java b/src/newt/classes/com/jogamp/newt/Window.java new file mode 100644 index 000000000..b78f7a9e8 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/Window.java @@ -0,0 +1,422 @@ +/** + * 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 com.jogamp.newt.event.WindowListener; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.MouseListener; +import jogamp.newt.Debug; +import javax.media.nativewindow.CapabilitiesChooser; +import javax.media.nativewindow.CapabilitiesImmutable; +import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.SurfaceUpdatedListener; +import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.nativewindow.util.Insets; + +/** + * Specifying the public Window functionality for the + * using a Window and for shadowing one like {@link com.jogamp.newt.opengl.GLWindow}. + */ +public interface Window extends NativeWindow, WindowClosingProtocol { + 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_WINDOW_EVENT = Debug.debug("Window.WindowEvent"); + public static final boolean DEBUG_IMPLEMENTATION = Debug.debug("Window"); + + /** A 1s timeout while waiting for a native action response, ie {@link #setVisible(boolean)}. */ + public static final long TIMEOUT_NATIVEWINDOW = 1000; + + // + // Lifecycle + // + + /** + * @return True if native window is valid, can be created or recovered. + * Otherwise false, ie this window is unrecoverable due to a <code>destroy(true)</code> call. + * + * @see #destroy(boolean) + * @see #setVisible(boolean) + */ + boolean isValid(); + + /** + * @return true if the native window handle is valid and ready to operate, ie + * if the native window has been created, otherwise false. + * + * @see #setVisible(boolean) + * @see #destroy(boolean) + */ + boolean isNativeValid(); + + /** + * @return The associated Screen + */ + Screen getScreen(); + + /** + * Set the CapabilitiesChooser to help determine the native visual type. + * + * @param chooser the new CapabilitiesChooser + * @return the previous CapabilitiesChooser + */ + CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser); + + /** + * Gets an immutable set of requested capabilities. + * + * @return the requested capabilities + */ + CapabilitiesImmutable getRequestedCapabilities(); + + /** + * Gets an immutable set of chosen capabilities. + * + * @return the chosen capabilities + */ + CapabilitiesImmutable getChosenCapabilities(); + + /** + * Destroy the Window and it's children, incl. native destruction.<br> + * The Window can be recreate via {@link #setVisible(boolean) setVisible(true)}. + * <p> + * 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> + * @see #invalidate() + * @see #setVisible(boolean) + */ + void destroy(); + + /** + * Destroys the Window via {@link #destroy()} and clears all Object references, + * eg. all states, size, position, parent handles, list of child Windows and reference to it's Screen.<br> + * This Window cannot be recreated after calling this method anymore.<br> + */ + void invalidate(); + + /** + * <p> + * <code>setVisible</code> makes the window and children visible if <code>visible</code> is true, + * otherwise the window and children becomes invisible.<br></p> + * <p> + * The <code>setVisible(true)</code> is responsible to actual create the native window.<br></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 ) { + * createNative(); + * } + * } else if ( this.visible != visible ) { + * this.visible = visible; + * setNativeSizeImpl(); + * } + * </pre></p> + * <p> + * In case this window is a child window and a parent {@link javax.media.nativewindow.NativeWindow} is being used,<br> + * the parent's {@link javax.media.nativewindow.NativeWindow} handle is retrieved via {@link javax.media.nativewindow.NativeWindow#getWindowHandle()}.<br> + * If this action fails, ie if the parent {@link javax.media.nativewindow.NativeWindow} is not valid yet,<br> + * no native window is created yet and <code>setVisible(true)</code> shall be repeated when it is.<br></p> + */ + void setVisible(boolean visible); + + boolean isVisible(); + + // + // Child Window Management + // + + void addChild(NativeWindow win); + + void removeChild(NativeWindow win); + + // + // Modes / States + // + + /** + * Sets the size of the client area of the window, excluding decorations + * Total size of the window will be + * {@code width+insets.left+insets.right, height+insets.top+insets.bottom}<br> + * <p> + * Zero size semantics are respected, see {@link #setVisible(boolean)}:<br> + * <pre> + * if ( 0 != windowHandle && 0>=width*height && visible ) { + * setVisible(false); + * } else if ( 0 == windowHandle && 0<width*height && visible ) { + * setVisible(true); + * } else { + * // as expected .. + * } + * </pre></p> + * <p> + * This call is ignored if in fullscreen mode.<br></p> + * + * @param width of the client area of the window + * @param height of the client area of the window + */ + void setSize(int width, int height); + + /** + * Returns the width of the client area of this window + * @return width of the client area + */ + int getWidth(); + + /** + * Returns the height of the client area of this window + * @return height of the client area + */ + int getHeight(); + + /** Defining ids for the reparenting strategy */ + public interface ReparentAction { + /** No native reparenting valid */ + static final int ACTION_INVALID = -1; + + /** No native reparenting action required, no change*/ + static final int ACTION_UNCHANGED = 0; + + /** Native reparenting incl. Window tree */ + static final int ACTION_NATIVE_REPARENTING = 1; + + /** Native window creation after tree change - instead of reparenting. */ + static final int ACTION_NATIVE_CREATION = 2; + + /** Change Window tree only, native creation is pending */ + static final int ACTION_NATIVE_CREATION_PENDING = 3; + } + + /** + * Change this window's parent window.<br> + * <P> + * In case the old parent is not null and a Window, + * this window is removed from it's list of children.<br> + * In case the new parent is not null and a Window, + * this window is added to it's list of children.<br></P> + * + * @param newParent The new parent NativeWindow. If null, this Window becomes a top level window. + * + * @return The issued reparent action type (strategy) as defined in Window.ReparentAction + */ + int reparentWindow(NativeWindow newParent); + + int reparentWindow(NativeWindow newParent, boolean forceDestroyCreate); + + boolean setFullscreen(boolean fullscreen); + + boolean isFullscreen(); + + /** + * Sets the location of the top left corner of the window, including + * decorations (so the client area will be placed at + * {@code x+insets.left,y+insets.top}.<br> + * + * This call is ignored if in fullscreen mode.<br> + * + * @param x coord of the top left corner + * @param y coord of the top left corner + */ + void setPosition(int x, int y); + + int getX(); + + int getY(); + + /** + * Returns the insets for this native window (the difference between the + * size of the toplevel window with the decorations and the client area). + * + * @return insets for this platform window + */ + Insets getInsets(); + + void setUndecorated(boolean value); + + boolean isUndecorated(); + + void setTitle(String title); + + String getTitle(); + + static interface FocusRunnable { + /** + * @return false if NEWT shall proceed requesting the focus, + * true if NEWT shall not request the focus. + */ + public boolean run(); + } + + /** + * May set to a {@link FocusRunnable}, {@link FocusRunnable#run()} before Newt requests the native focus. + * This allows notifying a covered window toolkit like AWT that the focus is requested, + * hence focus traversal can be made transparent. + */ + void setFocusAction(FocusRunnable focusAction); + + void requestFocus(); + + boolean hasFocus(); + + void windowRepaint(int x, int y, int width, int height); + + void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event); + + void runOnEDTIfAvail(boolean wait, final Runnable task); + + + // + // SurfaceUpdateListener + // + + /** + * Appends the given {@link com.jogamp.newt.event.SurfaceUpdatedListener} to the end of + * the list. + */ + void addSurfaceUpdatedListener(SurfaceUpdatedListener l); + + /** + * + * Inserts the given {@link com.jogamp.newt.event.SurfaceUpdatedListener} 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 addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException; + + void removeAllSurfaceUpdatedListener(); + + void removeSurfaceUpdatedListener(SurfaceUpdatedListener l); + + SurfaceUpdatedListener getSurfaceUpdatedListener(int index); + + SurfaceUpdatedListener[] getSurfaceUpdatedListeners(); + + + // + // WindowListener + // + + public void sendWindowEvent(int eventType); + + /** + * + * Appends the given {@link com.jogamp.newt.event.WindowListener} to the end of + * the list. + */ + void addWindowListener(WindowListener l); + + /** + * + * Inserts the given {@link com.jogamp.newt.event.WindowListener} 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 addWindowListener(int index, WindowListener l) throws IndexOutOfBoundsException; + + void removeWindowListener(WindowListener l); + + WindowListener getWindowListener(int index); + + WindowListener[] getWindowListeners(); + + // + // KeyListener + // + + + /** + * + * Appends the given {@link com.jogamp.newt.event.KeyListener} to the end of + * the list. + */ + void addKeyListener(KeyListener l); + + /** + * + * Inserts the given {@link com.jogamp.newt.event.KeyListener} 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 addKeyListener(int index, KeyListener l); + + void removeKeyListener(KeyListener l); + + KeyListener getKeyListener(int index); + + KeyListener[] getKeyListeners(); + + + // + // MouseListener + // + + /** + * + * Appends the given {@link com.jogamp.newt.event.MouseListener} to the end of + * the list. + */ + void addMouseListener(MouseListener l); + + /** + * + * Inserts the given {@link com.jogamp.newt.event.MouseListener} 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 addMouseListener(int index, MouseListener l); + + void removeMouseListener(MouseListener l); + + MouseListener getMouseListener(int index); + + MouseListener[] getMouseListeners(); + +} diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java new file mode 100644 index 000000000..0eda5c2a3 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -0,0 +1,378 @@ +/** + * 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.awt; + +import com.jogamp.newt.Display; +import java.lang.reflect.*; +import java.security.*; + +import java.awt.Canvas; +import java.awt.EventQueue; +import java.awt.Graphics; +import java.awt.KeyboardFocusManager; + +import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.nativewindow.awt.AWTWindowClosingProtocol; +import jogamp.nativewindow.awt.AWTMisc; + +import com.jogamp.newt.event.awt.AWTAdapter; +import com.jogamp.newt.event.awt.AWTParentWindowAdapter; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.Window; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowListener; +import jogamp.newt.Debug; +import javax.swing.MenuSelectionManager; + +public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProtocol { + public static final boolean DEBUG = Debug.debug("Window"); + + NativeWindow nativeWindow = null; + Window newtChild = null; + int newtChildCloseOp; + AWTAdapter awtAdapter = null; + + private AWTWindowClosingProtocol awtWindowClosingProtocol = + new AWTWindowClosingProtocol(this, new Runnable() { + public void run() { + NewtCanvasAWT.this.destroy(); + } + }); + + /** + * Instantiates a NewtCanvas without a NEWT child.<br> + */ + public NewtCanvasAWT() { + super(); + } + + /** + * Instantiates a NewtCanvas with a NEWT child. + */ + public NewtCanvasAWT(Window child) { + super(); + setNEWTChild(child); + } + + class FocusAction implements Window.FocusRunnable { + public boolean run() { + if ( EventQueue.isDispatchThread() ) { + focusActionImpl.run(); + } else { + try { + EventQueue.invokeAndWait(focusActionImpl); + } catch (Exception e) { + throw new NativeWindowException(e); + } + } + return focusActionImpl.result; + } + + class FocusActionImpl implements Runnable { + public final boolean result = false; // NEWT shall always proceed requesting the native focus + public void run() { + if(DEBUG) { + System.err.println("FocusActionImpl.run() "+Display.getThreadName()); + } + NewtCanvasAWT.this.requestFocusAWTParent(); + KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + kfm.clearGlobalFocusOwner(); + } + } + FocusActionImpl focusActionImpl = new FocusActionImpl(); + } + FocusAction focusAction = new FocusAction(); + + WindowListener clearAWTMenusOnNewtFocus = new WindowAdapter() { + @Override + public void windowGainedFocus(WindowEvent arg0) { + MenuSelectionManager.defaultManager().clearSelectedPath(); + } + }; + + /** sets a new NEWT child, provoking reparenting on the NEWT level. */ + public NewtCanvasAWT setNEWTChild(Window child) { + if(newtChild!=child) { + newtChild = child; + if(null!=nativeWindow) { + java.awt.Container cont = AWTMisc.getContainer(this); + // reparent right away, addNotify has been called already + reparentWindow( (null!=newtChild) ? true : false, cont ); + } + } + return this; + } + + /** @return the current NEWT child */ + public Window getNEWTChild() { + return newtChild; + } + + /** @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 nativeWindow; } + + public int getDefaultCloseOperation() { + return awtWindowClosingProtocol.getDefaultCloseOperation(); + } + + public int setDefaultCloseOperation(int op) { + return awtWindowClosingProtocol.setDefaultCloseOperation(op); + } + + void configureNewtChild(boolean attach) { + if(null!=awtAdapter) { + awtAdapter.removeFrom(this); + awtAdapter=null; + } + if( null != newtChild ) { + if(attach) { + awtAdapter = new AWTParentWindowAdapter(newtChild).addTo(this); + if(newtChild.isValid()) { + newtChild.addWindowListener(clearAWTMenusOnNewtFocus); + } + newtChild.setFocusAction(focusAction); // enable AWT focus traversal + newtChildCloseOp = newtChild.setDefaultCloseOperation(WindowClosingProtocol.DO_NOTHING_ON_CLOSE); + awtWindowClosingProtocol.addClosingListenerOneShot(); + } else { + if(newtChild.isValid()) { + newtChild.removeWindowListener(clearAWTMenusOnNewtFocus); + } + newtChild.setFocusAction(null); + newtChild.setDefaultCloseOperation(newtChildCloseOp); + awtWindowClosingProtocol.removeClosingListener(); + } + } + } + + @Override + public void addNotify() { + + // before native peer is valid: X11 + disableBackgroundErase(); + + // creates the native peer + super.addNotify(); + + // after native peer is valid: Windows + disableBackgroundErase(); + + java.awt.Container cont = AWTMisc.getContainer(this); + if(DEBUG) { + // if ( isShowing() == false ) -> Container was not visible yet. + // if ( isShowing() == true ) -> Container is already visible. + System.err.println("NewtCanvasAWT.addNotify: "+newtChild+", "+this+", visible "+isVisible()+", showing "+isShowing()+ + ", displayable "+isDisplayable()+" -> "+cont); + } + reparentWindow(true, cont); + } + + @Override + public void removeNotify() { + java.awt.Container cont = AWTMisc.getContainer(this); + if(DEBUG) { + System.err.println("NewtCanvasAWT.removeNotify: "+newtChild+", from "+cont); + } + reparentWindow(false, cont); + super.removeNotify(); + } + + void reparentWindow(boolean add, java.awt.Container cont) { + if(null==newtChild) { + return; // nop + } + + newtChild.setFocusAction(null); // no AWT focus traversal .. + if(add) { + nativeWindow = NewtFactoryAWT.getNativeWindow(this, newtChild.getRequestedCapabilities()); + if(null!=nativeWindow) { + if(DEBUG) { + System.err.println("NewtCanvasAWT.reparentWindow: "+newtChild); + } + final int w = cont.getWidth(); + final int h = cont.getHeight(); + setSize(w, h); + newtChild.setSize(w, h); + newtChild.reparentWindow(nativeWindow); + newtChild.setVisible(true); + configureNewtChild(true); + newtChild.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout to listener + newtChild.windowRepaint(0, 0, w, h); + } + } else { + configureNewtChild(false); + nativeWindow = null; + newtChild.setVisible(false); + newtChild.reparentWindow(null); + } + } + + /** + * Destroys this resource: + * <ul> + * <li> Make the NEWT Child invisible </li> + * <li> Disconnects the NEWT Child from this Canvas NativeWindow, reparent to NULL </li> + * <li> Issues <code>destroy()</code> on the NEWT Child</li> + * <li> Remove reference to the NEWT Child</li> + * <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(): "+newtChild+", from "+cont); + } + configureNewtChild(false); + nativeWindow = null; + newtChild.setVisible(false); + newtChild.reparentWindow(null); + newtChild.destroy(); + newtChild=null; + if(null!=cont) { + cont.remove(this); + } + } + } + + @Override + public void paint(Graphics g) { + awtWindowClosingProtocol.addClosingListenerOneShot(); + if(null!=newtChild) { + newtChild.windowRepaint(0, 0, getWidth(), getHeight()); + } + } + @Override + public void update(Graphics g) { + awtWindowClosingProtocol.addClosingListenerOneShot(); + if(null!=newtChild) { + newtChild.windowRepaint(0, 0, getWidth(), getHeight()); + } + } + + final void requestFocusAWTParent() { + super.requestFocus(); + } + + final void requestFocusNEWTChild() { + if(null!=newtChild) { + newtChild.setFocusAction(null); + newtChild.requestFocus(); + newtChild.setFocusAction(focusAction); + } + } + + @Override + public void requestFocus() { + requestFocusAWTParent(); + requestFocusNEWTChild(); + } + + @Override + public boolean requestFocus(boolean temporary) { + boolean res = super.requestFocus(temporary); + if(res) { + requestFocusNEWTChild(); + } + return res; + } + + @Override + public boolean requestFocusInWindow() { + boolean res = super.requestFocusInWindow(); + if(res) { + requestFocusNEWTChild(); + } + return res; + } + + @Override + public boolean requestFocusInWindow(boolean temporary) { + boolean res = super.requestFocusInWindow(temporary); + if(res) { + requestFocusNEWTChild(); + } + return res; + } + + // Disables the AWT's erasing of this Canvas's background on Windows + // in Java SE 6. This internal API is not available in previous + // releases, but the system property + // -Dsun.awt.noerasebackground=true can be specified to get similar + // results globally in previous releases. + private static boolean disableBackgroundEraseInitialized; + private static Method disableBackgroundEraseMethod; + private void disableBackgroundErase() { + if (!disableBackgroundEraseInitialized) { + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + Class clazz = getToolkit().getClass(); + while (clazz != null && disableBackgroundEraseMethod == null) { + try { + disableBackgroundEraseMethod = + clazz.getDeclaredMethod("disableBackgroundErase", + new Class[] { Canvas.class }); + disableBackgroundEraseMethod.setAccessible(true); + } catch (Exception e) { + clazz = clazz.getSuperclass(); + } + } + } catch (Exception e) { + } + return null; + } + }); + } catch (Exception e) { + } + disableBackgroundEraseInitialized = true; + if(DEBUG) { + System.err.println("NewtCanvasAWT: TK disableBackgroundErase method found: "+ + (null!=disableBackgroundEraseMethod)); + } + } + if (disableBackgroundEraseMethod != null) { + Throwable t=null; + try { + disableBackgroundEraseMethod.invoke(getToolkit(), new Object[] { this }); + } catch (Exception e) { + t = e; + } + if(DEBUG) { + System.err.println("NewtCanvasAWT: TK disableBackgroundErase error: "+t); + } + } + } +} + diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtFactoryAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtFactoryAWT.java new file mode 100644 index 000000000..e1370f05e --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/awt/NewtFactoryAWT.java @@ -0,0 +1,73 @@ +/** + * 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.awt; + + +import javax.media.nativewindow.*; +import javax.media.nativewindow.awt.*; + +import com.jogamp.newt.NewtFactory; +import jogamp.newt.Debug; + +public class NewtFactoryAWT extends NewtFactory { + public static final boolean DEBUG_IMPLEMENTATION = Debug.debug("Window"); + + /** + * Wraps an AWT component into a {@link javax.media.nativewindow.NativeWindow} utilizing the {@link javax.media.nativewindow.NativeWindowFactory},<br> + * using a configuration agnostic dummy {@link javax.media.nativewindow.DefaultGraphicsConfiguration}.<br> + * <p> + * The actual wrapping implementation is {@link jogamp.nativewindow.jawt.JAWTWindow}.<br></p> + * <p> + * Purpose of this wrapping is to access the AWT window handle,<br> + * not to actually render into it.<br> + * Hence the dummy configuration only.</p> + * + * @param awtCompObject must be of type java.awt.Component + */ + public static NativeWindow getNativeWindow(Object awtCompObject, CapabilitiesImmutable capsRequested) { + if(null==awtCompObject) { + throw new NativeWindowException("Null AWT Component"); + } + if( ! (awtCompObject instanceof java.awt.Component) ) { + throw new NativeWindowException("AWT Component not a java.awt.Component"); + } + return getNativeWindow( (java.awt.Component) awtCompObject, capsRequested ); + } + + public static NativeWindow getNativeWindow(java.awt.Component awtComp, CapabilitiesImmutable capsRequested) { + DefaultGraphicsConfiguration config = AWTGraphicsConfiguration.create(awtComp, capsRequested, capsRequested); + NativeWindow awtNative = NativeWindowFactory.getNativeWindow(awtComp, config); // a JAWTWindow + if(DEBUG_IMPLEMENTATION) { + System.err.println("NewtFactoryAWT.getNativeWindow: "+awtComp+" -> "+awtNative); + } + return awtNative; + } +} + diff --git a/src/newt/classes/com/jogamp/newt/event/InputEvent.java b/src/newt/classes/com/jogamp/newt/event/InputEvent.java new file mode 100644 index 000000000..148787845 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/InputEvent.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.newt.event; + +public abstract class InputEvent extends NEWTEvent +{ + public static final int SHIFT_MASK = 1 << 0; + public static final int CTRL_MASK = 1 << 1; + public static final int META_MASK = 1 << 2; + public static final int ALT_MASK = 1 << 3; + public static final int ALT_GRAPH_MASK = 1 << 5; + public static final int BUTTON1_MASK = 1 << 6; + public static final int BUTTON2_MASK = 1 << 7; + public static final int BUTTON3_MASK = 1 << 8; + + protected InputEvent(int eventType, Object source, long when, int modifiers) { + super(eventType, source, when); + this.modifiers=modifiers; + } + + public int getModifiers() { + return modifiers; + } + public boolean isAltDown() { + return (modifiers&ALT_MASK)!=0; + } + public boolean isAltGraphDown() { + return (modifiers&ALT_GRAPH_MASK)!=0; + } + public boolean isControlDown() { + return (modifiers&CTRL_MASK)!=0; + } + public boolean isMetaDown() { + return (modifiers&META_MASK)!=0; + } + public boolean isShiftDown() { + return (modifiers&SHIFT_MASK)!=0; + } + + public boolean isButton1Down() { + return (modifiers&BUTTON1_MASK)!=0; + } + + public boolean isButton2Down() { + return (modifiers&BUTTON2_MASK)!=0; + } + + public boolean isButton3Down() { + return (modifiers&BUTTON3_MASK)!=0; + } + + public String toString() { + return "InputEvent[modifiers:"+modifiers+", "+super.toString()+"]"; + } + + private int modifiers; +} diff --git a/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java b/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java new file mode 100644 index 000000000..93c8409b1 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/KeyAdapter.java @@ -0,0 +1,41 @@ +/** + * Copyright 2010 JogAmp Community. 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: + * + * 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; + +public abstract class KeyAdapter implements KeyListener +{ + public void keyPressed(KeyEvent e) { + } + public void keyReleased(KeyEvent e) { + } + public void keyTyped(KeyEvent e) { + } +} + diff --git a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java new file mode 100644 index 000000000..2c3fd9cb2 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java @@ -0,0 +1,736 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.newt.event; + +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; + } + + public char getKeyChar() { + return keyChar; + } + public int getKeyCode() { + return keyCode; + } + + public String toString() { + return "KeyEvent["+getEventTypeString(getEventType())+ + ", code "+keyCode+"("+toHexString(keyCode)+"), char '"+keyChar+"' ("+toHexString((int)keyChar)+"), isActionKey "+isActionKey()+", "+super.toString()+"]"; + } + + public static String getEventTypeString(int type) { + switch(type) { + case EVENT_KEY_PRESSED: return "EVENT_KEY_PRESSED"; + case EVENT_KEY_RELEASED: return "EVENT_KEY_RELEASED"; + case EVENT_KEY_TYPED: return "EVENT_KEY_TYPED"; + default: return "unknown (" + type + ")"; + } + } + + public boolean isActionKey() { + switch (keyCode) { + case VK_HOME: + case VK_END: + case VK_PAGE_UP: + case VK_PAGE_DOWN: + case VK_UP: + case VK_DOWN: + case VK_LEFT: + case VK_RIGHT: + + case VK_F1: + case VK_F2: + case VK_F3: + case VK_F4: + case VK_F5: + case VK_F6: + case VK_F7: + case VK_F8: + case VK_F9: + case VK_F10: + case VK_F11: + case VK_F12: + case VK_F13: + case VK_F14: + case VK_F15: + case VK_F16: + case VK_F17: + case VK_F18: + case VK_F19: + case VK_F20: + case VK_F21: + case VK_F22: + case VK_F23: + case VK_F24: + case VK_PRINTSCREEN: + case VK_CAPS_LOCK: + case VK_PAUSE: + case VK_INSERT: + + case VK_HELP: + case VK_WINDOWS: + return true; + } + return false; + } + + private int keyCode; + private 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; + + /** + * Constant for the non-numpad <b>left</b> arrow key. + * @see #VK_KP_LEFT + */ + public static final int VK_LEFT = 0x25; + + /** + * Constant for the non-numpad <b>up</b> arrow key. + * @see #VK_KP_UP + */ + public static final int VK_UP = 0x26; + + /** + * Constant for the non-numpad <b>right</b> arrow key. + * @see #VK_KP_RIGHT + */ + public static final int VK_RIGHT = 0x27; + + /** + * Constant for the non-numpad <b>down</b> arrow key. + * @see #VK_KP_DOWN + */ + public static final int VK_DOWN = 0x28; + + /** + * Constant for the comma key, "," + */ + public static final int VK_COMMA = 0x2C; + + /** + * Constant for the minus key, "-" + * @since 1.2 + */ + public static final int VK_MINUS = 0x2D; + + /** + * Constant for the period key, "." + */ + public static final int VK_PERIOD = 0x2E; + + /** + * Constant for the forward slash key, "/" + */ + 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; + + /** + * Constant for the semicolon key, ";" + */ + public static final int VK_SEMICOLON = 0x3B; + + /** + * Constant for the equals key, "=" + */ + 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; + + /** + * Constant for the open bracket key, "[" + */ + public static final int VK_OPEN_BRACKET = 0x5B; + + /** + * Constant for the back slash key, "\" + */ + public static final int VK_BACK_SLASH = 0x5C; + + /** + * Constant for the close bracket key, "]" + */ + public static final int VK_CLOSE_BRACKET = 0x5D; + + public static final int VK_NUMPAD0 = 0x60; + public static final int VK_NUMPAD1 = 0x61; + public static final int VK_NUMPAD2 = 0x62; + public static final int VK_NUMPAD3 = 0x63; + public static final int VK_NUMPAD4 = 0x64; + public static final int VK_NUMPAD5 = 0x65; + public static final int VK_NUMPAD6 = 0x66; + public static final int VK_NUMPAD7 = 0x67; + public static final int VK_NUMPAD8 = 0x68; + public static final int VK_NUMPAD9 = 0x69; + public static final int VK_MULTIPLY = 0x6A; + public static final int VK_ADD = 0x6B; + + /** + * This constant is obsolete, and is included only for backwards + * compatibility. + * @see #VK_SEPARATOR + */ + public static final int VK_SEPARATER = 0x6C; + + /** + * Constant for the Numpad Separator key. + * @since 1.4 + */ + public static final int VK_SEPARATOR = VK_SEPARATER; + + 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 F1 function key. */ + public static final int VK_F1 = 0x70; + + /** Constant for the F2 function key. */ + public static final int VK_F2 = 0x71; + + /** Constant for the F3 function key. */ + public static final int VK_F3 = 0x72; + + /** Constant for the F4 function key. */ + public static final int VK_F4 = 0x73; + + /** Constant for the F5 function key. */ + public static final int VK_F5 = 0x74; + + /** Constant for the F6 function key. */ + public static final int VK_F6 = 0x75; + + /** Constant for the F7 function key. */ + public static final int VK_F7 = 0x76; + + /** Constant for the F8 function key. */ + public static final int VK_F8 = 0x77; + + /** Constant for the F9 function key. */ + public static final int VK_F9 = 0x78; + + /** Constant for the F10 function key. */ + public static final int VK_F10 = 0x79; + + /** Constant for the F11 function key. */ + public static final int VK_F11 = 0x7A; + + /** Constant for the F12 function key. */ + public static final int VK_F12 = 0x7B; + + /** + * 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; + + public static final int VK_BACK_QUOTE = 0xC0; + public static final int VK_QUOTE = 0xDE; + + /** + * 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 numeric keypad <b>down</b> arrow key. + * @see #VK_DOWN + * @since 1.2 + */ + public static final int VK_KP_DOWN = 0xE1; + + /** + * 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 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; + + /** + * 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 + */ + public static final int VK_NUMBER_SIGN = 0x0208; + + /** + * Constant for the "+" key. + * @since 1.2 + */ + public static final int VK_PLUS = 0x0209; + + /** + * Constant for the ")" key. + * @since 1.2 + */ + public static final int VK_RIGHT_PARENTHESIS = 0x020A; + + /** + * Constant for the "_" key. + * @since 1.2 + */ + public static final int VK_UNDERSCORE = 0x020B; + + /** + * 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 + */ + public static final int VK_WINDOWS = 0x020C; + + /** + * Constant for the Microsoft Windows Context Menu key. + * @since 1.5 + */ + 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; + + /** + * Constant for the Alphanumeric function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard: eisuu */ + public static final int VK_ALPHANUMERIC = 0x00F0; + + /** + * Constant for the Katakana function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard: katakana */ + public static final int VK_KATAKANA = 0x00F1; + + /** + * Constant for the Hiragana function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard: hiragana */ + public static final int VK_HIRAGANA = 0x00F2; + + /** + * Constant for the Full-Width Characters function key. + * @since 1.2 + */ + /* Japanese PC 106 keyboard: zenkaku */ + public static final int VK_FULL_WIDTH = 0x00F3; + + /** + * 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; + + /** + * 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; + + /** + * 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; + + /** + * 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; + + /** + * 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; + + /** + * Constant for the Begin key. + * @since 1.5 + */ + public static final int VK_BEGIN = 0xFF58; + + /** + * 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 new file mode 100644 index 000000000..dae343d80 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/KeyListener.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.newt.event; + +public interface KeyListener extends NEWTEventListener +{ + public void keyPressed(KeyEvent e); + public void keyReleased(KeyEvent e); + public void keyTyped(KeyEvent e) ; +} + diff --git a/src/newt/classes/com/jogamp/newt/event/MouseAdapter.java b/src/newt/classes/com/jogamp/newt/event/MouseAdapter.java new file mode 100644 index 000000000..3607ae634 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/MouseAdapter.java @@ -0,0 +1,50 @@ +/** + * 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.event; + +public abstract class MouseAdapter implements MouseListener +{ + 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) { + } + public void mouseWheelMoved(MouseEvent e) { + } +} + diff --git a/src/newt/classes/com/jogamp/newt/event/MouseEvent.java b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java new file mode 100644 index 000000000..fbe32d41d --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.newt.event; + +public class MouseEvent extends InputEvent +{ + public static final int BUTTON1 = 1; + public static final int BUTTON2 = 2; + public static final int BUTTON3 = 3; + public static final int BUTTON4 = 4; + public static final int BUTTON5 = 5; + public static final int BUTTON6 = 6; + public static final int BUTTON_NUMBER = 6; + + public static final int getClickTimeout() { + return 300; + } + + public MouseEvent(int eventType, Object source, long when, + int modifiers, int x, int y, int clickCount, int button, + int rotation) + { + super(eventType, source, when, modifiers); + this.x=x; + this.y=y; + this.clickCount=clickCount; + this.button=button; + this.wheelRotation = rotation; + } + + public int getButton() { + return button; + } + public int getClickCount() { + return clickCount; + } + public int getX() { + return x; + } + public int getY() { + return y; + } + public int getWheelRotation() { + return wheelRotation; + } + + public String toString() { + return "MouseEvent["+getEventTypeString(getEventType())+ + ", "+x+"/"+y+", button "+button+", count "+clickCount+ + ", wheel rotation "+wheelRotation+ + ", "+super.toString()+"]"; + } + + public static String getEventTypeString(int 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 + ")"; + } + } + + private int x, y, clickCount, button, wheelRotation; + + 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; +} diff --git a/src/newt/classes/com/jogamp/newt/event/MouseListener.java b/src/newt/classes/com/jogamp/newt/event/MouseListener.java new file mode 100644 index 000000000..5ec086b94 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/MouseListener.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.newt.event; + +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); + 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 new file mode 100644 index 000000000..10673be3d --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.newt.event; + +/** + * NEWT events are provided for notification purposes ONLY;<br> + * The NEWT will automatically handle the event semantics internally, regardless of whether a program is receiving these events or not.<br> + * The actual event semantic is processed before the event is send.<br> + * + * 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> + * </ul><br> + */ +public class NEWTEvent extends java.util.EventObject { + private boolean isSystemEvent; + private int eventType; + private 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) { + 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() { + return eventType; + } + + /** Returns the timestamp, in milliseconds, of this event. */ + public final long getWhen() { + 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 + * communication. + * @param attachment User application specific object + */ + public final void setAttachment(Object attachment) { + this.attachment=attachment; + } + + /** + * @return The user application specific attachment, or null + */ + public final Object getAttachment() { + return attachment; + } + + public String toString() { + return "NEWTEvent[sys:"+isSystemEvent()+", source:"+getSource().getClass().getName()+", when:"+getWhen()+" d "+(System.currentTimeMillis()-getWhen())+"ms]"; + } + + public static String toHexString(int hex) { + return "0x" + Integer.toHexString(hex); + } + + public static String toHexString(long hex) { + return "0x" + Long.toHexString(hex); + } + +} diff --git a/src/newt/classes/com/jogamp/newt/event/NEWTEventConsumer.java b/src/newt/classes/com/jogamp/newt/event/NEWTEventConsumer.java new file mode 100644 index 000000000..6aa19e5f8 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/NEWTEventConsumer.java @@ -0,0 +1,40 @@ +/** + * 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.event; + +public interface NEWTEventConsumer { + + /** + * Consume the event + * + * @return true if the event has been consumed, + * otherwise it returns false for later propagation. + */ + public boolean consumeEvent(NEWTEvent event); +} diff --git a/src/newt/classes/com/jogamp/newt/event/NEWTEventFiFo.java b/src/newt/classes/com/jogamp/newt/event/NEWTEventFiFo.java new file mode 100644 index 000000000..fe224bba6 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/NEWTEventFiFo.java @@ -0,0 +1,62 @@ +/** + * 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.event; + +import java.util.LinkedList; + +public class NEWTEventFiFo +{ + private LinkedList/*<NEWTEvent>*/ events = new LinkedList/*<NEWTEvent>*/(); + + /** Add NEWTEvent to tail */ + public synchronized void put(NEWTEvent event) { + events.addLast(event); + notifyAll(); + } + + /** Remove NEWTEvent from head */ + public synchronized NEWTEvent get() { + if (0 == events.size()) { + return null; + } + + return (NEWTEvent) events.removeFirst(); + } + + /** Get NEWTEvents in queue */ + public synchronized int size() { + return events.size(); + } + + /** Clear all NEWTEvents from queue */ + public synchronized void clear() { + events.clear(); + } + +} diff --git a/src/newt/classes/com/jogamp/newt/event/NEWTEventListener.java b/src/newt/classes/com/jogamp/newt/event/NEWTEventListener.java new file mode 100644 index 000000000..677136573 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/NEWTEventListener.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.newt.event; + +public interface NEWTEventListener extends java.util.EventListener +{ +} + diff --git a/src/newt/classes/com/jogamp/newt/event/ScreenModeListener.java b/src/newt/classes/com/jogamp/newt/event/ScreenModeListener.java new file mode 100644 index 000000000..7bca23cfe --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/ScreenModeListener.java @@ -0,0 +1,39 @@ +/** + * 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.event; + +import com.jogamp.newt.ScreenMode; + +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); +} diff --git a/src/newt/classes/com/jogamp/newt/event/TraceKeyAdapter.java b/src/newt/classes/com/jogamp/newt/event/TraceKeyAdapter.java new file mode 100644 index 000000000..98ba5a24d --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/TraceKeyAdapter.java @@ -0,0 +1,56 @@ +/** + * 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.event; + +public class TraceKeyAdapter implements KeyListener { + + KeyListener downstream; + + public TraceKeyAdapter() { + this.downstream = null; + } + + public TraceKeyAdapter(KeyListener downstream) { + this.downstream = downstream; + } + + public void keyPressed(KeyEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.keyPressed(e); } + } + public void keyReleased(KeyEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.keyReleased(e); } + } + public void keyTyped(KeyEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.keyTyped(e); } + } +} + diff --git a/src/newt/classes/com/jogamp/newt/event/TraceMouseAdapter.java b/src/newt/classes/com/jogamp/newt/event/TraceMouseAdapter.java new file mode 100644 index 000000000..14ee633a0 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/TraceMouseAdapter.java @@ -0,0 +1,76 @@ +/** + * 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.event; + +public class TraceMouseAdapter implements MouseListener { + + MouseListener downstream; + + public TraceMouseAdapter() { + this.downstream = null; + } + + public TraceMouseAdapter(MouseListener downstream) { + this.downstream = downstream; + } + + public void mouseClicked(MouseEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.mouseClicked(e); } + } + public void mouseEntered(MouseEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.mouseEntered(e); } + } + public void mouseExited(MouseEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.mouseExited(e); } + } + public void mousePressed(MouseEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.mousePressed(e); } + } + public void mouseReleased(MouseEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.mouseReleased(e); } + } + public void mouseMoved(MouseEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.mouseMoved(e); } + } + public void mouseDragged(MouseEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.mouseDragged(e); } + } + public void mouseWheelMoved(MouseEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.mouseWheelMoved(e); } + } +} + diff --git a/src/newt/classes/com/jogamp/newt/event/TraceWindowAdapter.java b/src/newt/classes/com/jogamp/newt/event/TraceWindowAdapter.java new file mode 100644 index 000000000..8542820c4 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/TraceWindowAdapter.java @@ -0,0 +1,71 @@ +/** + * 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.event; + +public class TraceWindowAdapter implements WindowListener { + + WindowListener downstream; + + public TraceWindowAdapter() { + this.downstream = null; + } + + public TraceWindowAdapter(WindowListener downstream) { + this.downstream = downstream; + } + + public void windowResized(WindowEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.windowResized(e); } + } + public void windowMoved(WindowEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.windowMoved(e); } + } + public void windowDestroyNotify(WindowEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.windowDestroyNotify(e); } + } + public void windowDestroyed(WindowEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.windowDestroyed(e); } + } + public void windowGainedFocus(WindowEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.windowGainedFocus(e); } + } + public void windowLostFocus(WindowEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.windowLostFocus(e); } + } + public void windowRepaint(WindowUpdateEvent e) { + System.err.println(e); + if(null!=downstream) { downstream.windowRepaint(e); } + } +} diff --git a/src/newt/classes/com/jogamp/newt/event/WindowAdapter.java b/src/newt/classes/com/jogamp/newt/event/WindowAdapter.java new file mode 100644 index 000000000..b9e487e9b --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/WindowAdapter.java @@ -0,0 +1,47 @@ +/** + * 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.event; + +public abstract class WindowAdapter implements WindowListener +{ + public void windowResized(WindowEvent e) { + } + public void windowMoved(WindowEvent e) { + } + public void windowDestroyNotify(WindowEvent e) { + } + public void windowDestroyed(WindowEvent e) { + } + public void windowGainedFocus(WindowEvent e) { + } + public void windowLostFocus(WindowEvent e) { + } + public void windowRepaint(WindowUpdateEvent e) { + } +} diff --git a/src/newt/classes/com/jogamp/newt/event/WindowEvent.java b/src/newt/classes/com/jogamp/newt/event/WindowEvent.java new file mode 100644 index 000000000..f3d62d8c6 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/WindowEvent.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.newt.event; + +/** + * NEWT Window events are provided for notification purposes ONLY.<br> + * NEWT will automatically handle component moves and resizes internally, regardless of whether a program is receiving these events or not. <br> + * The actual event semantic, here move and resize, is processed before the event is send.<br> + */ +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 WindowEvent(int eventType, Object source, long when) { + super(eventType, source, when); + } + + public static String getEventTypeString(int type) { + switch(type) { + case EVENT_WINDOW_RESIZED: return "WINDOW_RESIZED"; + case EVENT_WINDOW_MOVED: return "WINDOW_MOVED"; + case EVENT_WINDOW_DESTROY_NOTIFY: return "EVENT_WINDOW_DESTROY_NOTIFY"; + case EVENT_WINDOW_GAINED_FOCUS: return "EVENT_WINDOW_GAINED_FOCUS"; + case EVENT_WINDOW_LOST_FOCUS: return "EVENT_WINDOW_LOST_FOCUS"; + case EVENT_WINDOW_REPAINT: return "EVENT_WINDOW_REPAINT"; + case EVENT_WINDOW_DESTROYED: return "EVENT_WINDOW_DESTROYED"; + default: return "unknown (" + type + ")"; + } + } + public String toString() { + return "WindowEvent["+getEventTypeString(getEventType()) + + ", " + super.toString() + "]"; + } +} diff --git a/src/newt/classes/com/jogamp/newt/event/WindowListener.java b/src/newt/classes/com/jogamp/newt/event/WindowListener.java new file mode 100644 index 000000000..e841a06cf --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/WindowListener.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.newt.event; + +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); + + /** Window has been moved. */ + public void windowMoved(WindowEvent e); + + /** Window will be destroyed. Release of resources is recommended. */ + public void windowDestroyNotify(WindowEvent e); + + /** Window has been destroyed.*/ + public void windowDestroyed(WindowEvent e); + + /** Window gained focus. */ + public void windowGainedFocus(WindowEvent e); + + /** Window lost focus. */ + public void windowLostFocus(WindowEvent e); + + /** Window area shall be repainted. */ + public void windowRepaint(WindowUpdateEvent e); +} diff --git a/src/newt/classes/com/jogamp/newt/event/WindowUpdateEvent.java b/src/newt/classes/com/jogamp/newt/event/WindowUpdateEvent.java new file mode 100644 index 000000000..7cd6ee370 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/WindowUpdateEvent.java @@ -0,0 +1,49 @@ +/** + * 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.event; + +import javax.media.nativewindow.util.Rectangle; + +public class WindowUpdateEvent extends WindowEvent { + Rectangle bounds; + + public WindowUpdateEvent(int eventType, Object source, long when, Rectangle bounds) + { + super(eventType, source, when); + this.bounds = bounds; + } + + public Rectangle getBounds() { + return bounds; + } + + public String toString() { + return "WindowUpdateEvent["+super.toString()+", "+bounds+"]"; + } +} diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTAdapter.java new file mode 100644 index 000000000..e24002659 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTAdapter.java @@ -0,0 +1,180 @@ +/** + * 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.event.awt; + +import jogamp.newt.Debug; + +/** + * Convenient adapter forwarding AWT events to NEWT via the event listener model.<br> + * <p> + * You may attach an instance of this adapter to an AWT Component. When an event happens, + * it is converted to a NEWT event and the given NEWT listener is being called.<br></p> + * <p> + * This adapter fullfills three use cases. First as a plain utility to write code AWT agnostic, + * ie write an {@link javax.media.opengl.GLEvenListener} and some appropriate NEWT {@link com.jogamp.newt.event.NEWTEventListener}.<br></p> + * <p> + * Attach the {@link javax.media.opengl.GLEvenListener} to a NEWT {@link javax.media.opengl.GLAutoDrawable}, e.g. {@link com.jogamp.newt.opengl.GLWindow},<br> + * or to an AWT {@link javax.media.opengl.GLAutoDrawable}, e.g. {@link javax.media.opengl.awt.GLCanvas}.<br> + * <br> + * Attach the NEWT {@link com.jogamp.newt.event.NEWTEventListener} to a NEWT component, e.g. {@link com.jogamp.newt.Window},<br> + * or to an AWT component, e.g. {@link java.awt.Component}.<br></p> + * <p> + * Common:<br> + * <pre> + // your demo/render code + 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() { ... } ; + * </pre> </p> + * <p> + * Default NEWT use case, without using the AWTAdapter:<br> + * <pre> + // the NEWT GLAutoDrawable and Window + GLWindow glWindow = GLWindow.create(); + + // attach the renderer demo1 + glWindow.addGLEventListener(demo1); + + // attach the NEWT mouse event listener to glWindow + glWindow.addMouseListener(mouseListener); + * </pre> </p> + * <p> + * AWT use case, AWTAdapter used as an AWT event translator and forwarder to your NEWT listener:<br> + * <pre> + // the AWT GLAutoDrawable and Canvas + GLCanvas glCanvas = new GLCanvas(); + + // attach the renderer demo1 + glCanvas.addGLEventListener(demo1); + + // attach the AWTMouseAdapter to glCanvas, which translates and forwards events to the NEWT mouseListener + new AWTMouseAdapter(mouseListener).addTo(glCanvas); + * </pre> </p> + * <p> + * Previous code in detail:<br> + * <pre> + AWTMouseAdapter mouseAdapter = new AWTMouseAdapter(mouseListener); + glCanvas.addMouseListener(mouseAdapter); + glCanvas.addMouseMotionListener(mouseAdapter); + * </pre> </p> + * + * <p> + * Second use case is just a litte variation of the previous use case, where we pass a NEWT Window <br> + * to be used as the source of the event.<br></p> + * <p> + * <pre> + com.jogamp.newt.event.MouseListener mouseListener = new com.jogamp.newt.event.MouseAdapter() { ... } ;<br> + Component comp = ... ; // the AWT component<br> + GLWindow glWindow = GLWindow.create(); // the NEWT component<br> + <br> + new AWTMouseAdapter(mouseListener, glWindow).addTo(comp);<br> + * </pre> </p> + * + * Last but not least, the AWTAdapter maybe used as a general AWT event forwarder to NEWT.<br> + * + * <p> + * <pre> + com.jogamp.newt.event.MouseListener mouseListener = new com.jogamp.newt.event.MouseAdapter() { ... } ;<br> + Component comp = ... ; // the AWT component<br> + GLWindow glWindow = GLWindow.create(); // the NEWT component<br> + glWindow.addMouseListener(mouseListener); // add the custom EventListener to the NEWT component<br> + <br> + new AWTMouseAdapter(glWindow).addTo(comp); // forward all AWT events to glWindow, as NEWT events<br> + * </pre> </p> + * + * @see #attachTo + */ +public abstract class AWTAdapter implements java.util.EventListener +{ + public static final boolean DEBUG_IMPLEMENTATION = Debug.debug("Window"); + + com.jogamp.newt.event.NEWTEventListener newtListener; + com.jogamp.newt.Window newtWindow; + + /** + * Simply wrap aroung a NEWT EventListener, exposed as an AWT EventListener.<br> + * The NEWT EventListener will be called when an event happens.<br> + */ + public AWTAdapter(com.jogamp.newt.event.NEWTEventListener newtListener) { + if(null==newtListener) { + throw new RuntimeException("Argument newtListener is null"); + } + this.newtListener = newtListener; + this.newtWindow = null; + } + + /** + * Wrap aroung a NEWT EventListener, exposed as an AWT EventListener,<br> + * where the given NEWT Window impersonates as the event's source. + * The NEWT EventListener will be called when an event happens.<br> + */ + public AWTAdapter(com.jogamp.newt.event.NEWTEventListener newtListener, com.jogamp.newt.Window newtProxy) { + if(null==newtListener) { + throw new RuntimeException("Argument newtListener is null"); + } + if(null==newtProxy) { + throw new RuntimeException("Argument newtProxy is null"); + } + this.newtListener = newtListener; + this.newtWindow = newtProxy; + } + + /** + * Create a pipeline adapter, AWT EventListener.<br> + * Once attached to an AWT component, it sends the converted AWT events to the NEWT downstream window.<br> + * This is only supported with EDT enabled! + */ + public AWTAdapter(com.jogamp.newt.Window downstream) { + if(null==downstream) { + throw new RuntimeException("Argument downstream is null"); + } + this.newtListener = null; + this.newtWindow = downstream; + if( null == newtWindow.getScreen().getDisplay().getEDTUtil() ) { + throw new RuntimeException("EDT not enabled"); + } + } + + /** + * Due to the fact that some NEWT {@link com.jogamp.newt.event.NEWTEventListener} + * are mapped to more than one {@link java.util.EventListener}, + * this method is for your convenience to use this Adapter as a listener for all types.<br> + * E.g. {@link com.jogamp.newt.event.MouseListener} is mapped to {@link java.awt.event.MouseListener} and {@link java.awt.event.MouseMotionListener}. + */ + public abstract AWTAdapter addTo(java.awt.Component awtComponent); + + /** @see #addTo(java.awt.Component) */ + public abstract AWTAdapter removeFrom(java.awt.Component awtComponent); + + void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { + newtWindow.enqueueEvent(wait, event); + } +} + diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java new file mode 100644 index 000000000..8fe6ff63a --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTKeyAdapter.java @@ -0,0 +1,82 @@ +/** + * 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.event.awt; + +public class AWTKeyAdapter extends AWTAdapter implements java.awt.event.KeyListener +{ + public AWTKeyAdapter(com.jogamp.newt.event.KeyListener newtListener) { + super(newtListener); + } + + public AWTKeyAdapter(com.jogamp.newt.event.KeyListener newtListener, com.jogamp.newt.Window newtProxy) { + super(newtListener, newtProxy); + } + + public AWTKeyAdapter(com.jogamp.newt.Window downstream) { + super(downstream); + } + + public AWTAdapter addTo(java.awt.Component awtComponent) { + awtComponent.addKeyListener(this); + return this; + } + + public AWTAdapter removeFrom(java.awt.Component awtComponent) { + awtComponent.removeKeyListener(this); + return this; + } + + public void keyPressed(java.awt.event.KeyEvent e) { + com.jogamp.newt.event.KeyEvent event = AWTNewtEventFactory.createKeyEvent(e, newtWindow); + if(null!=newtListener) { + ((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) { + ((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); + } + } +} + diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTMouseAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTMouseAdapter.java new file mode 100644 index 000000000..bd421073a --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTMouseAdapter.java @@ -0,0 +1,120 @@ +/** + * 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.event.awt; + +public class AWTMouseAdapter extends AWTAdapter implements java.awt.event.MouseListener, java.awt.event.MouseMotionListener +{ + public AWTMouseAdapter(com.jogamp.newt.event.MouseListener newtListener) { + super(newtListener); + } + + public AWTMouseAdapter(com.jogamp.newt.event.MouseListener newtListener, com.jogamp.newt.Window newtProxy) { + super(newtListener, newtProxy); + } + + public AWTMouseAdapter(com.jogamp.newt.Window downstream) { + super(downstream); + } + + public AWTAdapter addTo(java.awt.Component awtComponent) { + awtComponent.addMouseListener(this); + awtComponent.addMouseMotionListener(this); + return this; + } + + public AWTAdapter removeFrom(java.awt.Component awtComponent) { + awtComponent.removeMouseListener(this); + awtComponent.removeMouseMotionListener(this); + return this; + } + + public void mouseClicked(java.awt.event.MouseEvent e) { + com.jogamp.newt.event.MouseEvent event = AWTNewtEventFactory.createMouseEvent(e, newtWindow); + if(null!=newtListener) { + ((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) { + ((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) { + ((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) { + ((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) { + ((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) { + ((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) { + ((com.jogamp.newt.event.MouseListener)newtListener).mouseMoved(event); + } else { + enqueueEvent(false, event); + } + } +} + diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTNewtEventFactory.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTNewtEventFactory.java new file mode 100644 index 000000000..20c0d15d1 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTNewtEventFactory.java @@ -0,0 +1,146 @@ +/** + * 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.event.awt; + +import com.jogamp.common.util.IntIntHashMap; + +class AWTNewtEventFactory { + + protected static final IntIntHashMap eventTypeAWT2NEWT; + + static { + IntIntHashMap map = new IntIntHashMap(); + map.setKeyNotFoundValue(-1); + // 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; + } + + public static final int awtModifiers2Newt(int awtMods, boolean mouseHint) { + int newtMods = 0; + 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; + 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; + } + return 0; + } + + 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(-1 < type) { + return new com.jogamp.newt.event.WindowEvent(type, ((null==newtSource)?(Object)event.getComponent():(Object)newtSource), System.currentTimeMillis()); + } + return null; // no mapping .. + } + + 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(-1 < type) { + return new com.jogamp.newt.event.WindowEvent(type, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, System.currentTimeMillis()); + } + return null; // no mapping .. + } + + 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(-1 < type) { + return new com.jogamp.newt.event.WindowEvent(type, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, System.currentTimeMillis()); + } + return null; // no mapping .. + } + + 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(-1 < type) { + int rotation = 0; + if (event instanceof java.awt.event.MouseWheelEvent) { + rotation = ((java.awt.event.MouseWheelEvent)event).getWheelRotation(); + } + + return new com.jogamp.newt.event.MouseEvent( + type, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, event.getWhen(), + awtModifiers2Newt(event.getModifiers(), true), + event.getX(), event.getY(), event.getClickCount(), + awtButton2Newt(event.getButton()), rotation); + } + return null; // no mapping .. + } + + 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(-1 < type) { + return new com.jogamp.newt.event.KeyEvent( + type, (null==newtSource)?(Object)event.getComponent():(Object)newtSource, event.getWhen(), + awtModifiers2Newt(event.getModifiers(), false), + event.getKeyCode(), event.getKeyChar()); + } + return null; // no mapping .. + } + +} + diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTParentWindowAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTParentWindowAdapter.java new file mode 100644 index 000000000..68f2b3e0f --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTParentWindowAdapter.java @@ -0,0 +1,130 @@ +/** + * 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.event.awt; + +/** + * 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 AWTParentWindowAdapter(com.jogamp.newt.Window downstream) { + super(downstream); + } + + public AWTAdapter addTo(java.awt.Component awtComponent) { + awtComponent.addHierarchyListener(this); + return super.addTo(awtComponent); + } + + public AWTAdapter removeFrom(java.awt.Component awtComponent) { + awtComponent.removeHierarchyListener(this); + return super.removeFrom(awtComponent); + } + + public void focusGained(java.awt.event.FocusEvent e) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("AWT: focusGained: "+ e); + } + } + + public void focusLost(java.awt.event.FocusEvent e) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("AWT: focusLost: "+ e); + } + } + + public void componentResized(java.awt.event.ComponentEvent e) { + // 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); + } + if(newtWindow.isValid()) { + 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()); + } + } + } else if(newtWindow.isVisible()) { + newtWindow.setVisible(false); + } + }}); + } + } + + public void componentMoved(java.awt.event.ComponentEvent e) { + // no propagation to NEWT child window + } + + public void windowActivated(java.awt.event.WindowEvent e) { + // no propagation to NEWT child window + } + + public void windowDeactivated(java.awt.event.WindowEvent e) { + // no propagation to NEWT child window + } + + public void hierarchyChanged(java.awt.event.HierarchyEvent e) { + if( null == newtListener ) { + long bits = e.getChangeFlags(); + final java.awt.Component changed = e.getChanged(); + if( 0 != ( java.awt.event.HierarchyEvent.SHOWING_CHANGED & bits ) ) { + final boolean showing = changed.isShowing(); + if(DEBUG_IMPLEMENTATION) { + System.err.println("AWT: hierarchyChanged SHOWING_CHANGED: showing "+showing+", "+changed); + } + if(newtWindow.isValid()) { + newtWindow.runOnEDTIfAvail(false, new Runnable() { + public void run() { + if(newtWindow.isVisible() != showing) { + newtWindow.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); + } + } + } + } +} + diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTWindowAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTWindowAdapter.java new file mode 100644 index 000000000..85fe5407b --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTWindowAdapter.java @@ -0,0 +1,204 @@ +/** + * 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.event.awt; + +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(com.jogamp.newt.event.WindowListener newtListener, com.jogamp.newt.Window newtProxy) { + super(newtListener, newtProxy); + } + + public AWTWindowAdapter(com.jogamp.newt.Window downstream) { + super(downstream); + } + + public AWTAdapter addTo(java.awt.Component awtComponent) { + java.awt.Window win = getWindow(awtComponent); + awtComponent.addComponentListener(this); + awtComponent.addFocusListener(this); + if( 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; + } + + public AWTAdapter removeFrom(java.awt.Component awtComponent) { + awtComponent.removeFocusListener(this); + awtComponent.removeComponentListener(this); + java.awt.Window win = getWindow(awtComponent); + if( null != win && null != windowClosingListener ) { + win.removeWindowListener(windowClosingListener); + } + if(awtComponent instanceof java.awt.Window) { + ((java.awt.Window)awtComponent).removeWindowListener(this); + } + return this; + } + + static java.awt.Window getWindow(java.awt.Component comp) { + while( null != comp && !(comp instanceof java.awt.Window) ) { + comp = comp.getParent(); + } + if(comp instanceof java.awt.Window) { + return (java.awt.Window) comp; + } + return null; + } + + public void focusGained(java.awt.event.FocusEvent e) { + com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); + if(null!=newtListener) { + ((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); + if(null!=newtListener) { + ((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); + if(null!=newtListener) { + ((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); + if(null!=newtListener) { + ((com.jogamp.newt.event.WindowListener)newtListener).windowMoved(event); + } else { + enqueueEvent(false, event); + } + } + + public void componentShown(java.awt.event.ComponentEvent e) { + final java.awt.Component comp = e.getComponent(); + if(DEBUG_IMPLEMENTATION) { + System.err.println("AWT: componentShown: "+comp); + } + /** + if(null==newtListener) { + if(newtWindow.isValid()) { + newtWindow.runOnEDTIfAvail(false, new Runnable() { + public void run() { + newtWindow.setVisible(true); + } + }); + } + }*/ + } + + public void componentHidden(java.awt.event.ComponentEvent e) { + final java.awt.Component comp = e.getComponent(); + if(DEBUG_IMPLEMENTATION) { + System.err.println("AWT: componentHidden: "+comp); + } + /** + if(null==newtListener) { + if(newtWindow.isValid()) { + newtWindow.runOnEDTIfAvail(false, new Runnable() { + public void run() { + newtWindow.setVisible(false); + } + }); + } + }*/ + } + + public void windowActivated(java.awt.event.WindowEvent e) { + com.jogamp.newt.event.WindowEvent event = AWTNewtEventFactory.createWindowEvent(e, newtWindow); + if(null!=newtListener) { + ((com.jogamp.newt.event.WindowListener)newtListener).windowGainedFocus(event); + } else { + enqueueEvent(false, event); + } + } + + public void windowClosed(java.awt.event.WindowEvent e) { } + + public void windowClosing(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) { + ((com.jogamp.newt.event.WindowListener)newtListener).windowLostFocus(event); + } else { + enqueueEvent(false, event); + } + } + + public void windowDeiconified(java.awt.event.WindowEvent e) { } + + public void windowIconified(java.awt.event.WindowEvent e) { } + + public void windowOpened(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); + } + } + + 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) { } + } +} + diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java new file mode 100644 index 000000000..efbd9594b --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -0,0 +1,938 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.newt.opengl; + +import java.util.List; + +import com.jogamp.common.GlueGenVersion; +import com.jogamp.common.util.VersionUtil; +import com.jogamp.nativewindow.NativeWindowVersion; +import com.jogamp.newt.*; +import com.jogamp.newt.event.*; +import jogamp.newt.WindowImpl; + +import javax.media.nativewindow.*; +import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.Insets; +import javax.media.opengl.*; + +import jogamp.opengl.GLDrawableHelper; +import com.jogamp.opengl.JoglVersion; + +/** + * An implementation of {@link javax.media.opengl.GLAutoDrawable} interface, + * using an aggregation of a {@link com.jogamp.newt.Window} implementation. + * <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> + * This design decision is made in favor of a more performant and simplified + * implementation. Also the event dispatcher shall be implemented OpenGL agnostic.<br> + * To be able to use OpenGL commands from within such input {@link com.jogamp.newt.event.NEWTEventListener},<br> + * you can inject {@link javax.media.opengl.GLRunnable} objects + * via {@link #invoke(boolean, javax.media.opengl.GLRunnable)} to the OpenGL command stream.<br> + * <p> + */ +public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer { + private WindowImpl window; + + /** + * Constructor. Do not call this directly -- use {@link #create()} instead. + */ + protected GLWindow(Window window) { + resetCounter(); + this.window = (WindowImpl) window; + ((WindowImpl)this.window).setHandleDestroyNotify(false); + window.addWindowListener(new WindowAdapter() { + @Override + public void windowRepaint(WindowUpdateEvent e) { + if( !GLWindow.this.window.isWindowLockedByOtherThread() && !GLWindow.this.helper.isExternalAnimatorAnimating() ) { + display(); + } + } + + @Override + public void windowResized(WindowEvent e) { + sendReshape = true; + if( !GLWindow.this.window.isWindowLockedByOtherThread() && !GLWindow.this.helper.isExternalAnimatorAnimating() ) { + display(); + } + } + + @Override + public void windowDestroyNotify(WindowEvent e) { + if( DISPOSE_ON_CLOSE == GLWindow.this.getDefaultCloseOperation() ) { + // Is an animator thread perform rendering? + if (GLWindow.this.helper.isExternalAnimatorRunning()) { + // Pause animations before initiating safe destroy. + GLAnimatorControl ctrl = GLWindow.this.helper.getAnimator(); + boolean isPaused = ctrl.pause(); + destroy(); + if(isPaused) { + ctrl.resume(); + } + } else if (GLWindow.this.window.isWindowLockedByOtherThread()) { + // Window is locked by another thread + // Flag that destroy should be performed on the next + // attempt to display. + sendDestroy = true; + } else { + // Without an external thread animating or locking the + // surface, we are safe. + destroy (); + } + } + } + }); + this.window.setLifecycleHook(new GLLifecycleHook()); + } + + /** + * Creates a new GLWindow attaching a new Window referencing a new Screen + * with the given GLCapabilities. + * <P> + * The resulting GLWindow owns the Window, Screen and Device, ie it will be destructed. + */ + public static GLWindow create(GLCapabilitiesImmutable caps) { + return new GLWindow(NewtFactory.createWindow(caps)); + } + + /** + * Creates a new GLWindow attaching a new Window referencing the given Screen + * with the given GLCapabilities. + * <P> + * The resulting GLWindow owns the Window, ie it will be destructed. + */ + public static GLWindow create(Screen screen, GLCapabilitiesImmutable caps) { + return new GLWindow(NewtFactory.createWindow(screen, caps)); + } + + /** + * Creates a new GLWindow attaching the given window. + * <P> + * The resulting GLWindow does not own the given Window, ie it will not be destructed. + */ + public static GLWindow create(Window window) { + return new GLWindow(window); + } + + /** + * Creates a new GLWindow attaching a new child Window + * of the given <code>parentNativeWindow</code> with the given GLCapabilities. + * <P> + * The Display/Screen will be compatible with the <code>parentNativeWindow</code>, + * or even identical in case it's a Newt Window. + * <P> + * The resulting GLWindow owns the Window, ie it will be destructed. + */ + public static GLWindow create(NativeWindow parentNativeWindow, GLCapabilitiesImmutable caps) { + return new GLWindow(NewtFactory.createWindow(parentNativeWindow, caps)); + } + + //---------------------------------------------------------------------- + // WindowClosingProtocol implementation + // + public int getDefaultCloseOperation() { + return window.getDefaultCloseOperation(); + } + + public int setDefaultCloseOperation(int op) { + return window.setDefaultCloseOperation(op); + } + + //---------------------------------------------------------------------- + // Window Access + // + + public CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) { + return window.setCapabilitiesChooser(chooser); + } + + public final CapabilitiesImmutable getChosenCapabilities() { + if (drawable == null) { + return window.getChosenCapabilities(); + } + + return drawable.getChosenGLCapabilities(); + } + + public final CapabilitiesImmutable getRequestedCapabilities() { + return window.getRequestedCapabilities(); + } + + public final Window getWindow() { + return window; + } + + public final NativeWindow getParent() { + return window.getParent(); + } + + public final Screen getScreen() { + return window.getScreen(); + } + + public final void setTitle(String title) { + window.setTitle(title); + } + + public final String getTitle() { + return window.getTitle(); + } + + public final void setUndecorated(boolean value) { + window.setUndecorated(value); + } + + public final boolean isUndecorated() { + return window.isUndecorated(); + } + + public final void setFocusAction(FocusRunnable focusAction) { + window.setFocusAction(focusAction); + } + + public final void requestFocus() { + window.requestFocus(); + } + + public boolean hasFocus() { + return window.hasFocus(); + } + + public final Insets getInsets() { + return window.getInsets(); + } + + public final void setPosition(int x, int y) { + window.setPosition(x, y); + } + + public final boolean setFullscreen(boolean fullscreen) { + return window.setFullscreen(fullscreen); + } + + public final boolean isFullscreen() { + return window.isFullscreen(); + } + + public final boolean isVisible() { + return window.isVisible(); + } + + @Override + public final String toString() { + return "NEWT-GLWindow[ \n\tHelper: " + helper + ", \n\tDrawable: " + drawable + + ", \n\tContext: " + context + /** ", \n\tWindow: "+window+", \n\tFactory: "+factory+ */ "]"; + } + + public final int reparentWindow(NativeWindow newParent) { + return window.reparentWindow(newParent); + } + + public final int reparentWindow(NativeWindow newParent, boolean forceDestroyCreate) { + return window.reparentWindow(newParent, forceDestroyCreate); + } + + public final void removeChild(NativeWindow win) { + window.removeChild(win); + } + + public final void addChild(NativeWindow win) { + window.addChild(win); + } + + //---------------------------------------------------------------------- + // Window.LifecycleHook Implementation + // + + public final void destroy() { + window.destroy(); + } + + public final void setVisible(boolean visible) { + window.setVisible(visible); + } + + public final void setSize(int width, int height) { + window.setSize(width, height); + } + + public final boolean isValid() { + return window.isValid(); + } + + public final boolean isNativeValid() { + return window.isNativeValid(); + } + + public Point getLocationOnScreen(Point storage) { + return window.getLocationOnScreen(storage); + } + + // Hide methods here .. + protected class GLLifecycleHook implements WindowImpl.LifecycleHook { + + private class DisposeAction implements Runnable { + public final void run() { + // Lock: Covered by DestroyAction .. + helper.dispose(GLWindow.this); + } + } + DisposeAction disposeAction = new DisposeAction(); + + public synchronized void destroyActionPreLock() { + // nop + } + + public synchronized void destroyActionInLock() { + if(Window.DEBUG_WINDOW_EVENT || Window.DEBUG_IMPLEMENTATION) { + String msg = "GLWindow.destroy() "+Thread.currentThread()+", start"; + System.err.println(msg); + //Exception e1 = new Exception(msg); + //e1.printStackTrace(); + } + + if( window.isNativeValid() && null != drawable && drawable.isRealized() ) { + if( null != context && context.isCreated() ) { + // Catch dispose GLExceptions by GLEventListener, just 'print' them + // so we can continue with the destruction. + try { + helper.invokeGL(drawable, context, disposeAction, null); + } catch (GLException gle) { + gle.printStackTrace(); + } + context.destroy(); + } + drawable.setRealized(false); + } + context = null; + drawable = null; + + if(Window.DEBUG_WINDOW_EVENT || Window.DEBUG_IMPLEMENTATION) { + System.err.println("GLWindow.destroy() "+Thread.currentThread()+", fin"); + } + } + + public synchronized void invalidate(boolean unrecoverable) { + if(Window.DEBUG_WINDOW_EVENT || Window.DEBUG_IMPLEMENTATION) { + String msg = "GLWindow.invalidate("+unrecoverable+") "+Thread.currentThread()+", start"; + System.err.println(msg); + //Exception e1 = new Exception(msg); + //e1.printStackTrace(); + } + if(unrecoverable) { + GLAnimatorControl ctrl = GLWindow.this.getAnimator(); + if ( null!=ctrl ) { + ctrl.remove(GLWindow.this); + } + helper=null; + } + } + + public synchronized void resetCounter() { + if(Window.DEBUG_WINDOW_EVENT || Window.DEBUG_IMPLEMENTATION) { + System.err.println("GLWindow.resetCounter() "+Thread.currentThread()); + } + GLWindow.this.resetCounter(); + } + + public synchronized void setVisibleActionPost(boolean visible, boolean nativeWindowCreated) { + if(Window.DEBUG_WINDOW_EVENT || Window.DEBUG_IMPLEMENTATION) { + String msg = "GLWindow.setVisibleActionPost("+visible+", "+nativeWindowCreated+") "+Thread.currentThread()+", start"; + System.err.println(msg); + // Exception e1 = new Exception(msg); + // e1.printStackTrace(); + } + + /* 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.getGraphicsConfiguration()); + } else { + nw = window; + } + GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) nw.getGraphicsConfiguration().getNativeGraphicsConfiguration().getChosenCapabilities(); + if(null==factory) { + factory = GLDrawableFactory.getFactory(glCaps.getGLProfile()); + } + if(null==drawable) { + drawable = factory.createGLDrawable(nw); + } + drawable.setRealized(true); + context = drawable.createContext(sharedContext); + } + if(Window.DEBUG_WINDOW_EVENT || Window.DEBUG_IMPLEMENTATION) { + String msg = "GLWindow.setVisibleActionPost("+visible+", "+nativeWindowCreated+") "+Thread.currentThread()+", fin"; + System.err.println(msg); + //Exception e1 = new Exception(msg); + //e1.printStackTrace(); + } + } + + public synchronized boolean pauseRenderingAction() { + boolean animatorPaused = false; + GLAnimatorControl ctrl = GLWindow.this.getAnimator(); + if ( null!=ctrl ) { + animatorPaused = ctrl.pause(); + } + return animatorPaused; + } + + public synchronized void resumeRenderingAction() { + GLAnimatorControl ctrl = GLWindow.this.getAnimator(); + if ( null!=ctrl && ctrl.isPaused() ) { + ctrl.resume(); + } + } + } + + //---------------------------------------------------------------------- + // OpenGL-related methods and state + // + + private GLContext sharedContext = null; + private GLDrawableFactory factory; + private GLDrawable drawable; + private GLContext context; + private GLDrawableHelper helper = new GLDrawableHelper(); + // To make reshape events be sent immediately before a display event + private boolean sendReshape=false; + private boolean sendDestroy=false; + private boolean perfLog = false; + private long startTime, curTime, lastCheck; + private int totalFrames, lastFrames; + + public GLDrawableFactory getFactory() { + return factory; + } + + /** + * Specifies an {@link javax.media.opengl.GLContext OpenGL context} to share with.<br> + * At native creation, {@link #setVisible(boolean) setVisible(true)}, + * a {@link javax.media.opengl.GLDrawable drawable} and {@link javax.media.opengl.GLContext context} is created besides the native Window itself,<br> + * hence you shall set the shared context before. + * + * @param sharedContext The OpenGL context shared by this GLWindow's one + */ + public void setSharedContext(GLContext sharedContext) { + this.sharedContext = sharedContext; + } + + public void setContext(GLContext newCtx) { + context = newCtx; + } + + public GLContext getContext() { + return context; + } + + public GL getGL() { + if (context == null) { + return null; + } + return context.getGL(); + } + + public GL setGL(GL gl) { + if (context != null) { + context.setGL(gl); + return gl; + } + return null; + } + + public void addGLEventListener(GLEventListener listener) { + if(null!=helper) { + helper.addGLEventListener(listener); + } + } + + public void addGLEventListener(int index, GLEventListener listener) { + if(null!=helper) { + helper.addGLEventListener(index, listener); + } + } + + public void removeGLEventListener(GLEventListener listener) { + if(null!=helper) { + helper.removeGLEventListener(listener); + } + } + + public void setAnimator(GLAnimatorControl animatorControl) { + if(null!=helper) { + helper.setAnimator(animatorControl); + } + } + + public GLAnimatorControl getAnimator() { + if(null!=helper) { + return helper.getAnimator(); + } + return null; + } + + public boolean getPerfLogEnabled() { return perfLog; } + + public void enablePerfLog(boolean v) { + perfLog = v; + } + + public void invoke(boolean wait, GLRunnable glRunnable) { + if(null!=helper) { + helper.invoke(this, wait, glRunnable); + } + } + + public void display() { + display(false); + } + + public void display(boolean forceReshape) { + if( null == window ) { return; } + + if(sendDestroy || ( null!=window && window.hasDeviceChanged() && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) ) { + sendDestroy=false; + destroy(); + return; + } + + if( null == context && isVisible() && 0<getWidth()*getHeight() ) { + // retry native window and drawable/context creation + setVisible(true); + } + + if(forceReshape) { + sendReshape = true; + } + + if( isVisible() && null != context ) { + if( NativeSurface.LOCK_SURFACE_NOT_READY < lockSurface() ) { + try { + helper.invokeGL(drawable, context, displayAction, initAction); + } finally { + unlockSurface(); + } + } + } + } + + /** This implementation uses a static value */ + public void setAutoSwapBufferMode(boolean onOrOff) { + if(null!=helper) { + helper.setAutoSwapBufferMode(onOrOff); + } + } + + /** This implementation uses a static value */ + public boolean getAutoSwapBufferMode() { + if(null!=helper) { + return helper.getAutoSwapBufferMode(); + } + return false; + } + + public void swapBuffers() { + if(drawable!=null && context != null) { + // Lock: Locked Surface/Window by MakeCurrent/Release + if (context != GLContext.getCurrent()) { + // Assume we should try to make the context current before swapping the buffers + helper.invokeGL(drawable, context, swapBuffersAction, initAction); + } else { + drawable.swapBuffers(); + } + } + } + + private class InitAction implements Runnable { + public final void run() { + // Lock: Locked Surface/Window by MakeCurrent/Release + helper.init(GLWindow.this); + resetCounter(); + } + } + private InitAction initAction = new InitAction(); + + private class DisplayAction implements Runnable { + public final void run() { + // Lock: Locked Surface/Window by display _and_ MakeCurrent/Release + if (sendReshape) { + helper.reshape(GLWindow.this, 0, 0, getWidth(), getHeight()); + sendReshape = false; + } + + helper.display(GLWindow.this); + + curTime = System.currentTimeMillis(); + totalFrames++; + + if(perfLog) { + long dt0, dt1; + lastFrames++; + dt0 = curTime-lastCheck; + if ( dt0 > 5000 ) { + dt1 = curTime-startTime; + System.err.println(dt0/1000 +"s: "+ lastFrames + "f, " + (lastFrames*1000)/dt0 + " fps, "+dt0/lastFrames+" ms/f; "+ + "total: "+ dt1/1000+"s, "+(totalFrames*1000)/dt1 + " fps, "+dt1/totalFrames+" ms/f"); + lastCheck=curTime; + lastFrames=0; + } + } + } + } + private DisplayAction displayAction = new DisplayAction(); + + /** + * @return Time of the first display call in milliseconds. + * This value is reset if becoming visible again or reparenting. + */ + public final long getStartTime() { + return startTime; + } + + /** + * @return Time of the last display call in milliseconds. + * This value is reset if becoming visible again or reparenting. + */ + public final long getCurrentTime() { + return curTime; + } + + /** + * @return Duration <code>getCurrentTime() - getStartTime()</code>. + * + * @see #getStartTime() + * @see #getCurrentTime() + */ + public final long getDuration() { + return getCurrentTime()-getStartTime(); + } + + /** + * @return Number of frames displayed since the first display call, ie <code>getStartTime()</code>. + * This value is reset if becoming visible again or reparenting. + */ + public final int getTotalFrames() { + return totalFrames; + } + + /** Reset all counter (startTime, currentTime, frame number) */ + public final synchronized void resetCounter() { + startTime = System.currentTimeMillis(); // overwrite startTime to real init one + curTime = startTime; + lastCheck = startTime; + totalFrames = 0; lastFrames = 0; + } + + private class SwapBuffersAction implements Runnable { + public final void run() { + drawable.swapBuffers(); + } + } + private SwapBuffersAction swapBuffersAction = new SwapBuffersAction(); + + //---------------------------------------------------------------------- + // GLDrawable methods + // + + public final NativeSurface getNativeSurface() { + return null!=drawable ? drawable.getNativeSurface() : null; + } + + public final long getHandle() { + return null!=drawable ? drawable.getHandle() : 0; + } + + public final int getX() { + return window.getX(); + } + + public final int getY() { + return window.getY(); + } + + public final int getWidth() { + return window.getWidth(); + } + + public final int getHeight() { + return window.getHeight(); + } + + //---------------------------------------------------------------------- + // GLDrawable methods that are not really needed + // + + public final GLContext createContext(GLContext shareWith) { + return drawable.createContext(shareWith); + } + + public final void setRealized(boolean realized) { + } + + public final boolean isRealized() { + return ( null != drawable ) ? drawable.isRealized() : false; + } + + public final GLCapabilitiesImmutable getChosenGLCapabilities() { + if (drawable == null) { + throw new GLException("No drawable yet"); + } + + return drawable.getChosenGLCapabilities(); + } + + public final GLProfile getGLProfile() { + if (drawable == null) { + throw new GLException("No drawable yet"); + } + + return drawable.getGLProfile(); + } + + //---------------------------------------------------------------------- + // NEWTEventConsumer + // + public boolean consumeEvent(NEWTEvent event) { + return window.consumeEvent(event); + } + + //---------------------------------------------------------------------- + // Window completion + // + public final void windowRepaint(int x, int y, int width, int height) { + window.windowRepaint(x, y, width, height); + } + + public final void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { + window.enqueueEvent(wait, event); + } + + public final void runOnEDTIfAvail(boolean wait, final Runnable task) { + window.runOnEDTIfAvail(wait, task); + } + + public final SurfaceUpdatedListener getSurfaceUpdatedListener(int index) { + return window.getSurfaceUpdatedListener(index); + } + + public final SurfaceUpdatedListener[] getSurfaceUpdatedListeners() { + return window.getSurfaceUpdatedListeners(); + } + + public final void removeAllSurfaceUpdatedListener() { + window.removeAllSurfaceUpdatedListener(); + } + + public final void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { + window.removeSurfaceUpdatedListener(l); + } + + public final void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { + window.addSurfaceUpdatedListener(l); + } + + public final void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) throws IndexOutOfBoundsException { + window.addSurfaceUpdatedListener(index, l); + } + + public void sendWindowEvent(int eventType) { + window.sendWindowEvent(eventType); + } + + public final WindowListener getWindowListener(int index) { + return window.getWindowListener(index); + } + + public final WindowListener[] getWindowListeners() { + return window.getWindowListeners(); + } + + public final void removeWindowListener(WindowListener l) { + window.removeWindowListener(l); + } + + public final void addWindowListener(WindowListener l) { + window.addWindowListener(l); + } + + public final void addWindowListener(int index, WindowListener l) throws IndexOutOfBoundsException { + window.addWindowListener(index, l); + } + + public final void addKeyListener(KeyListener l) { + window.addKeyListener(l); + } + + public final void addKeyListener(int index, KeyListener l) { + window.addKeyListener(index, l); + } + + public final void removeKeyListener(KeyListener l) { + window.removeKeyListener(l); + } + + public final KeyListener getKeyListener(int index) { + return window.getKeyListener(index); + } + + public final KeyListener[] getKeyListeners() { + return window.getKeyListeners(); + } + + public final void addMouseListener(MouseListener l) { + window.addMouseListener(l); + } + + public final void addMouseListener(int index, MouseListener l) { + window.addMouseListener(index, l); + } + + public final void removeMouseListener(MouseListener l) { + window.removeMouseListener(l); + } + + public final MouseListener getMouseListener(int index) { + return window.getMouseListener(index); + } + + public final MouseListener[] getMouseListeners() { + return window.getMouseListeners(); + } + + //---------------------------------------------------------------------- + // NativeWindow completion + // + + public final int lockSurface() { + return window.lockSurface(); + } + + public final void unlockSurface() throws NativeWindowException { + window.unlockSurface(); + } + + public final boolean isSurfaceLockedByOtherThread() { + return window.isSurfaceLockedByOtherThread(); + } + + public final boolean isSurfaceLocked() { + return window.isSurfaceLocked(); + } + + public final Thread getSurfaceLockOwner() { + return window.getSurfaceLockOwner(); + + } + + public final boolean surfaceSwap() { + return window.surfaceSwap(); + } + + public final void invalidate() { + window.invalidate(); + } + + public final long getWindowHandle() { + return window.getWindowHandle(); + + } + + public final long getSurfaceHandle() { + return window.getSurfaceHandle(); + + } + + public final AbstractGraphicsConfiguration getGraphicsConfiguration() { + return window.getGraphicsConfiguration(); + } + + public final long getDisplayHandle() { + return window.getDisplayHandle(); + } + + public final int getScreenIndex() { + return window.getScreenIndex(); + } + + public final void surfaceUpdated(Object updater, NativeSurface ns, long when) { + window.surfaceUpdated(updater, ns, when); + } + + /** + * A most simple JOGL AWT test entry + */ + public static void main(String args[]) { + System.err.println(VersionUtil.getPlatformInfo()); + System.err.println(GlueGenVersion.getInstance()); + System.err.println(NativeWindowVersion.getInstance()); + System.err.println(JoglVersion.getInstance()); + System.err.println(NewtVersion.getInstance()); + + GLProfile glp = GLProfile.getDefault(); + GLDrawableFactory factory = GLDrawableFactory.getFactory(glp); + List/*<GLCapabilitiesImmutable>*/ availCaps = factory.getAvailableCapabilities(null); + for(int i=0; i<availCaps.size(); i++) { + System.err.println(availCaps.get(i)); + } + GLCapabilitiesImmutable caps = new GLCapabilities( glp ); + + GLWindow glWindow = GLWindow.create(caps); + glWindow.setSize(128, 128); + + glWindow.addGLEventListener(new GLEventListener() { + public void init(GLAutoDrawable drawable) { + GL gl = drawable.getGL(); + System.err.println(JoglVersion.getGLInfo(gl, null)); + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + } + + public void display(GLAutoDrawable drawable) { + } + + public void dispose(GLAutoDrawable drawable) { + } + }); + + glWindow.setVisible(true); + glWindow.invalidate(); + } + +} diff --git a/src/newt/classes/com/jogamp/newt/util/EDTUtil.java b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java new file mode 100644 index 000000000..d1a11a788 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/util/EDTUtil.java @@ -0,0 +1,115 @@ +/** + * 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; + +/** + * EDT stands for Event Dispatch Thread. + * <p> + * EDTUtil comprises the functionality of: + * <ul> + * <li> Periodically issuing an event dispatch command on the EDT.</li> + * <li> Ability to enqueue tasks, executed on the EDT.</li> + * <li> Controlling the EDT, ie start and stop in a sane manner.</li> + * </ul> + * The EDT pattern is a common tool to comply with todays windowing toolkits, + * where the common denominator in regards to multithreading is to: + * <ul> + * <li> Create a Window on one thread </li> + * <li> Modify the Window within the same thread </li> + * <li> Handle incoming events from within the same thread </li> + * </ul> + * Note: This is not true on MacOSX, where all these actions have to be + * performed by a unique, so called main thread.<br> + */ +public interface EDTUtil { + + public static final long defaultEDTPollGranularity = 10; // 10ms, 1/100s + + /** + * Create a new EDT. One should invoke <code>reset()</code><br> + * after <code>invokeStop(..)</code> in case another <code>start()</code> or <code>invoke(..)</code> + * is expected. + * + * @see #start() + * @see #invoke(boolean, java.lang.Runnable) + * @see #invokeStop(java.lang.Runnable) + */ + public void reset(); + + /** + * Start the EDT + */ + public void start(); + + /** + * @return True if the current thread is the EDT thread + */ + public boolean isCurrentThreadEDT(); + + /** + * @return True if EDT is running + */ + public boolean isRunning(); + + /** + * Append the final task to the EDT task queue, + * signals EDT to stop and wait until stopped.<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> + * </ul> + */ + public void invokeStop(Runnable finalTask); + + /** + * Append task to the EDT task queue.<br> + * Wait until execution is finished if <code>wait == true</code>.<br> + * Shall start the thread if not running.<br> + * Can be issued from within EDT, ie from within an enqueued task.<br> + * + * @throws RuntimeException in case EDT is stopped and not {@link #reset()} + */ + public void 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. + */ + public void waitUntilIdle(); + + /** + * Wait until EDT task is stopped.<br> + * No <code>stop</code> action is performed, {@link #invokeStop(java.lang.Runnable)} should be used before. + */ + public void waitUntilStopped(); +} + diff --git a/src/newt/classes/com/jogamp/newt/util/MainThread.java b/src/newt/classes/com/jogamp/newt/util/MainThread.java new file mode 100644 index 000000000..8bb725b99 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/util/MainThread.java @@ -0,0 +1,406 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +package com.jogamp.newt.util; + +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + +import javax.media.nativewindow.NativeWindowFactory; + +import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.common.util.RunnableTask; +import com.jogamp.newt.Display; +import jogamp.newt.Debug; +import jogamp.newt.NEWTJNILibLoader; +import jogamp.newt.awt.AWTEDTUtil; + +/** + * NEWT Utility class MainThread<P> + * + * This class provides a startup singleton <i>main thread</i>, + * from which a new thread with the users main class is launched.<br> + * + * Such behavior is necessary for native windowing toolkits, + * where the windowing management must happen on the so called + * <i>main thread</i> e.g. for Mac OS X !<br> + * + * Utilizing this class as a launchpad, now you are able to + * use a NEWT multithreaded application with window handling within the different threads, + * even on these restricted platforms.<br> + * + * To support your NEWT Window platform, + * you have to pass your <i>main thread</i> actions to {@link #invoke invoke(..)}, + * have a look at the {@link com.jogamp.newt.macosx.MacWindow MacWindow} implementation.<br> + * <i>TODO</i>: Some hardcoded dependencies exist in this implementation, + * where you have to patch this code or factor it out. <P> + * + * If your platform is not Mac OS X, but you want to test your code without modifying + * this class, you have to set the system property <code>newt.MainThread.force</code> to <code>true</code>.<P> + * + * The code is compatible with all other platform, which support multithreaded windowing handling. + * Since those platforms won't trigger the <i>main thread</i> serialization, the main method + * will be simply executed, in case you haven't set <code>newt.MainThread.force</code> to <code>true</code>.<P> + * + * Test case on Mac OS X (or any other platform): + <PRE> + java -XstartOnFirstThread com.jogamp.newt.util.MainThread demos.es1.RedSquare -GL2 -GL2 -GL2 -GL2 + </PRE> + * Which starts 4 threads, each with a window and OpenGL rendering.<br> + */ +public class MainThread implements EDTUtil { + private static final AccessControlContext localACC = AccessController.getContext(); + public static final boolean MAIN_THREAD_CRITERIA = ( !NativeWindowFactory.isAWTAvailable() && + NativeWindowFactory.TYPE_MACOSX.equals(NativeWindowFactory.getNativeWindowType(false)) + ) || Debug.getBooleanProperty("newt.MainThread.force", true, localACC); + + protected static final boolean DEBUG = Debug.debug("MainThread"); + + private static final MainThread singletonMainThread = new MainThread(); // one singleton MainThread + + private static boolean isExit=false; + private static volatile boolean isRunning=false; + private static final Object taskWorkerLock=new Object(); + private static boolean shouldStop; + private static ArrayList tasks; + private static Thread mainThread; + + private static Timer pumpMessagesTimer=null; + private static TimerTask pumpMessagesTimerTask=null; + private static final Map/*<Display, Runnable>*/ pumpMessageDisplayMap = new HashMap(); + + private static boolean useMainThread = false; + + static class MainAction extends Thread { + private String mainClassName; + private String[] mainClassArgs; + + private Method mainClassMain; + + public MainAction(String mainClassName, String[] mainClassArgs) { + this.mainClassName=mainClassName; + this.mainClassArgs=mainClassArgs; + } + + @Override + public void run() { + if ( useMainThread ) { + // we have to start first to provide the service .. + singletonMainThread.waitUntilRunning(); + } + + // start user app .. + try { + Class mainClass = ReflectionUtil.getClass(mainClassName, true, getClass().getClassLoader()); + if(null==mainClass) { + throw new RuntimeException(new ClassNotFoundException("MainThread couldn't find main class "+mainClassName)); + } + try { + mainClassMain = mainClass.getDeclaredMethod("main", new Class[] { String[].class }); + mainClassMain.setAccessible(true); + } catch (Throwable t) { + throw new RuntimeException(t); + } + if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" invoke "+mainClassName); + mainClassMain.invoke(null, new Object[] { mainClassArgs } ); + } catch (InvocationTargetException ite) { + ite.getTargetException().printStackTrace(); + } catch (Throwable t) { + t.printStackTrace(); + } + + if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" user app fin"); + + if ( useMainThread ) { + singletonMainThread.invokeStop(new Runnable() { + public void run() { + // nop + }}); + if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" MainThread fin - stop"); + System.exit(0); + } + } + } + private static MainAction mainAction; + + /** Your new java application main entry, which pipelines your application */ + public static void main(String[] args) { + useMainThread = MAIN_THREAD_CRITERIA; + + if(DEBUG) System.err.println("MainThread.main(): "+Thread.currentThread().getName()+" useMainThread "+ useMainThread ); + + if(args.length==0) { + return; + } + + String mainClassName=args[0]; + String[] mainClassArgs=new String[args.length-1]; + if(args.length>1) { + System.arraycopy(args, 1, mainClassArgs, 0, args.length-1); + } + + NEWTJNILibLoader.loadNEWT(); + + mainAction = new MainAction(mainClassName, mainClassArgs); + + if(NativeWindowFactory.TYPE_MACOSX.equals(NativeWindowFactory.getNativeWindowType(false))) { + ReflectionUtil.callStaticMethod("jogamp.newt.macosx.MacDisplay", "initSingleton", + null, null, MainThread.class.getClassLoader()); + } + + if ( useMainThread ) { + shouldStop = false; + tasks = new ArrayList(); + mainThread = Thread.currentThread(); + + // dispatch user's main thread .. + mainAction.start(); + + // do our main thread task scheduling + singletonMainThread.run(); + } else { + // run user's main in this thread + mainAction.run(); + } + } + + public static MainThread getSingleton() { + return singletonMainThread; + } + + public static Runnable removePumpMessage(Display dpy) { + synchronized(pumpMessageDisplayMap) { + return (Runnable) pumpMessageDisplayMap.remove(dpy); + } + } + + public static void addPumpMessage(Display dpy, Runnable pumpMessage) { + if ( useMainThread ) { + return; // error ? + } + synchronized (pumpMessageDisplayMap) { + if(null == pumpMessagesTimer) { + pumpMessagesTimer = new Timer(); + pumpMessagesTimerTask = new TimerTask() { + public void run() { + synchronized(pumpMessageDisplayMap) { + for(Iterator i = pumpMessageDisplayMap.values().iterator(); i.hasNext(); ) { + ((Runnable) i.next()).run(); + } + } + } + }; + pumpMessagesTimer.scheduleAtFixedRate(pumpMessagesTimerTask, 0, defaultEDTPollGranularity); + } + pumpMessageDisplayMap.put(dpy, pumpMessage); + } + } + + final public void reset() { + if(NativeWindowFactory.isAWTAvailable()) { + AWTEDTUtil.getSingleton().reset(); + } + // nop + } + + final public void start() { + if(NativeWindowFactory.isAWTAvailable()) { + AWTEDTUtil.getSingleton().start(); + } + // nop + } + + final public boolean isCurrentThreadEDT() { + if(NativeWindowFactory.isAWTAvailable()) { + return AWTEDTUtil.getSingleton().isCurrentThreadEDT(); + } + return isRunning() && mainThread == Thread.currentThread() ; + } + + final public boolean isRunning() { + if( useMainThread ) { + synchronized(taskWorkerLock) { + return isRunning; + } + } + return true; // AWT is always running + } + + private void invokeLater(Runnable task) { + synchronized(taskWorkerLock) { + if(isRunning() && mainThread != Thread.currentThread()) { + tasks.add(task); + taskWorkerLock.notifyAll(); + } else { + // if !running or isEDTThread, do it right away + task.run(); + } + } + } + + final public void invokeStop(Runnable r) { + invokeImpl(true, r, true); + } + + final public void invoke(boolean wait, Runnable r) { + invokeImpl(wait, r, false); + } + + private void invokeImpl(boolean wait, Runnable r, boolean stop) { + if(r == null) { + return; + } + + if(NativeWindowFactory.isAWTAvailable()) { + AWTEDTUtil.getSingleton().invokeImpl(wait, r, stop); + return; + } + + // if this main thread is not being used or + // if this is already the main thread .. just execute. + // FIXME: start if not started .. sync logic with DefaultEDTUtil!!! + if( !isRunning() || mainThread == Thread.currentThread() ) { + r.run(); + return; + } + + boolean doWait = wait && isRunning() && mainThread != Thread.currentThread(); + Object lock = new Object(); + RunnableTask rTask = new RunnableTask(r, doWait?lock:null, true); + Throwable throwable = null; + synchronized(lock) { + invokeLater(rTask); + // FIXME .. + synchronized(taskWorkerLock) { + if(isRunning) { + shouldStop = true; + if(DEBUG) System.err.println("MainThread.stop(): "+Thread.currentThread().getName()+" start"); + } + taskWorkerLock.notifyAll(); + } + if( doWait ) { + try { + lock.wait(); + } catch (InterruptedException ie) { + throwable = ie; + } + } + } + if(null==throwable) { + throwable = rTask.getThrowable(); + } + if(null!=throwable) { + throw new RuntimeException(throwable); + } + } + + final public void waitUntilIdle() { + if(NativeWindowFactory.isAWTAvailable()) { + AWTEDTUtil.getSingleton().waitUntilIdle(); + } + } + + final public void waitUntilStopped() { + if(NativeWindowFactory.isAWTAvailable()) { + AWTEDTUtil.getSingleton().waitUntilStopped(); + } + } + + private void waitUntilRunning() { + synchronized(taskWorkerLock) { + if(isExit) return; + + while(!isRunning) { + try { + taskWorkerLock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + public void run() { + if(DEBUG) System.err.println("MainThread.run(): "+Thread.currentThread().getName()); + synchronized(taskWorkerLock) { + isRunning = true; + taskWorkerLock.notifyAll(); + } + while(!shouldStop) { + try { + // wait for something todo .. + synchronized(taskWorkerLock) { + while(!shouldStop && tasks.size()==0) { + try { + taskWorkerLock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + // take over the tasks .. + if(!shouldStop && tasks.size()>0) { + Runnable task = (Runnable) tasks.remove(0); + task.run(); // FIXME: could be run outside of lock + } + taskWorkerLock.notifyAll(); + } + } catch (Throwable t) { + // handle errors .. + t.printStackTrace(); + } finally { + // epilog - unlock locked stuff + } + } + if(DEBUG) System.err.println("MainThread.run(): "+Thread.currentThread().getName()+" fin"); + synchronized(taskWorkerLock) { + isRunning = false; + isExit = true; + taskWorkerLock.notifyAll(); + } + } +} + + diff --git a/src/newt/classes/com/jogamp/newt/util/MonitorMode.java b/src/newt/classes/com/jogamp/newt/util/MonitorMode.java new file mode 100644 index 000000000..fb2d0ceb5 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/util/MonitorMode.java @@ -0,0 +1,109 @@ +/** + * 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.DimensionReadOnly} size in [mm]</li> + * <li><code>refresh rate</code></li> + * </ul> + */ +public class MonitorMode implements Cloneable { + SurfaceSize surfaceSize; + DimensionReadOnly screenSizeMM; // in [mm] + int refreshRate; + + public MonitorMode(SurfaceSize surfaceSize, DimensionReadOnly screenSizeMM, int refreshRate) { + if(null==surfaceSize || refreshRate<=0) { + throw new IllegalArgumentException("surfaceSize must be set and refreshRate greater 0"); + } + this.surfaceSize=surfaceSize; + this.screenSizeMM=screenSizeMM; + this.refreshRate=refreshRate; + } + + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException ex) { + throw new InternalError(); + } + } + + public final SurfaceSize getSurfaceSize() { + return surfaceSize; + } + + public final DimensionReadOnly 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/ScreenModeUtil.java b/src/newt/classes/com/jogamp/newt/util/ScreenModeUtil.java new file mode 100644 index 000000000..3e0e3dac5 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/util/ScreenModeUtil.java @@ -0,0 +1,340 @@ +/** + * 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.DimensionReadOnly; +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() == ((ScreenMode)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, DimensionReadOnly resolution) { + if(null==screenModes || screenModes.size()==0) { + return null; + } + List out = new ArrayList(); + 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 = (ScreenMode)screenModes.get(i); + DimensionReadOnly 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 = ((ScreenMode)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 out = new ArrayList(); + for (int i=0; null!=screenModes && i<screenModes.size(); i++) { + ScreenMode sm = (ScreenMode)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 out = new ArrayList(); + for (int i=0; null!=screenModes && i<screenModes.size(); i++) { + ScreenMode sm = (ScreenMode)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 out = new ArrayList(); + for (int i=0; null!=screenModes && i<screenModes.size(); i++) { + ScreenMode sm = (ScreenMode)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 out = new ArrayList(); + for (int i=0; null!=screenModes && i<screenModes.size(); i++) { + ScreenMode sm = (ScreenMode)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 = ((ScreenMode)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 = (ScreenMode)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 = (ScreenMode)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 DimensionReadOnly 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(DimensionReadOnly 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, DimensionReadOnly 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 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 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 DimensionReadOnly 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 resolutionPool, + ArrayHashSet surfaceSizePool, + ArrayHashSet screenSizeMMPool, + ArrayHashSet monitorModePool, + ArrayHashSet screenModePool, + int[] modeProperties, int offset) { + ScreenMode screenMode = streamInImpl(resolutionPool, surfaceSizePool, screenSizeMMPool, monitorModePool, screenModePool, + modeProperties, offset); + return screenModePool.indexOf(screenMode); + } + + private static ScreenMode streamInImpl(ArrayHashSet resolutionPool, + ArrayHashSet surfaceSizePool, + ArrayHashSet screenSizeMMPool, + ArrayHashSet monitorModePool, + ArrayHashSet 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++; + DimensionReadOnly resolution = ScreenModeUtil.streamInResolution(modeProperties, offset); + offset += ScreenModeUtil.NUM_RESOLUTION_PROPERTIES; + if(null!=resolutionPool) { + resolution = (DimensionReadOnly) resolutionPool.getOrAdd(resolution); + } + + SurfaceSize surfaceSize = ScreenModeUtil.streamInSurfaceSize(resolution, modeProperties, offset); + offset += ScreenModeUtil.NUM_SURFACE_SIZE_PROPERTIES; + if(null!=surfaceSizePool) { + surfaceSize = (SurfaceSize) surfaceSizePool.getOrAdd(surfaceSize); + } + + DimensionReadOnly screenSizeMM = ScreenModeUtil.streamInResolution(modeProperties, offset); + offset += ScreenModeUtil.NUM_RESOLUTION_PROPERTIES; + if(null!=screenSizeMMPool) { + screenSizeMM = (DimensionReadOnly) 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 = (MonitorMode) monitorModePool.getOrAdd(monitorMode); + } + + ScreenMode screenMode = ScreenModeUtil.streamInScreenMode(monitorMode, modeProperties, offset); + if(null!=screenModePool) { + screenMode = (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/jogamp/newt/Debug.java b/src/newt/classes/jogamp/newt/Debug.java new file mode 100644 index 000000000..85fbbe764 --- /dev/null +++ b/src/newt/classes/jogamp/newt/Debug.java @@ -0,0 +1,140 @@ +/* + * 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. + */ + +package jogamp.newt; + +import java.security.*; + +/** Helper routines for logging and debugging. */ + +public class Debug { + // Some common properties + private static boolean verbose; + private static boolean debugAll; + private static AccessControlContext localACC; + + static { + localACC=AccessController.getContext(); + verbose = isPropertyDefined("newt.verbose", true); + debugAll = isPropertyDefined("newt.debug", true); + if (verbose) { + 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()); + } + } + + static int getIntProperty(final String property, final boolean jnlpAlias) { + return getIntProperty(property, jnlpAlias, localACC); + } + + public static int getIntProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc) { + int i=0; + try { + Integer iv = Integer.valueOf(Debug.getProperty(property, jnlpAlias, acc)); + i = iv.intValue(); + } catch (NumberFormatException nfe) {} + return i; + } + + static boolean getBooleanProperty(final String property, final boolean jnlpAlias) { + return getBooleanProperty(property, jnlpAlias, localACC); + } + + public static boolean getBooleanProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc) { + Boolean b = Boolean.valueOf(Debug.getProperty(property, jnlpAlias, acc)); + return b.booleanValue(); + } + + static boolean isPropertyDefined(final String property, final boolean jnlpAlias) { + return isPropertyDefined(property, jnlpAlias, localACC); + } + + public static boolean isPropertyDefined(final String property, final boolean jnlpAlias, final AccessControlContext acc) { + return (Debug.getProperty(property, jnlpAlias, acc) != null) ? true : false; + } + + static String getProperty(final String property, final boolean jnlpAlias) { + return getProperty(property, jnlpAlias, localACC); + } + + public static String getProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc) { + String s=null; + if(null!=acc && acc.equals(localACC)) { + s = (String) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + String val=null; + try { + val = System.getProperty(property); + } catch (Exception e) {} + if(null==val && jnlpAlias && !property.startsWith(jnlp_prefix)) { + try { + val = System.getProperty(jnlp_prefix + property); + } catch (Exception e) {} + } + return val; + } + }); + } else { + try { + s = System.getProperty(property); + } catch (Exception e) {} + if(null==s && jnlpAlias && !property.startsWith(jnlp_prefix)) { + try { + s = System.getProperty(jnlp_prefix + property); + } catch (Exception e) {} + } + } + return s; + } + public static final String jnlp_prefix = "jnlp." ; + + public static boolean verbose() { + return verbose; + } + + public static boolean debugAll() { + return debugAll; + } + + public static boolean debug(String subcomponent) { + return debugAll() || isPropertyDefined("newt.debug." + subcomponent, true); + } +} diff --git a/src/newt/classes/jogamp/newt/DefaultEDTUtil.java b/src/newt/classes/jogamp/newt/DefaultEDTUtil.java new file mode 100644 index 000000000..3b14f30fc --- /dev/null +++ b/src/newt/classes/jogamp/newt/DefaultEDTUtil.java @@ -0,0 +1,335 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +package jogamp.newt; + +import java.util.ArrayList; +import javax.media.nativewindow.NativeWindowException; +import com.jogamp.common.util.RunnableTask; +import com.jogamp.newt.util.EDTUtil; + +public class DefaultEDTUtil implements EDTUtil { + public static final boolean DEBUG = Debug.debug("EDT"); + + private ThreadGroup threadGroup; + private EventDispatchThread edt = null; + private Object edtLock = new Object(); // locking the EDT start/stop state + private String name; + int start_iter=0; + private Runnable dispatchMessages; + + public DefaultEDTUtil(ThreadGroup tg, String name, Runnable dispatchMessages) { + this.threadGroup = tg; + this.name=Thread.currentThread().getName()+"-"+name+"-EDT-"; + this.dispatchMessages=dispatchMessages; + this.edt = new EventDispatchThread(threadGroup, name); + this.edt.setDaemon(true); // don't stop JVM from shutdown .. + } + + public final void reset() { + synchronized(edtLock) { + waitUntilStopped(); + if(DEBUG) { + if(edt.tasks.size()>0) { + String msg = Thread.currentThread()+": EDT reset, remaining tasks: "+edt.tasks.size()+" - "+edt; + System.err.println(msg); + // Throwable t = new Throwable(msg); + // t.printStackTrace(); + } + System.err.println(Thread.currentThread()+": EDT reset - edt: "+edt); + } + this.edt = new EventDispatchThread(threadGroup, name); + this.edt.setDaemon(true); // don't stop JVM from shutdown .. + } + } + + public final void start() { + synchronized(edtLock) { + if(!edt.isRunning() && !edt.shouldStop) { + if(edt.isAlive()) { + throw new RuntimeException("EDT Thread.isAlive(): true, isRunning: "+edt.isRunning()+", edt: "+edt+", tasks: "+edt.tasks.size()); + } + start_iter++; + edt.setName(name+start_iter); + edt.shouldStop = false; + if(DEBUG) { + String msg = Thread.currentThread()+": EDT START - edt: "+edt; + System.err.println(msg); + // Throwable t = new Throwable(msg); + // t.printStackTrace(); + } + edt.start(); + } + } + } + + public final boolean isCurrentThreadEDT() { + return edt == Thread.currentThread(); + } + + public final boolean isRunning() { + return edt.isRunning() ; + } + + public final void invokeStop(Runnable task) { + invokeImpl(true, task, true); + } + + 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"); + } + Throwable throwable = null; + RunnableTask rTask = null; + Object rTaskLock = new Object(); + synchronized(rTaskLock) { // lock the optional task execution + synchronized(edtLock) { // lock the EDT status + if( edt.shouldStop ) { + // drop task .. + if(DEBUG) { + Throwable t = new Throwable("Warning: EDT about (1) to stop, won't enqueue new task: "+edt); + t.printStackTrace(); + } + return; + } + // Exception ee = new Exception("XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); + // ee.printStackTrace(); + if(stop) { + edt.shouldStop = true; + if(DEBUG) { + String msg = Thread.currentThread()+": EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - edt: "+edt; + System.err.println(msg); + // Throwable t = new Throwable(msg); + // t.printStackTrace(); + } + } + if( isCurrentThreadEDT() ) { + task.run(); + wait = false; // running in same thread (EDT) -> no wait + if(stop && edt.tasks.size()>0) { + String msg = "Warning: EDT about (2) to stop, having remaining tasks: "+edt.tasks.size()+" - "+edt; + if(DEBUG) { + Throwable t = new Throwable(msg); + t.printStackTrace(); + } else { + System.err.println(msg); + } + } + } else { + synchronized(edt.tasks) { + start(); // start if not started yet and !shouldStop + wait = wait && edt.isRunning(); + rTask = new RunnableTask(task, + wait ? rTaskLock : null, + true /* always catch and report Exceptions, don't disturb EDT */); + if(stop) { + rTask.setAttachment(new Boolean(true)); // mark final task + } + // append task .. + edt.tasks.add(rTask); + edt.tasks.notifyAll(); + } + } + } + if( wait ) { + try { + rTaskLock.wait(); // free lock, allow execution of rTask + } catch (InterruptedException ie) { + throwable = ie; + } + if(null==throwable) { + throwable = rTask.getThrowable(); + } + if(null!=throwable) { + if(throwable instanceof NativeWindowException) { + throw (NativeWindowException)throwable; + } + throw new RuntimeException(throwable); + } + } + } + if(DEBUG && stop) { + System.err.println(Thread.currentThread()+": EDT signal STOP X edt: "+edt); + } + } + + final public void waitUntilIdle() { + final EventDispatchThread _edt; + synchronized(edtLock) { + _edt = edt; + } + if(!_edt.isRunning() || _edt == Thread.currentThread()) { + return; + } + synchronized(_edt.tasks) { + while(_edt.isRunning() && _edt.tasks.size()>0) { + try { + _edt.tasks.notifyAll(); + _edt.tasks.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + final public void waitUntilStopped() { + synchronized(edtLock) { + if(edt.isRunning() && edt != Thread.currentThread() ) { + while(edt.isRunning()) { + try { + edtLock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + + class EventDispatchThread extends Thread { + volatile boolean shouldStop = false; + volatile boolean isRunning = false; + ArrayList tasks = new ArrayList(); // one shot tasks + + public EventDispatchThread(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) { + dispatchMessages.run(); + } + // wait and work on tasks + RunnableTask task = null; + synchronized(tasks) { + // wait for tasks + if(!shouldStop && tasks.size()==0) { + try { + tasks.wait(defaultEDTPollGranularity); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + // execute one task, if available + if(tasks.size()>0) { + task = (RunnableTask) tasks.remove(0); + tasks.notifyAll(); + } + } + if(null!=task) { + // Exceptions are always catched, see RunnableTask creation above + task.run(); + } + } 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) { + RunnableTask rt = ( tasks.size() > 0 ) ? (RunnableTask) tasks.get(0) : null ; + System.err.println(getName()+": EDT run() END "+ getName()+", tasks: "+tasks.size()+", "+rt+", "+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 = ( RunnableTask ) tasks.remove(0); + task.run(); + tasks.notifyAll(); + } + if(DEBUG) { + if(null!=task && task.getAttachment()==null) { + Throwable t = new Throwable("Warning: EDT exit: Last task Not Final: "+tasks.size()+", "+task+" - "+edt); + t.printStackTrace(); + } else if(tasks.size()>0) { + Throwable t = new Throwable("Warning: EDT exit: Remaining tasks Post Final: "+tasks.size()); + t.printStackTrace(); + } + } + } + } + isRunning = !shouldStop; + if(!isRunning) { + edtLock.notifyAll(); + } + } + if(DEBUG) { + System.err.println(getName()+": EDT run() EXIT "+ getName()+", "+error); + } + if(null!=error) { + throw error; + } + } + } + } +} + diff --git a/src/newt/classes/jogamp/newt/DisplayImpl.java b/src/newt/classes/jogamp/newt/DisplayImpl.java new file mode 100644 index 000000000..299f4fb54 --- /dev/null +++ b/src/newt/classes/jogamp/newt/DisplayImpl.java @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt; + +import com.jogamp.newt.Display; +import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.event.NEWTEvent; +import com.jogamp.newt.event.NEWTEventConsumer; +import jogamp.newt.event.NEWTEventTask; +import com.jogamp.newt.util.EDTUtil; +import com.jogamp.newt.util.MainThread; +import java.util.ArrayList; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.NativeWindowFactory; + +public abstract class DisplayImpl extends Display { + public static final boolean DEBUG_TEST_EDT_MAINTHREAD = Debug.isPropertyDefined("newt.test.EDTMainThread", true); // JAU EDT Test .. + + private static int serialno = 1; + + private static Class getDisplayClass(String type) + throws ClassNotFoundException + { + Class displayClass = NewtFactory.getCustomClass(type, "Display"); + if(null==displayClass) { + if (NativeWindowFactory.TYPE_EGL.equals(type)) { + displayClass = Class.forName("jogamp.newt.opengl.kd.KDDisplay"); + } else if (NativeWindowFactory.TYPE_WINDOWS.equals(type)) { + displayClass = Class.forName("jogamp.newt.windows.WindowsDisplay"); + } else if (NativeWindowFactory.TYPE_MACOSX.equals(type)) { + displayClass = Class.forName("jogamp.newt.macosx.MacDisplay"); + } else if (NativeWindowFactory.TYPE_X11.equals(type)) { + displayClass = Class.forName("jogamp.newt.x11.X11Display"); + } else if (NativeWindowFactory.TYPE_AWT.equals(type)) { + displayClass = Class.forName("jogamp.newt.awt.AWTDisplay"); + } else { + throw new RuntimeException("Unknown display type \"" + type + "\""); + } + } + return displayClass; + } + + /** Make sure to reuse a Display with the same name */ + public static Display create(String type, String name, final long handle, boolean reuse) { + try { + Class displayClass = getDisplayClass(type); + DisplayImpl display = (DisplayImpl) displayClass.newInstance(); + name = display.validateDisplayName(name, handle); + synchronized(displayList) { + if(reuse) { + Display display0 = Display.getLastDisplayOf(type, name, -1); + if(null != display0) { + if(DEBUG) { + System.err.println("Display.create() REUSE: "+display0+" "+getThreadName()); + } + return display0; + } + } + display.name = name; + display.type=type; + display.destroyWhenUnused=false; + display.refCount=0; + display.id = serialno++; + display.fqname = getFQName(display.type, display.name, display.id); + display.hashCode = display.fqname.hashCode(); + displayList.add(display); + } + display.createEDTUtil(); + if(DEBUG) { + System.err.println("Display.create() NEW: "+display+" "+getThreadName()); + } + return display; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final DisplayImpl other = (DisplayImpl) obj; + if (this.id != other.id) { + return false; + } + if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) { + return false; + } + if ((this.type == null) ? (other.type != null) : !this.type.equals(other.type)) { + return false; + } + return true; + } + + public int hashCode() { + return hashCode; + } + + public synchronized final void createNative() + throws NativeWindowException + { + if(null==aDevice) { + if(DEBUG) { + System.err.println("Display.createNative() START ("+getThreadName()+", "+this+")"); + } + final DisplayImpl f_dpy = this; + try { + runOnEDTIfAvail(true, new Runnable() { + public void run() { + f_dpy.createNativeImpl(); + }}); + } catch (Throwable t) { + throw new NativeWindowException(t); + } + if(null==aDevice) { + throw new NativeWindowException("Display.createNative() failed to instanciate an AbstractGraphicsDevice"); + } + if(DEBUG) { + System.err.println("Display.createNative() END ("+getThreadName()+", "+this+")"); + } + synchronized(displayList) { + displaysActive++; + } + } + } + + protected boolean shallRunOnEDT() { + return true; + } + + protected void createEDTUtil() { + if(NewtFactory.useEDT()) { + if ( ! DEBUG_TEST_EDT_MAINTHREAD ) { + Thread current = Thread.currentThread(); + edtUtil = new DefaultEDTUtil(current.getThreadGroup(), "Display-"+getFQName(), dispatchMessagesRunnable); + } else { + // Begin JAU EDT Test .. + MainThread.addPumpMessage(this, dispatchMessagesRunnable); + edtUtil = MainThread.getSingleton(); + // End JAU EDT Test .. + } + if(DEBUG) { + System.err.println("Display.createNative("+getFQName()+") Create EDTUtil: "+edtUtil.getClass().getName()); + } + } + } + + public final EDTUtil getEDTUtil() { + return edtUtil; + } + + private void stopEDT(final Runnable task) { + if( shallRunOnEDT() && null!=edtUtil ) { + edtUtil.invokeStop(task); + } else { + task.run(); + } + } + + public void runOnEDTIfAvail(boolean wait, final Runnable task) { + if( shallRunOnEDT() && null!=edtUtil && !edtUtil.isCurrentThreadEDT()) { + edtUtil.invoke(wait, task); + } else { + task.run(); + } + } + + public boolean validateEDT() { + if(0==refCount && null==aDevice && null != edtUtil && edtUtil.isRunning()) { + stopEDT( new Runnable() { + public void run() { + // nop + } + } ); + edtUtil.waitUntilStopped(); + edtUtil.reset(); + return true; + } + return false; + } + + 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+" "+getThreadName()); + } + final AbstractGraphicsDevice f_aDevice = aDevice; + final DisplayImpl f_dpy = this; + stopEDT( new Runnable() { + public void run() { + if ( null != f_aDevice ) { + f_dpy.closeNativeImpl(); + } + } + } ); + if(null!=edtUtil) { + if ( DEBUG_TEST_EDT_MAINTHREAD ) { + MainThread.removePumpMessage(this); // JAU EDT Test .. + } + edtUtil.waitUntilStopped(); + edtUtil.reset(); + } + aDevice = null; + refCount=0; + if(DEBUG) { + dumpDisplayList("Display.destroy("+getFQName()+") END"); + } + } + + public synchronized final int addReference() { + if(DEBUG) { + System.err.println("Display.addReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount+1)); + } + if ( 0 == refCount ) { + createNative(); + } + if(null == aDevice) { + throw new NativeWindowException ("Display.addReference() (refCount "+refCount+") null AbstractGraphicsDevice"); + } + return refCount++; + } + + + public synchronized final int removeReference() { + if(DEBUG) { + System.err.println("Display.removeReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount-1)); + } + refCount--; // could become < 0, in case of manual destruction without actual creation/addReference + if(0>=refCount) { + destroy(); + refCount=0; // fix < 0 + } + return refCount; + } + + public synchronized final int getReferenceCount() { + return refCount; + } + + protected abstract void createNativeImpl(); + protected abstract void closeNativeImpl(); + + public final int getId() { + return id; + } + + public final String getType() { + return type; + } + + public final String getName() { + return name; + } + + public final String getFQName() { + return fqname; + } + + public static final String nilString = "nil" ; + + public String validateDisplayName(String name, long handle) { + if(null==name && 0!=handle) { + name="wrapping-"+toHexString(handle); + } + return ( null == name ) ? nilString : name ; + } + + private static String getFQName(String type, String name, int id) { + if(null==type) type=nilString; + if(null==name) name=nilString; + StringBuilder sb = new StringBuilder(); + sb.append(type); + sb.append("_"); + sb.append(name); + sb.append("-"); + sb.append(id); + return sb.toString().intern(); + } + + public final long getHandle() { + if(null!=aDevice) { + return aDevice.getHandle(); + } + return 0; + } + + public final AbstractGraphicsDevice getGraphicsDevice() { + return aDevice; + } + + public final boolean isNativeValid() { + return null != aDevice; + } + + public boolean isEDTRunning() { + if(null!=edtUtil) { + return edtUtil.isRunning(); + } + return false; + } + + @Override + public String toString() { + return "NEWT-Display["+getFQName()+", refCount "+refCount+", hasEDT "+(null!=edtUtil)+", edtRunning "+isEDTRunning()+", "+aDevice+"]"; + } + + protected abstract void dispatchMessagesNative(); + + private Object eventsLock = new Object(); + private ArrayList/*<NEWTEvent>*/ events = new ArrayList(); + private volatile boolean haveEvents = false; + + class DispatchMessagesRunnable implements Runnable { + public void run() { + DisplayImpl.this.dispatchMessages(); + } + } + DispatchMessagesRunnable dispatchMessagesRunnable = new DispatchMessagesRunnable(); + + final void dispatchMessage(final NEWTEventTask eventTask) { + NEWTEvent event = eventTask.get(); + Object source = event.getSource(); + if(source instanceof NEWTEventConsumer) { + NEWTEventConsumer consumer = (NEWTEventConsumer) source ; + if(!consumer.consumeEvent(event)) { + // enqueue for later execution + enqueueEvent(false, event); + } + } else { + throw new RuntimeException("Event source not NEWT: "+source.getClass().getName()+", "+source); + } + eventTask.notifyIssuer(); + } + + public void dispatchMessages() { + // System.err.println("Display.dispatchMessages() 0 "+this+" "+getThreadName()); + if(0==refCount) return; // no screens + if(null==getGraphicsDevice()) return; // no native device + + ArrayList/*<NEWTEvent>*/ _events = null; + + if(haveEvents) { // volatile: ok + synchronized(eventsLock) { + if(haveEvents) { + // swap events list to free ASAP + _events = events; + events = new ArrayList(); + haveEvents = false; + } + eventsLock.notifyAll(); + } + if( null != _events ) { + for (int i=0; i < _events.size(); i++) { + dispatchMessage((NEWTEventTask) _events.get(i)); + } + } + } + + // System.err.println("Display.dispatchMessages() NATIVE "+this+" "+getThreadName()); + dispatchMessagesNative(); + } + + public void enqueueEvent(boolean wait, NEWTEvent e) { + if(!isEDTRunning()) { + // oops .. we are already dead + if(DEBUG) { + Throwable t = new Throwable("Warning: EDT already stopped: wait:="+wait+", "+e); + t.printStackTrace(); + } + return; + } + + // can't wait if we are on EDT -> consume right away + if(wait && edtUtil.isCurrentThreadEDT()) { + dispatchMessage(new NEWTEventTask(e, null)); + return; + } + + Object lock = new Object(); + NEWTEventTask eTask = new NEWTEventTask(e, wait?lock:null); + synchronized(lock) { + synchronized(eventsLock) { + events.add(eTask); + haveEvents = true; + eventsLock.notifyAll(); + } + if( wait ) { + try { + lock.wait(); + } catch (InterruptedException ie) { + throw new RuntimeException(ie); + } + } + } + } + + protected 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 AbstractGraphicsDevice aDevice; +} + diff --git a/src/newt/classes/jogamp/newt/NEWTJNILibLoader.java b/src/newt/classes/jogamp/newt/NEWTJNILibLoader.java new file mode 100644 index 000000000..7b89398db --- /dev/null +++ b/src/newt/classes/jogamp/newt/NEWTJNILibLoader.java @@ -0,0 +1,62 @@ +/* + * 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 + * 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. + */ + +package jogamp.newt; + +// FIXME: refactor Java SE dependencies +//import java.awt.Toolkit; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashSet; +import com.jogamp.common.jvm.JNILibLoaderBase; + +public class NEWTJNILibLoader extends JNILibLoaderBase { + + public static void loadNEWT() { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + loadLibrary("newt", null, true); + return null; + } + }); + } + +} diff --git a/src/newt/classes/jogamp/newt/OffscreenWindow.java b/src/newt/classes/jogamp/newt/OffscreenWindow.java new file mode 100644 index 000000000..a79b1a5a1 --- /dev/null +++ b/src/newt/classes/jogamp/newt/OffscreenWindow.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt; + +import javax.media.nativewindow.*; +import javax.media.nativewindow.util.Point; + +public class OffscreenWindow extends WindowImpl implements SurfaceChangeable { + + long surfaceHandle = 0; + + public OffscreenWindow() { + } + + static long nextWindowHandle = 0x100; // start here - a marker + + protected void createNativeImpl() { + if(0!=getParentWindowHandle()) { + throw new NativeWindowException("OffscreenWindow does not support window parenting"); + } + if(capsRequested.isOnscreen()) { + throw new NativeWindowException("Capabilities is onscreen"); + } + AbstractGraphicsScreen aScreen = getScreen().getGraphicsScreen(); + config = GraphicsConfigurationFactory.getFactory(aScreen.getDevice()).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, aScreen); + if (config == null) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + + synchronized(OffscreenWindow.class) { + setWindowHandle(nextWindowHandle++); + } + } + + protected void closeNativeImpl() { + // nop + } + + @Override + protected void invalidate(boolean unrecoverable) { + super.invalidate(unrecoverable); + surfaceHandle = 0; + } + + @Override + public synchronized void destroy() { + super.destroy(); + surfaceHandle = 0; + } + + public void setSurfaceHandle(long handle) { + surfaceHandle = handle ; + } + + @Override + public long getSurfaceHandle() { + return surfaceHandle; + } + + protected void setVisibleImpl(boolean visible, int x, int y, int width, int height) { + sizeChanged(width, height, false); + visibleChanged(visible); + } + + protected void requestFocusImpl(boolean reparented) { + } + + @Override + public void setSize(int width, int height) { + if(!visible) { + sizeChanged(width, height, false); + } + } + @Override + public void setPosition(int x, int y) { + // nop + } + @Override + public boolean setFullscreen(boolean fullscreen) { + // nop + return false; + } + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, boolean parentChange, int fullScreenChange, int decorationChange) { + shouldNotCallThis(); + return false; + } + + @Override + public Point getLocationOnScreen(Point storage) { + if(null!=storage) { + storage.setX(0); + storage.setY(0); + return storage; + } + return new Point(0,0); + } + + protected Point getLocationOnScreenImpl(int x, int y) { + return new Point(x,y); + } +} + diff --git a/src/newt/classes/jogamp/newt/ScreenImpl.java b/src/newt/classes/jogamp/newt/ScreenImpl.java new file mode 100644 index 000000000..065cd88eb --- /dev/null +++ b/src/newt/classes/jogamp/newt/ScreenImpl.java @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt; + +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.common.util.IntIntHashMap; +import com.jogamp.newt.Display; +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.ScreenModeUtil; + +import javax.media.nativewindow.*; + +import java.security.*; +import java.util.ArrayList; +import java.util.List; + +public abstract class ScreenImpl extends Screen implements ScreenModeListener { + protected static final boolean DEBUG_TEST_SCREENMODE_DISABLED = Debug.isPropertyDefined("newt.test.Screen.disableScreenMode", true); + + 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 int width=-1, height=-1; // detected values: set using setScreenSize + protected static int usrWidth=-1, usrHeight=-1; // property values: newt.ws.swidth and newt.ws.sheight + private static AccessControlContext localACC = AccessController.getContext(); + private List/*<ScreenModeListener>*/ referencedScreenModeListener = new ArrayList(); + long t0; // creationTime + + private static Class getScreenClass(String type) + throws ClassNotFoundException + { + Class screenClass = NewtFactory.getCustomClass(type, "Screen"); + if(null==screenClass) { + if (NativeWindowFactory.TYPE_EGL.equals(type)) { + screenClass = Class.forName("jogamp.newt.opengl.kd.KDScreen"); + } else if (NativeWindowFactory.TYPE_WINDOWS.equals(type)) { + screenClass = Class.forName("jogamp.newt.windows.WindowsScreen"); + } else if (NativeWindowFactory.TYPE_MACOSX.equals(type)) { + screenClass = Class.forName("jogamp.newt.macosx.MacScreen"); + } else if (NativeWindowFactory.TYPE_X11.equals(type)) { + screenClass = Class.forName("jogamp.newt.x11.X11Screen"); + } else if (NativeWindowFactory.TYPE_AWT.equals(type)) { + screenClass = Class.forName("jogamp.newt.awt.AWTScreen"); + } else { + throw new RuntimeException("Unknown window type \"" + type + "\""); + } + } + return screenClass; + } + + public static Screen create(Display display, final int idx) { + try { + if(usrWidth<0 || usrHeight<0) { + synchronized (Screen.class) { + if(usrWidth<0 || usrHeight<0) { + usrWidth = Debug.getIntProperty("newt.ws.swidth", true, localACC); + usrHeight = Debug.getIntProperty("newt.ws.sheight", true, localACC); + if(usrWidth>0 || usrHeight>0) { + System.err.println("User screen size "+usrWidth+"x"+usrHeight); + } + } + } + } + synchronized(screenList) { + { + Screen screen0 = ScreenImpl.getLastScreenOf(display, idx, -1); + if(null != screen0) { + if(DEBUG) { + System.err.println("Screen.create() REUSE: "+screen0+" "+Display.getThreadName()); + } + return screen0; + } + } + Class screenClass = getScreenClass(display.getType()); + ScreenImpl screen = (ScreenImpl) screenClass.newInstance(); + screen.display = (DisplayImpl) display; + screen.screen_idx = idx; + screen.fqname = (display.getFQName()+idx).intern(); + screen.hashCode = screen.fqname.hashCode(); + screenList.add(screen); + if(DEBUG) { + System.err.println("Screen.create() NEW: "+screen+" "+Display.getThreadName()); + } + return screen; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ScreenImpl other = (ScreenImpl) obj; + if (this.display != other.display && (this.display == null || !this.display.equals(other.display))) { + return false; + } + if (this.screen_idx != other.screen_idx) { + return false; + } + return true; + } + + public int hashCode() { + return hashCode; + } + + public synchronized final void createNative() + throws NativeWindowException + { + if(null == aScreen) { + if(DEBUG) { + System.err.println("Screen.createNative() START ("+DisplayImpl.getThreadName()+", "+this+")"); + } + t0 = System.currentTimeMillis(); + display.addReference(); + createNativeImpl(); + if(null == aScreen) { + throw new NativeWindowException("Screen.createNative() failed to instanciate an AbstractGraphicsScreen"); + } + if(DEBUG) { + System.err.println("Screen.createNative() END ("+DisplayImpl.getThreadName()+", "+this+")"); + } + synchronized(screenList) { + screensActive++; + } + } + initScreenModeStatus(); + } + + public synchronized final void destroy() { + releaseScreenModeStatus(); + + synchronized(screenList) { + screenList.remove(this); + if(0 < screensActive) { + screensActive--; + } + } + + if ( null != aScreen ) { + closeNativeImpl(); + aScreen = null; + } + refCount = 0; + display.removeReference(); + } + + public synchronized final int addReference() throws NativeWindowException { + if(DEBUG) { + System.err.println("Screen.addReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount+1)); + } + if ( 0 == refCount ) { + createNative(); + } + if(null == aScreen) { + throw new NativeWindowException("Screen.addReference() (refCount "+refCount+") null AbstractGraphicsScreen"); + } + return ++refCount; + } + + public synchronized final int removeReference() { + if(DEBUG) { + String msg = "Screen.removeReference() ("+DisplayImpl.getThreadName()+"): "+refCount+" -> "+(refCount-1); + // Throwable t = new Throwable(msg); + // t.printStackTrace(); + System.err.println(msg); + } + refCount--; // could become < 0, in case of manual destruction without actual creation/addReference + if(0>=refCount) { + destroy(); + refCount=0; // fix < 0 + } + return refCount; + } + + public synchronized final int getReferenceCount() { + return refCount; + } + + protected abstract void createNativeImpl(); + protected abstract void closeNativeImpl(); + + public final String getFQName() { + return fqname; + } + + protected void setScreenSize(int w, int h) { + System.err.println("Detected screen size "+w+"x"+h); + width=w; height=h; + } + + public final Display getDisplay() { + return display; + } + + public final int getIndex() { + return screen_idx; + } + + public final AbstractGraphicsScreen getGraphicsScreen() { + return aScreen; + } + + public final boolean isNativeValid() { + return null != aScreen; + } + + public final int getWidth() { + return (usrWidth>0) ? usrWidth : (width>0) ? width : 480; + } + + public final int getHeight() { + return (usrHeight>0) ? usrHeight : (height>0) ? height : 480; + } + + @Override + public String toString() { + return "NEWT-Screen["+getFQName()+", idx "+screen_idx+", refCount "+refCount+", "+getWidth()+"x"+getHeight()+", "+aScreen+", "+display+"]"; + } + + public final List/*<ScreenMode>*/ getScreenModes() { + ArrayHashSet screenModes = getScreenModesOrig(); + if(null != screenModes && 0 < screenModes.size()) { + return screenModes.toArrayList(); + } + return null; + } + + public ScreenMode getOriginalScreenMode() { + ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); + return ( null != sms ) ? sms.getOriginalScreenMode() : null ; + } + + public ScreenMode getCurrentScreenMode() { + ScreenMode smU = null; + ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); + if(null != sms) { + ScreenMode sm0 = ( DEBUG_TEST_SCREENMODE_DISABLED ) ? null : getCurrentScreenModeImpl(); + if(null == sm0) { + return null; + } + sms.lock(); + try { + smU = (ScreenMode) sms.getScreenModes().get(sm0); // unify via value hash + if(null == smU) { + throw new RuntimeException(sm0+" could not be hashed from ScreenMode list"); + } + + // if mode has changed somehow, update it .. + if( sms.getCurrentScreenMode().hashCode() != smU.hashCode() ) { + sms.fireScreenModeChanged(smU, true); + } + } finally { + sms.unlock(); + } + } + return smU; + } + + public boolean setCurrentScreenMode(ScreenMode screenMode) { + ScreenMode smU = (ScreenMode) getScreenModesOrig().get(screenMode); // unify via value hash + ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); + if(null!=sms) { + sms.lock(); + try { + if(DEBUG) { + System.err.println("Screen.setCurrentScreenMode ("+(System.currentTimeMillis()-t0)+"): 0.0 "+screenMode); + } + + sms.fireScreenModeChangeNotify(smU); + + if(DEBUG) { + System.err.println("Screen.setCurrentScreenMode ("+(System.currentTimeMillis()-t0)+"): 0.1 "+screenMode); + } + + boolean success = setCurrentScreenModeImpl(smU); + if(success) { + setScreenSize(screenMode.getMonitorMode().getSurfaceSize().getResolution().getWidth(), + screenMode.getMonitorMode().getSurfaceSize().getResolution().getHeight()); + } + + if(DEBUG) { + System.err.println("Screen.setCurrentScreenMode ("+(System.currentTimeMillis()-t0)+"): X.0 "+screenMode+", success: "+success); + } + + sms.fireScreenModeChanged(smU, success); + + if(DEBUG) { + System.err.println("Screen.setCurrentScreenMode ("+(System.currentTimeMillis()-t0)+"): X.X "+screenMode+", success: "+success); + } + + return success; + } finally { + sms.unlock(); + } + } + return false; + } + + public void screenModeChangeNotify(ScreenMode sm) { + for(int i=0; i<referencedScreenModeListener.size(); i++) { + ((ScreenModeListener)referencedScreenModeListener.get(i)).screenModeChangeNotify(sm); + } + } + + public void screenModeChanged(ScreenMode sm, boolean success) { + for(int i=0; i<referencedScreenModeListener.size(); i++) { + ((ScreenModeListener)referencedScreenModeListener.get(i)).screenModeChanged(sm, success); + } + } + + 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 getScreenModesOrig() { + ScreenModeStatus sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); + if(null!=sms) { + return sms.getScreenModes(); + } + 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; + } + + /** + * 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; + } + + /** + * 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[] getScreenModeNextImpl() { + return null; + } + + /** + * To be implemented by the native specification.<br> + * Is called within a thread safe environment.<br> + */ + protected ScreenMode getCurrentScreenModeImpl() { + return null; + } + + /** + * To be implemented by the native specification.<br> + * Is called within a thread safe environment.<br> + */ + protected boolean setCurrentScreenModeImpl(ScreenMode screenMode) { + return false; + } + + private void initScreenModeStatus() { + ScreenModeStatus sms; + ScreenModeStatus.lockScreenModeStatus(); + try { + sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); + if(null==sms) { + IntIntHashMap screenModesIdx2NativeIdx = new IntIntHashMap(); + + ArrayHashSet screenModes = collectNativeScreenModes(screenModesIdx2NativeIdx); + sms = new ScreenModeStatus(screenModes, screenModesIdx2NativeIdx); + if(null!=screenModes && screenModes.size()>0) { + ScreenMode originalScreenMode = ( DEBUG_TEST_SCREENMODE_DISABLED ) ? null : getCurrentScreenModeImpl(); + if(null != originalScreenMode) { + ScreenMode originalScreenMode0 = (ScreenMode) screenModes.get(originalScreenMode); // unify via value hash + if(null == originalScreenMode0) { + throw new RuntimeException(originalScreenMode+" could not be hashed from ScreenMode list"); + } + sms.setOriginalScreenMode(originalScreenMode0); + } + } + ScreenModeStatus.mapScreenModeStatus(this.getFQName(), sms); + } + sms.addListener(this); + } finally { + ScreenModeStatus.unlockScreenModeStatus(); + } + } + + /** ignores bpp < 15 */ + private ArrayHashSet collectNativeScreenModes(IntIntHashMap screenModesIdx2NativeId) { + ArrayHashSet resolutionPool = new ArrayHashSet(); + ArrayHashSet surfaceSizePool = new ArrayHashSet(); + ArrayHashSet screenSizeMMPool = new ArrayHashSet(); + ArrayHashSet monitorModePool = new ArrayHashSet(); + ArrayHashSet screenModePool = null; + + screenModePool = new ArrayHashSet(); + + 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(screenModeIdx >= 0) { + screenModesIdx2NativeId.put(screenModeIdx, nativeId); + } + } + 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; + } + + private void releaseScreenModeStatus() { + ScreenModeStatus sms; + ScreenModeStatus.lockScreenModeStatus(); + try { + sms = ScreenModeStatus.getScreenModeStatus(this.getFQName()); + if(null != sms) { + sms.lock(); + try { + if(0 == sms.removeListener(this)) { + if(!sms.isOriginalMode()) { + setCurrentScreenMode(sms.getOriginalScreenMode()); + } + ScreenModeStatus.unmapScreenModeStatus(this.getFQName()); + } + } finally { + sms.unlock(); + } + } + } finally { + ScreenModeStatus.unlockScreenModeStatus(); + } + } +} + diff --git a/src/newt/classes/jogamp/newt/ScreenModeStatus.java b/src/newt/classes/jogamp/newt/ScreenModeStatus.java new file mode 100644 index 000000000..4d8b8b5f6 --- /dev/null +++ b/src/newt/classes/jogamp/newt/ScreenModeStatus.java @@ -0,0 +1,207 @@ +/** + * 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.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 = new RecursiveLock(); + private ArrayHashSet/*<ScreenMode>*/ screenModes; + private IntIntHashMap screenModesIdx2NativeIdx; + private ScreenMode currentScreenMode; + private ScreenMode originalScreenMode; + private ArrayList/*<ScreenModeChangeListener>*/ listener = new ArrayList(); + + private static HashMap screenFQN2ScreenModeStatus = new HashMap(); + private static RecursiveLock screen2ScreenModeStatusLock = new RecursiveLock(); + + protected static void mapScreenModeStatus(String screenFQN, ScreenModeStatus sms) { + screen2ScreenModeStatusLock.lock(); + try { + ScreenModeStatus _sms = (ScreenModeStatus) 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 { + ScreenModeStatus sms = (ScreenModeStatus) screenFQN2ScreenModeStatus.remove(screenFQN); + if(DEBUG) { + System.err.println("ScreenModeStatus.unmap "+screenFQN+" -> "+sms); + } + } finally { + screen2ScreenModeStatusLock.unlock(); + } + } + + protected static ScreenModeStatus getScreenModeStatus(String screenFQN) { + screen2ScreenModeStatusLock.lock(); + try { + return (ScreenModeStatus) screenFQN2ScreenModeStatus.get(screenFQN); + } finally { + screen2ScreenModeStatusLock.unlock(); + } + } + + 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; + } + + 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(); + } + } + + public final boolean isOriginalMode() { + lock(); + try { + if(null != currentScreenMode && null != originalScreenMode) { + return currentScreenMode.hashCode() == originalScreenMode.hashCode(); + } + return true; + } finally { + unlock(); + } + } + + 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++) { + ((ScreenModeListener)listener.get(i)).screenModeChangeNotify(desiredScreenMode); + } + } finally { + unlock(); + } + } + + protected void fireScreenModeChanged(ScreenMode currentScreenMode, boolean success) { + lock(); + try { + if(success) { + this.currentScreenMode = currentScreenMode; + } + for(int i=0; i<listener.size(); i++) { + ((ScreenModeListener)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/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java new file mode 100644 index 000000000..cb1c0eda2 --- /dev/null +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -0,0 +1,2239 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt; + +import java.util.ArrayList; +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.RecursiveLock; +import com.jogamp.newt.ScreenMode; +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 javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; +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.DimensionReadOnly; +import javax.media.nativewindow.util.Insets; +import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.Rectangle; + +public abstract class WindowImpl implements Window, NEWTEventConsumer +{ + public static final boolean DEBUG_TEST_REPARENT_INCOMPATIBLE = Debug.isPropertyDefined("newt.test.Window.reparent.incompatible", true); + + private RecursiveLock windowLock = new RecursiveLock(); // Window instance wide lock + private RecursiveLock surfaceLock = new RecursiveLock(); // Surface only lock + private long windowHandle; + private ScreenImpl screen; + private boolean screenReferenceAdded = false; + private NativeWindow parentWindow; + private long parentWindowHandle; + protected AbstractGraphicsConfiguration config; + protected CapabilitiesImmutable capsRequested; + protected CapabilitiesChooser capabilitiesChooser = null; // default null -> default + protected boolean fullscreen, visible, hasFocus; + protected int width, height, x, y; + protected int nfs_width, nfs_height, nfs_x, nfs_y; // non fullscreen dimensions .. + protected String title = "Newt Window"; + protected boolean undecorated = false; + private LifecycleHook lifecycleHook = null; + + private DestroyAction destroyAction = new DestroyAction(); + private boolean handleDestroyNotify = true; + + private ReparentActionRecreate reparentActionRecreate = new ReparentActionRecreate(); + + private RequestFocusAction requestFocusAction = new RequestFocusAction(); + private FocusRunnable focusAction = null; + + private Object surfaceUpdatedListenersLock = new Object(); + private ArrayList surfaceUpdatedListeners; + + private Object childWindowsLock = new Object(); + private ArrayList childWindows; + + private ArrayList mouseListeners; + private int mouseButtonPressed; // current pressed mouse button number + private long lastMousePressed; // last time when a mouse button was pressed + private int lastMouseClickCount; // last mouse button click count + + private ArrayList keyListeners; + + private ArrayList windowListeners; + private boolean repaintQueued = false; + + ScreenModeListenerImpl screenModeListenerImpl = new ScreenModeListenerImpl(); + + private void initializeStates() { + invalidate(true); + + childWindows = new ArrayList(); + surfaceUpdatedListeners = new ArrayList(); + windowListeners = new ArrayList(); + mouseListeners = new ArrayList(); + + mouseButtonPressed = 0; // current pressed mouse button number + lastMousePressed = 0; // last time when a mouse button was pressed + lastMouseClickCount = 0; // last mouse button click count + keyListeners = new ArrayList(); + } + + // 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(); + } + } + } + + // + // Construction Methods + // + + private static Class getWindowClass(String type) + throws ClassNotFoundException + { + Class windowClass = NewtFactory.getCustomClass(type, "Window"); + if(null==windowClass) { + if (NativeWindowFactory.TYPE_EGL.equals(type)) { + windowClass = Class.forName("jogamp.newt.opengl.kd.KDWindow"); + } else if (NativeWindowFactory.TYPE_WINDOWS.equals(type)) { + windowClass = Class.forName("jogamp.newt.windows.WindowsWindow"); + } else if (NativeWindowFactory.TYPE_MACOSX.equals(type)) { + windowClass = Class.forName("jogamp.newt.macosx.MacWindow"); + } else if (NativeWindowFactory.TYPE_X11.equals(type)) { + windowClass = Class.forName("jogamp.newt.x11.X11Window"); + } else if (NativeWindowFactory.TYPE_AWT.equals(type)) { + windowClass = Class.forName("jogamp.newt.awt.AWTWindow"); + } else { + throw new NativeWindowException("Unknown window type \"" + type + "\""); + } + } + return windowClass; + } + + public static WindowImpl create(NativeWindow parentWindow, long parentWindowHandle, Screen screen, CapabilitiesImmutable caps) { + try { + Class windowClass; + if(caps.isOnscreen()) { + windowClass = getWindowClass(screen.getDisplay().getType()); + } else { + windowClass = OffscreenWindow.class; + } + WindowImpl window = (WindowImpl) windowClass.newInstance(); + window.initializeStates(); + window.parentWindow = parentWindow; + window.parentWindowHandle = parentWindowHandle; + window.screen = (ScreenImpl) screen; + window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable(); + window.setUndecorated(0!=parentWindowHandle); + return window; + } catch (Throwable t) { + t.printStackTrace(); + throw new NativeWindowException(t); + } + } + + public static WindowImpl create(Object[] cstrArguments, Screen screen, CapabilitiesImmutable caps) { + try { + Class windowClass = getWindowClass(screen.getDisplay().getType()); + Class[] cstrArgumentTypes = getCustomConstructorArgumentTypes(windowClass); + if(null==cstrArgumentTypes) { + throw new NativeWindowException("WindowClass "+windowClass+" doesn't support custom arguments in constructor"); + } + 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 ) ; + window.initializeStates(); + window.screen = (ScreenImpl) screen; + window.capsRequested = (CapabilitiesImmutable) caps.cloneMutable(); + return window; + } catch (Throwable t) { + throw new NativeWindowException(t); + } + } + + public static interface LifecycleHook { + /** + * Reset of internal state counter, ie totalFrames, etc. + * Called from EDT while window is locked. + */ + public abstract void resetCounter(); + + /** + * 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, + * allows releasing of resources depending on the native Window.<br> + * Surface not locked yet.<br> + * Called not necessarily from EDT. + */ + void destroyActionPreLock(); + + /** + * Invoked before Window destroy action, + * allows releasing of resources depending on the native Window.<br> + * Surface locked.<br> + * Called from EDT while window is locked. + */ + void destroyActionInLock(); + + /** + * Invoked after destruction from Window's invalidate method.<br> + * Called while window is locked. + * @param unrecoverable + */ + void invalidate(boolean unrecoverable); + + /** + * Invoked for expensive modifications, ie while reparenting and ScreenMode change.<br> + * No lock is hold when invoked.<br> + * + * @return true is paused, otherwise false. If true {@link #resumeRenderingAction()} shall be issued. + * + * @see #resumeRenderingAction() + */ + boolean pauseRenderingAction(); + + /** + * Invoked for expensive modifications, ie while reparenting and ScreenMode change. + * No lock is hold when invoked.<br> + * + * @see #pauseRenderingAction() + */ + void resumeRenderingAction(); + } + + private boolean createNative() { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.createNative() START ("+getThreadName()+", "+this+")"); + } + if( null != parentWindow && + NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindow.lockSurface() ) { + throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); + } + try { + if(validateParentWindowHandle()) { + if(screenReferenceAdded) { + throw new InternalError("XXX"); + } + screen.addReference(); + screenReferenceAdded = true; + createNativeImpl(); + setVisibleImpl(true, x, y, width, height); + screen.addScreenModeListener(screenModeListenerImpl); + setTitleImpl(title); + } + } finally { + if(null!=parentWindow) { + parentWindow.unlockSurface(); + } + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.createNative() END ("+getThreadName()+", "+this+")"); + } + return 0 != windowHandle ; + } + + private void removeScreenReference() { + if(screenReferenceAdded) { + // be nice, probably already called recursive via + // closeAndInvalidate() -> closeNativeIml() -> .. -> windowDestroyed() -> closeAndInvalidate() ! + // or via reparentWindow .. etc + screenReferenceAdded = false; + screen.removeReference(); + } + } + + private void closeAndInvalidate() { + windowLock.lock(); + try { + if( null != screen ) { + if( 0 != windowHandle ) { + screen.removeScreenModeListener(screenModeListenerImpl); + closeNativeImpl(); + removeScreenReference(); + } + Display dpy = screen.getDisplay(); + if(null != dpy) { + dpy.validateEDT(); + } + } + invalidate(false); + } finally { + windowLock.unlock(); + } + } + + private boolean validateParentWindowHandle() { + if(null!=parentWindow) { + parentWindowHandle = getNativeWindowHandle(parentWindow); + return 0 != parentWindowHandle ; + } + return true; + } + + private static long getNativeWindowHandle(NativeWindow nativeWindow) { + long handle = 0; + if(null!=nativeWindow) { + boolean wasLocked = false; + if( NativeSurface.LOCK_SURFACE_NOT_READY < nativeWindow.lockSurface() ) { + wasLocked = true; + try { + handle = nativeWindow.getWindowHandle(); + if(0==handle) { + throw new NativeWindowException("Parent native window handle is NULL, after succesful locking: "+nativeWindow); + } + } catch (NativeWindowException nwe) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.getNativeWindowHandle: not successful yet: "+nwe); + } + } finally { + nativeWindow.unlockSurface(); + } + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.getNativeWindowHandle: locked "+wasLocked+", "+nativeWindow); + } + } + return handle; + } + + + //---------------------------------------------------------------------- + // NativeSurface: Native implementation + // + + protected int lockSurfaceImpl() { return LOCK_SUCCESS; } + + protected void unlockSurfaceImpl() { } + + //---------------------------------------------------------------------- + // WindowClosingProtocol implementation + // + private Object closingListenerLock = new Object(); + private int defaultCloseOperation = DISPOSE_ON_CLOSE; + + public int getDefaultCloseOperation() { + synchronized (closingListenerLock) { + return defaultCloseOperation; + } + } + + public int setDefaultCloseOperation(int op) { + synchronized (closingListenerLock) { + int _op = defaultCloseOperation; + defaultCloseOperation = op; + return _op; + } + } + + //---------------------------------------------------------------------- + // Window: Native implementation + // + + /** + * The native implementation must set the native windowHandle.<br> + * + * The implementation should invoke the referenced java state callbacks + * to notify this Java object of state changes. + * + * @see #windowDestroyNotify() + * @see #focusChanged(boolean) + * @see #visibleChanged(boolean) + * @see #sizeChanged(int,int) + * @see #positionChanged(int,int) + * @see #windowDestroyNotify() + */ + protected abstract void createNativeImpl(); + + protected abstract void closeNativeImpl(); + + /** + * The native implementation must invoke {@link #focusChanged(boolean)} + * to change the focus state, if <code>force == false</code>. + * This may happen asynchronous within {@link #TIMEOUT_NATIVEWINDOW}. + * + * @param force if true, bypass {@link #focusChanged(boolean)} and force focus request + */ + protected abstract void requestFocusImpl(boolean force); + + /** + * The native implementation must invoke {@link #visibleChanged(boolean)} + * to change the visibility state. This may happen asynchronous within + * {@link #TIMEOUT_NATIVEWINDOW}. + */ + protected abstract void setVisibleImpl(boolean visible, int x, int y, int width, int height); + + /** + * The native implementation should invoke the referenced java state callbacks + * to notify this Java object of state changes. + * + * @param x -1 if no position change requested, otherwise greater than zero + * @param y -1 if no position change requested, otherwise greater than zero + * @param width -1 if no size change requested, otherwise greater than zero + * @param height -1 if no size change requested, otherwise greater than zero + * @param parentChange true if reparenting requested, otherwise false + * @param fullScreenChange 0 if unchanged, -1 fullscreen off, 1 fullscreen on + * @param decorationChange 0 if unchanged, -1 undecorated, 1 decorated + * + * @see #sizeChanged(int,int) + * @see #positionChanged(int,int) + */ + protected abstract boolean reconfigureWindowImpl(int x, int y, int width, int height, + boolean parentChange, int fullScreenChange, int decorationChange); + + protected void setTitleImpl(String title) {} + + /** + * Return screen coordinates of the given coordinates + * or null, in which case a NativeWindow traversal shall being used + * as demonstrated in {@link #getLocationOnScreen(javax.media.nativewindow.util.Point)}. + * + * @return if not null, the screen location of the given coordinates + */ + protected abstract Point getLocationOnScreenImpl(int x, int y); + + //---------------------------------------------------------------------- + // NativeSurface + // + + public final int lockSurface() { + windowLock.lock(); + surfaceLock.lock(); + int res = surfaceLock.getRecursionCount() == 0 ? LOCK_SURFACE_NOT_READY : LOCK_SUCCESS; + + if ( LOCK_SURFACE_NOT_READY == res ) { + try { + if( isNativeValid() ) { + final AbstractGraphicsDevice adevice = config.getScreen().getDevice(); + adevice.lock(); + try { + res = lockSurfaceImpl(); + } finally { + if (LOCK_SURFACE_NOT_READY >= res) { + adevice.unlock(); + } + } + } + } finally { + if (LOCK_SURFACE_NOT_READY >= res) { + surfaceLock.unlock(); + windowLock.unlock(); + } + } + } + return res; + } + + public final void unlockSurface() { + surfaceLock.validateLocked(); + windowLock.validateLocked(); + + if (surfaceLock.getRecursionCount() == 0) { + final AbstractGraphicsDevice adevice = config.getScreen().getDevice(); + try { + unlockSurfaceImpl(); + } finally { + adevice.unlock(); + } + } + surfaceLock.unlock(); + windowLock.unlock(); + } + + public final boolean isWindowLockedByOtherThread() { + return windowLock.isLockedByOtherThread(); + } + + public final boolean isWindowLocked() { + return windowLock.isLocked(); + } + + public final Thread getWindowLockOwner() { + return windowLock.getOwner(); + } + + public final boolean isSurfaceLockedByOtherThread() { + return surfaceLock.isLockedByOtherThread(); + } + + public final boolean isSurfaceLocked() { + return surfaceLock.isLocked(); + } + + public final Thread getSurfaceLockOwner() { + return surfaceLock.getOwner(); + } + + public long getSurfaceHandle() { + return windowHandle; // default: return window handle + } + + public boolean surfaceSwap() { + return false; + } + + public AbstractGraphicsConfiguration getGraphicsConfiguration() { + return config; + } + + public final long getDisplayHandle() { + return getScreen().getDisplay().getHandle(); + } + + public final int getScreenIndex() { + return getScreen().getIndex(); + } + + //---------------------------------------------------------------------- + // NativeWindow + // + + // public final void destroy() - see below + + public final NativeWindow getParent() { + return parentWindow; + } + + public final long getWindowHandle() { + return windowHandle; + } + + public Point getLocationOnScreen(Point storage) { + if(isNativeValid()) { + Point d; + windowLock.lock(); + try { + d = getLocationOnScreenImpl(0, 0); + } finally { + windowLock.unlock(); + } + if(null!=d) { + if(null!=storage) { + storage.translate(d.getX(),d.getY()); + return storage; + } + return d; + } + // fall through intended .. + } + + if(null!=storage) { + storage.translate(getX(),getY()); + } else { + storage = new Point(getX(),getY()); + } + if(null!=parentWindow) { + // traverse through parent list .. + parentWindow.getLocationOnScreen(storage); + } + return storage; + } + + //---------------------------------------------------------------------- + // Window + // + + public final boolean isNativeValid() { + return null != getScreen() && 0 != getWindowHandle() ; + } + + public final boolean isValid() { + return null != getScreen() ; + } + + public final Screen getScreen() { + return screen; + } + + final void setVisibleActionImpl(boolean visible) { + boolean nativeWindowCreated = false; + boolean madeVisible = false; + + windowLock.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 = (NativeWindow) childWindows.get(i); + if(nw instanceof WindowImpl) { + ((WindowImpl)nw).setVisible(false); + } + } + } + } + if(0==windowHandle && visible) { + if( 0<width*height ) { + nativeWindowCreated = createNative(); + WindowImpl.this.waitForVisible(visible, true); + madeVisible = visible; + } + } else if(WindowImpl.this.visible != visible) { + if(0 != windowHandle) { + setVisibleImpl(visible, x, y, width, height); + WindowImpl.this.waitForVisible(visible, true); + madeVisible = visible; + } + } + + if(null!=lifecycleHook) { + lifecycleHook.setVisibleActionPost(visible, nativeWindowCreated); + } + + if(0!=windowHandle && visible && null!=childWindows && childWindows.size()>0) { + synchronized(childWindowsLock) { + for(int i = 0; i < childWindows.size(); i++ ) { + NativeWindow nw = (NativeWindow) childWindows.get(i); + if(nw instanceof WindowImpl) { + ((WindowImpl)nw).setVisible(true); + } + } + } + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window setVisible: END ("+getThreadName()+") "+x+"/"+y+" "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+WindowImpl.this.visible+", nativeWindowCreated: "+nativeWindowCreated+", madeVisible: "+madeVisible); + } + } finally { + windowLock.unlock(); + } + if( nativeWindowCreated || madeVisible ) { + sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener + } + } + + private class VisibleAction implements Runnable { + boolean visible; + + private VisibleAction (boolean visible) { + this.visible = visible; + } + + public final void run() { + setVisibleActionImpl(visible); + } + } + + public void setVisible(boolean visible) { + if(isValid()) { + if( 0==windowHandle && visible && 0>=width*height ) { + // fast-path: not realized yet, make visible, but zero size + return; + } + + if(DEBUG_IMPLEMENTATION) { + String msg = "Window setVisible: START ("+getThreadName()+") "+x+"/"+y+" "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+this.visible+" -> "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+(null!=parentWindow); + System.err.println(msg); + Thread.dumpStack(); + } + runOnEDTIfAvail(true, new VisibleAction(visible)); + } + } + + private class SetSizeActionImpl implements Runnable { + int width, height; + + private SetSizeActionImpl(int w, int h) { + width = w; + height = h; + } + public final void run() { + windowLock.lock(); + try { + int visibleAction = 0; // 1 invisible, 2 visible (create) + if ( !fullscreen && ( width != WindowImpl.this.width || WindowImpl.this.height != height ) ) { + if(DEBUG_IMPLEMENTATION) { + String msg = "Window setSize: START "+WindowImpl.this.width+"x"+WindowImpl.this.height+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible; + System.err.println(msg); + } + if ( 0 != windowHandle && 0>=width*height && visible ) { + visibleAction=1; // invisible + WindowImpl.this.width = 0; + WindowImpl.this.height = 0; + } else if ( 0 == windowHandle && 0<width*height && visible ) { + visibleAction = 2; // visible (create) + WindowImpl.this.width = width; + WindowImpl.this.height = height; + } else if ( 0 != windowHandle ) { + // this width/height will be set by windowChanged, called by the native implementation + reconfigureWindowImpl(x, y, width, height, false, 0, 0); + } else { + WindowImpl.this.width = width; + WindowImpl.this.height = height; + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window setSize: END "+WindowImpl.this.width+"x"+WindowImpl.this.height+", visibleAction "+visibleAction); + } + switch(visibleAction) { + case 1: setVisibleActionImpl(false); break; + case 2: setVisibleActionImpl(true); break; + } + } + } finally { + windowLock.unlock(); + } + } + } + + public void setSize(int width, int height) { + if(isValid()) { + runOnEDTIfAvail(true, new SetSizeActionImpl(width, height)); + } + } + + private class DestroyAction implements Runnable { + public final void run() { + boolean animatorPaused = false; + if(null!=lifecycleHook) { + animatorPaused = lifecycleHook.pauseRenderingAction(); + } + if(null!=lifecycleHook) { + lifecycleHook.destroyActionPreLock(); + } + windowLock.lock(); + try { + if( !isValid() ) { + return; // nop + } + + // Childs first .. + synchronized(childWindowsLock) { + if(childWindows.size()>0) { + // avoid ConcurrentModificationException: parent -> child -> parent.removeChild(this) + ArrayList clonedChildWindows = (ArrayList) childWindows.clone(); + while( clonedChildWindows.size() > 0 ) { + NativeWindow nw = (NativeWindow) clonedChildWindows.remove(0); + if(nw instanceof WindowImpl) { + ((WindowImpl)nw).sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); + ((WindowImpl)nw).destroy(); + } else { + nw.destroy(); + } + } + } + } + + if(null!=lifecycleHook) { + // send synced destroy notification for proper cleanup, eg GLWindow/OpenGL + lifecycleHook.destroyActionInLock(); + } + + closeAndInvalidate(); + + // send synced destroyed notification + sendWindowEvent(WindowEvent.EVENT_WINDOW_DESTROYED); + + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.destroy() END "+getThreadName()/*+", "+WindowImpl.this*/); + } + } finally { + windowLock.unlock(); + } + if(animatorPaused) { + lifecycleHook.resumeRenderingAction(); + } + } + } + + public void destroy() { + if( isValid() ) { + if(DEBUG_IMPLEMENTATION) { + String msg = "Window.destroy() START "+getThreadName(); + System.err.println(msg); + //Exception ee = new Exception(msg); + //ee.printStackTrace(); + } + runOnEDTIfAvail(true, destroyAction); + } + } + + public final void invalidate() { + destroy(); + invalidate(true); + } + + /** + * @param unrecoverable If true, all states, size, position, parent handles, + * reference to it's Screen are reset. + * Otherwise you can recreate the window, via <code>setVisible(true)</code>. + * @see #invalidate() + * @see #destroy() + * @see #destroy(boolean) + */ + protected void invalidate(boolean unrecoverable) { + windowLock.lock(); + try { + if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { + String msg = "!!! Window Invalidate(unrecoverable: "+unrecoverable+") "+getThreadName(); + System.err.println(msg); + // Throwable t = new Throwable(msg); + // t.printStackTrace(); + } + + // Childs first .. + synchronized(childWindowsLock) { + // avoid ConcurrentModificationException: parent -> child -> parent.removeChild(this) + if(null!=childWindows && childWindows.size()>0) { + ArrayList clonedChildWindows = (ArrayList) childWindows.clone(); + while( clonedChildWindows.size() > 0 ) { + NativeWindow nw = (NativeWindow) clonedChildWindows.remove(0); + if(nw instanceof WindowImpl) { + ((WindowImpl)nw).invalidate(unrecoverable); + } + } + } + } + + if(null!=lifecycleHook) { + lifecycleHook.invalidate(unrecoverable); + } + + windowHandle = 0; + visible = false; + fullscreen = false; + hasFocus = false; + + if(unrecoverable) { + if(null!=parentWindow && parentWindow instanceof Window) { + ((Window)parentWindow).removeChild(WindowImpl.this); + } + screen = null; + + synchronized(childWindowsLock) { + childWindows = null; + } + synchronized(surfaceUpdatedListenersLock) { + surfaceUpdatedListeners = null; + } + windowListeners = null; + mouseListeners = null; + keyListeners = null; + + parentWindowHandle = 0; + parentWindow = null; + capsRequested = null; + lifecycleHook = null; + + // Default position and dimension will be re-set immediately by user + width = 128; + height = 128; + x=0; + y=0; + } + } finally { + windowLock.unlock(); + } + } + + private class ReparentActionImpl implements Runnable, ReparentAction { + NativeWindow newParentWindow; + boolean forceDestroyCreate; + int reparentAction; + + private ReparentActionImpl(NativeWindow newParentWindow, boolean forceDestroyCreate) { + this.newParentWindow = newParentWindow; + this.forceDestroyCreate = forceDestroyCreate; + this.reparentAction = -1; // ensure it's set + } + + private int getStrategy() { + return reparentAction; + } + + private void setScreen(ScreenImpl newScreen) { + WindowImpl.this.removeScreenReference(); + screen = newScreen; + } + + public final void run() { + boolean animatorPaused = false; + if(null!=lifecycleHook) { + animatorPaused = lifecycleHook.pauseRenderingAction(); + } + reparent(); + if(animatorPaused) { + lifecycleHook.resumeRenderingAction(); + } + } + + private void reparent() { + // mirror pos/size so native change notification can get overwritten + int x = WindowImpl.this.x; + int y = WindowImpl.this.y; + int width = WindowImpl.this.width; + int height = WindowImpl.this.height; + boolean wasVisible; + + windowLock.lock(); + try { + wasVisible = isVisible(); + + Window newParentWindowNEWT = null; + if(newParentWindow instanceof Window) { + newParentWindowNEWT = (Window) newParentWindow; + } + + long newParentWindowHandle = 0 ; + + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.reparent: START ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)+", visible "+wasVisible+", old parentWindow: "+Display.hashCodeNullSafe(parentWindow)+", new parentWindow: "+Display.hashCodeNullSafe(newParentWindow)+", forceDestroyCreate "+forceDestroyCreate+", DEBUG_TEST_REPARENT_INCOMPATIBLE "+DEBUG_TEST_REPARENT_INCOMPATIBLE+" "+x+"/"+y+" "+width+"x"+height); + } + + if(null!=lifecycleHook) { + lifecycleHook.resetCounter(); + } + + if(null!=newParentWindow) { + // reset position to 0/0 within parent space + x = 0; + y = 0; + + // refit if size is bigger than parent + if( width > newParentWindow.getWidth() ) { + width = newParentWindow.getWidth(); + } + if( height > newParentWindow.getHeight() ) { + height = newParentWindow.getHeight(); + } + + // Case: Child Window + newParentWindowHandle = getNativeWindowHandle(newParentWindow); + if(0 == newParentWindowHandle) { + // Case: Parent's native window not realized yet + if(null==newParentWindowNEWT) { + throw new NativeWindowException("Reparenting with non NEWT Window type only available after it's realized: "+newParentWindow); + } + // Destroy this window and use parent's Screen. + // It may be created properly when the parent is made visible. + destroy(); + setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() ); + reparentAction = ACTION_NATIVE_CREATION_PENDING; + } else if(newParentWindow != getParent()) { + // Case: Parent's native window realized and changed + if( !isNativeValid() ) { + // May create a new compatible Screen/Display and + // mark it for creation. + if(null!=newParentWindowNEWT) { + setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() ); + } else { + Screen newScreen = NewtFactory.createCompatibleScreen(newParentWindow, getScreen()); + if( getScreen() != newScreen ) { + // auto destroy on-the-fly created Screen/Display + setScreen( (ScreenImpl) newScreen ); + } + } + if( 0<width*height ) { + reparentAction = ACTION_NATIVE_CREATION; + } else { + reparentAction = ACTION_NATIVE_CREATION_PENDING; + } + } else if ( DEBUG_TEST_REPARENT_INCOMPATIBLE || forceDestroyCreate || + !NewtFactory.isScreenCompatible(newParentWindow, getScreen()) ) { + // Destroy this window, may create a new compatible Screen/Display, + // and mark it for creation. + destroy(); + if(null!=newParentWindowNEWT) { + setScreen( (ScreenImpl) newParentWindowNEWT.getScreen() ); + } else { + setScreen( (ScreenImpl) NewtFactory.createCompatibleScreen(newParentWindow, getScreen()) ); + } + reparentAction = ACTION_NATIVE_CREATION; + } else { + // Mark it for native reparenting + reparentAction = ACTION_NATIVE_REPARENTING; + } + } else { + // Case: Parent's native window realized and not changed + reparentAction = ACTION_UNCHANGED; + } + } else { + if( null != parentWindow ) { + // child -> top + // put client to current parent+child position + Point p = getLocationOnScreen(null); + x = p.getX(); + y = p.getY(); + } + + // Case: Top Window + if( 0 == getParentWindowHandle() ) { + // Already Top Window + reparentAction = ACTION_UNCHANGED; + } else if( !isNativeValid() || DEBUG_TEST_REPARENT_INCOMPATIBLE || forceDestroyCreate ) { + // Destroy this window and mark it for [pending] creation. + destroy(); + if( 0<width*height ) { + reparentAction = ACTION_NATIVE_CREATION; + } else { + reparentAction = ACTION_NATIVE_CREATION_PENDING; + } + } else { + // Mark it for native reparenting + reparentAction = ACTION_NATIVE_REPARENTING; + } + } + parentWindowHandle = newParentWindowHandle; + + if ( ACTION_UNCHANGED > reparentAction ) { + throw new NativeWindowException("Internal Error: reparentAction not set"); + } + + if( ACTION_UNCHANGED == reparentAction ) { + 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 "+reparentAction+", visible "+wasVisible); + } + + // rearrange window tree + if(null!=parentWindow && parentWindow instanceof Window) { + ((Window)parentWindow).removeChild(WindowImpl.this); + } + parentWindow = newParentWindow; + if(parentWindow instanceof Window) { + ((Window)parentWindow).addChild(WindowImpl.this); + } + + if( ACTION_NATIVE_CREATION_PENDING == reparentAction ) { + return; + } + + if( ACTION_NATIVE_REPARENTING == reparentAction ) { + DisplayImpl display = (DisplayImpl) screen.getDisplay(); + display.dispatchMessagesNative(); // status up2date + if(wasVisible) { + setVisibleImpl(false, x, y, width, height); + WindowImpl.this.waitForVisible(false, true); + } + + // Lock parentWindow only during reparenting (attempt) + NativeWindow parentWindowLocked = null; + if( null != parentWindow ) { + parentWindowLocked = parentWindow; + if( NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) { + throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); + } + } + boolean ok = false; + try { + // write back mirrored values, to be able to detect satisfaction + WindowImpl.this.x = x; + WindowImpl.this.y = y; + WindowImpl.this.width = width; + WindowImpl.this.height = height; + ok = reconfigureWindowImpl(x, y, width, height, true, 0, isUndecorated()?-1:1); + } finally { + if(null!=parentWindowLocked) { + parentWindowLocked.unlockSurface(); + } + } + + // set visible again, and revalidate 'ok', + // since it has been experience that in some cases the reparented window gets hidden + if(ok) { + display.dispatchMessagesNative(); // status up2date + if(wasVisible) { + setVisibleImpl(true, x, y, width, height); + ok = WindowImpl.this.waitForVisible(true, false); + display.dispatchMessagesNative(); // status up2date + if( ok && + ( WindowImpl.this.x != x || + WindowImpl.this.y != y || + WindowImpl.this.width != width || + WindowImpl.this.height != height ) ) + { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.reparent (reconfig)"); + } + // reset pos/size .. due to some native impl flakyness + reconfigureWindowImpl(x, y, width, height, false, 0, 0); + display.dispatchMessagesNative(); // status up2date + WindowImpl.this.waitForVisible(true, false); + display.dispatchMessagesNative(); // status up2date + } + } + } + + if(ok) { + if(wasVisible) { + requestFocusImpl(true); + display.dispatchMessagesNative(); // status up2date + } + } else { + // native reparent failed -> try creation + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.reparent: native reparenting failed ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)+" -> "+toHexString(newParentWindowHandle)+" - Trying recreation"); + } + destroy(); + reparentAction = ACTION_NATIVE_CREATION ; + } + } + + // write back mirrored values, ensuring persitence + // and not relying on native messaging + WindowImpl.this.x = x; + WindowImpl.this.y = y; + WindowImpl.this.width = width; + WindowImpl.this.height = height; + + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.reparentWindow: END ("+getThreadName()+") windowHandle "+toHexString(windowHandle)+", visible: "+visible+", parentWindowHandle "+toHexString(parentWindowHandle)+", parentWindow "+ Display.hashCodeNullSafe(parentWindow)+" "+x+"/"+y+" "+width+"x"+height); + } + } finally { + windowLock.unlock(); + } + + if(wasVisible) { + switch (reparentAction) { + case ACTION_NATIVE_REPARENTING: + // trigger a resize/relayout and repaint to listener + sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); + break; + + case ACTION_NATIVE_CREATION: + // This may run on the new Display/Screen connection, hence a new EDT task + runOnEDTIfAvail(true, reparentActionRecreate); + break; + } + } + } + } + + private class ReparentActionRecreate implements Runnable { + public final void run() { + windowLock.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)); + } + setVisible(true); // native creation + } finally { + windowLock.unlock(); + } + } + } + + public final int reparentWindow(NativeWindow newParent) { + return reparentWindow(newParent, false); + } + + public int reparentWindow(NativeWindow newParent, boolean forceDestroyCreate) { + int reparentActionStrategy = ReparentAction.ACTION_INVALID; + if(isValid()) { + ReparentActionImpl reparentAction = new ReparentActionImpl(newParent, forceDestroyCreate); + runOnEDTIfAvail(true, reparentAction); + reparentActionStrategy = reparentAction.getStrategy(); + } + return reparentActionStrategy; + } + + public CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) { + CapabilitiesChooser old = this.capabilitiesChooser; + this.capabilitiesChooser = chooser; + return old; + } + + public final CapabilitiesImmutable getChosenCapabilities() { + return config.getNativeGraphicsConfiguration().getChosenCapabilities(); + } + + public final CapabilitiesImmutable getRequestedCapabilities() { + return capsRequested; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + if (title == null) { + title = ""; + } + this.title = title; + if(0 != getWindowHandle()) { + setTitleImpl(title); + } + } + + private class DecorationActionImpl implements Runnable { + boolean undecorated; + + private DecorationActionImpl(boolean undecorated) { + this.undecorated = undecorated; + } + + public final void run() { + windowLock.lock(); + try { + if(!fullscreen && isNativeValid() && WindowImpl.this.undecorated != undecorated) { + WindowImpl.this.undecorated = undecorated; + // mirror pos/size so native change notification can get overwritten + int x = WindowImpl.this.x; + int y = WindowImpl.this.y; + int width = WindowImpl.this.width; + int height = WindowImpl.this.height; + + if( 0 != windowHandle ) { + DisplayImpl display = (DisplayImpl) screen.getDisplay(); + display.dispatchMessagesNative(); // status up2date + boolean wasVisible = isVisible(); + setVisibleImpl(false, x, y, width, height); + WindowImpl.this.waitForVisible(false, true); + display.dispatchMessagesNative(); // status up2date + reconfigureWindowImpl(x, y, width, height, false, 0, undecorated?-1:1); + display.dispatchMessagesNative(); // status up2date + if(wasVisible) { + setVisibleImpl(true, x, y, width, height); + WindowImpl.this.waitForVisible(true, true); + display.dispatchMessagesNative(); // status up2date + if( WindowImpl.this.x != x || + WindowImpl.this.y != y || + WindowImpl.this.width != width || + WindowImpl.this.height != height ) + { + // reset pos/size .. due to some native impl flakyness + reconfigureWindowImpl(x, y, width, height, false, 0, 0); + display.dispatchMessagesNative(); // status up2date + } + requestFocusImpl(true); + display.dispatchMessagesNative(); // status up2date + } + } + } + } finally { + windowLock.unlock(); + } + sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener + } + } + + public void setUndecorated(boolean value) { + if(isValid()) { + runOnEDTIfAvail(true, new DecorationActionImpl(value)); + } + } + + public boolean isUndecorated() { + return 0 != parentWindowHandle || undecorated || fullscreen ; + } + + public void requestFocus() { + enqueueRequestFocus(true); + } + + public boolean hasFocus() { + return hasFocus; + } + + public Insets getInsets() { + return new Insets(0,0,0,0); + } + + public final int getWidth() { + return width; + } + + public final int getHeight() { + return height; + } + + public final int getX() { + return x; + } + + public final int getY() { + return y; + } + + public final boolean isVisible() { + return visible; + } + + public final boolean isFullscreen() { + return fullscreen; + } + + + //---------------------------------------------------------------------- + // Window + // + + /** + * If the implementation is capable of detecting a device change + * return true and clear the status/reason of the change. + */ + public boolean hasDeviceChanged() { + return false; + } + + public LifecycleHook getLifecycleHook() { + return lifecycleHook; + } + + public LifecycleHook setLifecycleHook(LifecycleHook hook) { + LifecycleHook old = lifecycleHook; + lifecycleHook = hook; + return old; + } + + /** If this Window actually wraps one from another toolkit such as + the AWT, this will return a non-null value. */ + public Object getWrappedWindow() { + return null; + } + + /** + * If set to true, the default value, this NEWT Window implementation will + * handle the destruction (ie {@link #destroy()} call) within {@link #windowDestroyNotify()} implementation.<br> + * If set to false, it's up to the caller/owner to handle destruction within {@link #windowDestroyNotify()}. + */ + public void setHandleDestroyNotify(boolean b) { + handleDestroyNotify = b; + } + + //---------------------------------------------------------------------- + // WindowImpl + // + + protected final long getParentWindowHandle() { + return parentWindowHandle; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append(getClass().getName()+"[Config "+config+ + "\n, "+screen+ + "\n, ParentWindow "+parentWindow+ + "\n, ParentWindowHandle "+toHexString(parentWindowHandle)+ + "\n, WindowHandle "+toHexString(getWindowHandle())+ + "\n, SurfaceHandle "+toHexString(getSurfaceHandle())+ " (lockedExt window "+isWindowLockedByOtherThread()+", surface "+isSurfaceLockedByOtherThread()+")"+ + "\n, Pos "+getX()+"/"+getY()+", size "+getWidth()+"x"+getHeight()+ + "\n, Visible "+isVisible()+ + "\n, Undecorated "+undecorated+ + "\n, Fullscreen "+fullscreen+ + "\n, WrappedWindow "+getWrappedWindow()+ + "\n, ChildWindows "+childWindows.size()); + + sb.append(", SurfaceUpdatedListeners num "+surfaceUpdatedListeners.size()+" ["); + for (int i = 0; i < surfaceUpdatedListeners.size(); i++ ) { + sb.append(surfaceUpdatedListeners.get(i)+", "); + } + sb.append("], WindowListeners num "+windowListeners.size()+" ["); + for (int i = 0; i < windowListeners.size(); i++ ) { + sb.append(windowListeners.get(i)+", "); + } + sb.append("], MouseListeners num "+mouseListeners.size()+" ["); + for (int i = 0; i < mouseListeners.size(); i++ ) { + sb.append(mouseListeners.get(i)+", "); + } + sb.append("], KeyListeners num "+keyListeners.size()+" ["); + for (int i = 0; i < keyListeners.size(); i++ ) { + sb.append(keyListeners.get(i)+", "); + } + sb.append("] ]"); + return sb.toString(); + } + + protected final void setWindowHandle(long handle) { + windowHandle = handle; + } + + public void runOnEDTIfAvail(boolean wait, final Runnable task) { + Screen scrn = getScreen(); + if(null==scrn) { + throw new RuntimeException("Null screen of inner class: "+this); + } + DisplayImpl d = (DisplayImpl) scrn.getDisplay(); + d.runOnEDTIfAvail(wait, task); + } + + private class RequestFocusAction implements Runnable { + public final void run() { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.RequestFocusAction: ("+getThreadName()+"): "+hasFocus+" -> true - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + } + WindowImpl.this.requestFocusImpl(false); + } + } + + protected void enqueueRequestFocus(boolean wait) { + runOnEDTIfAvail(wait, requestFocusAction); + } + + /** + * May set to a {@link FocusRunnable}, {@link FocusRunnable#run()} before Newt requests the native focus. + * This allows notifying a covered window toolkit like AWT that the focus is requested, + * hence focus traversal can be made transparent. + */ + public void setFocusAction(FocusRunnable focusAction) { + this.focusAction = focusAction; + } + protected boolean focusAction() { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.focusAction() START - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle())); + } + boolean res; + if(null!=focusAction) { + res = focusAction.run(); + } else { + res = false; + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.focusAction() END - "+getThreadName()+", focusAction: "+focusAction+" - windowHandle "+toHexString(getWindowHandle())+", res: "+res); + } + return res; + } + + private class SetPositionActionImpl implements Runnable { + int x, y; + + private SetPositionActionImpl(int x, int y) { + this.x = x; + this.y = y; + } + public final void run() { + windowLock.lock(); + try { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window setPosition: "+WindowImpl.this.x+"/"+WindowImpl.this.y+" -> "+x+"/"+y+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)); + } + if ( WindowImpl.this.x != x || WindowImpl.this.y != y ) { + if(!fullscreen) { + if(0!=windowHandle) { + // this.x/this.y will be set by windowChanged, called by the native implementation + reconfigureWindowImpl(x, y, -1, -1, false, 0, 0); + } else { + WindowImpl.this.x = x; + WindowImpl.this.y = y; + } + } + } + } finally { + windowLock.unlock(); + } + } + } + + public void setPosition(int x, int y) { + if(isValid()) { + runOnEDTIfAvail(true, new SetPositionActionImpl(x, y)); + } + } + + private class FullScreenActionImpl implements Runnable { + boolean fullscreen; + + private FullScreenActionImpl (boolean fullscreen) { + this.fullscreen = fullscreen; + } + + public final void run() { + windowLock.lock(); + try { + if(isNativeValid() && WindowImpl.this.fullscreen != fullscreen) { + int x,y,w,h; + WindowImpl.this.fullscreen = fullscreen; + if(fullscreen) { + x = 0; y = 0; + w = screen.getWidth(); + h = screen.getHeight(); + nfs_width = width; + nfs_height = height; + nfs_x = x; + nfs_y = y; + } else { + x = nfs_x; + y = nfs_y; + w = nfs_width; + h = nfs_height; + } + if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { + System.err.println("Window fs: "+fullscreen+" "+x+"/"+y+" "+w+"x"+h+", "+isUndecorated()+", "+screen); + } + + DisplayImpl display = (DisplayImpl) screen.getDisplay(); + display.dispatchMessagesNative(); // status up2date + boolean wasVisible = isVisible(); + setVisibleImpl(false, x, y, width, height); + WindowImpl.this.waitForVisible(false, true); + display.dispatchMessagesNative(); // status up2date + + // write back mirrored values, to be able to detect satisfaction + WindowImpl.this.x = x; + WindowImpl.this.y = y; + WindowImpl.this.width = w; + WindowImpl.this.height = h; + reconfigureWindowImpl(x, y, w, h, getParentWindowHandle()!=0, fullscreen?1:-1, isUndecorated()?-1:1); + display.dispatchMessagesNative(); // status up2date + + if(wasVisible) { + setVisibleImpl(true, x, y, width, height); + boolean ok = WindowImpl.this.waitForVisible(true, true, Screen.SCREEN_MODE_CHANGE_TIMEOUT); + display.dispatchMessagesNative(); // status up2date + if( ok && + ( WindowImpl.this.x != x || + WindowImpl.this.y != y || + WindowImpl.this.width != w || + WindowImpl.this.height != h ) ) + { + if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { + System.err.println("Window fs (reconfig): "+x+"/"+y+" "+w+"x"+h+", "+screen); + } + // reset pos/size .. due to some native impl flakyness + reconfigureWindowImpl(x, y, width, height, false, 0, 0); + display.dispatchMessagesNative(); // status up2date + } + requestFocusImpl(true); + display.dispatchMessagesNative(); // status up2date + if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { + System.err.println("Window fs done"); + } + } + } + } finally { + windowLock.unlock(); + } + sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener + } + } + + public boolean setFullscreen(boolean fullscreen) { + if(isValid()) { + runOnEDTIfAvail(true, new FullScreenActionImpl(fullscreen)); + } + return this.fullscreen; + } + + private class ScreenModeListenerImpl implements ScreenModeListener { + boolean animatorPaused = false; + + public void screenModeChangeNotify(ScreenMode sm) { + if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { + System.err.println("Window.screenModeChangeNotify: "+sm); + } + + if(null!=lifecycleHook) { + animatorPaused = lifecycleHook.pauseRenderingAction(); + } + } + + public void screenModeChanged(ScreenMode sm, boolean success) { + if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { + System.err.println("Window.screenModeChanged: "+sm+", success: "+success); + } + + if(success) { + DimensionReadOnly screenSize = sm.getMonitorMode().getSurfaceSize().getResolution(); + if ( getHeight() > screenSize.getHeight() || + getWidth() > screenSize.getWidth() ) { + setSize(screenSize.getWidth(), screenSize.getHeight()); + } + } + + if(animatorPaused) { + lifecycleHook.resumeRenderingAction(); + } + sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); // trigger a resize/relayout and repaint to listener + } + } + + //---------------------------------------------------------------------- + // Child Window Management + // + + public final void removeChild(NativeWindow win) { + synchronized(childWindowsLock) { + childWindows.remove(win); + } + } + + public final void addChild(NativeWindow win) { + if (win == null) { + return; + } + synchronized(childWindowsLock) { + childWindows.add(win); + } + } + + //---------------------------------------------------------------------- + // Generic Event Support + // + private void doEvent(boolean enqueue, boolean wait, com.jogamp.newt.event.NEWTEvent event) { + boolean done = false; + + if(!enqueue) { + done = consumeEvent(event); + wait = done; // don't wait if event can't be consumed now + } + + if(!done) { + enqueueEvent(wait, event); + } + } + + public void enqueueEvent(boolean wait, com.jogamp.newt.event.NEWTEvent event) { + if(isValid()) { + ((DisplayImpl)getScreen().getDisplay()).enqueueEvent(wait, event); + } + } + + public boolean consumeEvent(NEWTEvent e) { + switch(e.getEventType()) { + // special repaint treatment + case WindowEvent.EVENT_WINDOW_REPAINT: + // queue repaint event in case window is locked, ie in operation + if( isWindowLocked() ) { + // make sure only one repaint event is queued + if(!repaintQueued) { + repaintQueued=true; + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.consumeEvent: queued "+e); + // Exception ee = new Exception("Window.windowRepaint: "+e); + // ee.printStackTrace(); + } + return false; + } + return true; + } + repaintQueued=false; // no repaint event queued + break; + + // common treatment + case WindowEvent.EVENT_WINDOW_RESIZED: + // queue event in case window is locked, ie in operation + if( isWindowLocked() ) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.consumeEvent: queued "+e); + // Exception ee = new Exception("Window.windowRepaint: "+e); + // ee.printStackTrace(); + } + return false; + } + break; + default: + break; + } + if(e instanceof WindowEvent) { + consumeWindowEvent((WindowEvent)e); + } else if(e instanceof KeyEvent) { + consumeKeyEvent((KeyEvent)e); + } else if(e instanceof MouseEvent) { + consumeMouseEvent((MouseEvent)e); + } else { + throw new NativeWindowException("Unexpected NEWTEvent type " + e); + } + return true; + } + + // + // SurfaceUpdatedListener Support + // + + public void addSurfaceUpdatedListener(SurfaceUpdatedListener l) { + addSurfaceUpdatedListener(-1, l); + } + + public void addSurfaceUpdatedListener(int index, SurfaceUpdatedListener l) + throws IndexOutOfBoundsException + { + if(l == null) { + return; + } + synchronized(surfaceUpdatedListenersLock) { + if(0>index) { + index = surfaceUpdatedListeners.size(); + } + surfaceUpdatedListeners.add(index, l); + } + } + + public void removeSurfaceUpdatedListener(SurfaceUpdatedListener l) { + if (l == null) { + return; + } + synchronized(surfaceUpdatedListenersLock) { + surfaceUpdatedListeners.remove(l); + } + } + + public void removeAllSurfaceUpdatedListener() { + synchronized(surfaceUpdatedListenersLock) { + surfaceUpdatedListeners = new ArrayList(); + } + } + + public SurfaceUpdatedListener getSurfaceUpdatedListener(int index) { + synchronized(surfaceUpdatedListenersLock) { + if(0>index) { + index = surfaceUpdatedListeners.size()-1; + } + return (SurfaceUpdatedListener) surfaceUpdatedListeners.get(index); + } + } + + public SurfaceUpdatedListener[] getSurfaceUpdatedListeners() { + synchronized(surfaceUpdatedListenersLock) { + return (SurfaceUpdatedListener[]) surfaceUpdatedListeners.toArray(); + } + } + + public void surfaceUpdated(Object updater, NativeSurface ns, long when) { + synchronized(surfaceUpdatedListenersLock) { + for(int i = 0; i < surfaceUpdatedListeners.size(); i++ ) { + SurfaceUpdatedListener l = (SurfaceUpdatedListener) surfaceUpdatedListeners.get(i); + l.surfaceUpdated(updater, ns, when); + } + } + } + + // + // 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); + } + private void doMouseEvent(boolean enqueue, boolean wait, int eventType, int modifiers, + int x, int y, int button, int rotation) { + if(x<0||y<0||x>=width||y>=height) { + return; // .. invalid .. + } + if(DEBUG_MOUSE_EVENT) { + System.err.println("doMouseEvent: enqueue"+enqueue+", wait "+wait+", "+MouseEvent.getEventTypeString(eventType)+ + ", mod "+modifiers+", pos "+x+"/"+y+", button "+button); + } + if(button<0||button>MouseEvent.BUTTON_NUMBER) { + throw new NativeWindowException("Invalid mouse button number" + button); + } + long when = System.currentTimeMillis(); + MouseEvent eClicked = null; + MouseEvent e = null; + + 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); + } + } 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); + } + doEvent(enqueue, wait, e); + if(null!=eClicked) { + if(DEBUG_MOUSE_EVENT) { + System.err.println("doMouseEvent: synthesized MOUSE_CLICKED event"); + } + doEvent(enqueue, wait, eClicked); + } + } + + + public void addMouseListener(MouseListener l) { + addMouseListener(-1, l); + } + + public void addMouseListener(int index, MouseListener l) { + if(l == null) { + return; + } + ArrayList clonedListeners = (ArrayList) mouseListeners.clone(); + if(0>index) { + index = clonedListeners.size(); + } + clonedListeners.add(index, l); + mouseListeners = clonedListeners; + } + + public void removeMouseListener(MouseListener l) { + if (l == null) { + return; + } + ArrayList clonedListeners = (ArrayList) mouseListeners.clone(); + clonedListeners.remove(l); + mouseListeners = clonedListeners; + } + + public MouseListener getMouseListener(int index) { + ArrayList clonedListeners = (ArrayList) mouseListeners.clone(); + if(0>index) { + index = clonedListeners.size()-1; + } + return (MouseListener) clonedListeners.get(index); + } + + public MouseListener[] getMouseListeners() { + return (MouseListener[]) mouseListeners.toArray(); + } + + protected void consumeMouseEvent(MouseEvent e) { + if(DEBUG_MOUSE_EVENT) { + System.err.println("consumeMouseEvent: event: "+e); + } + + for(int i = 0; i < mouseListeners.size(); i++ ) { + MouseListener l = (MouseListener) mouseListeners.get(i); + switch(e.getEventType()) { + case MouseEvent.EVENT_MOUSE_CLICKED: + l.mouseClicked(e); + break; + case MouseEvent.EVENT_MOUSE_ENTERED: + l.mouseEntered(e); + break; + case MouseEvent.EVENT_MOUSE_EXITED: + l.mouseExited(e); + break; + case MouseEvent.EVENT_MOUSE_PRESSED: + l.mousePressed(e); + break; + case MouseEvent.EVENT_MOUSE_RELEASED: + l.mouseReleased(e); + break; + case MouseEvent.EVENT_MOUSE_MOVED: + l.mouseMoved(e); + break; + case MouseEvent.EVENT_MOUSE_DRAGGED: + l.mouseDragged(e); + break; + case MouseEvent.EVENT_MOUSE_WHEEL_MOVED: + l.mouseWheelMoved(e); + break; + default: + throw new NativeWindowException("Unexpected mouse event type " + e.getEventType()); + } + } + } + + // + // KeyListener/Event Support + // + + public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { + consumeKeyEvent(new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers, keyCode, keyChar) ); + } + + public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { + enqueueEvent(wait, new KeyEvent(eventType, this, System.currentTimeMillis(), modifiers, keyCode, keyChar) ); + } + + public void addKeyListener(KeyListener l) { + addKeyListener(-1, l); + } + + public void addKeyListener(int index, KeyListener l) { + if(l == null) { + return; + } + ArrayList clonedListeners = (ArrayList) keyListeners.clone(); + if(0>index) { + index = clonedListeners.size(); + } + clonedListeners.add(index, l); + keyListeners = clonedListeners; + } + + public void removeKeyListener(KeyListener l) { + if (l == null) { + return; + } + ArrayList clonedListeners = (ArrayList) keyListeners.clone(); + clonedListeners.remove(l); + keyListeners = clonedListeners; + } + + public KeyListener getKeyListener(int index) { + ArrayList clonedListeners = (ArrayList) keyListeners.clone(); + if(0>index) { + index = clonedListeners.size()-1; + } + return (KeyListener) clonedListeners.get(index); + } + + public KeyListener[] getKeyListeners() { + return (KeyListener[]) keyListeners.toArray(); + } + + protected void consumeKeyEvent(KeyEvent e) { + if(DEBUG_KEY_EVENT) { + System.err.println("consumeKeyEvent: "+e); + } + for(int i = 0; i < keyListeners.size(); i++ ) { + KeyListener l = (KeyListener) keyListeners.get(i); + switch(e.getEventType()) { + case KeyEvent.EVENT_KEY_PRESSED: + l.keyPressed(e); + break; + 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()); + } + } + } + + // + // WindowListener/Event Support + // + public void sendWindowEvent(int eventType) { + consumeWindowEvent( new WindowEvent(eventType, this, System.currentTimeMillis()) ); + } + + public void enqueueWindowEvent(boolean wait, int eventType) { + enqueueEvent( wait, new WindowEvent(eventType, this, System.currentTimeMillis()) ); + } + + public void addWindowListener(WindowListener l) { + addWindowListener(-1, l); + } + + public void addWindowListener(int index, WindowListener l) + throws IndexOutOfBoundsException + { + if(l == null) { + return; + } + ArrayList clonedListeners = (ArrayList) windowListeners.clone(); + if(0>index) { + index = clonedListeners.size(); + } + clonedListeners.add(index, l); + windowListeners = clonedListeners; + } + + public final void removeWindowListener(WindowListener l) { + if (l == null) { + return; + } + ArrayList clonedListeners = (ArrayList) windowListeners.clone(); + clonedListeners.remove(l); + windowListeners = clonedListeners; + } + + public WindowListener getWindowListener(int index) { + ArrayList clonedListeners = (ArrayList) windowListeners.clone(); + if(0>index) { + index = clonedListeners.size()-1; + } + return (WindowListener) clonedListeners.get(index); + } + + public WindowListener[] getWindowListeners() { + return (WindowListener[]) windowListeners.toArray(); + } + + protected void consumeWindowEvent(WindowEvent e) { + if(DEBUG_WINDOW_EVENT) { + System.err.println("consumeWindowEvent: "+e+", visible "+isVisible()+" "+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()); + } + for(int i = 0; i < windowListeners.size(); i++ ) { + WindowListener l = (WindowListener) windowListeners.get(i); + switch(e.getEventType()) { + case WindowEvent.EVENT_WINDOW_RESIZED: + l.windowResized(e); + break; + case WindowEvent.EVENT_WINDOW_MOVED: + l.windowMoved(e); + break; + case WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY: + l.windowDestroyNotify(e); + break; + case WindowEvent.EVENT_WINDOW_DESTROYED: + l.windowDestroyed(e); + break; + case WindowEvent.EVENT_WINDOW_GAINED_FOCUS: + l.windowGainedFocus(e); + break; + case WindowEvent.EVENT_WINDOW_LOST_FOCUS: + l.windowLostFocus(e); + break; + case WindowEvent.EVENT_WINDOW_REPAINT: + l.windowRepaint((WindowUpdateEvent)e); + break; + default: + throw + new NativeWindowException("Unexpected window event type " + + e.getEventType()); + } + } + } + + /** + * @param focusGained + */ + protected void focusChanged(boolean focusGained) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.focusChanged: ("+getThreadName()+"): "+this.hasFocus+" -> "+focusGained+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + } + hasFocus = focusGained; + if (focusGained) { + sendWindowEvent(WindowEvent.EVENT_WINDOW_GAINED_FOCUS); + } else { + sendWindowEvent(WindowEvent.EVENT_WINDOW_LOST_FOCUS); + } + } + + protected void visibleChanged(boolean visible) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.visibleChanged ("+getThreadName()+"): "+this.visible+" -> "+visible+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + } + this.visible = visible ; + } + + private boolean waitForVisible(boolean visible, boolean failFast) { + return waitForVisible(visible, failFast, TIMEOUT_NATIVEWINDOW); + } + + private boolean waitForVisible(boolean visible, boolean failFast, long timeOut) { + DisplayImpl display = (DisplayImpl) screen.getDisplay(); + for(long sleep = timeOut; 0<sleep && this.visible != visible; sleep-=10 ) { + display.dispatchMessagesNative(); // status up2date + try { + Thread.sleep(10); + } catch (InterruptedException ie) {} + sleep -=10; + } + if(this.visible != visible) { + if(failFast) { + throw new NativeWindowException("Visibility not reached as requested within "+timeOut+"ms : requested "+visible+", is "+this.visible); + } else if (DEBUG_IMPLEMENTATION) { + System.err.println("******* Visibility not reached as requested within "+timeOut+"ms : requested "+visible+", is "+this.visible); + } + } + return this.visible == visible; + } + + protected void sizeChanged(int newWidth, int newHeight, boolean force) { + if(force || width != newWidth || height != newHeight) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.sizeChanged: ("+getThreadName()+"): force "+force+", "+width+"x"+height+" -> "+newWidth+"x"+newHeight+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + } + width = newWidth; + height = newHeight; + if(isNativeValid()) { + sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); + } + } + } + + protected void positionChanged(int newX, int newY) { + if( 0==parentWindowHandle && ( x != newX || y != newY ) ) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.positionChanged: ("+getThreadName()+"): "+x+"/"+y+" -> "+newX+"/"+newY+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + } + x = newX; + y = newY; + sendWindowEvent(WindowEvent.EVENT_WINDOW_MOVED); + } + } + + protected void windowDestroyNotify() { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.windowDestroyNotify START "+getThreadName()); + } + + // send synced destroy notifications + enqueueWindowEvent(true, WindowEvent.EVENT_WINDOW_DESTROY_NOTIFY); + + if(handleDestroyNotify && DISPOSE_ON_CLOSE == defaultCloseOperation && isValid()) { + destroy(); + } + + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.windowDestroyeNotify END "+getThreadName()); + } + } + + public void windowRepaint(int x, int y, int width, int height) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.windowRepaint "+getThreadName()+" - "+x+"/"+y+" "+width+"x"+height); + // Exception ee = new Exception("Window.windowRepaint: "+" - "+x+"/"+y+" "+width+"x"+height); + // ee.printStackTrace(); + } + + if(isNativeValid()) { + if(0>width) { + width=this.width; + } + if(0>height) { + height=this.height; + } + + NEWTEvent e = new WindowUpdateEvent(WindowEvent.EVENT_WINDOW_REPAINT, this, System.currentTimeMillis(), + new Rectangle(x, y, width, height)); + doEvent(false, false, e); + } + } + + // + // Reflection helper .. + // + + private static Class[] getCustomConstructorArgumentTypes(Class windowClass) { + Class[] argTypes = null; + try { + Method m = windowClass.getDeclaredMethod("getCustomConstructorArgumentTypes", new Class[] {}); + argTypes = (Class[]) m.invoke(null, (Object[])null); + } catch (Throwable t) {} + return argTypes; + } + + private static int verifyConstructorArgumentTypes(Class[] types, Object[] args) { + if(types.length != args.length) { + return -1; + } + for(int i=0; i<args.length; i++) { + if(!types[i].isInstance(args[i])) { + return i; + } + } + return args.length; + } + + private static String getArgsStrList(Object[] args) { + StringBuilder sb = new StringBuilder(); + for(int i=0; i<args.length; i++) { + sb.append(args[i].getClass()); + if(i<args.length) { + sb.append(", "); + } + } + return sb.toString(); + } + + private static String getTypeStrList(Class[] types) { + StringBuilder sb = new StringBuilder(); + for(int i=0; i<types.length; i++) { + sb.append(types[i]); + if(i<types.length) { + sb.append(", "); + } + } + 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) { + return Display.toHexString(hex); + } + + public static String toHexString(long hex) { + return Display.toHexString(hex); + } +} + diff --git a/src/newt/classes/jogamp/newt/awt/AWTCanvas.java b/src/newt/classes/jogamp/newt/awt/AWTCanvas.java new file mode 100644 index 000000000..01e813449 --- /dev/null +++ b/src/newt/classes/jogamp/newt/awt/AWTCanvas.java @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.awt; + +import java.awt.Canvas; +import java.awt.GraphicsDevice; +import java.awt.GraphicsConfiguration; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.CapabilitiesChooser; +import javax.media.nativewindow.CapabilitiesImmutable; +import javax.media.nativewindow.GraphicsConfigurationFactory; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.awt.AWTGraphicsConfiguration; +import javax.media.nativewindow.awt.AWTGraphicsDevice; +import javax.media.nativewindow.awt.AWTGraphicsScreen; +import com.jogamp.newt.Window; + +public class AWTCanvas extends Canvas { + private GraphicsDevice device; + private GraphicsConfiguration chosen; + private AWTGraphicsConfiguration awtConfig; + + private CapabilitiesChooser chooser=null; + private CapabilitiesImmutable capabilities; + + private boolean displayConfigChanged=false; + + public AWTCanvas(CapabilitiesImmutable capabilities, CapabilitiesChooser chooser) { + super(); + + if(null==capabilities) { + throw new NativeWindowException("Capabilities null"); + } + this.capabilities=capabilities; + this.chooser=chooser; + } + + public AWTGraphicsConfiguration getAWTGraphicsConfiguration() { + return awtConfig; + } + + public boolean hasDeviceChanged() { + boolean res = displayConfigChanged; + displayConfigChanged=false; + return res; + } + + public void addNotify() { + + /** + * 'super.addNotify()' determines the GraphicsConfiguration, + * while calling this class's overriden 'getGraphicsConfiguration()' method + * after which it creates the native peer. + * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration + * is being used in getGraphicsConfiguration(). + * This code order also allows recreation, ie re-adding the GLCanvas. + */ + awtConfig = chooseGraphicsConfiguration(capabilities, capabilities, chooser, device); + if(Window.DEBUG_IMPLEMENTATION) { + Exception e = new Exception("Info: Created Config: "+awtConfig); + e.printStackTrace(); + } + if(null==awtConfig) { + throw new NativeWindowException("Error: NULL AWTGraphicsConfiguration"); + } + chosen = awtConfig.getGraphicsConfiguration(); + + // before native peer is valid: X11 + disableBackgroundErase(); + + // issues getGraphicsConfiguration() and creates the native peer + super.addNotify(); + + // after native peer is valid: Windows + disableBackgroundErase(); + + GraphicsConfiguration gc = super.getGraphicsConfiguration(); + if(null!=gc) { + device = gc.getDevice(); + } + } + + public void removeNotify() { + try { + dispose(); + } finally { + super.removeNotify(); + } + } + + private void dispose() { + if(null != awtConfig) { + AbstractGraphicsDevice adevice = awtConfig.getNativeGraphicsConfiguration().getScreen().getDevice(); + String adeviceMsg=null; + if(Window.DEBUG_IMPLEMENTATION) { + adeviceMsg = adevice.toString(); + } + boolean closed = adevice.close(); + if(Window.DEBUG_IMPLEMENTATION) { + System.err.println("AWTCanvas.dispose(): closed GraphicsDevice: "+adeviceMsg+", result: "+closed); + } + } + } + + /** + * Overridden to choose a GraphicsConfiguration on a parent container's + * GraphicsDevice because both devices + */ + 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. + * - This canvas has been added to a component hierarchy and has + * an ancestor with a non-null GC, but the native peer has not + * yet been created. This means we can still choose the GC on + * all platforms since the peer hasn't been created. + */ + final GraphicsConfiguration gc = super.getGraphicsConfiguration(); + /* + * 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. + */ + if (gc != null && chosen != null && !chosen.equals(gc)) { + /* + * 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( + awtConfig.getChosenCapabilities(), awtConfig.getRequestedCapabilities(), chooser, gc.getDevice()); + final GraphicsConfiguration compatible = (null!=config)?config.getGraphicsConfiguration():null; + if(Window.DEBUG_IMPLEMENTATION) { + 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); + System.err.println("!!! Created Config (n): Choosen GC "+compatible); + System.err.println("!!! Created Config (n): HAVE CF "+awtConfig); + System.err.println("!!! Created Config (n): Choosen CF "+config); + System.err.println("!!! Created Config (n): EQUALS CAPS "+config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities())); + } + + if (compatible != null) { + /* + * Save the new GC for equals test above, and to return to + * any outside callers of this method. + */ + chosen = compatible; + if( !config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities())) { + displayConfigChanged=true; + } + awtConfig = config; + } + } + + /* + * If a compatible GC was not found in the block above, this will + * 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) { + /* + * The GC is null, which means we have no native peer, and are not + * part of a (realized) component hierarchy. So we return the + * desired visual that was selected in the constructor (possibly + * null). + */ + return chosen; + } + + /* + * Otherwise we have not explicitly selected a GC in the constructor, so + * just return what Canvas would have. + */ + return gc; + } + + private static AWTGraphicsConfiguration chooseGraphicsConfiguration(CapabilitiesImmutable capsChosen, + CapabilitiesImmutable capsRequested, + CapabilitiesChooser chooser, + GraphicsDevice device) { + AbstractGraphicsScreen aScreen = AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT); + AWTGraphicsConfiguration config = (AWTGraphicsConfiguration) + GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class).chooseGraphicsConfiguration(capsChosen, + capsRequested, + chooser, aScreen); + if (config == null) { + throw new NativeWindowException("Error: Couldn't fetch AWTGraphicsConfiguration"); + } + + return config; + } + + // Disables the AWT's erasing of this Canvas's background on Windows + // in Java SE 6. This internal API is not available in previous + // releases, but the system property + // -Dsun.awt.noerasebackground=true can be specified to get similar + // results globally in previous releases. + private static boolean disableBackgroundEraseInitialized; + private static Method disableBackgroundEraseMethod; + private void disableBackgroundErase() { + if (!disableBackgroundEraseInitialized) { + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + Class clazz = getToolkit().getClass(); + while (clazz != null && disableBackgroundEraseMethod == null) { + try { + disableBackgroundEraseMethod = + clazz.getDeclaredMethod("disableBackgroundErase", + new Class[] { Canvas.class }); + disableBackgroundEraseMethod.setAccessible(true); + } catch (Exception e) { + clazz = clazz.getSuperclass(); + } + } + } catch (Exception e) { + } + return null; + } + }); + } catch (Exception e) { + } + disableBackgroundEraseInitialized = true; + if(Window.DEBUG_IMPLEMENTATION) { + System.err.println("AWTCanvas: TK disableBackgroundErase method found: "+ + (null!=disableBackgroundEraseMethod)); + } + } + if (disableBackgroundEraseMethod != null) { + Throwable t=null; + try { + disableBackgroundEraseMethod.invoke(getToolkit(), new Object[] { this }); + } catch (Exception e) { + // FIXME: workaround for 6504460 (incorrect backport of 6333613 in 5.0u10) + // throw new GLException(e); + t = e; + } + if(Window.DEBUG_IMPLEMENTATION) { + System.err.println("AWTCanvas: TK disableBackgroundErase error: "+t); + } + } + } +} diff --git a/src/newt/classes/jogamp/newt/awt/AWTDisplay.java b/src/newt/classes/jogamp/newt/awt/AWTDisplay.java new file mode 100644 index 000000000..4c864c111 --- /dev/null +++ b/src/newt/classes/jogamp/newt/awt/AWTDisplay.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.awt; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.awt.AWTGraphicsDevice; +import com.jogamp.newt.NewtFactory; +import jogamp.newt.DisplayImpl; + +public class AWTDisplay extends DisplayImpl { + public AWTDisplay() { + } + + protected void createNativeImpl() { + aDevice = (AWTGraphicsDevice) AWTGraphicsDevice.createDevice(null, AbstractGraphicsDevice.DEFAULT_UNIT); // default + } + + protected void setAWTGraphicsDevice(AWTGraphicsDevice d) { + aDevice = d; + } + + protected void closeNativeImpl() { } + + @Override + protected void createEDTUtil() { + if(NewtFactory.useEDT()) { + edtUtil = AWTEDTUtil.getSingleton(); + if(DEBUG) { + System.err.println("AWTDisplay.createNative("+getFQName()+") Create EDTUtil: "+edtUtil.getClass().getName()); + } + } + } + + protected void dispatchMessagesNative() { /* nop */ } +} + diff --git a/src/newt/classes/jogamp/newt/awt/AWTEDTUtil.java b/src/newt/classes/jogamp/newt/awt/AWTEDTUtil.java new file mode 100644 index 000000000..7b638af31 --- /dev/null +++ b/src/newt/classes/jogamp/newt/awt/AWTEDTUtil.java @@ -0,0 +1,108 @@ +/** + * 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.awt; + +import java.awt.EventQueue; +import javax.media.nativewindow.NativeWindowException; +import com.jogamp.newt.util.EDTUtil; +import jogamp.newt.Debug; + +public class AWTEDTUtil implements EDTUtil { + public static final boolean DEBUG = Debug.debug("EDT"); + + private static AWTEDTUtil singletonMainThread = new AWTEDTUtil(); // one singleton MainThread + + public static AWTEDTUtil getSingleton() { + return singletonMainThread; + } + + AWTEDTUtil() { + // package private access .. + } + + final public void reset() { + // nop + } + + final public void start() { + // nop + } + + final public boolean isCurrentThreadEDT() { + return EventQueue.isDispatchThread(); + } + + final public boolean isRunning() { + return true; // AWT is always running + } + + final public void invokeStop(Runnable r) { + invokeImpl(true, r, true); + } + + final public void invoke(boolean wait, Runnable r) { + invokeImpl(wait, r, false); + } + + /** + * Public access to provide simple dispatching from other EDTUtil implementations + * @param wait true if invokeLater + * @param r the Runnable action + * @param stop true if EDT shall stop (ignored with AWT) + */ + final public void invokeImpl(boolean wait, Runnable r, boolean stop) { + if(r == null) { + return; + } + + // handover to AWT MainThread .. + try { + if ( isCurrentThreadEDT() ) { + r.run(); + return; + } + if(wait) { + EventQueue.invokeAndWait(r); + } else { + EventQueue.invokeLater(r); + } + } catch (Exception e) { + throw new NativeWindowException(e); + } + } + + final public void waitUntilIdle() { + } + + final public void waitUntilStopped() { + } + +} + + diff --git a/src/newt/classes/jogamp/newt/awt/AWTScreen.java b/src/newt/classes/jogamp/newt/awt/AWTScreen.java new file mode 100644 index 000000000..d05933321 --- /dev/null +++ b/src/newt/classes/jogamp/newt/awt/AWTScreen.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.awt; + +import java.awt.DisplayMode; + +import jogamp.newt.ScreenImpl; +import javax.media.nativewindow.awt.AWTGraphicsDevice; +import javax.media.nativewindow.awt.AWTGraphicsScreen; + +public class AWTScreen extends ScreenImpl { + public AWTScreen() { + } + + protected void createNativeImpl() { + aScreen = new AWTGraphicsScreen((AWTGraphicsDevice)display.getGraphicsDevice()); + + final DisplayMode mode = ((AWTGraphicsDevice)getDisplay().getGraphicsDevice()).getGraphicsDevice().getDisplayMode(); + if(null != mode) { + setScreenSize(mode.getWidth(), mode.getHeight()); + } + } + + protected void setAWTGraphicsScreen(AWTGraphicsScreen s) { + aScreen = s; + } + + // done by AWTWindow .. + protected void setScreenSize(int w, int h) { + super.setScreenSize(w, h); + } + + protected void closeNativeImpl() { } +} diff --git a/src/newt/classes/jogamp/newt/awt/AWTWindow.java b/src/newt/classes/jogamp/newt/awt/AWTWindow.java new file mode 100644 index 000000000..b07a9e313 --- /dev/null +++ b/src/newt/classes/jogamp/newt/awt/AWTWindow.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.awt; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.DisplayMode; +import java.awt.Frame; +import java.awt.Insets; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.awt.AWTGraphicsDevice; +import javax.media.nativewindow.awt.AWTGraphicsScreen; +import javax.media.nativewindow.util.Point; +import jogamp.newt.WindowImpl; +import com.jogamp.newt.event.awt.AWTKeyAdapter; +import com.jogamp.newt.event.awt.AWTMouseAdapter; +import com.jogamp.newt.event.awt.AWTWindowAdapter; + +/** An implementation of the Newt Window class built using the + AWT. This is provided for convenience of porting to platforms + supporting Java SE. */ + +public class AWTWindow extends WindowImpl { + + public AWTWindow() { + this(null); + } + + public static Class[] getCustomConstructorArgumentTypes() { + return new Class[] { Container.class } ; + } + + public AWTWindow(Container container) { + super(); + title = "AWT NewtWindow"; + this.container = container; + if(container instanceof Frame) { + frame = (Frame) container; + } + } + + private boolean owningFrame; + private Container container = null; + private Frame frame = null; // same instance as container, just for impl. convenience + private AWTCanvas canvas; + + protected void requestFocusImpl(boolean reparented) { + container.requestFocus(); + } + + @Override + protected void setTitleImpl(final String title) { + if (frame != null) { + frame.setTitle(title); + } + } + + protected void createNativeImpl() { + if(0!=getParentWindowHandle()) { + throw new RuntimeException("Window parenting not supported in AWT, use AWTWindow(Frame) cstr for wrapping instead"); + } + + if(null==container) { + frame = new Frame(); + container = frame; + owningFrame=true; + } else { + owningFrame=false; + width = container.getWidth(); + height = container.getHeight(); + x = container.getX(); + y = container.getY(); + } + if(null!=frame) { + frame.setTitle(getTitle()); + } + container.setLayout(new BorderLayout()); + canvas = new AWTCanvas(capsRequested, AWTWindow.this.capabilitiesChooser); + + addWindowListener(new LocalWindowListener()); + + new AWTMouseAdapter(this).addTo(canvas); // fwd all AWT Mouse events to here + new AWTKeyAdapter(this).addTo(canvas); // fwd all AWT Key events to here + + // canvas.addComponentListener(listener); + container.add(canvas, BorderLayout.CENTER); + container.setSize(width, height); + container.setLocation(x, y); + new AWTWindowAdapter(this).addTo(container); // fwd all AWT Window events to here + + if(null!=frame) { + frame.setUndecorated(undecorated||fullscreen); + } + + setWindowHandle(1); // just a marker .. + } + + protected void closeNativeImpl() { + setWindowHandle(0); // just a marker .. + if(null!=container) { + container.setVisible(false); + container.remove(canvas); + container.setEnabled(false); + canvas.setEnabled(false); + } + if(owningFrame && null!=frame) { + frame.dispose(); + owningFrame=false; + frame = null; + } + } + + @Override + public boolean hasDeviceChanged() { + boolean res = canvas.hasDeviceChanged(); + if(res) { + config = canvas.getAWTGraphicsConfiguration(); + if (config == null) { + throw new NativeWindowException("Error Device change null GraphicsConfiguration: "+this); + } + updateDeviceData(); + } + return res; + } + + protected void setVisibleImpl(final boolean visible, int x, int y, int width, int height) { + container.setVisible(visible); + + reconfigureWindowImpl(x, y, width, height, false, 0, 0); + config = canvas.getAWTGraphicsConfiguration(); + + if (config == null) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + + updateDeviceData(); + visibleChanged(visible); + } + + private void updateDeviceData() { + // propagate new info .. + ((AWTScreen)getScreen()).setAWTGraphicsScreen((AWTGraphicsScreen)config.getScreen()); + ((AWTDisplay)getScreen().getDisplay()).setAWTGraphicsDevice((AWTGraphicsDevice)config.getScreen().getDevice()); + + final DisplayMode mode = ((AWTGraphicsDevice)config.getScreen().getDevice()).getGraphicsDevice().getDisplayMode(); + if(null != mode) { + ((AWTScreen)getScreen()).setScreenSize(mode.getWidth(), mode.getHeight()); + } + + } + + @Override + public javax.media.nativewindow.util.Insets getInsets() { + final int insets[] = new int[] { 0, 0, 0, 0 }; + Insets contInsets = container.getInsets(); + insets[0] = contInsets.top; + insets[1] = contInsets.left; + insets[2] = contInsets.bottom; + insets[3] = contInsets.right; + return new javax.media.nativewindow.util.Insets(insets[0],insets[1],insets[2],insets[3]); + } + + protected boolean reconfigureWindowImpl(final int x, final int y, final int width, final int height, final boolean parentChange, final int fullScreenChange, final int decorationChange) { + if(decorationChange!=0 && null!=frame) { + if(!container.isDisplayable()) { + frame.setUndecorated(isUndecorated()); + } else { + if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { + System.err.println("AWTWindow can't undecorate already created frame"); + } + } + } + int _x=(x>=0)?x:AWTWindow.this.x; + int _y=(x>=0)?y:AWTWindow.this.y; + int _w=(width>0)?width:AWTWindow.this.width; + int _h=(height>0)?height:AWTWindow.this.height; + + container.setLocation(_x, _y); + Insets insets = container.getInsets(); + container.setSize(_w + insets.left + insets.right, + _h + insets.top + insets.bottom); + return true; + } + + protected Point getLocationOnScreenImpl(int x, int y) { + java.awt.Point ap = canvas.getLocationOnScreen(); + ap.translate(x, y); + return new Point((int)(ap.getX()+0.5),(int)(ap.getY()+0.5)); + } + + @Override + public Object getWrappedWindow() { + return canvas; + } + + class LocalWindowListener extends com.jogamp.newt.event.WindowAdapter { + @Override + public void windowMoved(com.jogamp.newt.event.WindowEvent e) { + if(null!=container) { + x = container.getX(); + y = container.getY(); + } + } + @Override + public void windowResized(com.jogamp.newt.event.WindowEvent e) { + if(null!=canvas) { + width = canvas.getWidth(); + height = canvas.getHeight(); + } + } + } +} diff --git a/src/newt/classes/jogamp/newt/awt/opengl/VersionApplet.java b/src/newt/classes/jogamp/newt/awt/opengl/VersionApplet.java new file mode 100644 index 000000000..18524d0ba --- /dev/null +++ b/src/newt/classes/jogamp/newt/awt/opengl/VersionApplet.java @@ -0,0 +1,174 @@ +package jogamp.newt.awt.opengl; + +import java.applet.Applet; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.TextArea; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import java.util.List; + +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +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.os.Platform; +import com.jogamp.common.util.VersionUtil; +import com.jogamp.nativewindow.NativeWindowVersion; +import com.jogamp.newt.NewtVersion; +import com.jogamp.opengl.JoglVersion; + +public class VersionApplet extends Applet { + static { + GLProfile.initSingleton(false); + } + TextArea tareaVersion; + TextArea tareaCaps; + GLCanvas canvas; + + public static void main(String[] args) { + Frame frame = new Frame("JOGL Version Applet"); + frame.setSize(800, 600); + frame.setLayout(new BorderLayout()); + + VersionApplet va = new VersionApplet(); + frame.addWindowListener(new ClosingWindowAdapter(frame, va)); + + va.init(); + frame.add(va, BorderLayout.CENTER); + frame.validate(); + + frame.setVisible(true); + va.start(); + } + + static class ClosingWindowAdapter extends WindowAdapter { + Frame f; + VersionApplet va; + public ClosingWindowAdapter(Frame f, VersionApplet va) { + this.f = f; + this.va = va; + } + public void windowClosing(WindowEvent ev) { + f.setVisible(false); + va.stop(); + va.destroy(); + f.remove(va); + f.dispose(); + System.exit(0); + } + } + + private synchronized void my_init() { + if(null != canvas) { return; } + + GLProfile glp = GLProfile.getDefault(); + GLCapabilities glcaps = new GLCapabilities(glp); + + setLayout(new BorderLayout()); + String s; + + tareaVersion = new TextArea(120, 60); + s = VersionUtil.getPlatformInfo().toString(); + System.err.println(s); + tareaVersion.append(s); + + s = GlueGenVersion.getInstance().toString(); + System.err.println(s); + tareaVersion.append(s); + + s = NativeWindowVersion.getInstance().toString(); + System.err.println(s); + tareaVersion.append(NativeWindowVersion.getInstance().toString()); + + s = JoglVersion.getInstance().toString(); + System.err.println(s); + tareaVersion.append(s); + + s = NewtVersion.getInstance().toString(); + System.err.println(s); + tareaVersion.append(s); + + tareaCaps = new TextArea(120, 20); + GLDrawableFactory factory = GLDrawableFactory.getFactory(glp); + List/*<GLCapabilitiesImmutable>*/ availCaps = factory.getAvailableCapabilities(null); + for(int i=0; i<availCaps.size(); i++) { + s = ((GLCapabilitiesImmutable) availCaps.get(i)).toString(); + System.err.println(s); + tareaCaps.append(s); + tareaCaps.append(Platform.getNewline()); + } + + Container grid = new Container(); + grid.setLayout(new GridLayout(2, 1)); + grid.add(tareaVersion); + grid.add(tareaCaps); + add(grid, BorderLayout.CENTER); + + canvas = new GLCanvas(glcaps); + canvas.addGLEventListener(new GLInfo()); + canvas.setSize(10, 10); + add(canvas, BorderLayout.SOUTH); + validate(); + } + + private synchronized void my_release() { + if(null!=canvas) { + remove(canvas); + canvas.destroy(); + canvas = null; + remove(tareaVersion); + tareaVersion=null; + } + } + + public void init() { + System.err.println("VersionApplet: init() - begin"); + my_init(); + System.err.println("VersionApplet: init() - end"); + } + + public void start() { + System.err.println("VersionApplet: start() - begin"); + System.err.println("VersionApplet: start() - end"); + } + + public void stop() { + System.err.println("VersionApplet: stop() - begin"); + System.err.println("VersionApplet: stop() - end"); + } + + public void destroy() { + System.err.println("VersionApplet: destroy() - start"); + my_release(); + System.err.println("VersionApplet: destroy() - end"); + } + + class GLInfo implements GLEventListener { + public void init(GLAutoDrawable drawable) { + GL gl = drawable.getGL(); + String s = JoglVersion.getGLInfo(gl, null).toString(); + System.err.println(s); + tareaVersion.append(s); + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + } + + public void display(GLAutoDrawable drawable) { + } + + public void dispose(GLAutoDrawable drawable) { + } + } + +} diff --git a/src/newt/classes/jogamp/newt/event/NEWTEventTask.java b/src/newt/classes/jogamp/newt/event/NEWTEventTask.java new file mode 100644 index 000000000..fae6560b4 --- /dev/null +++ b/src/newt/classes/jogamp/newt/event/NEWTEventTask.java @@ -0,0 +1,56 @@ +/** + * 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.event; + +import com.jogamp.newt.event.NEWTEvent; + +/** + * Helper class to provide a NEWTEvent queue implementation with a NEWTEvent wrapper + * which notifies after sending the event for the <code>invokeAndWait()</code> semantics. + */ +public class NEWTEventTask { + NEWTEvent event; + Object notifyObject; + + public NEWTEventTask(NEWTEvent event, Object notifyObject) { + this.event = event ; + this.notifyObject = notifyObject ; + } + + public NEWTEvent get() { return event; } + + public void notifyIssuer() { + if(null != notifyObject) { + synchronized (notifyObject) { + notifyObject.notifyAll(); + } + } + } +} + diff --git a/src/newt/classes/jogamp/newt/intel/gdl/Display.java b/src/newt/classes/jogamp/newt/intel/gdl/Display.java new file mode 100644 index 000000000..b1afdb55e --- /dev/null +++ b/src/newt/classes/jogamp/newt/intel/gdl/Display.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.intel.gdl; + +import jogamp.newt.*; +import javax.media.nativewindow.*; + +public class Display extends jogamp.newt.DisplayImpl { + static int initCounter = 0; + + static { + NEWTJNILibLoader.loadNEWT(); + + if (!Screen.initIDs()) { + throw new NativeWindowException("Failed to initialize GDL Screen jmethodIDs"); + } + if (!Window.initIDs()) { + throw new NativeWindowException("Failed to initialize GDL Window jmethodIDs"); + } + } + + public static void initSingleton() { + // just exist to ensure static init has been run + } + + + public Display() { + } + + protected void createNativeImpl() { + synchronized(Display.class) { + if(0==initCounter) { + displayHandle = CreateDisplay(); + if(0==displayHandle) { + throw new NativeWindowException("Couldn't initialize GDL Display"); + } + } + initCounter++; + } + aDevice = new DefaultGraphicsDevice(NativeWindowFactory.TYPE_DEFAULT, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT, displayHandle); + } + + protected void closeNativeImpl() { + if(0==displayHandle) { + throw new NativeWindowException("displayHandle null; initCnt "+initCounter); + } + synchronized(Display.class) { + if(initCounter>0) { + initCounter--; + if(0==initCounter) { + DestroyDisplay(displayHandle); + } + } + } + } + + protected void dispatchMessagesNative() { + if(0!=displayHandle) { + DispatchMessages(displayHandle, focusedWindow); + } + } + + protected void setFocus(Window focus) { + focusedWindow = focus; + } + + private long displayHandle = 0; + private Window focusedWindow = null; + private native long CreateDisplay(); + private native void DestroyDisplay(long displayHandle); + private native void DispatchMessages(long displayHandle, Window focusedWindow); +} + diff --git a/src/newt/classes/jogamp/newt/intel/gdl/Screen.java b/src/newt/classes/jogamp/newt/intel/gdl/Screen.java new file mode 100644 index 000000000..b351fe6a9 --- /dev/null +++ b/src/newt/classes/jogamp/newt/intel/gdl/Screen.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.intel.gdl; + +import jogamp.newt.*; +import javax.media.nativewindow.*; + +public class Screen extends jogamp.newt.ScreenImpl { + + static { + Display.initSingleton(); + } + + public Screen() { + } + + protected void createNativeImpl() { + AbstractGraphicsDevice adevice = getDisplay().getGraphicsDevice(); + GetScreenInfo(adevice.getHandle(), screen_idx); + aScreen = new DefaultGraphicsScreen(adevice, screen_idx); + } + + protected void closeNativeImpl() { } + + //---------------------------------------------------------------------- + // Internals only + // + + protected static native boolean initIDs(); + private native void GetScreenInfo(long displayHandle, int screen_idx); + + // called by GetScreenInfo() .. + private void screenCreated(int width, int height) { + setScreenSize(width, height); + } +} + diff --git a/src/newt/classes/jogamp/newt/intel/gdl/Window.java b/src/newt/classes/jogamp/newt/intel/gdl/Window.java new file mode 100644 index 000000000..d6003beae --- /dev/null +++ b/src/newt/classes/jogamp/newt/intel/gdl/Window.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.intel.gdl; + +import javax.media.nativewindow.*; +import javax.media.nativewindow.util.Point; + +public class Window extends jogamp.newt.WindowImpl { + static { + Display.initSingleton(); + } + + public Window() { + } + + static long nextWindowHandle = 1; + + protected void createNativeImpl() { + if(0!=getParentWindowHandle()) { + throw new NativeWindowException("GDL Window does not support window parenting"); + } + AbstractGraphicsScreen aScreen = getScreen().getGraphicsScreen(); + AbstractGraphicsDevice aDevice = getScreen().getDisplay().getGraphicsDevice(); + + config = GraphicsConfigurationFactory.getFactory(aDevice).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, aScreen); + if (config == null) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + + synchronized(Window.class) { + setWindowHandle(nextWindowHandle++); // just a marker + + surfaceHandle = CreateSurface(aDevice.getHandle(), getScreen().getWidth(), getScreen().getHeight(), x, y, width, height); + if (surfaceHandle == 0) { + throw new NativeWindowException("Error creating window"); + } + } + } + + protected void closeNativeImpl() { + if(0!=surfaceHandle) { + synchronized(Window.class) { + CloseSurface(getDisplayHandle(), surfaceHandle); + } + surfaceHandle = 0; + ((Display)getScreen().getDisplay()).setFocus(null); + } + } + + protected void setVisibleImpl(boolean visible, int x, int y, int width, int height) { + reconfigureWindowImpl(x, y, width, height, false, 0, 0); + if(visible) { + ((Display)getScreen().getDisplay()).setFocus(this); + } + this.visibleChanged(visible); + } + + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, boolean parentChange, int fullScreenChange, int decorationChange) { + Screen screen = (Screen) getScreen(); + + int _x=(x>=0)?x:this.x; + int _y=(x>=0)?y:this.y; + int _w=(width>0)?width:this.width; + int _h=(height>0)?height:this.height; + + if(_w>screen.getWidth()) { + _w=screen.getWidth(); + } + if(_h>screen.getHeight()) { + _h=screen.getHeight(); + } + if((_x+_w)>screen.getWidth()) { + _x=screen.getWidth()-_w; + } + if((_y+_h)>screen.getHeight()) { + _y=screen.getHeight()-_h; + } + + if(0!=surfaceHandle) { + SetBounds0(surfaceHandle, getScreen().getWidth(), getScreen().getHeight(), _x, _y, _w, _h); + } + + return true; + } + + protected void requestFocusImpl(boolean reparented) { + ((Display)getScreen().getDisplay()).setFocus(this); + } + + @Override + public final long getSurfaceHandle() { + return surfaceHandle; + } + + protected Point getLocationOnScreenImpl(int x, int y) { + return new Point(x,y); + } + + //---------------------------------------------------------------------- + // Internals only + // + + protected static native boolean initIDs(); + private native long CreateSurface(long displayHandle, int scrn_width, int scrn_height, int x, int y, int width, int height); + 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) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + private long surfaceHandle; +} diff --git a/src/newt/classes/jogamp/newt/macosx/MacDisplay.java b/src/newt/classes/jogamp/newt/macosx/MacDisplay.java new file mode 100644 index 000000000..49f2ff5d8 --- /dev/null +++ b/src/newt/classes/jogamp/newt/macosx/MacDisplay.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.macosx; + +import javax.media.nativewindow.*; +import javax.media.nativewindow.macosx.*; +import com.jogamp.newt.*; +import jogamp.newt.*; +import com.jogamp.newt.util.MainThread; + +public class MacDisplay extends DisplayImpl { + static { + NEWTJNILibLoader.loadNEWT(); + + if(!initNSApplication0()) { + throw new NativeWindowException("Failed to initialize native Application hook"); + } + if(!MacWindow.initIDs0()) { + throw new NativeWindowException("Failed to initialize jmethodIDs"); + } + if(DEBUG) { + System.err.println("MacDisplay.init App and IDs OK "+Thread.currentThread().getName()); + } + } + + public static void initSingleton() { + // just exist to ensure static init has been run + } + + public MacDisplay() { + } + + protected void dispatchMessagesNative() { + dispatchMessages0(); + } + + protected void createNativeImpl() { + aDevice = new MacOSXGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); + } + + protected void closeNativeImpl() { } + + @Override + protected void createEDTUtil() { + if(NewtFactory.useEDT()) { + final Display f_dpy = this; + MainThread.addPumpMessage(this, + new Runnable() { + public void run() { + if(null!=f_dpy.getGraphicsDevice()) { + f_dpy.dispatchMessages(); + } } } ); + edtUtil = MainThread.getSingleton(); + edtUtil.start(); + } + } + + protected void releaseEDTUtil() { + if(null!=edtUtil) { + MainThread.removePumpMessage(this); + edtUtil.waitUntilStopped(); + edtUtil=null; + } + } + + private static native boolean initNSApplication0(); + protected native void dispatchMessages0(); +} + diff --git a/src/newt/classes/jogamp/newt/macosx/MacScreen.java b/src/newt/classes/jogamp/newt/macosx/MacScreen.java new file mode 100644 index 000000000..30028602c --- /dev/null +++ b/src/newt/classes/jogamp/newt/macosx/MacScreen.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.macosx; + +import com.jogamp.newt.*; +import jogamp.newt.ScreenImpl; +import javax.media.nativewindow.*; + +public class MacScreen extends ScreenImpl { + static { + MacDisplay.initSingleton(); + } + + public MacScreen() { + } + + protected void createNativeImpl() { + aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); + setScreenSize(getWidthImpl0(screen_idx), getHeightImpl0(screen_idx)); + } + + protected void closeNativeImpl() { } + + private static native int getWidthImpl0(int scrn_idx); + private static native int getHeightImpl0(int scrn_idx); +} diff --git a/src/newt/classes/jogamp/newt/macosx/MacWindow.java b/src/newt/classes/jogamp/newt/macosx/MacWindow.java new file mode 100644 index 000000000..a27f04797 --- /dev/null +++ b/src/newt/classes/jogamp/newt/macosx/MacWindow.java @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.macosx; + +import javax.media.nativewindow.*; +import com.jogamp.common.util.locks.RecursiveLock; + +import com.jogamp.newt.event.*; +import jogamp.newt.*; +import javax.media.nativewindow.util.Insets; +import javax.media.nativewindow.util.Point; + +public class MacWindow extends WindowImpl { + + // Window styles + private static final int NSBorderlessWindowMask = 0; + private static final int NSTitledWindowMask = 1 << 0; + private static final int NSClosableWindowMask = 1 << 1; + private static final int NSMiniaturizableWindowMask = 1 << 2; + private static final int NSResizableWindowMask = 1 << 3; + + // Window backing store types + private static final int NSBackingStoreRetained = 0; + private static final int NSBackingStoreNonretained = 1; + private static final int NSBackingStoreBuffered = 2; + + // Key constants handled differently on Mac OS X than other platforms + private static final int NSUpArrowFunctionKey = 0xF700; + private static final int NSDownArrowFunctionKey = 0xF701; + private static final int NSLeftArrowFunctionKey = 0xF702; + private static final int NSRightArrowFunctionKey = 0xF703; + private static final int NSF1FunctionKey = 0xF704; + private static final int NSF2FunctionKey = 0xF705; + private static final int NSF3FunctionKey = 0xF706; + private static final int NSF4FunctionKey = 0xF707; + private static final int NSF5FunctionKey = 0xF708; + private static final int NSF6FunctionKey = 0xF709; + private static final int NSF7FunctionKey = 0xF70A; + private static final int NSF8FunctionKey = 0xF70B; + private static final int NSF9FunctionKey = 0xF70C; + private static final int NSF10FunctionKey = 0xF70D; + private static final int NSF11FunctionKey = 0xF70E; + private static final int NSF12FunctionKey = 0xF70F; + private static final int NSF13FunctionKey = 0xF710; + private static final int NSF14FunctionKey = 0xF711; + private static final int NSF15FunctionKey = 0xF712; + private static final int NSF16FunctionKey = 0xF713; + private static final int NSF17FunctionKey = 0xF714; + private static final int NSF18FunctionKey = 0xF715; + private static final int NSF19FunctionKey = 0xF716; + private static final int NSF20FunctionKey = 0xF717; + private static final int NSF21FunctionKey = 0xF718; + private static final int NSF22FunctionKey = 0xF719; + private static final int NSF23FunctionKey = 0xF71A; + private static final int NSF24FunctionKey = 0xF71B; + private static final int NSF25FunctionKey = 0xF71C; + private static final int NSF26FunctionKey = 0xF71D; + private static final int NSF27FunctionKey = 0xF71E; + private static final int NSF28FunctionKey = 0xF71F; + private static final int NSF29FunctionKey = 0xF720; + private static final int NSF30FunctionKey = 0xF721; + private static final int NSF31FunctionKey = 0xF722; + private static final int NSF32FunctionKey = 0xF723; + private static final int NSF33FunctionKey = 0xF724; + private static final int NSF34FunctionKey = 0xF725; + private static final int NSF35FunctionKey = 0xF726; + private static final int NSInsertFunctionKey = 0xF727; + private static final int NSDeleteFunctionKey = 0xF728; + private static final int NSHomeFunctionKey = 0xF729; + private static final int NSBeginFunctionKey = 0xF72A; + private static final int NSEndFunctionKey = 0xF72B; + private static final int NSPageUpFunctionKey = 0xF72C; + private static final int NSPageDownFunctionKey = 0xF72D; + private static final int NSPrintScreenFunctionKey = 0xF72E; + private static final int NSScrollLockFunctionKey = 0xF72F; + private static final int NSPauseFunctionKey = 0xF730; + private static final int NSSysReqFunctionKey = 0xF731; + private static final int NSBreakFunctionKey = 0xF732; + private static final int NSResetFunctionKey = 0xF733; + private static final int NSStopFunctionKey = 0xF734; + private static final int NSMenuFunctionKey = 0xF735; + private static final int NSUserFunctionKey = 0xF736; + private static final int NSSystemFunctionKey = 0xF737; + private static final int NSPrintFunctionKey = 0xF738; + private static final int NSClearLineFunctionKey = 0xF739; + private static final int NSClearDisplayFunctionKey = 0xF73A; + private static final int NSInsertLineFunctionKey = 0xF73B; + private static final int NSDeleteLineFunctionKey = 0xF73C; + private static final int NSInsertCharFunctionKey = 0xF73D; + private static final int NSDeleteCharFunctionKey = 0xF73E; + private static final int NSPrevFunctionKey = 0xF73F; + private static final int NSNextFunctionKey = 0xF740; + private static final int NSSelectFunctionKey = 0xF741; + private static final int NSExecuteFunctionKey = 0xF742; + private static final int NSUndoFunctionKey = 0xF743; + private static final int NSRedoFunctionKey = 0xF744; + private static final int NSFindFunctionKey = 0xF745; + private static final int NSHelpFunctionKey = 0xF746; + private static final int NSModeSwitchFunctionKey = 0xF747; + + private volatile long surfaceHandle; + + // non fullscreen dimensions .. + private final Insets insets = new Insets(0,0,0,0); + + static { + MacDisplay.initSingleton(); + } + + public MacWindow() { + } + + protected void createNativeImpl() { + config = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice()).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen()); + if (config == null) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + } + + protected void closeNativeImpl() { + nsViewLock.lock(); + try { + if(DEBUG_IMPLEMENTATION) { System.err.println("MacWindow.CloseAction "+Thread.currentThread().getName()); } + if (getWindowHandle() != 0) { + close0(getWindowHandle()); + } + } catch (Throwable t) { + if(DEBUG_IMPLEMENTATION) { + Exception e = new Exception("Warning: closeNative failed - "+Thread.currentThread().getName(), t); + e.printStackTrace(); + } + } finally { + setWindowHandle(0); + nsViewLock.unlock(); + } + } + + @Override + public final long getSurfaceHandle() { + return surfaceHandle; + } + + @Override + public Insets getInsets() { + // in order to properly calculate insets we need the window to be + // created + nsViewLock.lock(); + try { + createWindow(false, getX(), getY(), getWidth(), getHeight(), isFullscreen()); + return (Insets) insets.clone(); + } finally { + nsViewLock.unlock(); + } + } + + private RecursiveLock nsViewLock = new RecursiveLock(); + + @Override + protected int lockSurfaceImpl() { + nsViewLock.lock(); + return LOCK_SUCCESS; + } + + @Override + protected void unlockSurfaceImpl() { + nsViewLock.unlock(); + } + + protected void setVisibleImpl(boolean visible, int x, int y, int width, int height) { + nsViewLock.lock(); + try { + if (visible) { + createWindow(false, x, y, width, height, isFullscreen()); + if (getWindowHandle() != 0) { + makeKeyAndOrderFront0(getWindowHandle()); + } + } else { + if (getWindowHandle() != 0) { + orderOut0(getWindowHandle()); + } + } + visibleChanged(visible); + } finally { + nsViewLock.unlock(); + } + } + + @Override + protected void setTitleImpl(final String title) { + // FIXME: move nsViewLock up to window lock + nsViewLock.lock(); + try { + setTitle0(getWindowHandle(), title); + } finally { + nsViewLock.unlock(); + } + } + + protected void requestFocusImpl(boolean reparented) { + // FIXME: move nsViewLock up to window lock + nsViewLock.lock(); + try { + makeKey0(getWindowHandle()); + } finally { + nsViewLock.unlock(); + } + } + + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, boolean parentChange, int fullScreenChange, int decorationChange) { + nsViewLock.lock(); + try { + if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { + System.err.println("MacWindow reconfig: parentChange "+parentChange+", fullScreenChange "+fullScreenChange+", decorationChange "+decorationChange+" "+x+"/"+y+" "+width+"x"+height); + } + int _x=(x>=0)?x:this.x; + int _y=(x>=0)?y:this.y; + int _w=(width>0)?width:this.width; + int _h=(height>0)?height:this.height; + + if(decorationChange!=0 || parentChange || fullScreenChange!=0) { + createWindow(true, _x, _y, _w, _h, fullScreenChange>0); + if (getWindowHandle() != 0) { + makeKeyAndOrderFront0(getWindowHandle()); + } + } else { + if(x>=0 || y>=0) { + setFrameTopLeftPoint0(getParentWindowHandle(), getWindowHandle(), _x, _y); + } + if(width>0 || height>0) { + setContentSize0(getWindowHandle(), _w, _h); + } + } + } finally { + nsViewLock.unlock(); + } + return true; + } + + protected Point getLocationOnScreenImpl(int x, int y) { + return null; + } + + private void insetsChanged(int left, int top, int right, int bottom) { + if (DEBUG_IMPLEMENTATION) { + System.err.println(Thread.currentThread().getName()+ + " Insets changed to " + left + ", " + top + ", " + right + ", " + bottom); + } + if (left != -1 && top != -1 && right != -1 && bottom != -1) { + insets.left = left; + insets.top = top; + insets.right = right; + insets.bottom = bottom; + } + } + + private char convertKeyChar(char keyChar) { + if (keyChar == '\r') { + // Turn these into \n + return '\n'; + } + + 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; + } + } + + // NSEvent's charactersIgnoringModifiers doesn't ignore the shift key + if (keyChar >= 'a' && keyChar <= 'z') { + return Character.toUpperCase(keyChar); + } + + return keyChar; + } + + @Override + public void enqueueKeyEvent(boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { + int key = convertKeyChar(keyChar); + if(DEBUG_IMPLEMENTATION) System.err.println("MacWindow.enqueueKeyEvent "+Thread.currentThread().getName()); + // 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 + super.enqueueKeyEvent(wait, eventType, modifiers, key, keyChar); + } + + private void createWindow(final boolean recreate, final int x, final int y, final int width, final int height, final boolean fullscreen) { + + if(0!=getWindowHandle() && !recreate) { + return; + } + + try { + //runOnEDTIfAvail(true, new Runnable() { + // public void run() { + 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()); + setWindowHandle(0); + } else { + surfaceHandle = 0; + } + setWindowHandle(createWindow0(getParentWindowHandle(), + x, y, width, height, fullscreen, + (isUndecorated() ? + NSBorderlessWindowMask : + NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask), + NSBackingStoreBuffered, + getScreen().getIndex(), surfaceHandle)); + if (getWindowHandle() == 0) { + throw new NativeWindowException("Could create native window "+Thread.currentThread().getName()+" "+this); + } + surfaceHandle = contentView0(getWindowHandle()); + setTitle0(getWindowHandle(), getTitle()); + // don't make the window visible on window creation + // makeKeyAndOrderFront0(windowHandle); + // } } ); + } catch (Exception ie) { + ie.printStackTrace(); + } + + enqueueWindowEvent(false, WindowEvent.EVENT_WINDOW_MOVED); + enqueueWindowEvent(false, WindowEvent.EVENT_WINDOW_RESIZED); + enqueueWindowEvent(false, WindowEvent.EVENT_WINDOW_GAINED_FOCUS); + } + + protected static native boolean initIDs0(); + private native long createWindow0(long parentWindowHandle, int x, int y, int w, int h, + boolean fullscreen, int windowStyle, + int backingStoreType, + int screen_idx, long view); + private native void makeKeyAndOrderFront0(long window); + private native void makeKey0(long window); + private native void orderOut0(long window); + private native void close0(long window); + private native void setTitle0(long window, String title); + private native long contentView0(long window); + private native long changeContentView0(long parentWindowHandle, 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); +} diff --git a/src/newt/classes/jogamp/newt/opengl/broadcom/egl/Display.java b/src/newt/classes/jogamp/newt/opengl/broadcom/egl/Display.java new file mode 100644 index 000000000..494908a81 --- /dev/null +++ b/src/newt/classes/jogamp/newt/opengl/broadcom/egl/Display.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.opengl.broadcom.egl; + +import jogamp.newt.*; +import jogamp.opengl.egl.*; +import javax.media.nativewindow.*; +import javax.media.nativewindow.egl.*; + +public class Display extends jogamp.newt.DisplayImpl { + + static { + NEWTJNILibLoader.loadNEWT(); + + if (!Window.initIDs()) { + throw new NativeWindowException("Failed to initialize BCEGL Window jmethodIDs"); + } + } + + public static void initSingleton() { + // just exist to ensure static init has been run + } + + + public Display() { + } + + protected void createNativeImpl() { + long handle = CreateDisplay(Screen.fixedWidth, Screen.fixedHeight); + if (handle == EGL.EGL_NO_DISPLAY) { + throw new NativeWindowException("BC EGL CreateDisplay failed"); + } + aDevice = new EGLGraphicsDevice(handle, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); + } + + protected void closeNativeImpl() { + if (aDevice.getHandle() != EGL.EGL_NO_DISPLAY) { + DestroyDisplay(aDevice.getHandle()); + } + } + + protected void dispatchMessagesNative() { + // n/a .. DispatchMessages(); + } + + private native long CreateDisplay(int width, int height); + private native void DestroyDisplay(long dpy); + private native void DispatchMessages(); +} + diff --git a/src/newt/classes/jogamp/newt/opengl/broadcom/egl/Screen.java b/src/newt/classes/jogamp/newt/opengl/broadcom/egl/Screen.java new file mode 100644 index 000000000..2224f7860 --- /dev/null +++ b/src/newt/classes/jogamp/newt/opengl/broadcom/egl/Screen.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.opengl.broadcom.egl; + +import javax.media.nativewindow.*; + +public class Screen extends jogamp.newt.ScreenImpl { + + static { + Display.initSingleton(); + } + + + public Screen() { + } + + protected void createNativeImpl() { + aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); + setScreenSize(fixedWidth, fixedHeight); + } + + protected void closeNativeImpl() { } + + //---------------------------------------------------------------------- + // Internals only + // + + static final int fixedWidth = 1920; + static final int fixedHeight = 1080; +} + diff --git a/src/newt/classes/jogamp/newt/opengl/broadcom/egl/Window.java b/src/newt/classes/jogamp/newt/opengl/broadcom/egl/Window.java new file mode 100644 index 000000000..9532178f3 --- /dev/null +++ b/src/newt/classes/jogamp/newt/opengl/broadcom/egl/Window.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.opengl.broadcom.egl; + +import jogamp.opengl.egl.*; +import javax.media.nativewindow.*; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.util.Point; +import javax.media.opengl.GLCapabilitiesImmutable; + +public class Window extends jogamp.newt.WindowImpl { + static { + Display.initSingleton(); + } + + public Window() { + } + + protected void createNativeImpl() { + if(0!=getParentWindowHandle()) { + throw new RuntimeException("Window parenting not supported (yet)"); + } + // query a good configuration .. even thought we drop this one + // and reuse the EGLUtil choosen one later. + config = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice()).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen()); + if (config == null) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + setSizeImpl(getScreen().getWidth(), getScreen().getHeight()); + + setWindowHandle(realizeWindow(true, width, height)); + if (0 == getWindowHandle()) { + throw new NativeWindowException("Error native Window Handle is null"); + } + } + + protected void closeNativeImpl() { + if(0!=windowHandleClose) { + CloseWindow(getDisplayHandle(), windowHandleClose); + } + } + + protected void setVisibleImpl(boolean visible, int x, int y, int width, int height) { + reconfigureWindowImpl(x, y, width, height, false, 0, 0); + visibleChanged(visible); + } + + protected void requestFocusImpl(boolean reparented) { } + + protected void setSizeImpl(int width, int height) { + if(0!=getWindowHandle()) { + // n/a in BroadcomEGL + System.err.println("BCEGL Window.setSizeImpl n/a in BroadcomEGL with realized window"); + } else { + this.width = width; + this.height = height; + } + } + + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, + boolean parentChange, int fullScreenChange, int decorationChange) { + if(0!=getWindowHandle()) { + if(0!=fullScreenChange) { + if( fullScreenChange > 0 ) { + // n/a in BroadcomEGL + System.err.println("setFullscreen n/a in BroadcomEGL"); + return false; + } + } + } + if(width>0 || height>0) { + if(0!=getWindowHandle()) { + // n/a in BroadcomEGL + System.err.println("BCEGL Window.setSizeImpl n/a in BroadcomEGL with realized window"); + } else { + this.width=(width>0)?width:this.width; + this.height=(height>0)?height:this.height; + } + } + if(x>=0 || y>=0) { + System.err.println("BCEGL Window.setPositionImpl n/a in BroadcomEGL"); + } + return true; + } + + protected Point getLocationOnScreenImpl(int x, int y) { + return new Point(x,y); + } + + + @Override + public boolean surfaceSwap() { + SwapWindow(getDisplayHandle(), getWindowHandle()); + return true; + } + + //---------------------------------------------------------------------- + // Internals only + // + + protected static native boolean initIDs(); + private native long CreateWindow(long eglDisplayHandle, boolean chromaKey, int width, int height); + private native void CloseWindow(long eglDisplayHandle, long eglWindowHandle); + private native void SwapWindow(long eglDisplayHandle, long eglWindowHandle); + + + private long realizeWindow(boolean chromaKey, int width, int height) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("BCEGL Window.realizeWindow() with: chroma "+chromaKey+", "+width+"x"+height+", "+config); + } + long handle = CreateWindow(getDisplayHandle(), chromaKey, width, height); + if (0 == handle) { + throw new NativeWindowException("Error native Window Handle is null"); + } + windowHandleClose = handle; + return handle; + } + + private void windowCreated(int cfgID, int width, int height) { + this.width = width; + this.height = height; + GLCapabilitiesImmutable capsReq = (GLCapabilitiesImmutable) config.getRequestedCapabilities(); + config = EGLGraphicsConfiguration.create(capsReq, getScreen().getGraphicsScreen(), cfgID); + if (config == null) { + throw new NativeWindowException("Error creating EGLGraphicsConfiguration from id: "+cfgID+", "+this); + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("BCEGL Window.windowCreated(): "+toHexString(cfgID)+", "+width+"x"+height+", "+config); + } + } + + private long windowHandleClose; +} diff --git a/src/newt/classes/jogamp/newt/opengl/kd/KDDisplay.java b/src/newt/classes/jogamp/newt/opengl/kd/KDDisplay.java new file mode 100644 index 000000000..c9d83a750 --- /dev/null +++ b/src/newt/classes/jogamp/newt/opengl/kd/KDDisplay.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.opengl.kd; + +import com.jogamp.newt.*; +import jogamp.newt.*; +import jogamp.opengl.egl.*; +import javax.media.nativewindow.*; +import javax.media.nativewindow.egl.*; + +public class KDDisplay extends DisplayImpl { + + static { + NEWTJNILibLoader.loadNEWT(); + + if (!KDWindow.initIDs()) { + throw new NativeWindowException("Failed to initialize KDWindow jmethodIDs"); + } + } + + public static void initSingleton() { + // just exist to ensure static init has been run + } + + + public KDDisplay() { + } + + protected void createNativeImpl() { + // FIXME: map name to EGL_*_DISPLAY + long handle = EGL.eglGetDisplay(EGL.EGL_DEFAULT_DISPLAY); + if (handle == EGL.EGL_NO_DISPLAY) { + throw new NativeWindowException("eglGetDisplay failed"); + } + if (!EGL.eglInitialize(handle, null, null)) { + throw new NativeWindowException("eglInitialize failed"); + } + aDevice = new EGLGraphicsDevice(handle, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); + } + + protected void closeNativeImpl() { + if (aDevice.getHandle() != EGL.EGL_NO_DISPLAY) { + EGL.eglTerminate(aDevice.getHandle()); + } + } + + protected void dispatchMessagesNative() { + DispatchMessages(); + } + + private native void DispatchMessages(); +} + diff --git a/src/newt/classes/jogamp/newt/opengl/kd/KDScreen.java b/src/newt/classes/jogamp/newt/opengl/kd/KDScreen.java new file mode 100644 index 000000000..2996fb194 --- /dev/null +++ b/src/newt/classes/jogamp/newt/opengl/kd/KDScreen.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.opengl.kd; + +import com.jogamp.newt.*; +import jogamp.newt.ScreenImpl; +import javax.media.nativewindow.*; + +public class KDScreen extends ScreenImpl { + static { + KDDisplay.initSingleton(); + } + + public KDScreen() { + } + + protected void createNativeImpl() { + aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); + } + + protected void closeNativeImpl() { } + + // elevate access to this package .. + protected void setScreenSize(int w, int h) { + super.setScreenSize(w, h); + } +} diff --git a/src/newt/classes/jogamp/newt/opengl/kd/KDWindow.java b/src/newt/classes/jogamp/newt/opengl/kd/KDWindow.java new file mode 100644 index 000000000..9cfa13cd9 --- /dev/null +++ b/src/newt/classes/jogamp/newt/opengl/kd/KDWindow.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.opengl.kd; + +import jogamp.newt.*; +import jogamp.opengl.egl.*; +import javax.media.nativewindow.*; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.util.Point; +import javax.media.opengl.GLCapabilitiesImmutable; + +public class KDWindow extends WindowImpl { + private static final String WINDOW_CLASS_NAME = "NewtWindow"; + + static { + KDDisplay.initSingleton(); + } + + public KDWindow() { + } + + protected void createNativeImpl() { + if(0!=getParentWindowHandle()) { + throw new RuntimeException("Window parenting not supported (yet)"); + } + config = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice()).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen()); + if (config == null) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + + GLCapabilitiesImmutable eglCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + int[] eglAttribs = EGLGraphicsConfiguration.GLCapabilities2AttribList(eglCaps); + + eglWindowHandle = CreateWindow(getDisplayHandle(), eglAttribs); + if (eglWindowHandle == 0) { + throw new NativeWindowException("Error creating egl window: "+config); + } + setVisible0(eglWindowHandle, false); + setWindowHandle(RealizeWindow(eglWindowHandle)); + if (0 == getWindowHandle()) { + throw new NativeWindowException("Error native Window Handle is null"); + } + windowHandleClose = eglWindowHandle; + } + + protected void closeNativeImpl() { + if(0!=windowHandleClose) { + CloseWindow(windowHandleClose, windowUserData); + windowUserData=0; + } + } + + protected void setVisibleImpl(boolean visible, int x, int y, int width, int height) { + setVisible0(eglWindowHandle, visible); + reconfigureWindowImpl(x, y, width, height, false, 0, 0); + visibleChanged(visible); + } + + protected void requestFocusImpl(boolean reparented) { } + + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, + boolean parentChange, int fullScreenChange, int decorationChange) { + if(0!=eglWindowHandle) { + if(0!=fullScreenChange) { + boolean fs = fullScreenChange > 0; + setFullScreen0(eglWindowHandle, fs); + if(fs) { + return true; + } + } + // int _x=(x>=0)?x:this.x; + // int _y=(x>=0)?y:this.y; + int _w=(width>0)?width:this.width; + int _h=(height>0)?height:this.height; + if(width>0 || height>0) { + setSize0(eglWindowHandle, _w, _h); + } + if(x>=0 || y>=0) { + System.err.println("setPosition n/a in KD"); + } + } + return true; + } + + protected Point getLocationOnScreenImpl(int x, int y) { + return new Point(x,y); + } + + //---------------------------------------------------------------------- + // Internals only + // + + protected static native boolean initIDs(); + private native long CreateWindow(long displayHandle, int[] attributes); + 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; + } + + @Override + protected void sizeChanged(int newWidth, int newHeight, boolean force) { + if(fullscreen) { + ((KDScreen)getScreen()).setScreenSize(width, height); + } + super.sizeChanged(newWidth, newHeight, force); + } + + private long eglWindowHandle; + private long windowHandleClose; + private long windowUserData; +} diff --git a/src/newt/classes/jogamp/newt/windows/WindowsDisplay.java b/src/newt/classes/jogamp/newt/windows/WindowsDisplay.java new file mode 100644 index 000000000..750c38092 --- /dev/null +++ b/src/newt/classes/jogamp/newt/windows/WindowsDisplay.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.windows; + +import jogamp.nativewindow.windows.RegisteredClass; +import jogamp.nativewindow.windows.RegisteredClassFactory; +import jogamp.newt.DisplayImpl; +import jogamp.newt.NEWTJNILibLoader; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.windows.WindowsGraphicsDevice; + +public class WindowsDisplay extends DisplayImpl { + + private static final String newtClassBaseName = "_newt_clazz" ; + private static RegisteredClassFactory sharedClassFactory; + + static { + NEWTJNILibLoader.loadNEWT(); + + if (!WindowsWindow.initIDs0()) { + throw new NativeWindowException("Failed to initialize WindowsWindow jmethodIDs"); + } + sharedClassFactory = new RegisteredClassFactory(newtClassBaseName, WindowsWindow.getNewtWndProc0()); + } + + public static void initSingleton() { + // just exist to ensure static init has been run + } + + private RegisteredClass sharedClass; + + public WindowsDisplay() { + } + + protected void createNativeImpl() { + sharedClass = sharedClassFactory.getSharedClass(); + aDevice = new WindowsGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); + } + + protected void closeNativeImpl() { + sharedClassFactory.releaseSharedClass(); + } + + protected void dispatchMessagesNative() { + DispatchMessages0(); + } + + protected long getHInstance() { + return sharedClass.getHandle(); + } + + protected String getWindowClassName() { + return sharedClass.getName(); + } + + //---------------------------------------------------------------------- + // Internals only + // + private static native void DispatchMessages0(); +} + diff --git a/src/newt/classes/jogamp/newt/windows/WindowsScreen.java b/src/newt/classes/jogamp/newt/windows/WindowsScreen.java new file mode 100644 index 000000000..ea7fe8d55 --- /dev/null +++ b/src/newt/classes/jogamp/newt/windows/WindowsScreen.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ +package jogamp.newt.windows; + +import com.jogamp.common.util.ArrayHashSet; +import java.util.ArrayList; + +import com.jogamp.newt.*; +import jogamp.newt.ScreenImpl; +import com.jogamp.newt.ScreenMode; +import jogamp.newt.ScreenModeStatus; +import com.jogamp.newt.util.ScreenModeUtil; + +import javax.media.nativewindow.*; + +public class WindowsScreen extends ScreenImpl { + + static { + WindowsDisplay.initSingleton(); + } + + public WindowsScreen() { + } + + protected void createNativeImpl() { + aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); + setScreenSize(getWidthImpl0(screen_idx), getHeightImpl0(screen_idx)); + } + + protected void closeNativeImpl() { + } + + private int[] getScreenModeIdx(int idx) { + int[] modeProps = getScreenMode0(screen_idx, idx); + if (null == modeProps || 0 == modeProps.length) { + return null; + } + if(modeProps.length < ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL) { + throw new RuntimeException("properties array too short, should be >= "+ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL+", is "+modeProps.length); + } + return modeProps; + } + + private int nativeModeIdx; + + protected int[] getScreenModeFirstImpl() { + nativeModeIdx = 0; + return getScreenModeNextImpl(); + } + + protected int[] getScreenModeNextImpl() { + int[] modeProps = getScreenModeIdx(nativeModeIdx); + if (null != modeProps && 0 < modeProps.length) { + nativeModeIdx++; + return modeProps; + } + return null; + } + + protected ScreenMode getCurrentScreenModeImpl() { + int[] modeProps = getScreenModeIdx(-1); + if (null != modeProps && 0 < modeProps.length) { + return ScreenModeUtil.streamIn(modeProps, 0); + } + return null; + } + + protected boolean setCurrentScreenModeImpl(ScreenMode sm) { + return setScreenMode0(screen_idx, + sm.getMonitorMode().getSurfaceSize().getResolution().getWidth(), + sm.getMonitorMode().getSurfaceSize().getResolution().getHeight(), + sm.getMonitorMode().getSurfaceSize().getBitsPerPixel(), + sm.getMonitorMode().getRefreshRate(), + sm.getRotation()); + } + + // Native calls + private native int getWidthImpl0(int scrn_idx); + + private native int getHeightImpl0(int scrn_idx); + + private native int[] getScreenMode0(int screen_index, int mode_index); + private native boolean setScreenMode0(int screen_index, int width, int height, int bits, int freq, int rot); +} diff --git a/src/newt/classes/jogamp/newt/windows/WindowsWindow.java b/src/newt/classes/jogamp/newt/windows/WindowsWindow.java new file mode 100644 index 000000000..653de295d --- /dev/null +++ b/src/newt/classes/jogamp/newt/windows/WindowsWindow.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.windows; + +import jogamp.nativewindow.windows.GDI; +import jogamp.newt.WindowImpl; +import javax.media.nativewindow.GraphicsConfigurationFactory; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.util.Insets; +import javax.media.nativewindow.util.Point; + +public class WindowsWindow extends WindowImpl { + + private long hmon; + private long hdc; + private long windowHandleClose; + private final Insets insets = new Insets(0, 0, 0, 0); + + static { + WindowsDisplay.initSingleton(); + } + + public WindowsWindow() { + } + + @Override + protected int lockSurfaceImpl() { + if (0 != hdc) { + throw new InternalError("surface not released"); + } + hdc = GDI.GetDC(getWindowHandle()); + hmon = MonitorFromWindow0(getWindowHandle()); + return ( 0 != hdc ) ? LOCK_SUCCESS : LOCK_SURFACE_NOT_READY; + } + + @Override + protected void unlockSurfaceImpl() { + if (0 == hdc) { + throw new InternalError("surface not acquired"); + } + GDI.ReleaseDC(getWindowHandle(), hdc); + hdc=0; + } + + @Override + public final long getSurfaceHandle() { + return hdc; + } + + @Override + public boolean hasDeviceChanged() { + if(0!=getWindowHandle()) { + long _hmon = MonitorFromWindow0(getWindowHandle()); + if (hmon != _hmon) { + if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { + Exception e = new Exception("Info: Window Device Changed "+Thread.currentThread().getName()+ + ", HMON "+toHexString(hmon)+" -> "+toHexString(_hmon)); + e.printStackTrace(); + } + hmon = _hmon; + return true; + } + } + return false; + } + + protected void createNativeImpl() { + WindowsScreen screen = (WindowsScreen) getScreen(); + WindowsDisplay display = (WindowsDisplay) screen.getDisplay(); + config = GraphicsConfigurationFactory.getFactory(display.getGraphicsDevice()).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, screen.getGraphicsScreen()); + if (config == null) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + setWindowHandle(CreateWindow0(display.getHInstance(), display.getWindowClassName(), display.getWindowClassName(), + getParentWindowHandle(), 0, undecorated, x, y, width, height)); + if (getWindowHandle() == 0) { + throw new NativeWindowException("Error creating window"); + } + windowHandleClose = getWindowHandle(); + if(DEBUG_IMPLEMENTATION || DEBUG_WINDOW_EVENT) { + Exception e = new Exception("Info: Window new window handle "+Thread.currentThread().getName()+ + " (Parent HWND "+toHexString(getParentWindowHandle())+ + ") : HWND "+toHexString(getWindowHandle())+", "+Thread.currentThread()); + e.printStackTrace(); + } + } + + protected void closeNativeImpl() { + if (hdc != 0) { + if(windowHandleClose != 0) { + try { + GDI.ReleaseDC(windowHandleClose, hdc); + } catch (Throwable t) { + if(DEBUG_IMPLEMENTATION) { + Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); + e.printStackTrace(); + } + } + } + hdc = 0; + } + if(windowHandleClose != 0) { + try { + GDI.DestroyWindow(windowHandleClose); + } catch (Throwable t) { + if(DEBUG_IMPLEMENTATION) { + Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); + e.printStackTrace(); + } + } finally { + windowHandleClose = 0; + } + } + } + + protected void setVisibleImpl(boolean visible, int x, int y, int width, int height) { + setVisible0(getWindowHandle(), visible, (getParentWindowHandle()==0)?true:false, x, y, width, height); + visibleChanged(visible); + } + + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, + boolean parentChange, int fullScreenChange, int decorationChange) { + reconfigureWindow0( (fullScreenChange>0)?0:getParentWindowHandle(), + getWindowHandle(), x, y, width, height, isVisible(), parentChange, fullScreenChange, decorationChange); + return true; + } + + protected void requestFocusImpl(boolean force) { + requestFocus0(getWindowHandle(), force); + } + + @Override + protected void setTitleImpl(final String title) { + setTitle0(getWindowHandle(), title); + } + + @Override + public Insets getInsets() { + return (Insets)insets.clone(); + } + + protected Point getLocationOnScreenImpl(int x, int y) { + return GDI.GetRelativeLocation( getWindowHandle(), 0 /*root win*/, x, y); + } + + //---------------------------------------------------------------------- + // Internals only + // + protected static native boolean initIDs0(); + protected static native long getNewtWndProc0(); + + private native long CreateWindow0(long hInstance, String wndClassName, String wndName, + long parentWindowHandle, long visualID, boolean isUndecorated, + int x, int y, int width, int height); + private native long MonitorFromWindow0(long windowHandle); + private native void setVisible0(long windowHandle, boolean visible, boolean top, int x, int y, int width, int height); + private native void reconfigureWindow0(long parentWindowHandle, long windowHandle, + int x, int y, int width, int height, boolean isVisible, + boolean parentChange, int fullScreenChange, int decorationChange); + private static native void setTitle0(long windowHandle, String title); + private native void requestFocus0(long windowHandle, boolean force); + + private void insetsChanged(int left, int top, int right, int bottom) { + if (left != -1 && top != -1 && right != -1 && bottom != -1) { + if (left != insets.left || top != insets.top || right != insets.right || bottom != insets.bottom) { + insets.left = left; + insets.top = top; + insets.right = right; + insets.bottom = bottom; + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.insetsChanged: "+insets); + } + } + } + } +} diff --git a/src/newt/classes/jogamp/newt/x11/X11Display.java b/src/newt/classes/jogamp/newt/x11/X11Display.java new file mode 100644 index 000000000..d4a83abe0 --- /dev/null +++ b/src/newt/classes/jogamp/newt/x11/X11Display.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.x11; + +import javax.media.nativewindow.*; +import javax.media.nativewindow.x11.*; +import jogamp.newt.*; +import jogamp.nativewindow.x11.X11Util; + +public class X11Display extends DisplayImpl { + + static { + NEWTJNILibLoader.loadNEWT(); + + if ( !initIDs0() ) { + throw new NativeWindowException("Failed to initialize X11Display jmethodIDs"); + } + + if (!X11Window.initIDs0()) { + throw new NativeWindowException("Failed to initialize X11Window jmethodIDs"); + } + } + + public static void initSingleton() { + // just exist to ensure static init has been run + } + + + public X11Display() { + } + + public String validateDisplayName(String name, long handle) { + return X11Util.validateDisplayName(name, handle); + } + + protected void createNativeImpl() { + long handle = X11Util.createDisplay(name); + if( 0 == handle ) { + throw new RuntimeException("Error creating display: "+name); + } + try { + CompleteDisplay0(handle); + } catch(RuntimeException e) { + X11Util.closeDisplay(handle); + throw e; + } + aDevice = new X11GraphicsDevice(handle, AbstractGraphicsDevice.DEFAULT_UNIT, NativeWindowFactory.getNullToolkitLock()); + // aDevice = new X11GraphicsDevice(handle, AbstractGraphicsDevice.DEFAULT_UNIT, NativeWindowFactory.createDefaultToolkitLockNoAWT(NativeWindowFactory.TYPE_X11, handle)); + // aDevice = new X11GraphicsDevice(handle, AbstractGraphicsDevice.DEFAULT_UNIT); + } + + protected void closeNativeImpl() { + X11Util.closeDisplay(getHandle()); + } + + protected void dispatchMessagesNative() { + long dpy = getHandle(); + if(0!=dpy) { + DispatchMessages0(dpy, javaObjectAtom, windowDeleteAtom); + } + } + + protected long getJavaObjectAtom() { return javaObjectAtom; } + protected long getWindowDeleteAtom() { return windowDeleteAtom; } + + //---------------------------------------------------------------------- + // Internals only + // + private static native boolean initIDs0(); + + private native void CompleteDisplay0(long handle); + + private native void DispatchMessages0(long display, long javaObjectAtom, long windowDeleteAtom); + + private void displayCompleted(long javaObjectAtom, long windowDeleteAtom) { + this.javaObjectAtom=javaObjectAtom; + this.windowDeleteAtom=windowDeleteAtom; + } + + private long windowDeleteAtom; + private long javaObjectAtom; +} + diff --git a/src/newt/classes/jogamp/newt/x11/X11Screen.java b/src/newt/classes/jogamp/newt/x11/X11Screen.java new file mode 100644 index 000000000..4788f6b52 --- /dev/null +++ b/src/newt/classes/jogamp/newt/x11/X11Screen.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ +package jogamp.newt.x11; + +import jogamp.nativewindow.x11.X11Util; +import jogamp.newt.ScreenImpl; +import com.jogamp.newt.ScreenMode; +import com.jogamp.newt.util.ScreenModeUtil; +import java.util.List; + +import javax.media.nativewindow.x11.*; + +public class X11Screen extends ScreenImpl { + + static { + X11Display.initSingleton(); + } + + public X11Screen() { + } + + protected void createNativeImpl() { + long handle = GetScreen0(display.getHandle(), screen_idx); + if (handle == 0) { + throw new RuntimeException("Error creating screen: " + screen_idx); + } + aScreen = new X11GraphicsScreen((X11GraphicsDevice) getDisplay().getGraphicsDevice(), screen_idx); + setScreenSize(getWidth0(display.getHandle(), screen_idx), + getHeight0(display.getHandle(), screen_idx)); + } + + protected void closeNativeImpl() { + } + + private int[] nrotations; + private int nrotation_index; + private int nres_number; + private int nres_index; + private int[] nrates; + private int nrate_index; + private int nmode_number; + + protected int[] getScreenModeFirstImpl() { + // initialize iterators and static data + nrotations = getAvailableScreenModeRotations0(display.getHandle(), screen_idx); + if(null==nrotations || 0==nrotations.length) { + return null; + } + nrotation_index = 0; + + nres_number = getNumScreenModeResolutions0(display.getHandle(), screen_idx); + if(0==nres_number) { + return null; + } + nres_index = 0; + + nrates = getScreenModeRates0(display.getHandle(), screen_idx, nres_index); + if(null==nrates || 0==nrates.length) { + return null; + } + nrate_index = 0; + + nmode_number = 0; + + return getScreenModeNextImpl(); + } + + protected int[] getScreenModeNextImpl() { + // assemble: w x h x bpp x f x r + + /** + 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(display.getHandle(), 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 bpp = 32; // FIXME + int rate = nrates[nrate_index]; + if(0>=rate) { + throw new InternalError("invalid rate: "+rate+" at index "+nrate_index+"/"+nrates.length); + } + 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++] = bpp; // bpp + props[i++] = res[2]; // widthmm + props[i++] = res[3]; // heightmm + props[i++] = rate; // rate + props[i++] = rotation; + props[i - ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL] = i - 1; // count without extra element + + nmode_number++; + + // iteration: r -> f -> bpp -> [w x h] + nrotation_index++; + if(nrotation_index == nrotations.length) { + nrotation_index=0; + nrate_index++; + if(null == nrates || nrate_index == nrates.length){ + nres_index++; + if(nres_index == nres_number) { + // done + nrates=null; + nrotations=null; + return null; + } + + nrates = getScreenModeRates0(display.getHandle(), screen_idx, nres_index); + if(null==nrates || 0==nrates.length) { + return null; + } + nrate_index = 0; + } + } + + return props; + } + + protected ScreenMode getCurrentScreenModeImpl() { + int resNumber = getNumScreenModeResolutions0(display.getHandle(), screen_idx); + if(0==resNumber) { + return null; + } + int resIdx = getCurrentScreenResolutionIndex0(display.getHandle(), screen_idx); + if(0>resIdx) { + return null; + } + if(resIdx>=resNumber) { + throw new RuntimeException("Invalid resolution index: ! "+resIdx+" < "+resNumber); + } + int[] res = getScreenModeResolution0(display.getHandle(), 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); + } + int rate = getCurrentScreenRate0(display.getHandle(), screen_idx); + if(0>rate) { + return null; + } + int rot = getCurrentScreenRotation0(display.getHandle(), screen_idx); + if(0>rot) { + return null; + } + + 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++] = 32; // FIXME: bpp + props[i++] = res[2]; // widthmm + props[i++] = res[3]; // heightmm + props[i++] = rate; // rate + props[i++] = rot; + props[i - ScreenModeUtil.NUM_SCREEN_MODE_PROPERTIES_ALL] = i; // count + return ScreenModeUtil.streamIn(props, 0); + } + + protected boolean setCurrentScreenModeImpl(ScreenMode screenMode) { + List screenModes = this.getScreenModesOrig(); + int screenModeIdx = screenModes.indexOf(screenMode); + if(0>screenModeIdx) { + throw new RuntimeException("ScreenMode not element of ScreenMode list: "+screenMode); + } + int resNumber = getNumScreenModeResolutions0(display.getHandle(), screen_idx); + int resIdx = getScreenModesIdx2NativeIdx().get(screenModeIdx); + if(0>resIdx || resIdx>=resNumber) { + throw new RuntimeException("Invalid resolution index: ! 0 < "+resIdx+" < "+resNumber+", screenMode["+screenModeIdx+"] "+screenMode); + } + + long dpy = X11Util.createDisplay(display.getName()); + if( 0 == dpy ) { + throw new RuntimeException("Error creating display: "+display.getName()); + } + + boolean done = false; + long t0 = System.currentTimeMillis(); + try { + int f = screenMode.getMonitorMode().getRefreshRate(); + int r = screenMode.getRotation(); + if( setCurrentScreenModeStart0(dpy, screen_idx, resIdx, f, r) ) { + while(!done && System.currentTimeMillis()-t0 < SCREEN_MODE_CHANGE_TIMEOUT) { + done = setCurrentScreenModePollEnd0(dpy, screen_idx, resIdx, f, r); + if(!done) { + Thread.yield(); + } + } + } + } finally { + X11Util.closeDisplay(dpy); + } + + if(!done) { + System.err.println("X11Screen.setCurrentScreenModeImpl: TO ("+SCREEN_MODE_CHANGE_TIMEOUT+") reached: "+ + (System.currentTimeMillis()-t0)+"ms"); + } + return done; + } + + //---------------------------------------------------------------------- + // Internals only + // + 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 int getCurrentScreenResolutionIndex0(long display, int screen_index); + private static native int getCurrentScreenRate0(long display, int screen_index); + private static native int getCurrentScreenRotation0(long display, int screen_index); + + /** needs own Display connection for XRANDR event handling */ + private static native boolean setCurrentScreenModeStart0(long display, int screen_index, 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/x11/X11Window.java b/src/newt/classes/jogamp/newt/x11/X11Window.java new file mode 100644 index 000000000..8f9455629 --- /dev/null +++ b/src/newt/classes/jogamp/newt/x11/X11Window.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package jogamp.newt.x11; + +import jogamp.nativewindow.x11.X11Util; +import jogamp.newt.WindowImpl; +import javax.media.nativewindow.*; +import javax.media.nativewindow.x11.*; +import javax.media.nativewindow.util.Point; + +public class X11Window extends WindowImpl { + private static final String WINDOW_CLASS_NAME = "NewtWindow"; + + static { + X11Display.initSingleton(); + } + + public X11Window() { + } + + protected void createNativeImpl() { + X11Screen screen = (X11Screen) getScreen(); + X11Display display = (X11Display) screen.getDisplay(); + config = GraphicsConfigurationFactory.getFactory(display.getGraphicsDevice()).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, screen.getGraphicsScreen()); + if (config == null) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + X11GraphicsConfiguration x11config = (X11GraphicsConfiguration) config; + long visualID = x11config.getVisualID(); + long w = CreateWindow0(getParentWindowHandle(), + display.getHandle(), screen.getIndex(), visualID, + display.getJavaObjectAtom(), display.getWindowDeleteAtom(), + x, y, width, height, isUndecorated()); + if (w == 0) { + throw new NativeWindowException("Error creating window: "+w); + } + setWindowHandle(w); + windowHandleClose = w; + } + + protected void closeNativeImpl() { + if(0!=windowHandleClose && null!=getScreen() ) { + X11Display display = (X11Display) getScreen().getDisplay(); + try { + CloseWindow0(display.getHandle(), windowHandleClose, + display.getJavaObjectAtom(), display.getWindowDeleteAtom()); + } catch (Throwable t) { + if(DEBUG_IMPLEMENTATION) { + Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); + e.printStackTrace(); + } + } finally { + windowHandleClose = 0; + } + } + } + + protected void setVisibleImpl(boolean visible, int x, int y, int width, int height) { + setVisible0(getDisplayHandle(), getWindowHandle(), visible, x, y, width, height); + } + + protected boolean reconfigureWindowImpl(int x, int y, int width, int height, + boolean parentChange, int fullScreenChange, int decorationChange) { + reparentHandle=0; + reparentCount=0; + long reqNewParentHandle = ( fullScreenChange > 0 ) ? 0 : getParentWindowHandle() ; + + reconfigureWindow0( getDisplayHandle(), getScreenIndex(), reqNewParentHandle, getWindowHandle(), + x, y, width, height, isVisible(), parentChange, fullScreenChange, decorationChange); + return true; + } + + protected void requestFocusImpl(boolean force) { + requestFocus0(getDisplayHandle(), getWindowHandle(), force); + } + + @Override + protected void setTitleImpl(String title) { + setTitle0(getDisplayHandle(), getWindowHandle(), title); + } + + protected Point getLocationOnScreenImpl(int x, int y) { + return X11Util.GetRelativeLocation( getDisplayHandle(), getScreenIndex(), getWindowHandle(), 0 /*root win*/, x, y); + } + + //---------------------------------------------------------------------- + // Internals only + // + + protected static native boolean initIDs0(); + private native long CreateWindow0(long parentWindowHandle, long display, int screen_index, + long visualID, long javaObjectAtom, long windowDeleteAtom, + int x, int y, int width, int height, boolean undecorated); + private native void CloseWindow0(long display, long windowHandle, long javaObjectAtom, long windowDeleteAtom); + private native void setVisible0(long display, long windowHandle, boolean visible, int x, int y, int width, int height); + private native void reconfigureWindow0(long display, int screen_index, long parentWindowHandle, long windowHandle, + int x, int y, int width, int height, boolean isVisible, + boolean parentChange, int fullScreenChange, int decorationChange); + private native void setTitle0(long display, long windowHandle, String title); + private native void requestFocus0(long display, long windowHandle, boolean force); + private native Object getRelativeLocation0(long display, int screen_index, long src_win, long dest_win, int src_x, int src_y); + + private void windowReparented(long gotParentHandle) { + reparentHandle = gotParentHandle; + reparentCount++; + if(DEBUG_IMPLEMENTATION) { + System.err.println("******** new parent ("+reparentCount+"): " + toHexString(reparentHandle) ); + } + } + + private long windowHandleClose; + private volatile long reparentHandle; + private volatile int reparentCount; +} diff --git a/src/newt/native/BroadcomEGL.c b/src/newt/native/BroadcomEGL.c new file mode 100644 index 000000000..0cca90420 --- /dev/null +++ b/src/newt/native/BroadcomEGL.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#ifdef _WIN32 + #include <windows.h> +#else + #include <inttypes.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "jogamp_newt_opengl_broadcom_egl_Window.h" + +#include "MouseEvent.h" +#include "KeyEvent.h" + +#include <EGL/egl.h> + +typedef unsigned int GLuint; + +EGLDisplay EGLUtil_CreateDisplayByNative( GLuint uiWidth, GLuint uiHeight ); +void EGLUtil_DestroyDisplay( EGLDisplay eglDisplay ); + +EGLSurface EGLUtil_CreateWindowByNative( EGLDisplay eglDisplay, /* bool */ GLuint bChromakey, GLuint *puiWidth, GLuint *puiHeight ); +void EGLUtil_DestroyWindow( EGLDisplay eglDisplay, EGLSurface eglSurface ); +void EGLUtil_SwapWindow( EGLDisplay eglDisplay, EGLSurface eglSurface ); + +#define VERBOSE_ON 1 + +#ifdef VERBOSE_ON + #define DBG_PRINT(...) fprintf(stdout, __VA_ARGS__) +#else + #define DBG_PRINT(...) +#endif + +static jmethodID windowCreatedID = NULL; + +/** + * Display + */ + +JNIEXPORT void JNICALL Java_jogamp_newt_opengl_broadcom_egl_Display_DispatchMessages + (JNIEnv *env, jobject obj) +{ + // FIXME: n/a + (void) env; + (void) obj; +} + +JNIEXPORT jlong JNICALL Java_jogamp_newt_opengl_broadcom_egl_Display_CreateDisplay + (JNIEnv *env, jobject obj, jint width, jint height) +{ + (void) env; + (void) obj; + EGLDisplay dpy = EGLUtil_CreateDisplayByNative( (GLuint) width, (GLuint) height ); + if(NULL==dpy) { + fprintf(stderr, "[CreateDisplay] failed: NULL\n"); + } else { + DBG_PRINT( "[CreateDisplay] ok: %p, %ux%u\n", dpy, width, height); + } + return (jlong) (intptr_t) dpy; +} + +JNIEXPORT void JNICALL Java_jogamp_newt_opengl_broadcom_egl_Display_DestroyDisplay + (JNIEnv *env, jobject obj, jlong display) +{ + EGLDisplay dpy = (EGLDisplay)(intptr_t)display; + (void) env; + (void) obj; + DBG_PRINT( "[DestroyDisplay] dpy %p\n", dpy); + + EGLUtil_DestroyDisplay(dpy); + + DBG_PRINT( "[DestroyDisplay] X\n"); +} + +/** + * Window + */ + +JNIEXPORT jboolean JNICALL Java_jogamp_newt_opengl_broadcom_egl_Window_initIDs + (JNIEnv *env, jclass clazz) +{ + windowCreatedID = (*env)->GetMethodID(env, clazz, "windowCreated", "(III)V"); + if (windowCreatedID == NULL) { + DBG_PRINT( "initIDs failed\n" ); + return JNI_FALSE; + } + DBG_PRINT( "initIDs ok\n" ); + return JNI_TRUE; +} + +JNIEXPORT jlong JNICALL Java_jogamp_newt_opengl_broadcom_egl_Window_CreateWindow + (JNIEnv *env, jobject obj, jlong display, jboolean chromaKey, jint width, jint height) +{ + EGLDisplay dpy = (EGLDisplay)(intptr_t)display; + EGLSurface window = 0; + GLuint uiWidth=(GLuint)width, uiHeight=(GLuint)height; + + if(dpy==NULL) { + fprintf(stderr, "[RealizeWindow] invalid display connection..\n"); + return 0; + } + DBG_PRINT( "[RealizeWindow.Create] dpy %p %ux%u\n", dpy, uiWidth, uiHeight); + + window = EGLUtil_CreateWindowByNative( dpy, chromaKey, &uiWidth, &uiHeight ); + + if(NULL==window) { + fprintf(stderr, "[RealizeWindow.Create] failed: NULL\n"); + return 0; + } + EGLint cfgID=0; + if(EGL_FALSE==eglQuerySurface(dpy, window, EGL_CONFIG_ID, &cfgID)) { + fprintf(stderr, "[RealizeWindow.ConfigID] failed: window %p\n", window); + EGLUtil_DestroyWindow(dpy, window); + return 0; + } + (*env)->CallVoidMethod(env, obj, windowCreatedID, (jint) cfgID, (jint)uiWidth, (jint)uiHeight); + DBG_PRINT( "[RealizeWindow.Create] ok: win %p, cfgid %d, %ux%u\n", window, cfgID, uiWidth, uiHeight); + + // release and destroy already made context .. + EGLContext ctx = eglGetCurrentContext(); + DBG_PRINT( "[RealizeWindow.Create] ctx %p - KEEP ALIVE \n", ctx); + /*eglMakeCurrent(dpy, + EGL_NO_SURFACE, + EGL_NO_SURFACE, + EGL_NO_CONTEXT); */ + DBG_PRINT( "[RealizeWindow.Create] 2\n"); + // eglDestroyContext(dpy, ctx); // culprit ? FIXME ? + DBG_PRINT( "[RealizeWindow.Create] 2 - eglDestroyContext - DISABLED - Duh ?\n"); + + DBG_PRINT( "[RealizeWindow.Create] X\n"); + + return (jlong) (intptr_t) window; +} + +JNIEXPORT void JNICALL Java_jogamp_newt_opengl_broadcom_egl_Window_CloseWindow + (JNIEnv *env, jobject obj, jlong display, jlong window) +{ + EGLDisplay dpy = (EGLDisplay) (intptr_t) display; + EGLSurface surf = (EGLSurface) (intptr_t) window; + + DBG_PRINT( "[CloseWindow] dpy %p, win %p\n", dpy, surf); + + EGLUtil_DestroyWindow(dpy, surf); + + DBG_PRINT( "[CloseWindow] X\n"); +} + +JNIEXPORT void JNICALL Java_jogamp_newt_opengl_broadcom_egl_Window_SwapWindow + (JNIEnv *env, jobject obj, jlong display, jlong window) +{ + EGLDisplay dpy = (EGLDisplay) (intptr_t) display; + EGLSurface surf = (EGLSurface) (intptr_t) window; + + DBG_PRINT( "[SwapWindow] dpy %p, win %p\n", dpy, surf); + + EGLUtil_SwapWindow( dpy, surf ); + + DBG_PRINT( "[SwapWindow] X\n"); +} + diff --git a/src/newt/native/InputEvent.h b/src/newt/native/InputEvent.h new file mode 100644 index 000000000..b42c06d21 --- /dev/null +++ b/src/newt/native/InputEvent.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#ifndef _INPUT_EVENT_H_ +#define _INPUT_EVENT_H_ + +#define EVENT_SHIFT_MASK 1 +#define EVENT_CTRL_MASK 2 +#define EVENT_META_MASK 4 +#define EVENT_ALT_MASK 8 +#define EVENT_ALT_GRAPH_MASK 32 +#define EVENT_BUTTON1_MASK (1<<6) +#define EVENT_BUTTON2_MASK (1<<7) +#define EVENT_BUTTON3_MASK (1<<8) + +#endif diff --git a/src/newt/native/IntelGDL.c b/src/newt/native/IntelGDL.c new file mode 100644 index 000000000..953181ccc --- /dev/null +++ b/src/newt/native/IntelGDL.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#include <inttypes.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "jogamp_newt_intel_gdl_Display.h" +#include "jogamp_newt_intel_gdl_Screen.h" +#include "jogamp_newt_intel_gdl_Window.h" + +#include "MouseEvent.h" +#include "KeyEvent.h" + +#include <gdl.h> +#include <gdl_version.h> + +#define VERBOSE_ON 1 + +#ifdef VERBOSE_ON + #define DBG_PRINT(...) fprintf(stdout, "*** INTEL-GDL: " __VA_ARGS__) +#else + #define DBG_PRINT(...) +#endif + +static jmethodID screenCreatedID = NULL; +static jmethodID updateBoundsID = NULL; + +#define NUM_PLANES 5 +static jobject newtWindows[NUM_PLANES] = { NULL, NULL, NULL, NULL, NULL } ; +static gdl_plane_id_t planes[NUM_PLANES] = { GDL_PLANE_ID_UPP_A, GDL_PLANE_ID_UPP_B, GDL_PLANE_ID_UPP_C, GDL_PLANE_ID_UPP_D, GDL_PLANE_ID_UPP_E }; + +static int getWindowIdx(jobject win) { + int i; + for(i=0; i<NUM_PLANES && newtWindows[i]!=win; i++) ; + return (i<NUM_PLANES)?i:-1; +} +static int getPlaneIdx(gdl_plane_id_t plane) { + int i; + for(i=0; i<NUM_PLANES && planes[i]!=plane; i++) ; + return (i<NUM_PLANES)?i:-1; +} + +static jobject getNewtWindow(gdl_plane_id_t plane) { + int idx = getPlaneIdx(plane); + if(idx>0) { + return newtWindows[idx]; + } + return NULL; +} +static gdl_plane_id_t getPlane(jobject win) { + int idx = getWindowIdx(win); + if(idx>0) { + return planes[idx]; + } + return GDL_PLANE_ID_UNDEFINED; +} + +static gdl_plane_id_t allocPlane(JNIEnv *env, jobject newtWindow) { + int i = getWindowIdx(NULL); + if (i<NUM_PLANES) { + newtWindows[i] = (*env)->NewGlobalRef(env, newtWindow); + return planes[i]; + } + return GDL_PLANE_ID_UNDEFINED; +} +static void freePlane(JNIEnv *env, gdl_plane_id_t plane) { + int i = getPlaneIdx(plane); + if (i<NUM_PLANES) { + if(NULL!=newtWindows[i]) { + (*env)->DeleteGlobalRef(env, newtWindows[i]); + newtWindows[i] = NULL;; + } + } +} + +static void JNI_ThrowNew(JNIEnv *env, const char *throwable, const char* message) { + jclass throwableClass = (*env)->FindClass(env, throwable); + if (throwableClass == NULL) { + (*env)->FatalError(env, "Failed to load throwable class"); + } + + if ((*env)->ThrowNew(env, throwableClass, message) != 0) { + (*env)->FatalError(env, "Failed to throw throwable"); + } +} + + +/** + * Display + */ + +JNIEXPORT void JNICALL Java_jogamp_newt_intel_gdl_Display_DispatchMessages + (JNIEnv *env, jobject obj, jlong displayHandle, jobject focusedWindow) +{ + // FIXME: n/a + (void) env; + (void) obj; + (void) displayHandle; + /** + gdl_driver_info_t * p_driver_info = (gdl_driver_info_t *) (intptr_t) displayHandle; + jobject newtWin = getNewtWindow(plane); + if(NULL!=newtWin) { + // here we can dispatch messages .. etc + } */ +} + +JNIEXPORT jlong JNICALL Java_jogamp_newt_intel_gdl_Display_CreateDisplay + (JNIEnv *env, jobject obj) +{ + gdl_ret_t retval; + gdl_driver_info_t * p_driver_info = NULL; + + (void) env; + (void) obj; + + DBG_PRINT("[CreateDisplay]\n"); + + retval = gdl_init(0); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_init"); + return (jlong)0; + } + + p_driver_info = calloc(sizeof(gdl_driver_info_t), 1); + retval = gdl_get_driver_info(p_driver_info); + if (retval != GDL_SUCCESS) { + free(p_driver_info); + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_get_driver_info"); + return (jlong)0; + } + DBG_PRINT("[gdl_get_driver_info: major %d minor %d vers %d build %d flags %x name %s size %d avail %d]\n", + p_driver_info->header_version_major, p_driver_info->header_version_minor, + p_driver_info->gdl_version, p_driver_info->build_tag, p_driver_info->flags, + p_driver_info->name, p_driver_info->mem_size, p_driver_info->mem_avail); + + + return (jlong) (intptr_t) p_driver_info; +} + +JNIEXPORT void JNICALL Java_jogamp_newt_intel_gdl_Display_DestroyDisplay + (JNIEnv *env, jobject obj, jlong displayHandle) +{ + gdl_driver_info_t * p_driver_info = (gdl_driver_info_t *) (intptr_t) displayHandle; + (void) env; + (void) obj; + + if(NULL!=p_driver_info) { + gdl_close(); + free(p_driver_info); + } + + DBG_PRINT("[DestroyDisplay] X\n"); +} + +/** + * Screen + */ + +JNIEXPORT jboolean JNICALL Java_jogamp_newt_intel_gdl_Screen_initIDs + (JNIEnv *env, jclass clazz) +{ + screenCreatedID = (*env)->GetMethodID(env, clazz, "screenCreated", "(II)V"); + if (screenCreatedID == NULL) { + DBG_PRINT("initIDs failed\n" ); + return JNI_FALSE; + } + DBG_PRINT("initIDs ok\n" ); + return JNI_TRUE; +} + +JNIEXPORT void JNICALL Java_jogamp_newt_intel_gdl_Screen_GetScreenInfo + (JNIEnv *env, jobject obj, jlong displayHandle, jint idx) +{ + gdl_driver_info_t * p_driver_info = (gdl_driver_info_t *) (intptr_t) displayHandle; + gdl_display_info_t display_info; + gdl_display_id_t id; + gdl_ret_t retval; + + switch(idx) { + case 1: + id = GDL_DISPLAY_ID_1; + break; + default: + id = GDL_DISPLAY_ID_0; + } + + retval = gdl_get_display_info(id, &display_info); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_get_display_info"); + return; + } + + DBG_PRINT("[gdl_get_display_info: width %d height %d]\n", + display_info.tvmode.width, display_info.tvmode.height); + + (*env)->CallVoidMethod(env, obj, screenCreatedID, (jint)display_info.tvmode.width, (jint)display_info.tvmode.height); +} + +/** + * Window + */ + +JNIEXPORT jboolean JNICALL Java_jogamp_newt_intel_gdl_Window_initIDs + (JNIEnv *env, jclass clazz) +{ + updateBoundsID = (*env)->GetMethodID(env, clazz, "updateBounds", "(IIII)V"); + if (updateBoundsID == NULL) { + DBG_PRINT("initIDs failed\n" ); + return JNI_FALSE; + } + DBG_PRINT("initIDs ok\n" ); + return JNI_TRUE; +} + +JNIEXPORT jlong JNICALL Java_jogamp_newt_intel_gdl_Window_CreateSurface + (JNIEnv *env, jobject obj, jlong displayHandle, jint scr_width, jint scr_height, jint x, jint y, jint width, jint height) { + + gdl_driver_info_t * p_driver_info = (gdl_driver_info_t *) (intptr_t) displayHandle; + gdl_ret_t retval; + gdl_pixel_format_t pixelFormat = GDL_PF_ARGB_32; + gdl_color_space_t colorSpace = GDL_COLOR_SPACE_RGB; + gdl_rectangle_t srcRect, dstRect; + + (void) env; + (void) obj; + + gdl_plane_id_t plane = allocPlane(env, obj); + if(plane == GDL_PLANE_ID_UNDEFINED) { + DBG_PRINT("CreateSurface failed, couldn't alloc plane\n" ); + return 0; + } + + DBG_PRINT("[CreateSurface: screen %dx%d, win %d/%d %dx%d plane %d]\n", + scr_width, scr_height, x, y, width, height, plane); + + /** Overwrite - TEST - Check semantics of dstRect! + x = 0; + y = 0; + width = scr_width; + height = scr_height; */ + + srcRect.origin.x = x; + srcRect.origin.y = y; + srcRect.width = width; + srcRect.height = height; + + dstRect.origin.x = x; + dstRect.origin.y = y; + dstRect.width = width; + dstRect.height = height; + + retval = gdl_plane_reset(plane); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_reset"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_config_begin(plane); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_config_begin"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_set_attr(GDL_PLANE_SRC_COLOR_SPACE, &colorSpace); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr color space"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_set_attr(GDL_PLANE_PIXEL_FORMAT, &pixelFormat); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr pixel format"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_set_attr(GDL_PLANE_DST_RECT, &dstRect); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr dstRect"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_set_attr(GDL_PLANE_SRC_RECT, &srcRect); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr srcRect"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_config_end(GDL_FALSE); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_config_end"); + freePlane(env, plane); + return (jlong)0; + } + + (*env)->CallVoidMethod(env, obj, updateBoundsID, (jint)x, (jint)y, (jint)width, (jint)height); + + DBG_PRINT("[CreateSurface] returning plane %d\n", plane); + + return (jlong) (intptr_t) plane; +} + +JNIEXPORT void JNICALL Java_jogamp_newt_intel_gdl_Window_CloseSurface + (JNIEnv *env, jobject obj, jlong display, jlong surface) +{ + gdl_plane_id_t plane = (gdl_plane_id_t) (intptr_t) surface ; + freePlane(env, plane); + + DBG_PRINT("[CloseSurface] plane %d\n", plane); +} + +JNIEXPORT void JNICALL Java_jogamp_newt_intel_gdl_Window_SetBounds0 + (JNIEnv *env, jobject obj, jlong surface, jint scr_width, jint scr_height, jint x, jint y, jint width, jint height) { + + gdl_plane_id_t plane = (gdl_plane_id_t) (intptr_t) surface ; + gdl_ret_t retval; + gdl_rectangle_t srcRect, dstRect; + + (void) env; + (void) obj; + + DBG_PRINT("[SetBounds0: screen %dx%d, win %d/%d %dx%d plane %d]\n", + scr_width, scr_height, x, y, width, height, plane); + + srcRect.origin.x = x; + srcRect.origin.y = y; + srcRect.width = width; + srcRect.height = height; + + dstRect.origin.x = x; + dstRect.origin.y = y; + dstRect.width = width; + dstRect.height = height; + + retval = gdl_plane_config_begin(plane); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_config_begin"); + return; + } + + retval = gdl_plane_set_attr(GDL_PLANE_DST_RECT, &dstRect); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr dstRect"); + return; + } + + retval = gdl_plane_set_attr(GDL_PLANE_SRC_RECT, &srcRect); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr srcRect"); + return; + } + + retval = gdl_plane_config_end(GDL_FALSE); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_config_end"); + return; + } + + (*env)->CallVoidMethod(env, obj, updateBoundsID, (jint)x, (jint)y, (jint)width, (jint)height); + + DBG_PRINT("[SetBounds0] returning plane %d\n", plane); +} + diff --git a/src/newt/native/KDWindow.c b/src/newt/native/KDWindow.c new file mode 100644 index 000000000..be0488fd1 --- /dev/null +++ b/src/newt/native/KDWindow.c @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#ifdef _WIN32 + #include <windows.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#ifdef _WIN32 + /* This typedef is apparently needed for Microsoft compilers before VC8, + and on Windows CE */ + #if !defined(__MINGW64__) && ( defined(UNDER_CE) || _MSC_VER <= 1400 ) + #ifdef _WIN64 + typedef long long intptr_t; + #else + typedef int intptr_t; + #endif + #elif !defined(__MINGW64__) && _MSC_VER <= 1500 + #ifdef _WIN64 // [ + typedef __int64 intptr_t; + #else // _WIN64 ][ + typedef int intptr_t; + #endif // _WIN64 ] + #else + #include <inttypes.h> + #endif +#else + #include <inttypes.h> +#endif + +#include <KD/kd.h> + +#include "jogamp_newt_opengl_kd_KDWindow.h" + +#include "MouseEvent.h" +#include "KeyEvent.h" + +// #define VERBOSE_ON 1 + +#ifdef VERBOSE_ON + #define DBG_PRINT(...) fprintf(stdout, __VA_ARGS__) +#else + #define DBG_PRINT(...) +#endif + +#ifdef VERBOSE_ON + #ifdef _WIN32_WCE + #define STDOUT_FILE "\\Storage Card\\stdout.txt" + #define STDERR_FILE "\\Storage Card\\stderr.txt" + #endif +#endif + +#define JOGL_KD_USERDATA_MAGIC 0xDEADBEEF +typedef struct { + long magic; + KDWindow * kdWindow; + jobject javaWindow; +} JOGLKDUserdata; + +static jmethodID windowCreatedID = NULL; +static jmethodID sizeChangedID = NULL; +static jmethodID visibleChangedID = NULL; +static jmethodID windowDestroyNotifyID = NULL; +static jmethodID sendMouseEventID = NULL; +static jmethodID sendKeyEventID = NULL; + +/** + * Display + */ + +JNIEXPORT void JNICALL Java_jogamp_newt_opengl_kd_KDDisplay_DispatchMessages + (JNIEnv *env, jobject obj) +{ + const KDEvent * evt; + int numEvents = 0; + + // Periodically take a break + while( numEvents<100 && NULL!=(evt=kdWaitEvent(0)) ) { + KDWindow *kdWindow; + jobject javaWindow; + JOGLKDUserdata * userData = (JOGLKDUserdata *)(intptr_t)evt->userptr; + if(NULL == userData || userData->magic!=JOGL_KD_USERDATA_MAGIC) { + DBG_PRINT( "event unrelated: evt type: 0x%X\n", evt->type); + continue; + } + kdWindow = userData->kdWindow; + javaWindow = userData->javaWindow; + DBG_PRINT( "[DispatchMessages]: userData %p, evt type: 0x%X\n", userData, evt->type); + + numEvents++; + + // FIXME: support resize and window re-positioning events + + switch(evt->type) { + case KD_EVENT_WINDOW_FOCUS: + { + KDboolean hasFocus; + kdGetWindowPropertybv(kdWindow, KD_WINDOWPROPERTY_FOCUS, &hasFocus); + DBG_PRINT( "event window focus : src: %p\n", userData); + } + break; + case KD_EVENT_WINDOW_CLOSE: + { + DBG_PRINT( "event window close : src: %p\n", userData); + (*env)->CallVoidMethod(env, javaWindow, windowDestroyNotifyID); + } + break; + case KD_EVENT_WINDOWPROPERTY_CHANGE: + { + const KDEventWindowProperty* prop = &evt->data.windowproperty; + switch (prop->pname) { + case KD_WINDOWPROPERTY_SIZE: + { + KDint32 v[2]; + if(!kdGetWindowPropertyiv(kdWindow, KD_WINDOWPROPERTY_SIZE, v)) { + DBG_PRINT( "event window size change : src: %p %dx%d\n", userData, v[0], v[1]); + (*env)->CallVoidMethod(env, javaWindow, sizeChangedID, (jint) v[0], (jint) v[1], JNI_FALSE); + } else { + DBG_PRINT( "event window size change error: src: %p %dx%d\n", userData, v[0], v[1]); + } + } + break; + case KD_WINDOWPROPERTY_FOCUS: + DBG_PRINT( "event window focus: src: %p\n", userData); + break; + case KD_WINDOWPROPERTY_VISIBILITY: + { + KDboolean visible; + kdGetWindowPropertybv(kdWindow, KD_WINDOWPROPERTY_VISIBILITY, &visible); + DBG_PRINT( "event window visibility: src: %p, v:%d\n", userData, visible); + (*env)->CallVoidMethod(env, javaWindow, visibleChangedID, visible?JNI_TRUE:JNI_FALSE); + } + break; + default: + break; + } + } + break; + case KD_EVENT_INPUT_POINTER: + { + const KDEventInputPointer* ptr = &(evt->data.inputpointer); + // button idx: evt->data.input.index + // pressed = ev->data.input.value.i + // time = ev->timestamp + if(KD_INPUT_POINTER_SELECT==ptr->index) { + DBG_PRINT( "event mouse click: src: %p, s:%d, (%d,%d)\n", userData, ptr->select, ptr->x, ptr->y); + (*env)->CallVoidMethod(env, javaWindow, sendMouseEventID, + (ptr->select==0) ? (jint) EVENT_MOUSE_RELEASED : (jint) EVENT_MOUSE_PRESSED, + (jint) 0, + (jint) ptr->x, (jint) ptr->y, 1, 0); + } else { + DBG_PRINT( "event mouse: src: %d, s:%p, i:0x%X (%d,%d)\n", userData, ptr->select, ptr->index, ptr->x, ptr->y); + (*env)->CallVoidMethod(env, javaWindow, sendMouseEventID, (jint) EVENT_MOUSE_MOVED, + 0, + (jint) ptr->x, (jint) ptr->y, 0, 0); + } + } + break; + } + } +} + +/** + * Window + */ + +JNIEXPORT jboolean JNICALL Java_jogamp_newt_opengl_kd_KDWindow_initIDs + (JNIEnv *env, jclass clazz) +{ +#ifdef VERBOSE_ON + #ifdef _WIN32_WCE + _wfreopen(TEXT(STDOUT_FILE),L"w",stdout); + _wfreopen(TEXT(STDERR_FILE),L"w",stderr); + #endif +#endif + windowCreatedID = (*env)->GetMethodID(env, clazz, "windowCreated", "(J)V"); + sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(IIZ)V"); + visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(Z)V"); + windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "()V"); + sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIII)V"); + sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); + if (windowCreatedID == NULL || + sizeChangedID == NULL || + visibleChangedID == NULL || + windowDestroyNotifyID == NULL || + sendMouseEventID == NULL || + sendKeyEventID == NULL) { + DBG_PRINT( "initIDs failed\n" ); + return JNI_FALSE; + } + DBG_PRINT( "initIDs ok\n" ); + return JNI_TRUE; +} + +JNIEXPORT jlong JNICALL Java_jogamp_newt_opengl_kd_KDWindow_CreateWindow + (JNIEnv *env, jobject obj, jlong display, jintArray jAttrs) +{ + jint * attrs = NULL; + jsize attrsLen; + EGLDisplay dpy = (EGLDisplay)(intptr_t)display; + KDWindow *window = 0; + + if(dpy==NULL) { + fprintf(stderr, "[CreateWindow] invalid display connection..\n"); + return 0; + } + + attrsLen = (*env)->GetArrayLength(env, jAttrs); + if(0==attrsLen) { + fprintf(stderr, "[CreateWindow] attribute array size 0..\n"); + return 0; + } + attrs = (*env)->GetIntArrayElements(env, jAttrs, 0); + if(NULL==attrs) { + fprintf(stderr, "[CreateWindow] attribute array NULL..\n"); + return 0; + } + + JOGLKDUserdata * userData = kdMalloc(sizeof(JOGLKDUserdata)); + userData->magic = JOGL_KD_USERDATA_MAGIC; + window = kdCreateWindow(dpy, attrs, (void *)userData); + + (*env)->ReleaseIntArrayElements(env, jAttrs, attrs, 0); + + if(NULL==window) { + kdFree(userData); + fprintf(stderr, "[CreateWindow] failed: 0x%X\n", kdGetError()); + } else { + userData->javaWindow = (*env)->NewGlobalRef(env, obj); + userData->kdWindow = window; + (*env)->CallVoidMethod(env, obj, windowCreatedID, (jlong) (intptr_t) userData); + DBG_PRINT( "[CreateWindow] ok: %p, userdata %p\n", window, userData); + } + return (jlong) (intptr_t) window; +} + +JNIEXPORT jlong JNICALL Java_jogamp_newt_opengl_kd_KDWindow_RealizeWindow + (JNIEnv *env, jobject obj, jlong window) +{ + KDWindow *w = (KDWindow*) (intptr_t) window; + EGLNativeWindowType nativeWindow=0; + + jint res = kdRealizeWindow(w, &nativeWindow); + if(res) { + fprintf(stderr, "[RealizeWindow] failed: 0x%X, 0x%X\n", res, kdGetError()); + nativeWindow = NULL; + } + DBG_PRINT( "[RealizeWindow] ok: %p\n", nativeWindow); + return (jlong) (intptr_t) nativeWindow; +} + +JNIEXPORT jint JNICALL Java_jogamp_newt_opengl_kd_KDWindow_CloseWindow + (JNIEnv *env, jobject obj, jlong window, jlong juserData) +{ + KDWindow *w = (KDWindow*) (intptr_t) window; + JOGLKDUserdata * userData = (JOGLKDUserdata*) (intptr_t) juserData; + int res = kdDestroyWindow(w); + (*env)->DeleteGlobalRef(env, userData->javaWindow); + kdFree(userData); + + DBG_PRINT( "[CloseWindow] res: %d\n", res); + return res; +} + +/* + * Class: jogamp_newt_opengl_kd_KDWindow + * Method: setVisible0 + * Signature: (JJZ)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_opengl_kd_KDWindow_setVisible0 + (JNIEnv *env, jobject obj, jlong window, jboolean visible) +{ + KDWindow *w = (KDWindow*) (intptr_t) window; + KDboolean v = (visible==JNI_TRUE)?KD_TRUE:KD_FALSE; + kdSetWindowPropertybv(w, KD_WINDOWPROPERTY_VISIBILITY, &v); + DBG_PRINT( "[setVisible] v=%d\n", visible); + (*env)->CallVoidMethod(env, obj, visibleChangedID, visible); // FIXME: or send via event ? +} + +JNIEXPORT void JNICALL Java_jogamp_newt_opengl_kd_KDWindow_setFullScreen0 + (JNIEnv *env, jobject obj, jlong window, jboolean fullscreen) +{ +/** not supported, due to missing NV property .. + KDWindow *w = (KDWindow*) (intptr_t) window; + KDboolean v = fullscreen; + + int res = kdSetWindowPropertybv(w, KD_WINDOWPROPERTY_FULLSCREEN_NV, &v); + DBG_PRINT( "[setFullScreen] v=%d, res=%d\n", fullscreen, res); + (void)res; +*/ + (void)env; + (void)obj; + (void)window; + (void)fullscreen; +} + +JNIEXPORT void JNICALL Java_jogamp_newt_opengl_kd_KDWindow_setSize0 + (JNIEnv *env, jobject obj, jlong window, jint width, jint height) +{ + KDWindow *w = (KDWindow*) (intptr_t) window; + KDint32 v[] = { width, height }; + + int res = kdSetWindowPropertyiv(w, KD_WINDOWPROPERTY_SIZE, v); + DBG_PRINT( "[setSize] v=%dx%d, res=%d\n", width, height, res); + (void)res; + + (*env)->CallVoidMethod(env, obj, sizeChangedID, (jint) width, (jint) height, JNI_FALSE); +} + diff --git a/src/newt/native/KeyEvent.h b/src/newt/native/KeyEvent.h new file mode 100644 index 000000000..1ead0f5e8 --- /dev/null +++ b/src/newt/native/KeyEvent.h @@ -0,0 +1,200 @@ + +#ifndef _KEY_EVENT_H_ +#define _KEY_EVENT_H_ + +#define EVENT_KEY_PRESSED 300 +#define EVENT_KEY_RELEASED 301 +#define EVENT_KEY_TYPED 302 + +#define J_CHAR_UNDEFINED 0xFFFF; +#define J_VK_ENTER '\n' +#define J_VK_BACK_SPACE '\b' +#define J_VK_TAB '\t' +#define J_VK_CANCEL 0x03 +#define J_VK_CLEAR 0x0C +#define J_VK_SHIFT 0x10 +#define J_VK_CONTROL 0x11 +#define J_VK_ALT 0x12 +#define J_VK_PAUSE 0x13 +#define J_VK_CAPS_LOCK 0x14 +#define J_VK_ESCAPE 0x1B +#define J_VK_SPACE 0x20 +#define J_VK_PAGE_UP 0x21 +#define J_VK_PAGE_DOWN 0x22 +#define J_VK_END 0x23 +#define J_VK_HOME 0x24 +#define J_VK_LEFT 0x25 +#define J_VK_UP 0x26 +#define J_VK_RIGHT 0x27 +#define J_VK_DOWN 0x28 +#define J_VK_COMMA 0x2C +#define J_VK_MINUS 0x2D +#define J_VK_PERIOD 0x2E +#define J_VK_SLASH 0x2F +#define J_VK_0 0x30 +#define J_VK_1 0x31 +#define J_VK_2 0x32 +#define J_VK_3 0x33 +#define J_VK_4 0x34 +#define J_VK_5 0x35 +#define J_VK_6 0x36 +#define J_VK_7 0x37 +#define J_VK_8 0x38 +#define J_VK_9 0x39 +#define J_VK_SEMICOLON 0x3B +#define J_VK_EQUALS 0x3D +#define J_VK_A 0x41 +#define J_VK_B 0x42 +#define J_VK_C 0x43 +#define J_VK_D 0x44 +#define J_VK_E 0x45 +#define J_VK_F 0x46 +#define J_VK_G 0x47 +#define J_VK_H 0x48 +#define J_VK_I 0x49 +#define J_VK_J 0x4A +#define J_VK_K 0x4B +#define J_VK_L 0x4C +#define J_VK_M 0x4D +#define J_VK_N 0x4E +#define J_VK_O 0x4F +#define J_VK_P 0x50 +#define J_VK_Q 0x51 +#define J_VK_R 0x52 +#define J_VK_S 0x53 +#define J_VK_T 0x54 +#define J_VK_U 0x55 +#define J_VK_V 0x56 +#define J_VK_W 0x57 +#define J_VK_X 0x58 +#define J_VK_Y 0x59 +#define J_VK_Z 0x5A +#define J_VK_OPEN_BRACKET 0x5B +#define J_VK_BACK_SLASH 0x5C +#define J_VK_CLOSE_BRACKET 0x5D +#define J_VK_NUMPAD0 0x60 +#define J_VK_NUMPAD1 0x61 +#define J_VK_NUMPAD2 0x62 +#define J_VK_NUMPAD3 0x63 +#define J_VK_NUMPAD4 0x64 +#define J_VK_NUMPAD5 0x65 +#define J_VK_NUMPAD6 0x66 +#define J_VK_NUMPAD7 0x67 +#define J_VK_NUMPAD8 0x68 +#define J_VK_NUMPAD9 0x69 +#define J_VK_MULTIPLY 0x6A +#define J_VK_ADD 0x6B +#define J_VK_SEPARATOR 0x6C +#define J_VK_SUBTRACT 0x6D +#define J_VK_DECIMAL 0x6E +#define J_VK_DIVIDE 0x6F +#define J_VK_DELETE 0x7F /* ASCII DEL */ +#define J_VK_NUM_LOCK 0x90 +#define J_VK_SCROLL_LOCK 0x91 +#define J_VK_F1 0x70 +#define J_VK_F2 0x71 +#define J_VK_F3 0x72 +#define J_VK_F4 0x73 +#define J_VK_F5 0x74 +#define J_VK_F6 0x75 +#define J_VK_F7 0x76 +#define J_VK_F8 0x77 +#define J_VK_F9 0x78 +#define J_VK_F10 0x79 +#define J_VK_F11 0x7A +#define J_VK_F12 0x7B +#define J_VK_F13 0xF000 +#define J_VK_F14 0xF001 +#define J_VK_F15 0xF002 +#define J_VK_F16 0xF003 +#define J_VK_F17 0xF004 +#define J_VK_F18 0xF005 +#define J_VK_F19 0xF006 +#define J_VK_F20 0xF007 +#define J_VK_F21 0xF008 +#define J_VK_F22 0xF009 +#define J_VK_F23 0xF00A +#define J_VK_F24 0xF00B +#define J_VK_PRINTSCREEN 0x9A +#define J_VK_INSERT 0x9B +#define J_VK_HELP 0x9C +#define J_VK_META 0x9D +#define J_VK_BACK_QUOTE 0xC0 +#define J_VK_QUOTE 0xDE +#define J_VK_KP_UP 0xE0 +#define J_VK_KP_DOWN 0xE1 +#define J_VK_KP_LEFT 0xE2 +#define J_VK_KP_RIGHT 0xE3 +#define J_VK_DEAD_GRAVE 0x80 +#define J_VK_DEAD_ACUTE 0x81 +#define J_VK_DEAD_CIRCUMFLEX 0x82 +#define J_VK_DEAD_TILDE 0x83 +#define J_VK_DEAD_MACRON 0x84 +#define J_VK_DEAD_BREVE 0x85 +#define J_VK_DEAD_ABOVEDOT 0x86 +#define J_VK_DEAD_DIAERESIS 0x87 +#define J_VK_DEAD_ABOVERING 0x88 +#define J_VK_DEAD_DOUBLEACUTE 0x89 +#define J_VK_DEAD_CARON 0x8a +#define J_VK_DEAD_CEDILLA 0x8b +#define J_VK_DEAD_OGONEK 0x8c +#define J_VK_DEAD_IOTA 0x8d +#define J_VK_DEAD_VOICED_SOUND 0x8e +#define J_VK_DEAD_SEMIVOICED_SOUND 0x8f +#define J_VK_AMPERSAND 0x96 +#define J_VK_ASTERISK 0x97 +#define J_VK_QUOTEDBL 0x98 +#define J_VK_LESS 0x99 +#define J_VK_GREATER 0xa0 +#define J_VK_BRACELEFT 0xa1 +#define J_VK_BRACERIGHT 0xa2 +#define J_VK_AT 0x0200 +#define J_VK_COLON 0x0201 +#define J_VK_CIRCUMFLEX 0x0202 +#define J_VK_DOLLAR 0x0203 +#define J_VK_EURO_SIGN 0x0204 +#define J_VK_EXCLAMATION_MARK 0x0205 +#define J_VK_INVERTED_EXCLAMATION_MARK 0x0206 +#define J_VK_LEFT_PARENTHESIS 0x0207 +#define J_VK_NUMBER_SIGN 0x0208 +#define J_VK_PLUS 0x0209 +#define J_VK_RIGHT_PARENTHESIS 0x020A +#define J_VK_UNDERSCORE 0x020B +#define J_VK_WINDOWS 0x020C +#define J_VK_CONTEXT_MENU 0x020D +#define J_VK_FINAL 0x0018 +#define J_VK_CONVERT 0x001C +#define J_VK_NONCONVERT 0x001D +#define J_VK_ACCEPT 0x001E +#define J_VK_MODECHANGE 0x001F +#define J_VK_KANA 0x0015 +#define J_VK_KANJI 0x0019 +#define J_VK_ALPHANUMERIC 0x00F0 +#define J_VK_KATAKANA 0x00F1 +#define J_VK_HIRAGANA 0x00F2 +#define J_VK_FULL_WIDTH 0x00F3 +#define J_VK_HALF_WIDTH 0x00F4 +#define J_VK_ROMAN_CHARACTERS 0x00F5 +#define J_VK_ALL_CANDIDATES 0x0100 +#define J_VK_PREVIOUS_CANDIDATE 0x0101 +#define J_VK_CODE_INPUT 0x0102 +#define J_VK_JAPANESE_KATAKANA 0x0103 +#define J_VK_JAPANESE_HIRAGANA 0x0104 +#define J_VK_JAPANESE_ROMAN 0x0105 +#define J_VK_KANA_LOCK 0x0106 +#define J_VK_INPUT_METHOD_ON_OFF 0x0107 +#define J_VK_CUT 0xFFD1 +#define J_VK_COPY 0xFFCD +#define J_VK_PASTE 0xFFCF +#define J_VK_UNDO 0xFFCB +#define J_VK_AGAIN 0xFFC9 +#define J_VK_FIND 0xFFD0 +#define J_VK_PROPS 0xFFCA +#define J_VK_STOP 0xFFC8 +#define J_VK_COMPOSE 0xFF20 +#define J_VK_ALT_GRAPH 0xFF7E +#define J_VK_BEGIN 0xFF58 +#define J_VK_UNDEFINED 0x0 + +#endif + diff --git a/src/newt/native/MacWindow.m b/src/newt/native/MacWindow.m new file mode 100644 index 000000000..3b708acce --- /dev/null +++ b/src/newt/native/MacWindow.m @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#import <inttypes.h> + +#import "jogamp_newt_macosx_MacWindow.h" +#import "NewtMacWindow.h" + +#import "MouseEvent.h" +#import "KeyEvent.h" + +#import <ApplicationServices/ApplicationServices.h> + +#import <stdio.h> + +NSString* jstringToNSString(JNIEnv* env, jstring jstr) +{ + const jchar* jstrChars = (*env)->GetStringChars(env, jstr, NULL); + NSString* str = [[NSString alloc] initWithCharacters: jstrChars length: (*env)->GetStringLength(env, jstr)]; + (*env)->ReleaseStringChars(env, jstr, jstrChars); + return str; +} + +void setFrameTopLeftPoint(NSWindow* pwin, NSWindow* win, jint x, jint y) +{ + NSScreen* screen = [NSScreen mainScreen]; + + // this allows for better compatibility with awt behavior + NSRect visibleRect; // either screen or parent-window + NSPoint pt; + int d_pty=0; // parent titlebar height + int d_ptx=0; + + if(NULL==pwin) { + visibleRect = [screen frame]; + } else { + visibleRect = [pwin frame]; + NSView* pview = [pwin contentView]; + NSRect viewRect = [pview frame]; + d_pty = visibleRect.size.height - viewRect.size.height; + (void) d_ptx; + //d_pty = visibleRect.origin.y - viewRect.size.height; + //d_ptx = visibleRect.size.height - viewRect.size.height; + fprintf(stderr, "pwin %lf/%lf %lfx%lf, pview %lf/%lf %lfx%lf -> %d/%d\n", + visibleRect.origin.x, + visibleRect.origin.y, + visibleRect.size.width, + visibleRect.size.height, + viewRect.origin.x, + viewRect.origin.y, + viewRect.size.width, + viewRect.size.height, + (int)x, (int)y); + + } + + pt = NSMakePoint(visibleRect.origin.x + x, visibleRect.origin.y + visibleRect.size.height - y - d_pty); + + [win setFrameTopLeftPoint: pt]; +} + +static NewtView * changeContentView(JNIEnv *env, jobject javaWindowObject, NSWindow *pwin, NSWindow *win, NewtView *newView) { + NSView* oldNSView = [win contentView]; + NewtView* oldView = NULL; + + if(NULL!=oldNSView) { +NS_DURING + // Available >= 10.5 - Makes the menubar disapear + if([oldNSView isInFullScreenMode]) { + [oldNSView exitFullScreenModeWithOptions: NULL]; + } +NS_HANDLER +NS_ENDHANDLER + if( [oldNSView isMemberOfClass:[NewtView class]] ) { + oldView = (NewtView *) oldNSView; + + jobject globJavaWindowObject = [oldView getJavaWindowObject]; + (*env)->DeleteGlobalRef(env, globJavaWindowObject); + [oldView setJavaWindowObject: NULL]; + } + /** FIXME: Tried child window: auto clip or message reception .. + if(NULL!=pwin) { + [oldView removeFromSuperview]; + } */ + } + if(NULL!=newView) { + jobject globJavaWindowObject = (*env)->NewGlobalRef(env, javaWindowObject); + [newView setJavaWindowObject: globJavaWindowObject]; + [newView setJNIEnv: env]; + + /** FIXME: Tried child window: auto clip or message reception .. + if(NULL!=pwin) { + NSView* pview = [pwin contentView]; + [pview addSubview: newView]; + } */ + } + [win setContentView: newView]; + + // make sure the insets are updated in the java object + NewtMacWindow* newtw = (NewtMacWindow*)win; + [newtw updateInsets: env]; + + return oldView; +} + +/* + * Class: jogamp_newt_macosx_MacDisplay + * Method: initIDs + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_macosx_MacDisplay_initNSApplication0 + (JNIEnv *env, jclass clazz) +{ + static int initialized = 0; + + if(initialized) return JNI_TRUE; + initialized = 1; + + // This little bit of magic is needed in order to receive mouse + // motion events and allow key focus to be properly transferred. + // FIXME: are these Carbon APIs? They come from the + // ApplicationServices.framework. + ProcessSerialNumber psn; + if (GetCurrentProcess(&psn) == noErr) { + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + } + + // Initialize the shared NSApplication instance + [NSApplication sharedApplication]; + + // Need this when debugging, as it is necessary to attach gdb to + // the running java process -- "gdb java" doesn't work + // printf("Going to sleep for 10 seconds\n"); + // sleep(10); + + return (jboolean) JNI_TRUE; +} + +/* + * Class: jogamp_newt_macosx_MacDisplay + * Method: dispatchMessages0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_macosx_MacDisplay_dispatchMessages0 + (JNIEnv *env, jobject unused) +{ + NSEvent* event = NULL; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + +NS_DURING + + int num_events = 0; + + // Periodically take a break + do { + // FIXME: ignoring event mask for the time being + event = [NSApp nextEventMatchingMask: NSAnyEventMask + untilDate: [NSDate distantPast] + inMode: NSDefaultRunLoopMode + dequeue: YES]; + if (event != NULL) { + [NSApp sendEvent: event]; + + num_events++; + } + } while (num_events<100 && event != NULL); + +NS_HANDLER + + // just ignore it .. + +NS_ENDHANDLER + + [pool release]; +} + +/* + * Class: jogamp_newt_macosx_MacScreen + * Method: getWidthImpl + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_jogamp_newt_macosx_MacScreen_getWidthImpl0 + (JNIEnv *env, jclass clazz, jint screen_idx) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSArray *screens = [NSScreen screens]; + if(screen_idx<0) screen_idx=0; + if(screen_idx>=[screens count]) screen_idx=0; + NSScreen *screen = (NSScreen *) [screens objectAtIndex: screen_idx]; + NSRect rect = [screen frame]; + + [pool release]; + + return (jint) (rect.size.width); +} + +/* + * Class: jogamp_newt_macosx_MacScreen + * Method: getHeightImpl + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_jogamp_newt_macosx_MacScreen_getHeightImpl0 + (JNIEnv *env, jclass clazz, jint screen_idx) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSArray *screens = [NSScreen screens]; + if(screen_idx<0) screen_idx=0; + if(screen_idx>=[screens count]) screen_idx=0; + NSScreen *screen = (NSScreen *) [screens objectAtIndex: screen_idx]; + NSRect rect = [screen frame]; + + [pool release]; + + return (jint) (rect.size.height); +} + +/* + * Class: jogamp_newt_macosx_MacWindow + * Method: initIDs + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_macosx_MacWindow_initIDs0 + (JNIEnv *env, jclass clazz) +{ + static int initialized = 0; + + if(initialized) return JNI_TRUE; + initialized = 1; + + // Need this when debugging, as it is necessary to attach gdb to + // the running java process -- "gdb java" doesn't work + // printf("Going to sleep for 10 seconds\n"); + // sleep(10); + + return (jboolean) [NewtMacWindow initNatives: env forClass: clazz]; +} + +/* + * Class: jogamp_newt_macosx_MacWindow + * Method: createWindow0 + * Signature: (JIIIIZIIIJ)J + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_macosx_MacWindow_createWindow0 + (JNIEnv *env, jobject jthis, jlong parent, jint x, jint y, jint w, jint h, jboolean fullscreen, jint styleMask, + jint bufferingType, jint screen_idx, jlong jview) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSRect rect = NSMakeRect(x, y, w, h); + + NSArray *screens = [NSScreen screens]; + if(screen_idx<0) screen_idx=0; + if(screen_idx>=[screens count]) screen_idx=0; + NSScreen *screen = (NSScreen *) [screens objectAtIndex: screen_idx]; + + if (fullscreen) { + styleMask = NSBorderlessWindowMask; + NSRect rect = [screen frame]; + w = (jint) (rect.size.width); + h = (jint) (rect.size.height); + } + + // Allocate the window + NSWindow* window = [[[NewtMacWindow alloc] initWithContentRect: rect + styleMask: (NSUInteger) styleMask + backing: (NSBackingStoreType) bufferingType + screen: screen] retain]; + + NSObject *nsParentObj = (NSObject*) ((intptr_t) parent); + NSWindow* parentWindow = NULL; + if( nsParentObj != NULL && [nsParentObj isKindOfClass:[NSWindow class]] ) { + parentWindow = (NSWindow*) nsParentObj; + } else if( nsParentObj != NULL && [nsParentObj isKindOfClass:[NSView class]] ) { + NSView* view = (NSView*) nsParentObj; + parentWindow = [view window]; + fprintf(stderr, "createWindow0 - Parent is NSView : %p -> %p (win) \n", nsParentObj, parentWindow); + } else { + fprintf(stderr, "createWindow0 - Parent is neither NSWindow nor NSView : %p\n", nsParentObj); + } + if(NULL!=parentWindow) { + [parentWindow addChildWindow: window ordered: NSWindowAbove]; + [window setParentWindow: parentWindow]; + } + + if (fullscreen) { + [window setOpaque: YES]; + } else { + // If the window is undecorated, assume we want the possibility of + // a shaped window, so make it non-opaque and the background color clear + if ((styleMask & NSTitledWindowMask) == 0) { + [window setOpaque: NO]; + [window setBackgroundColor: [NSColor clearColor]]; + } + } + + // Immediately re-position the window based on an upper-left coordinate system + setFrameTopLeftPoint(parentWindow, window, x, y); + + // specify we want mouse-moved events + [window setAcceptsMouseMovedEvents:YES]; + + // Use given NewtView or allocate an NewtView if NULL + NewtView* view = (0==jview)? [[NewtView alloc] initWithFrame: rect] : (NewtView*) ((intptr_t) jview) ; + + // Set the content view + (void) changeContentView(env, jthis, parentWindow, window, view); + +NS_DURING + // Available >= 10.5 - Makes the menubar disapear + if(fullscreen) { + [view enterFullScreenMode: screen withOptions:NULL]; + } +NS_HANDLER +NS_ENDHANDLER + + // Set the next responder to be the window so that we can forward + // right mouse button down events + [view setNextResponder: window]; + + [pool release]; + + return (jlong) ((intptr_t) window); +} + +/* + * Class: jogamp_newt_macosx_MacWindow + * Method: makeKeyAndOrderFront + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_macosx_MacWindow_makeKeyAndOrderFront0 + (JNIEnv *env, jobject unused, jlong window) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + [win makeKeyAndOrderFront: win]; + [pool release]; +} + +/* + * Class: jogamp_newt_macosx_MacWindow + * Method: makeKey + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_macosx_MacWindow_makeKey0 + (JNIEnv *env, jobject unused, jlong window) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + [win makeKeyWindow]; + [pool release]; +} + +/* + * Class: jogamp_newt_macosx_MacWindow + * Method: orderOut + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_macosx_MacWindow_orderOut0 + (JNIEnv *env, jobject unused, jlong window) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + [win orderOut: win]; + [pool release]; +} + +/* + * Class: jogamp_newt_macosx_MacWindow + * Method: close0 + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_macosx_MacWindow_close0 + (JNIEnv *env, jobject unused, jlong window) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + NSView* view = [win contentView]; + [win orderOut: win]; +NS_DURING + if(NULL!=view) { + // Available >= 10.5 - Makes the menubar disapear + if([view isInFullScreenMode]) { + [view exitFullScreenModeWithOptions: NULL]; + } + } +NS_HANDLER +NS_ENDHANDLER + [win close]; + [pool release]; +} + +/* + * Class: jogamp_newt_macosx_MacWindow + * Method: setTitle0 + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_macosx_MacWindow_setTitle0 + (JNIEnv *env, jobject unused, jlong window, jstring title) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + NSString* str = jstringToNSString(env, title); + [str autorelease]; + [win setTitle: str]; + [pool release]; +} + +/* + * Class: jogamp_newt_macosx_MacWindow + * Method: contentView + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_macosx_MacWindow_contentView0 + (JNIEnv *env, jobject unused, jlong window) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + jlong res = (jlong) ((intptr_t) [win contentView]); + [pool release]; + return res; +} + +/* + * Class: jogamp_newt_macosx_MacWindow + * Method: changeContentView + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_macosx_MacWindow_changeContentView0 + (JNIEnv *env, jobject jthis, jlong parent, jlong window, jlong jview) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* pwin = (NewtMacWindow*) ((intptr_t) parent); + NSWindow* win = (NewtMacWindow*) ((intptr_t) window); + NewtView* newView = (NewtView *) ((intptr_t) jview); + + NewtView* oldView = changeContentView(env, jthis, pwin, win, newView); + + [pool release]; + + return (jlong) ((intptr_t) oldView); +} + +/* + * Class: jogamp_newt_macosx_MacWindow + * Method: setContentSize + * Signature: (JII)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_macosx_MacWindow_setContentSize0 + (JNIEnv *env, jobject unused, jlong window, jint w, jint h) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + NSSize sz = NSMakeSize(w, h); + [win setContentSize: sz]; + [pool release]; +} + +/* + * Class: jogamp_newt_macosx_MacWindow + * Method: setFrameTopLeftPoint + * Signature: (JII)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_macosx_MacWindow_setFrameTopLeftPoint0 + (JNIEnv *env, jobject unused, jlong parent, jlong window, jint x, jint y) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* pwin = (NSWindow*) ((intptr_t) parent); + NSWindow* win = (NSWindow*) ((intptr_t) window); + setFrameTopLeftPoint(pwin, win, x, y); + [pool release]; +} + diff --git a/src/newt/native/MouseEvent.h b/src/newt/native/MouseEvent.h new file mode 100644 index 000000000..e9c0476ef --- /dev/null +++ b/src/newt/native/MouseEvent.h @@ -0,0 +1,15 @@ + +#ifndef _MOUSE_EVENT_H_ +#define _MOUSE_EVENT_H_ + +// Generated by Java: EVENT_MOUSE_CLICKED = 200; +#define EVENT_MOUSE_ENTERED 201 +#define EVENT_MOUSE_EXITED 202 +#define EVENT_MOUSE_PRESSED 203 +#define EVENT_MOUSE_RELEASED 204 +#define EVENT_MOUSE_MOVED 205 +// can't find how to regenerate this file, adding manually +#define EVENT_MOUSE_WHEEL_MOVED 207 +// Generated by Java: EVENT_MOUSE_DRAGGED = 206; + +#endif diff --git a/src/newt/native/NewtCommon.c b/src/newt/native/NewtCommon.c new file mode 100644 index 000000000..0e3f99282 --- /dev/null +++ b/src/newt/native/NewtCommon.c @@ -0,0 +1,55 @@ + +#include "NewtCommon.h" + +static const char * const ClazzNameRuntimeException = "java/lang/RuntimeException"; +static jclass runtimeExceptionClz=NULL; + +void NewtCommon_FatalError(JNIEnv *env, const char* msg, ...) +{ + char buffer[512]; + va_list ap; + + va_start(ap, msg); + vsnprintf(buffer, sizeof(buffer), msg, ap); + va_end(ap); + + fprintf(stderr, "%s\n", buffer); + (*env)->FatalError(env, buffer); +} + +void NewtCommon_throwNewRuntimeException(JNIEnv *env, const char* msg, ...) +{ + char buffer[512]; + va_list ap; + + va_start(ap, msg); + vsnprintf(buffer, sizeof(buffer), msg, ap); + va_end(ap); + + (*env)->ThrowNew(env, runtimeExceptionClz, buffer); +} + +void NewtCommon_init(JNIEnv *env) { + if(NULL==runtimeExceptionClz) { + jclass c = (*env)->FindClass(env, ClazzNameRuntimeException); + if(NULL==c) { + NewtCommon_FatalError(env, "NEWT X11Window: can't find %s", ClazzNameRuntimeException); + } + runtimeExceptionClz = (jclass)(*env)->NewGlobalRef(env, c); + (*env)->DeleteLocalRef(env, c); + if(NULL==runtimeExceptionClz) { + NewtCommon_FatalError(env, "NEWT X11Window: can't use %s", ClazzNameRuntimeException); + } + } +} + +jchar* NewtCommon_GetNullTerminatedStringChars(JNIEnv* env, jstring str) +{ + jchar* strChars = NULL; + strChars = calloc((*env)->GetStringLength(env, str) + 1, sizeof(jchar)); + if (strChars != NULL) { + (*env)->GetStringRegion(env, str, 0, (*env)->GetStringLength(env, str), strChars); + } + return strChars; +} + diff --git a/src/newt/native/NewtCommon.h b/src/newt/native/NewtCommon.h new file mode 100644 index 000000000..f5835f7c8 --- /dev/null +++ b/src/newt/native/NewtCommon.h @@ -0,0 +1,15 @@ + +#ifndef NEWT_COMMON_H +#define NEWT_COMMON_H 1 + +#include <jni.h> +#include <stdlib.h> + +void NewtCommon_init(JNIEnv *env); + +jchar* NewtCommon_GetNullTerminatedStringChars(JNIEnv* env, jstring str); + +void NewtCommon_FatalError(JNIEnv *env, const char* msg, ...); +void NewtCommon_throwNewRuntimeException(JNIEnv *env, const char* msg, ...); + +#endif diff --git a/src/newt/native/NewtMacWindow.h b/src/newt/native/NewtMacWindow.h new file mode 100644 index 000000000..7f0cd60c6 --- /dev/null +++ b/src/newt/native/NewtMacWindow.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#import <AppKit/AppKit.h> +#import "jni.h" + +@interface NewtView : NSView +{ + jobject javaWindowObject; + + // This is set while messages are being dispatched and cleared afterward + JNIEnv* env; +} + +/* Set during event dispatching cycle */ +- (void) setJNIEnv: (JNIEnv*) env; +- (JNIEnv*) getJNIEnv; + +/* Register or deregister (NULL) the java Window object, + ie, if NULL, no events are send */ +- (void) setJavaWindowObject: (jobject) javaWindowObj; +- (jobject) getJavaWindowObject; + +- (void) rightMouseDown: (NSEvent*) theEvent; + +@end + +#if defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +@interface NewtMacWindow : NSWindow <NSWindowDelegate> +#else +@interface NewtMacWindow : NSWindow +#endif +{ +} + ++ (BOOL) initNatives: (JNIEnv*) env forClass: (jobject) clazz; + +- (void) updateInsets: (JNIEnv*) env; + +- (id) initWithContentRect: (NSRect) contentRect + styleMask: (NSUInteger) windowStyle + backing: (NSBackingStoreType) bufferingType + screen:(NSScreen *)screen; + +@end diff --git a/src/newt/native/NewtMacWindow.m b/src/newt/native/NewtMacWindow.m new file mode 100644 index 000000000..cba69498a --- /dev/null +++ b/src/newt/native/NewtMacWindow.m @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#import "NewtMacWindow.h" +#import "InputEvent.h" +#import "KeyEvent.h" +#import "MouseEvent.h" + +jint GetDeltaY(NSEvent *event, jint javaMods) { + CGFloat deltaY = 0.0; + CGEventRef cgEvent = [event CGEvent]; + + if (CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventIsContinuous)) { + // mouse pad case + deltaY = + CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1); + } else { + // traditional mouse wheel case + deltaY = [event deltaY]; + if (deltaY == 0.0 && (javaMods & EVENT_SHIFT_MASK) != 0) { + // shift+vertical wheel scroll produces horizontal scroll + // we convert it to vertical + deltaY = [event deltaX]; + } + if (deltaY < 1.0 && deltaY > -1.0) { + deltaY *= 10.0; + } else { + if (deltaY < 0.0) { + deltaY = deltaY - 0.5f; + } else { + deltaY = deltaY + 0.5f; + } + } + } + + if (deltaY > 0) { + return (NSInteger)deltaY; + } else if (deltaY < 0) { + return -(NSInteger)deltaY; + } + + return 0; +} + +static jmethodID sendMouseEventID = NULL; +static jmethodID sendKeyEventID = NULL; +static jmethodID insetsChangedID = NULL; +static jmethodID sizeChangedID = NULL; +static jmethodID visibleChangedID = NULL; +static jmethodID positionChangedID = NULL; +static jmethodID focusChangedID = NULL; +static jmethodID windowDestroyNotifyID = NULL; + +@implementation NewtView +- (void) setJNIEnv: (JNIEnv*) theEnv +{ + env = theEnv; +} +- (JNIEnv*) getJNIEnv +{ + return env; +} + +- (void) setJavaWindowObject: (jobject) javaWindowObj +{ + javaWindowObject = javaWindowObj; +} + +- (jobject) getJavaWindowObject +{ + return javaWindowObject; +} + +- (void) rightMouseDown: (NSEvent*) theEvent +{ + NSResponder* next = [self nextResponder]; + if (next != nil) { + [next rightMouseDown: theEvent]; + } +} + +- (void)viewWillDraw +{ + fprintf(stderr, "*************** viewWillDraw: 0x%p", javaWindowObject); fflush(stderr); + [super viewWillDraw]; +} + +- (void)viewDidHide +{ + (*env)->CallVoidMethod(env, javaWindowObject, visibleChangedID, JNI_FALSE); + [super viewDidHide]; +} + +- (void)viewDidUnhide +{ + (*env)->CallVoidMethod(env, javaWindowObject, visibleChangedID, JNI_TRUE); + [super viewDidUnhide]; +} + +@end + +@implementation NewtMacWindow + ++ (BOOL) initNatives: (JNIEnv*) env forClass: (jclass) clazz +{ + sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIII)V"); + sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); + sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(IIZ)V"); + visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(Z)V"); + insetsChangedID = (*env)->GetMethodID(env, clazz, "insetsChanged", "(IIII)V"); + positionChangedID = (*env)->GetMethodID(env, clazz, "positionChanged", "(II)V"); + focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(Z)V"); + windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "()V"); + if (sendMouseEventID && sendKeyEventID && sizeChangedID && visibleChangedID && insetsChangedID && + positionChangedID && focusChangedID && windowDestroyNotifyID) + { + return YES; + } + return NO; +} + +- (void) updateInsets: (JNIEnv*) env +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + NSRect frameRect = [self frame]; + NSRect contentRect = [self contentRectForFrameRect: frameRect]; + + // note: this is a simplistic implementation which doesn't take + // into account DPI and scaling factor + CGFloat l = contentRect.origin.x - frameRect.origin.x; + jint top = (jint)(frameRect.size.height - contentRect.size.height); + jint left = (jint)l; + jint bottom = (jint)(contentRect.origin.y - frameRect.origin.y); + jint right = (jint)(frameRect.size.width - (contentRect.size.width + l)); + + (*env)->CallVoidMethod(env, javaWindowObject, insetsChangedID, + left, top, right, bottom); +} + +- (id) initWithContentRect: (NSRect) contentRect + styleMask: (NSUInteger) windowStyle + backing: (NSBackingStoreType) bufferingType + screen:(NSScreen *)screen +{ + id res = [super initWithContentRect: contentRect + styleMask: windowStyle + backing: bufferingType + defer: YES + screen: screen]; + // Why is this necessary? Without it we don't get any of the + // delegate methods like resizing and window movement. + [self setDelegate: self]; + return res; +} + +- (BOOL) canBecomeKeyWindow +{ + // Even if the window is borderless, we still want it to be able + // to become the key window to receive keyboard events + return YES; +} + +static jint mods2JavaMods(NSUInteger mods) +{ + int javaMods = 0; + if (mods & NSShiftKeyMask) { + javaMods |= EVENT_SHIFT_MASK; + } + if (mods & NSControlKeyMask) { + javaMods |= EVENT_CTRL_MASK; + } + if (mods & NSCommandKeyMask) { + javaMods |= EVENT_META_MASK; + } + if (mods & NSAlternateKeyMask) { + javaMods |= EVENT_ALT_MASK; + } + return javaMods; +} + +- (void) sendKeyEvent: (NSEvent*) event eventType: (jint) evType +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + int i; + jint keyCode = (jint) [event keyCode]; + NSString* chars = [event charactersIgnoringModifiers]; + int len = [chars length]; + jint javaMods = mods2JavaMods([event modifierFlags]); + + for (i = 0; i < len; i++) { + // Note: the key code in the NSEvent does not map to anything we can use + jchar keyChar = (jchar) [chars characterAtIndex: i]; + + (*env)->CallVoidMethod(env, javaWindowObject, sendKeyEventID, + evType, javaMods, keyCode, keyChar); + } +} + +- (void) keyDown: (NSEvent*) theEvent +{ + [self sendKeyEvent: theEvent eventType: EVENT_KEY_PRESSED]; +} + +- (void) keyUp: (NSEvent*) theEvent +{ + [self sendKeyEvent: theEvent eventType: EVENT_KEY_RELEASED]; + [self sendKeyEvent: theEvent eventType: EVENT_KEY_TYPED]; +} + +- (void) sendMouseEvent: (NSEvent*) event eventType: (jint) evType +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + jint javaMods = mods2JavaMods([event modifierFlags]); + NSRect frameRect = [self frame]; + NSRect contentRect = [self contentRectForFrameRect: frameRect]; + // NSPoint location = [event locationInWindow]; + // The following computation improves the behavior of mouse drag + // events when they also affect the location of the window, but it + // still isn't perfect + NSPoint curLocation = [NSEvent mouseLocation]; + NSPoint location = NSMakePoint(curLocation.x - frameRect.origin.x, + curLocation.y - frameRect.origin.y); + + // convert to 1-based button number (or use zero if no button is involved) + // TODO: detect mouse button when mouse wheel scrolled + jint javaButtonNum = 0; + jint scrollDeltaY = 0; + switch ([event type]) { + case NSScrollWheel: { + scrollDeltaY = GetDeltaY(event, javaMods); + break; + } + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSLeftMouseDragged: + javaButtonNum = 1; + break; + case NSRightMouseDown: + case NSRightMouseUp: + case NSRightMouseDragged: + javaButtonNum = 3; + break; + case NSOtherMouseDown: + case NSOtherMouseUp: + case NSOtherMouseDragged: + javaButtonNum = 2; + break; + default: + javaButtonNum = 0; + break; + } + + if (evType == EVENT_MOUSE_WHEEL_MOVED && scrollDeltaY == 0) { + // ignore 0 increment wheel scroll events + return; + } + (*env)->CallVoidMethod(env, javaWindowObject, sendMouseEventID, + evType, javaMods, + (jint) location.x, + (jint) (contentRect.size.height - location.y), + javaButtonNum, scrollDeltaY); +} + +- (void) mouseEntered: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_ENTERED]; +} + +- (void) mouseExited: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_EXITED]; +} + +- (void) mouseMoved: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; +} + +- (void) scrollWheel: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_WHEEL_MOVED]; +} + +- (void) mouseDown: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; +} + +- (void) mouseDragged: (NSEvent*) theEvent +{ + // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; +} + +- (void) mouseUp: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; +} + +- (void) rightMouseDown: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; +} + +- (void) rightMouseDragged: (NSEvent*) theEvent +{ + // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; +} + +- (void) rightMouseUp: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; +} + +- (void) otherMouseDown: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; +} + +- (void) otherMouseDragged: (NSEvent*) theEvent +{ + // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; +} + +- (void) otherMouseUp: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; +} + +- (void)windowDidResize: (NSNotification*) notification +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + // update insets on every window resize for lack of better hook place + [self updateInsets: env]; + + NSRect frameRect = [self frame]; + NSRect contentRect = [self contentRectForFrameRect: frameRect]; + + (*env)->CallVoidMethod(env, javaWindowObject, sizeChangedID, + (jint) contentRect.size.width, + (jint) contentRect.size.height, JNI_FALSE); +} + +- (void)windowDidMove: (NSNotification*) notification +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + NSRect rect = [self frame]; + NSScreen* screen = NULL; + NSRect screenRect; + NSPoint pt; + + screen = [self screen]; + // this allows for better compatibility with awt behavior + screenRect = [screen frame]; + pt = NSMakePoint(rect.origin.x, screenRect.origin.y + screenRect.size.height - rect.origin.y - rect.size.height); + + (*env)->CallVoidMethod(env, javaWindowObject, positionChangedID, + (jint) pt.x, (jint) pt.y); +} + +- (void)windowWillClose: (NSNotification*) notification +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + (*env)->CallVoidMethod(env, javaWindowObject, windowDestroyNotifyID); + // Can't issue call here - locked window state, done from Java method + + // EOL .. + (*env)->DeleteGlobalRef(env, javaWindowObject); + [view setJavaWindowObject: NULL]; +} + +- (void) windowDidBecomeKey: (NSNotification *) notification +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + (*env)->CallVoidMethod(env, javaWindowObject, focusChangedID, JNI_TRUE); +} + +- (void) windowDidResignKey: (NSNotification *) notification +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + (*env)->CallVoidMethod(env, javaWindowObject, focusChangedID, JNI_FALSE); +} + +@end diff --git a/src/newt/native/ScreenMode.h b/src/newt/native/ScreenMode.h new file mode 100644 index 000000000..0a760d54a --- /dev/null +++ b/src/newt/native/ScreenMode.h @@ -0,0 +1,16 @@ +/** + * WARNING: must be synced with com.jogamp.newt.util.ScreenModeUtil#streamIn*(int[]) + */ + +#ifndef _SCREEN_MODE_H +#define _SCREEN_MODE_H + +#define NUM_RESOLUTION_PROPERTIES 2 /* width, height */ +#define NUM_SURFACE_SIZE_PROPERTIES 1 /* bpp */ +#define NUM_MONITOR_MODE_PROPERTIES 3 /* ScreenSizeMM[width, height], refresh-rate */ +#define NUM_SCREEN_MODE_PROPERTIES 1 /* rotation */ + +#define NUM_SCREEN_MODE_PROPERTIES_ALL 8 /* count + the above */ + +#endif + diff --git a/src/newt/native/WindowEvent.h b/src/newt/native/WindowEvent.h new file mode 100644 index 000000000..05491b43c --- /dev/null +++ b/src/newt/native/WindowEvent.h @@ -0,0 +1,13 @@ + +#ifndef _WINDOW_EVENT_H_ +#define _WINDOW_EVENT_H_ + +#define EVENT_WINDOW_RESIZED 100 +#define EVENT_WINDOW_MOVED 101 +#define EVENT_WINDOW_DESTROY_NOTIFY 102 +#define EVENT_WINDOW_GAINED_FOCUS 103 +#define EVENT_WINDOW_LOST_FOCUS 104 +#define EVENT_WINDOW_REPAINT 105 +#define EVENT_WINDOW_DESTROYED 106 + +#endif diff --git a/src/newt/native/WindowsWindow.c b/src/newt/native/WindowsWindow.c new file mode 100644 index 000000000..95f036b39 --- /dev/null +++ b/src/newt/native/WindowsWindow.c @@ -0,0 +1,1599 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#include <Windows.h> +#include <Windowsx.h> +#include <tchar.h> +#include <stdlib.h> +// NOTE: it looks like SHFullScreen and/or aygshell.dll is not available on the APX 2500 any more +// #ifdef UNDER_CE +// #include "aygshell.h" +// #endif + +/* This typedef is apparently needed for Microsoft compilers before VC8, + and on Windows CE and MingW32 */ +#if !defined(__MINGW64__) && ( defined(UNDER_CE) || _MSC_VER <= 1400 ) + #ifdef _WIN64 + typedef long long intptr_t; + #else + typedef int intptr_t; + #endif +#elif !defined(__MINGW64__) && _MSC_VER <= 1500 + #ifdef _WIN64 // [ + typedef __int64 intptr_t; + #else // _WIN64 ][ + typedef int intptr_t; + #endif // _WIN64 ] +#else + #include <inttypes.h> +#endif + +#if !defined(__MINGW64__) && _MSC_VER <= 1500 + // FIXME: Determine for which MSVC versions .. + #define strdup(s) _strdup(s) +#endif + +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 0x020A +#endif //WM_MOUSEWHEEL + +#ifndef WHEEL_DELTA +#define WHEEL_DELTA 120 +#endif //WHEEL_DELTA + +#ifndef WHEEL_PAGESCROLL +#define WHEEL_PAGESCROLL (UINT_MAX) +#endif //WHEEL_PAGESCROLL + +#ifndef GET_WHEEL_DELTA_WPARAM // defined for (_WIN32_WINNT >= 0x0500) +#define GET_WHEEL_DELTA_WPARAM(wParam) ((short)HIWORD(wParam)) +#endif + +#ifndef MONITOR_DEFAULTTONULL +#define MONITOR_DEFAULTTONULL 0 +#endif +#ifndef MONITOR_DEFAULTTOPRIMARY +#define MONITOR_DEFAULTTOPRIMARY 1 +#endif +#ifndef MONITOR_DEFAULTTONEAREST +#define MONITOR_DEFAULTTONEAREST 2 +#endif +#ifndef EDS_ROTATEDMODE +#define EDS_ROTATEDMODE 0x00000004 +#endif +#ifndef DISPLAY_DEVICE_ACTIVE +#define DISPLAY_DEVICE_ACTIVE 0x00000001 +#endif + +#include "jogamp_newt_windows_WindowsDisplay.h" +#include "jogamp_newt_windows_WindowsScreen.h" +#include "jogamp_newt_windows_WindowsWindow.h" + +#include "MouseEvent.h" +#include "InputEvent.h" +#include "KeyEvent.h" +#include "ScreenMode.h" + +#include "NewtCommon.h" + +// #define VERBOSE_ON 1 +// #define DEBUG_KEYS 1 + +#ifdef VERBOSE_ON + #define DBG_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) +#else + #define DBG_PRINT(...) +#endif + +#define STD_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) + +static jmethodID insetsChangedID = NULL; +static jmethodID sizeChangedID = NULL; +static jmethodID positionChangedID = NULL; +static jmethodID focusChangedID = NULL; +static jmethodID visibleChangedID = NULL; +static jmethodID windowDestroyNotifyID = NULL; +static jmethodID windowRepaintID = NULL; +static jmethodID enqueueMouseEventID = NULL; +static jmethodID sendMouseEventID = NULL; +static jmethodID enqueueKeyEventID = NULL; +static jmethodID sendKeyEventID = NULL; +static jmethodID focusActionID = NULL; +static jmethodID enqueueRequestFocusID = NULL; + +static RECT* UpdateInsets(JNIEnv *env, jobject window, HWND hwnd); + +typedef struct { + JNIEnv* jenv; + jobject jinstance; +} WindowUserData; + +typedef struct { + UINT javaKey; + UINT windowsKey; +} KeyMapEntry; + +// Static table, arranged more or less spatially. +static KeyMapEntry keyMapTable[] = { + // Modifier keys + {J_VK_CAPS_LOCK, VK_CAPITAL}, + {J_VK_SHIFT, VK_SHIFT}, + {J_VK_CONTROL, VK_CONTROL}, + {J_VK_ALT, VK_MENU}, + {J_VK_NUM_LOCK, VK_NUMLOCK}, + + // Miscellaneous Windows keys + {J_VK_WINDOWS, VK_LWIN}, + {J_VK_WINDOWS, VK_RWIN}, + {J_VK_CONTEXT_MENU, VK_APPS}, + + // Alphabet + {J_VK_A, 'A'}, + {J_VK_B, 'B'}, + {J_VK_C, 'C'}, + {J_VK_D, 'D'}, + {J_VK_E, 'E'}, + {J_VK_F, 'F'}, + {J_VK_G, 'G'}, + {J_VK_H, 'H'}, + {J_VK_I, 'I'}, + {J_VK_J, 'J'}, + {J_VK_K, 'K'}, + {J_VK_L, 'L'}, + {J_VK_M, 'M'}, + {J_VK_N, 'N'}, + {J_VK_O, 'O'}, + {J_VK_P, 'P'}, + {J_VK_Q, 'Q'}, + {J_VK_R, 'R'}, + {J_VK_S, 'S'}, + {J_VK_T, 'T'}, + {J_VK_U, 'U'}, + {J_VK_V, 'V'}, + {J_VK_W, 'W'}, + {J_VK_X, 'X'}, + {J_VK_Y, 'Y'}, + {J_VK_Z, 'Z'}, + {J_VK_0, '0'}, + {J_VK_1, '1'}, + {J_VK_2, '2'}, + {J_VK_3, '3'}, + {J_VK_4, '4'}, + {J_VK_5, '5'}, + {J_VK_6, '6'}, + {J_VK_7, '7'}, + {J_VK_8, '8'}, + {J_VK_9, '9'}, + {J_VK_ENTER, VK_RETURN}, + {J_VK_SPACE, VK_SPACE}, + {J_VK_BACK_SPACE, VK_BACK}, + {J_VK_TAB, VK_TAB}, + {J_VK_ESCAPE, VK_ESCAPE}, + {J_VK_INSERT, VK_INSERT}, + {J_VK_DELETE, VK_DELETE}, + {J_VK_HOME, VK_HOME}, + {J_VK_END, VK_END}, + {J_VK_PAGE_UP, VK_PRIOR}, + {J_VK_PAGE_DOWN, VK_NEXT}, + {J_VK_CLEAR, VK_CLEAR}, // NumPad 5 + + // NumPad with NumLock off & extended arrows block (triangular) + {J_VK_LEFT, VK_LEFT}, + {J_VK_RIGHT, VK_RIGHT}, + {J_VK_UP, VK_UP}, + {J_VK_DOWN, VK_DOWN}, + + // NumPad with NumLock on: numbers + {J_VK_NUMPAD0, VK_NUMPAD0}, + {J_VK_NUMPAD1, VK_NUMPAD1}, + {J_VK_NUMPAD2, VK_NUMPAD2}, + {J_VK_NUMPAD3, VK_NUMPAD3}, + {J_VK_NUMPAD4, VK_NUMPAD4}, + {J_VK_NUMPAD5, VK_NUMPAD5}, + {J_VK_NUMPAD6, VK_NUMPAD6}, + {J_VK_NUMPAD7, VK_NUMPAD7}, + {J_VK_NUMPAD8, VK_NUMPAD8}, + {J_VK_NUMPAD9, VK_NUMPAD9}, + + // NumPad with NumLock on + {J_VK_MULTIPLY, VK_MULTIPLY}, + {J_VK_ADD, VK_ADD}, + {J_VK_SEPARATOR, VK_SEPARATOR}, + {J_VK_SUBTRACT, VK_SUBTRACT}, + {J_VK_DECIMAL, VK_DECIMAL}, + {J_VK_DIVIDE, VK_DIVIDE}, + + // Functional keys + {J_VK_F1, VK_F1}, + {J_VK_F2, VK_F2}, + {J_VK_F3, VK_F3}, + {J_VK_F4, VK_F4}, + {J_VK_F5, VK_F5}, + {J_VK_F6, VK_F6}, + {J_VK_F7, VK_F7}, + {J_VK_F8, VK_F8}, + {J_VK_F9, VK_F9}, + {J_VK_F10, VK_F10}, + {J_VK_F11, VK_F11}, + {J_VK_F12, VK_F12}, + {J_VK_F13, VK_F13}, + {J_VK_F14, VK_F14}, + {J_VK_F15, VK_F15}, + {J_VK_F16, VK_F16}, + {J_VK_F17, VK_F17}, + {J_VK_F18, VK_F18}, + {J_VK_F19, VK_F19}, + {J_VK_F20, VK_F20}, + {J_VK_F21, VK_F21}, + {J_VK_F22, VK_F22}, + {J_VK_F23, VK_F23}, + {J_VK_F24, VK_F24}, + + {J_VK_PRINTSCREEN, VK_SNAPSHOT}, + {J_VK_SCROLL_LOCK, VK_SCROLL}, + {J_VK_PAUSE, VK_PAUSE}, + {J_VK_CANCEL, VK_CANCEL}, + {J_VK_HELP, VK_HELP}, + + // Japanese +/* + {J_VK_CONVERT, VK_CONVERT}, + {J_VK_NONCONVERT, VK_NONCONVERT}, + {J_VK_INPUT_METHOD_ON_OFF, VK_KANJI}, + {J_VK_ALPHANUMERIC, VK_DBE_ALPHANUMERIC}, + {J_VK_KATAKANA, VK_DBE_KATAKANA}, + {J_VK_HIRAGANA, VK_DBE_HIRAGANA}, + {J_VK_FULL_WIDTH, VK_DBE_DBCSCHAR}, + {J_VK_HALF_WIDTH, VK_DBE_SBCSCHAR}, + {J_VK_ROMAN_CHARACTERS, VK_DBE_ROMAN}, +*/ + + {J_VK_UNDEFINED, 0} +}; + +/* +Dynamic mapping table for OEM VK codes. This table is refilled +by BuildDynamicKeyMapTable when keyboard layout is switched. +(see NT4 DDK src/input/inc/vkoem.h for OEM VK_ values). +*/ +typedef struct { + // OEM VK codes known in advance + UINT windowsKey; + // depends on input langauge (kbd layout) + UINT javaKey; +} DynamicKeyMapEntry; + +static DynamicKeyMapEntry dynamicKeyMapTable[] = { + {0x00BA, J_VK_UNDEFINED}, // VK_OEM_1 + {0x00BB, J_VK_UNDEFINED}, // VK_OEM_PLUS + {0x00BC, J_VK_UNDEFINED}, // VK_OEM_COMMA + {0x00BD, J_VK_UNDEFINED}, // VK_OEM_MINUS + {0x00BE, J_VK_UNDEFINED}, // VK_OEM_PERIOD + {0x00BF, J_VK_UNDEFINED}, // VK_OEM_2 + {0x00C0, J_VK_UNDEFINED}, // VK_OEM_3 + {0x00DB, J_VK_UNDEFINED}, // VK_OEM_4 + {0x00DC, J_VK_UNDEFINED}, // VK_OEM_5 + {0x00DD, J_VK_UNDEFINED}, // VK_OEM_6 + {0x00DE, J_VK_UNDEFINED}, // VK_OEM_7 + {0x00DF, J_VK_UNDEFINED}, // VK_OEM_8 + {0x00E2, J_VK_UNDEFINED}, // VK_OEM_102 + {0, 0} +}; + +// Auxiliary tables used to fill the above dynamic table. We first +// find the character for the OEM VK code using ::MapVirtualKey and +// then go through these auxiliary tables to map it to Java VK code. + +typedef struct { + WCHAR c; + UINT javaKey; +} CharToVKEntry; + +static const CharToVKEntry charToVKTable[] = { + {L'!', J_VK_EXCLAMATION_MARK}, + {L'"', J_VK_QUOTEDBL}, + {L'#', J_VK_NUMBER_SIGN}, + {L'$', J_VK_DOLLAR}, + {L'&', J_VK_AMPERSAND}, + {L'\'', J_VK_QUOTE}, + {L'(', J_VK_LEFT_PARENTHESIS}, + {L')', J_VK_RIGHT_PARENTHESIS}, + {L'*', J_VK_ASTERISK}, + {L'+', J_VK_PLUS}, + {L',', J_VK_COMMA}, + {L'-', J_VK_MINUS}, + {L'.', J_VK_PERIOD}, + {L'/', J_VK_SLASH}, + {L':', J_VK_COLON}, + {L';', J_VK_SEMICOLON}, + {L'<', J_VK_LESS}, + {L'=', J_VK_EQUALS}, + {L'>', J_VK_GREATER}, + {L'@', J_VK_AT}, + {L'[', J_VK_OPEN_BRACKET}, + {L'\\', J_VK_BACK_SLASH}, + {L']', J_VK_CLOSE_BRACKET}, + {L'^', J_VK_CIRCUMFLEX}, + {L'_', J_VK_UNDERSCORE}, + {L'`', J_VK_BACK_QUOTE}, + {L'{', J_VK_BRACELEFT}, + {L'}', J_VK_BRACERIGHT}, + {0x00A1, J_VK_INVERTED_EXCLAMATION_MARK}, + {0x20A0, J_VK_EURO_SIGN}, // ???? + {0,0} +}; + +// For dead accents some layouts return ASCII punctuation, while some +// return spacing accent chars, so both should be listed. NB: MS docs +// say that conversion routings return spacing accent character, not +// combining. +static const CharToVKEntry charToDeadVKTable[] = { + {L'`', J_VK_DEAD_GRAVE}, + {L'\'', J_VK_DEAD_ACUTE}, + {0x00B4, J_VK_DEAD_ACUTE}, + {L'^', J_VK_DEAD_CIRCUMFLEX}, + {L'~', J_VK_DEAD_TILDE}, + {0x02DC, J_VK_DEAD_TILDE}, + {0x00AF, J_VK_DEAD_MACRON}, + {0x02D8, J_VK_DEAD_BREVE}, + {0x02D9, J_VK_DEAD_ABOVEDOT}, + {L'"', J_VK_DEAD_DIAERESIS}, + {0x00A8, J_VK_DEAD_DIAERESIS}, + {0x02DA, J_VK_DEAD_ABOVERING}, + {0x02DD, J_VK_DEAD_DOUBLEACUTE}, + {0x02C7, J_VK_DEAD_CARON}, // aka hacek + {L',', J_VK_DEAD_CEDILLA}, + {0x00B8, J_VK_DEAD_CEDILLA}, + {0x02DB, J_VK_DEAD_OGONEK}, + {0x037A, J_VK_DEAD_IOTA}, // ASCII ??? + {0x309B, J_VK_DEAD_VOICED_SOUND}, + {0x309C, J_VK_DEAD_SEMIVOICED_SOUND}, + {0,0} +}; + +// ANSI CP identifiers are no longer than this +#define MAX_ACP_STR_LEN 7 + +static void BuildDynamicKeyMapTable() +{ + HKL hkl = GetKeyboardLayout(0); + // Will need this to reset layout after dead keys. + UINT spaceScanCode = MapVirtualKeyEx(VK_SPACE, 0, hkl); + DynamicKeyMapEntry *dynamic; + + LANGID idLang = LOWORD(GetKeyboardLayout(0)); + UINT codePage; + TCHAR strCodePage[MAX_ACP_STR_LEN]; + // use the LANGID to create a LCID + LCID idLocale = MAKELCID(idLang, SORT_DEFAULT); + // get the ANSI code page associated with this locale + if (GetLocaleInfo(idLocale, LOCALE_IDEFAULTANSICODEPAGE, + strCodePage, sizeof(strCodePage)/sizeof(TCHAR)) > 0 ) + { + codePage = _ttoi(strCodePage); + } else { + codePage = GetACP(); + } + + // Entries in dynamic table that maps between Java VK and Windows + // VK are built in three steps: + // 1. Map windows VK to ANSI character (cannot map to unicode + // directly, since ::ToUnicode is not implemented on win9x) + // 2. Convert ANSI char to Unicode char + // 3. Map Unicode char to Java VK via two auxilary tables. + + for (dynamic = dynamicKeyMapTable; dynamic->windowsKey != 0; ++dynamic) + { + char cbuf[2] = { '\0', '\0'}; + WCHAR ucbuf[2] = { L'\0', L'\0' }; + int nchars; + UINT scancode; + const CharToVKEntry *charMap; + int nconverted; + WCHAR uc; + BYTE kbdState[256]; + + // Defaults to J_VK_UNDEFINED + dynamic->javaKey = J_VK_UNDEFINED; + + GetKeyboardState(kbdState); + + kbdState[dynamic->windowsKey] |= 0x80; // Press the key. + + // Unpress modifiers, since they are most likely pressed as + // part of the keyboard switching shortcut. + kbdState[VK_CONTROL] &= ~0x80; + kbdState[VK_SHIFT] &= ~0x80; + kbdState[VK_MENU] &= ~0x80; + + scancode = MapVirtualKeyEx(dynamic->windowsKey, 0, hkl); + nchars = ToAsciiEx(dynamic->windowsKey, scancode, kbdState, + (WORD*)cbuf, 0, hkl); + + // Auxiliary table used to map Unicode character to Java VK. + // Will assign a different table for dead keys (below). + charMap = charToVKTable; + + if (nchars < 0) { // Dead key + char junkbuf[2] = { '\0', '\0'}; + // Use a different table for dead chars since different layouts + // return different characters for the same dead key. + charMap = charToDeadVKTable; + + // We also need to reset layout so that next translation + // is unaffected by the dead status. We do this by + // translating <SPACE> key. + kbdState[dynamic->windowsKey] &= ~0x80; + kbdState[VK_SPACE] |= 0x80; + + ToAsciiEx(VK_SPACE, spaceScanCode, kbdState, + (WORD*)junkbuf, 0, hkl); + } + + nconverted = MultiByteToWideChar(codePage, 0, + cbuf, 1, ucbuf, 2); + + uc = ucbuf[0]; + { + const CharToVKEntry *map; + for (map = charMap; map->c != 0; ++map) { + if (uc == map->c) { + dynamic->javaKey = map->javaKey; + break; + } + } + } + + } // for each VK_OEM_* +} + +static jint GetModifiers() { + jint modifiers = 0; + // have to do &0xFFFF to avoid runtime assert caused by compiling with + // /RTCcsu + if (HIBYTE((GetKeyState(VK_CONTROL) & 0xFFFF)) != 0) { + modifiers |= EVENT_CTRL_MASK; + } + if (HIBYTE((GetKeyState(VK_SHIFT) & 0xFFFF)) != 0) { + modifiers |= EVENT_SHIFT_MASK; + } + if (HIBYTE((GetKeyState(VK_MENU) & 0xFFFF)) != 0) { + modifiers |= EVENT_ALT_MASK; + } + if (HIBYTE((GetKeyState(VK_LBUTTON) & 0xFFFF)) != 0) { + modifiers |= EVENT_BUTTON1_MASK; + } + if (HIBYTE((GetKeyState(VK_MBUTTON) & 0xFFFF)) != 0) { + modifiers |= EVENT_BUTTON2_MASK; + } + if (HIBYTE((GetKeyState(VK_RBUTTON) & 0xFFFF)) != 0) { + modifiers |= EVENT_BUTTON3_MASK; + } + + return modifiers; +} + +static int WmChar(JNIEnv *env, jobject window, UINT character, UINT repCnt, + UINT flags, BOOL system) +{ + // The Alt modifier is reported in the 29th bit of the lParam, + // i.e., it is the 13th bit of `flags' (which is HIWORD(lParam)). + BOOL alt_is_down = (flags & (1<<13)) != 0; + if (system && alt_is_down) { + if (character == VK_SPACE) { + return 1; + } + } + + if (character == VK_RETURN) { + character = J_VK_ENTER; + } + (*env)->CallVoidMethod(env, window, sendKeyEventID, + (jint) EVENT_KEY_TYPED, + GetModifiers(), + (jint) -1, + (jchar) character); + return 1; +} + +UINT WindowsKeyToJavaKey(UINT windowsKey, UINT modifiers) +{ + int i, j; + // for the general case, use a bi-directional table + for (i = 0; keyMapTable[i].windowsKey != 0; i++) { + if (keyMapTable[i].windowsKey == windowsKey) { + return keyMapTable[i].javaKey; + } + } + for (j = 0; dynamicKeyMapTable[j].windowsKey != 0; j++) { + if (dynamicKeyMapTable[j].windowsKey == windowsKey) { + if (dynamicKeyMapTable[j].javaKey != J_VK_UNDEFINED) { + return dynamicKeyMapTable[j].javaKey; + } else { + break; + } + } + } + + return J_VK_UNDEFINED; +} + +static int WmKeyDown(JNIEnv *env, jobject window, UINT wkey, UINT repCnt, + UINT flags, BOOL system) +{ + UINT modifiers = 0, jkey = 0, character = -1; + if (wkey == VK_PROCESSKEY) { + return 1; + } + + modifiers = GetModifiers(); + jkey = WindowsKeyToJavaKey(wkey, modifiers); + +/* + character = WindowsKeyToJavaChar(wkey, modifiers, SAVE); +*/ + + (*env)->CallVoidMethod(env, window, sendKeyEventID, + (jint) EVENT_KEY_PRESSED, + modifiers, + (jint) jkey, + (jchar) character); + + /* windows does not create a WM_CHAR for the Del key + for some reason, so we need to create the KEY_TYPED event on the + WM_KEYDOWN. + */ + if (jkey == J_VK_DELETE) { + (*env)->CallVoidMethod(env, window, sendKeyEventID, + (jint) EVENT_KEY_TYPED, + GetModifiers(), + (jint) -1, + (jchar) '\177'); + } + + return 0; +} + +static int WmKeyUp(JNIEnv *env, jobject window, UINT wkey, UINT repCnt, + UINT flags, BOOL system) +{ + UINT modifiers = 0, jkey = 0, character = -1; + if (wkey == VK_PROCESSKEY) { + return 1; + } + + modifiers = GetModifiers(); + jkey = WindowsKeyToJavaKey(wkey, modifiers); +/* + character = WindowsKeyToJavaChar(wkey, modifiers, SAVE); +*/ + + (*env)->CallVoidMethod(env, window, sendKeyEventID, + (jint) EVENT_KEY_RELEASED, + modifiers, + (jint) jkey, + (jchar) character); + + return 0; +} + +static void NewtWindows_requestFocus (JNIEnv *env, jobject window, HWND hwnd, jboolean force) { + HWND pHwnd, current; + pHwnd = GetParent(hwnd); + current = GetFocus(); + DBG_PRINT("*** WindowsWindow: requestFocus.S parent %p, window %p, isCurrent %d\n", + (void*) pHwnd, (void*)hwnd, current==hwnd); + if( JNI_TRUE==force || current!=hwnd) { + if( JNI_TRUE==force || JNI_FALSE == (*env)->CallBooleanMethod(env, window, focusActionID) ) { + UINT flags = SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE; + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, flags); + SetForegroundWindow(hwnd); // Slightly Higher Priority + SetFocus(hwnd);// Sets Keyboard Focus To Window + if(NULL!=pHwnd) { + SetActiveWindow(hwnd); + } + DBG_PRINT("*** WindowsWindow: requestFocus.X1\n"); + } else { + DBG_PRINT("*** WindowsWindow: requestFocus.X0\n"); + } + } + DBG_PRINT("*** WindowsWindow: requestFocus.XX\n"); +} + +#if 0 + +static RECT* UpdateInsets(JNIEnv *env, jobject window, HWND hwnd) +{ + // being naughty here + static RECT m_insets = { 0, 0, 0, 0 }; + RECT outside; + RECT inside; + POINT *rp_inside = (POINT *) (void *) &inside; + int dx, dy, dw, dh; + + if (IsIconic(hwnd)) { + m_insets.left = m_insets.top = m_insets.right = m_insets.bottom = -1; + return FALSE; + } + + m_insets.left = m_insets.top = m_insets.right = m_insets.bottom = 0; + + GetClientRect(hwnd, &inside); + GetWindowRect(hwnd, &outside); + + DBG_PRINT("*** WindowsWindow: UpdateInsets (a1) window %p, Inside CC: %d/%d - %d/%d %dx%d\n", + (void*)hwnd, + (int)inside.left, (int)inside.top, (int)inside.right, (int)inside.bottom, + (int)(inside.right - inside.left), (int)(inside.bottom - inside.top)); + DBG_PRINT("*** WindowsWindow: UpdateInsets (a1) window %p, Outside SC: %d/%d - %d/%d %dx%d\n", + (void*)hwnd, + (int)outside.left, (int)outside.top, (int)outside.right, (int)outside.bottom, + (int)(outside.right - outside.left), (int)(outside.bottom - outside.top)); + + // xform client -> screen coord + ClientToScreen(hwnd, rp_inside); + ClientToScreen(hwnd, rp_inside+1); + + DBG_PRINT("*** WindowsWindow: UpdateInsets (a2) window %p, Inside SC: %d/%d - %d/%d %dx%d\n", + (void*)hwnd, + (int)inside.left, (int)inside.top, (int)inside.right, (int)inside.bottom, + (int)(inside.right - inside.left), (int)(inside.bottom - inside.top)); + + m_insets.top = inside.top - outside.top; + m_insets.bottom = outside.bottom - inside.bottom; + m_insets.left = inside.left - outside.left; + m_insets.right = outside.right - inside.right; + + DBG_PRINT("*** WindowsWindow: UpdateInsets (1.0) window %p, %d/%d - %d/%d %dx%d\n", + (void*)hwnd, + (int)m_insets.left, (int)m_insets.top, (int)m_insets.right, (int)m_insets.bottom, + (int)(m_insets.right-m_insets.left), (int)(m_insets.top-m_insets.bottom)); + + (*env)->CallVoidMethod(env, window, insetsChangedID, + m_insets.left, m_insets.top, + m_insets.right, m_insets.bottom); + return &m_insets; +} + +#else + +static RECT* UpdateInsets(JNIEnv *env, jobject window, HWND hwnd) +{ + // being naughty here + static RECT m_insets = { 0, 0, 0, 0 }; + RECT outside; + RECT inside; + + if (IsIconic(hwnd)) { + m_insets.left = m_insets.top = m_insets.right = m_insets.bottom = -1; + return FALSE; + } + + m_insets.left = m_insets.top = m_insets.right = m_insets.bottom = 0; + + GetClientRect(hwnd, &inside); + GetWindowRect(hwnd, &outside); + + if (outside.right - outside.left > 0 && outside.bottom - outside.top > 0) { + MapWindowPoints(hwnd, 0, (LPPOINT)&inside, 2); + m_insets.top = inside.top - outside.top; + m_insets.bottom = outside.bottom - inside.bottom; + m_insets.left = inside.left - outside.left; + m_insets.right = outside.right - inside.right; + } else { + m_insets.top = -1; + } + if (m_insets.left < 0 || m_insets.top < 0 || + m_insets.right < 0 || m_insets.bottom < 0) + { + LONG style = GetWindowLong(hwnd, GWL_STYLE); + // TODO: TDV: better undecorated checking needed + + BOOL bIsUndecorated = (style & (WS_CHILD|WS_POPUP|WS_SYSMENU)) != 0; + if (!bIsUndecorated) { + /* Get outer frame sizes. */ + if (style & WS_THICKFRAME) { + m_insets.left = m_insets.right = + GetSystemMetrics(SM_CXSIZEFRAME); + m_insets.top = m_insets.bottom = + GetSystemMetrics(SM_CYSIZEFRAME); + } else { + m_insets.left = m_insets.right = + GetSystemMetrics(SM_CXDLGFRAME); + m_insets.top = m_insets.bottom = + GetSystemMetrics(SM_CYDLGFRAME); + } + + /* Add in title. */ + m_insets.top += GetSystemMetrics(SM_CYCAPTION); + } else { + /* undo the -1 set above */ + m_insets.left = m_insets.top = m_insets.right = m_insets.bottom = 0; + } + } + + DBG_PRINT("*** WindowsWindow: UpdateInsets window %p, %d/%d %dx%d\n", + (void*)hwnd, (int)m_insets.left, (int)m_insets.top, (int)(m_insets.right-m_insets.left), (int)(m_insets.top-m_insets.bottom)); + + (*env)->CallVoidMethod(env, window, insetsChangedID, + m_insets.left, m_insets.top, + m_insets.right, m_insets.bottom); + return &m_insets; +} + +#endif + +static void WmSize(JNIEnv *env, jobject window, HWND wnd, UINT type) +{ + RECT rc; + int w, h; + BOOL isVisible = IsWindowVisible(wnd); + + // make sure insets are up to date + (void)UpdateInsets(env, window, wnd); + + if (type == SIZE_MINIMIZED) { + // TODO: deal with minimized window sizing + return; + } + + GetClientRect(wnd, &rc); + + // we report back the dimensions of the client area + w = rc.right - rc.left; + h = rc.bottom - rc.top; + + DBG_PRINT("*** WindowsWindow: WmSize window %p, %dx%d, visible %d\n", (void*)wnd, w, h, isVisible); + + if(isVisible) { + (*env)->CallVoidMethod(env, window, sizeChangedID, w, h, JNI_FALSE); + } +} + +static LRESULT CALLBACK wndProc(HWND wnd, UINT message, + WPARAM wParam, LPARAM lParam) +{ + LRESULT res = 0; + int useDefWindowProc = 0; + JNIEnv *env = NULL; + jobject window = NULL; + BOOL isKeyDown = FALSE; + WindowUserData * wud; + +#ifdef DEBUG_KEYS + if ( WM_KEYDOWN == message ) { + STD_PRINT("*** WindowsWindow: wndProc window %p, 0x%X %d/%d\n", wnd, message, (int)LOWORD(lParam), (int)HIWORD(lParam)); + } +#endif + +#if !defined(__MINGW64__) && ( defined(UNDER_CE) || _MSC_VER <= 1200 ) + wud = (WindowUserData *) GetWindowLong(wnd, GWL_USERDATA); +#else + wud = (WindowUserData *) GetWindowLongPtr(wnd, GWLP_USERDATA); +#endif + if(NULL==wud) { + return DefWindowProc(wnd, message, wParam, lParam); + } + env = wud->jenv; + window = wud->jinstance; + + // DBG_PRINT("*** WindowsWindow: thread 0x%X - window %p -> %p, 0x%X %d/%d\n", (int)GetCurrentThreadId(), wnd, window, message, (int)LOWORD(lParam), (int)HIWORD(lParam)); + + if (NULL==window || NULL==env) { + return DefWindowProc(wnd, message, wParam, lParam); + } + + switch (message) { + + // + // The signal pipeline for destruction is: + // Java::DestroyWindow(wnd) _or_ window-close-button -> + // WM_CLOSE -> Java::windowDestroyNotify -> W_DESTROY + case WM_CLOSE: + (*env)->CallVoidMethod(env, window, windowDestroyNotifyID); + break; + + case WM_DESTROY: + { +#if !defined(__MINGW64__) && ( defined(UNDER_CE) || _MSC_VER <= 1200 ) + SetWindowLong(wnd, GWL_USERDATA, (intptr_t) NULL); +#else + SetWindowLongPtr(wnd, GWLP_USERDATA, (intptr_t) NULL); +#endif + free(wud); wud=NULL; + (*env)->DeleteGlobalRef(env, window); + } + break; + + case WM_SYSCHAR: + useDefWindowProc = WmChar(env, window, wParam, + LOWORD(lParam), HIWORD(lParam), FALSE); + break; + + case WM_CHAR: + useDefWindowProc = WmChar(env, window, wParam, + LOWORD(lParam), HIWORD(lParam), TRUE); + break; + + case WM_KEYDOWN: +#ifdef DEBUG_KEYS + STD_PRINT("*** WindowsWindow: windProc sending window %p -> %p, 0x%X %d/%d\n", wnd, window, message, (int)LOWORD(lParam), (int)HIWORD(lParam)); +#endif + useDefWindowProc = WmKeyDown(env, window, wParam, + LOWORD(lParam), HIWORD(lParam), FALSE); + break; + + case WM_KEYUP: + useDefWindowProc = WmKeyUp(env, window, wParam, + LOWORD(lParam), HIWORD(lParam), FALSE); + break; + + case WM_SIZE: + WmSize(env, window, wnd, (UINT)wParam); + break; + + case WM_SETTINGCHANGE: + if (wParam == SPI_SETNONCLIENTMETRICS) { + // make sure insets are updated, we don't need to resize the window + // because the size of the client area doesn't change + (void)UpdateInsets(env, window, wnd); + } else { + useDefWindowProc = 1; + } + break; + + + case WM_LBUTTONDOWN: + DBG_PRINT("*** WindowsWindow: LBUTTONDOWN\n"); + (*env)->CallVoidMethod(env, window, enqueueRequestFocusID, JNI_FALSE); + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_PRESSED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 1, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_LBUTTONUP: + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_RELEASED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 1, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_MBUTTONDOWN: + DBG_PRINT("*** WindowsWindow: MBUTTONDOWN\n"); + (*env)->CallVoidMethod(env, window, enqueueRequestFocusID, JNI_FALSE); + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_PRESSED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 2, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_MBUTTONUP: + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_RELEASED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 2, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_RBUTTONDOWN: + DBG_PRINT("*** WindowsWindow: RBUTTONDOWN\n"); + (*env)->CallVoidMethod(env, window, enqueueRequestFocusID, JNI_FALSE); + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_PRESSED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 3, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_RBUTTONUP: + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_RELEASED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 3, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_MOUSEMOVE: + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_MOVED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 0, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_MOUSEWHEEL: { + // need to convert the coordinates to component-relative + int x = GET_X_LPARAM(lParam); + int y = GET_Y_LPARAM(lParam); + POINT eventPt; + eventPt.x = x; + eventPt.y = y; + ScreenToClient(wnd, &eventPt); + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_WHEEL_MOVED, + GetModifiers(), + (jint) eventPt.x, (jint) eventPt.y, + (jint) 0, (jint) (GET_WHEEL_DELTA_WPARAM(wParam)/120.0f)); + useDefWindowProc = 1; + break; + } + + case WM_SETFOCUS: + (*env)->CallVoidMethod(env, window, focusChangedID, JNI_TRUE); + useDefWindowProc = 1; + break; + + case WM_KILLFOCUS: + (*env)->CallVoidMethod(env, window, focusChangedID, JNI_FALSE); + useDefWindowProc = 1; + break; + + case WM_SHOWWINDOW: + (*env)->CallVoidMethod(env, window, visibleChangedID, wParam==TRUE?JNI_TRUE:JNI_FALSE); + break; + + case WM_MOVE: + DBG_PRINT("*** WindowsWindow: WM_MOVE window %p, %d/%d\n", wnd, (int)LOWORD(lParam), (int)HIWORD(lParam)); + (*env)->CallVoidMethod(env, window, positionChangedID, + (jint)LOWORD(lParam), (jint)HIWORD(lParam)); + useDefWindowProc = 1; + break; + + case WM_PAINT: { + RECT r; + useDefWindowProc = 0; + if (GetUpdateRect(wnd, &r, TRUE /* erase background */)) { + /* + jint width = r.right-r.left; + jint height = r.bottom-r.top; + if (width > 0 && height > 0) { + (*env)->CallVoidMethod(env, window, windowRepaintID, r.left, r.top, width, height); + } + ValidateRect(wnd, &r); + */ + } + break; + } + case WM_ERASEBKGND: + // ignore erase background + (*env)->CallVoidMethod(env, window, windowRepaintID, 0, 0, -1, -1); + useDefWindowProc = 0; + res = 1; // OpenGL, etc .. erases the background, hence we claim to have just done this + break; + + + // FIXME: generate EVENT_MOUSE_ENTERED, EVENT_MOUSE_EXITED + default: + useDefWindowProc = 1; + } + + if (useDefWindowProc) + return DefWindowProc(wnd, message, wParam, lParam); + return res; +} + +/* + * Class: jogamp_newt_windows_WindowsDisplay + * Method: DispatchMessages + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_windows_WindowsDisplay_DispatchMessages0 + (JNIEnv *env, jclass clazz) +{ + int i = 0; + MSG msg; + BOOL gotOne; + + // Periodically take a break + do { + gotOne = PeekMessage(&msg, (HWND) NULL, 0, 0, PM_REMOVE); + // DBG_PRINT("*** WindowsWindow.DispatchMessages0: thread 0x%X - gotOne %d\n", (int)GetCurrentThreadId(), (int)gotOne); + if (gotOne) { + ++i; +#ifdef DEBUG_KEYS + if(WM_KEYDOWN == msg.message) { + STD_PRINT("*** WindowsWindow: DispatchMessages window %p, 0x%X %d/%d\n", msg.hwnd, msg.message, (int)LOWORD(msg.lParam), (int)HIWORD(msg.lParam)); + } +#endif + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } while (gotOne && i < 100); +} + +/* + * Class: jogamp_newt_windows_WindowsScreen + * Method: getWidthImpl + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_jogamp_newt_windows_WindowsScreen_getWidthImpl0 + (JNIEnv *env, jobject obj, jint scrn_idx) +{ + return (jint)GetSystemMetrics(SM_CXSCREEN); +} + +/* + * Class: jogamp_newt_windows_WindowsScreen + * Method: getHeightImpl + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_jogamp_newt_windows_WindowsScreen_getHeightImpl0 + (JNIEnv *env, jobject obj, jint scrn_idx) +{ + return (jint)GetSystemMetrics(SM_CYSCREEN); +} + +static int NewtScreen_RotationNativeCCW2NewtCCW(JNIEnv *env, int native) { + int newt; + switch (native) { + case DMDO_DEFAULT: + newt = 0; + break; + case DMDO_90: + newt = 90; + break; + case DMDO_180: + newt = 180; + break; + case DMDO_270: + newt = 270; + break; + default: + NewtCommon_throwNewRuntimeException(env, "invalid native rotation: %d", native); + break; + } + return newt; +} + +static int NewtScreen_RotationNewtCCW2NativeCCW(JNIEnv *env, jint newt) { + int native; + switch (newt) { + case 0: + native = DMDO_DEFAULT; + break; + case 90: + native = DMDO_90; + break; + case 180: + native = DMDO_180; + break; + case 270: + native = DMDO_270; + break; + default: + NewtCommon_throwNewRuntimeException(env, "invalid newt rotation: %d", newt); + } + return native; +} + +/* +static void NewtScreen_scanDisplayDevices() { + DISPLAY_DEVICE device; + int i = 0; + LPCTSTR name; + while(NULL != (name = NewtScreen_getDisplayDeviceName(&device, i))) { + fprintf(stderr, "*** [%d]: <%s> active %d\n", i, name, ( 0 != ( device.StateFlags & DISPLAY_DEVICE_ACTIVE ) ) ); + i++; + } +}*/ + +static LPCTSTR NewtScreen_getDisplayDeviceName(DISPLAY_DEVICE * device, int scrn_idx) { + device->cb = sizeof(DISPLAY_DEVICE); + if( FALSE == EnumDisplayDevices(NULL, scrn_idx, device, 0) ) { + DBG_PRINT("*** WindowsWindow: getDisplayDeviceName.EnumDisplayDevices(scrn_idx %d) -> FALSE\n", scrn_idx); + return NULL; + } + + if( 0 == ( device->StateFlags & DISPLAY_DEVICE_ACTIVE ) ) { + DBG_PRINT("*** WindowsWindow: !DISPLAY_DEVICE_ACTIVE(scrn_idx %d)\n", scrn_idx); + return NULL; + } + + return device->DeviceName; +} + +static HDC NewtScreen_createDisplayDC(LPCTSTR displayDeviceName) { + return CreateDC("DISPLAY", displayDeviceName, NULL, NULL); +} + +/* + * Class: jogamp_newt_windows_WindowsScreen + * Method: getScreenMode0 + * Signature: (II)[I + */ +JNIEXPORT jintArray JNICALL Java_jogamp_newt_windows_WindowsScreen_getScreenMode0 + (JNIEnv *env, jobject obj, jint scrn_idx, jint mode_idx) +{ + DISPLAY_DEVICE device; + int prop_num = NUM_SCREEN_MODE_PROPERTIES_ALL; + LPCTSTR deviceName = NewtScreen_getDisplayDeviceName(&device, scrn_idx); + if(NULL == deviceName) { + DBG_PRINT("*** WindowsWindow: getScreenMode.getDisplayDeviceName(scrn_idx %d) -> NULL\n", scrn_idx); + return (*env)->NewIntArray(env, 0); + } + + int devModeID; + int widthmm, heightmm; + if(-1 < mode_idx) { + // only at initialization time, where index >= 0 + HDC hdc = NewtScreen_createDisplayDC(deviceName); + widthmm = GetDeviceCaps(hdc, HORZSIZE); + heightmm = GetDeviceCaps(hdc, VERTSIZE); + DeleteDC(hdc); + devModeID = (int) mode_idx; + prop_num++; // add 1st extra prop, mode_idx + } else { + widthmm = 0; + heightmm = 0; + devModeID = ENUM_CURRENT_SETTINGS; + } + + DEVMODE dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (0 == EnumDisplaySettingsEx(deviceName, devModeID, &dm, ( ENUM_CURRENT_SETTINGS == devModeID ) ? 0 : EDS_ROTATEDMODE)) { + DBG_PRINT("*** WindowsWindow: getScreenMode.EnumDisplaySettingsEx(mode_idx %d/%d) -> NULL\n", mode_idx, devModeID); + return (*env)->NewIntArray(env, 0); + } + + // swap width and height, since Windows reflects rotated dimension, we don't + if (DMDO_90 == dm.dmDisplayOrientation || DMDO_270 == dm.dmDisplayOrientation) { + int tempWidth = dm.dmPelsWidth; + dm.dmPelsWidth = dm.dmPelsHeight; + dm.dmPelsHeight = tempWidth; + } + + jint prop[ prop_num ]; + int propIndex = 0; + + if( -1 < mode_idx ) { + prop[propIndex++] = mode_idx; + } + prop[propIndex++] = 0; // set later for verification of iterator + prop[propIndex++] = dm.dmPelsWidth; + prop[propIndex++] = dm.dmPelsHeight; + prop[propIndex++] = dm.dmBitsPerPel; + prop[propIndex++] = widthmm; + prop[propIndex++] = heightmm; + prop[propIndex++] = dm.dmDisplayFrequency; + prop[propIndex++] = NewtScreen_RotationNativeCCW2NewtCCW(env, dm.dmDisplayOrientation); + prop[propIndex - NUM_SCREEN_MODE_PROPERTIES_ALL] = ( -1 < mode_idx ) ? propIndex-1 : propIndex ; // count == NUM_SCREEN_MODE_PROPERTIES_ALL + + jintArray properties = (*env)->NewIntArray(env, prop_num); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", prop_num); + } + (*env)->SetIntArrayRegion(env, properties, 0, prop_num, prop); + + return properties; +} + +/* + * Class: jogamp_newt_windows_WindowsScreen + * Method: setScreenMode0 + * Signature: (IIIIII)Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_windows_WindowsScreen_setScreenMode0 + (JNIEnv *env, jobject object, jint scrn_idx, jint width, jint height, jint bits, jint rate, jint rot) +{ + DISPLAY_DEVICE device; + LPCTSTR deviceName = NewtScreen_getDisplayDeviceName(&device, scrn_idx); + if(NULL == deviceName) { + DBG_PRINT("*** WindowsWindow: setScreenMode.getDisplayDeviceName(scrn_idx %d) -> NULL\n", scrn_idx); + return JNI_FALSE; + } + + DEVMODE dm; + // initialize the DEVMODE structure + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + dm.dmPelsWidth = (int)width; + dm.dmPelsHeight = (int)height; + dm.dmBitsPerPel = (int)bits; + dm.dmDisplayFrequency = (int)rate; + dm.dmDisplayOrientation = NewtScreen_RotationNewtCCW2NativeCCW(env, rot); + + // swap width and height, since Windows reflects rotated dimension, we don't + if ( DMDO_90 == dm.dmDisplayOrientation || DMDO_270 == dm.dmDisplayOrientation ) { + int tempWidth = dm.dmPelsWidth; + dm.dmPelsWidth = dm.dmPelsHeight; + dm.dmPelsHeight = tempWidth; + } + + dm.dmFields = DM_DISPLAYORIENTATION | DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; + + return ( DISP_CHANGE_SUCCESSFUL == ChangeDisplaySettings(&dm, 0) ) ? JNI_TRUE : JNI_FALSE ; +} + +/* + * Class: jogamp_newt_windows_WindowsWindow + * Method: initIDs0 + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_windows_WindowsWindow_initIDs0 + (JNIEnv *env, jclass clazz) +{ + NewtCommon_init(env); + + insetsChangedID = (*env)->GetMethodID(env, clazz, "insetsChanged", "(IIII)V"); + sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(IIZ)V"); + positionChangedID = (*env)->GetMethodID(env, clazz, "positionChanged", "(II)V"); + focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(Z)V"); + visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(Z)V"); + windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "()V"); + windowRepaintID = (*env)->GetMethodID(env, clazz, "windowRepaint", "(IIII)V"); + enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZIIIIII)V"); + sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIII)V"); + enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZIIIC)V"); + sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); + enqueueRequestFocusID = (*env)->GetMethodID(env, clazz, "enqueueRequestFocus", "(Z)V"); + focusActionID = (*env)->GetMethodID(env, clazz, "focusAction", "()Z"); + + if (insetsChangedID == NULL || + sizeChangedID == NULL || + positionChangedID == NULL || + focusChangedID == NULL || + visibleChangedID == NULL || + windowDestroyNotifyID == NULL || + windowRepaintID == NULL || + enqueueMouseEventID == NULL || + sendMouseEventID == NULL || + enqueueKeyEventID == NULL || + sendKeyEventID == NULL || + focusActionID == NULL || + enqueueRequestFocusID == NULL) { + return JNI_FALSE; + } + BuildDynamicKeyMapTable(); + return JNI_TRUE; +} + +/* + * Class: jogamp_newt_windows_WindowsWindow + * Method: getNewtWndProc0 + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_windows_WindowsWindow_getNewtWndProc0 + (JNIEnv *env, jclass clazz) +{ + return (jlong) (intptr_t) wndProc; +} + +/* + * Class: jogamp_newt_windows_WindowsWindow + * Method: CreateWindow + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_windows_WindowsWindow_CreateWindow0 + (JNIEnv *env, jobject obj, + jlong hInstance, jstring jWndClassName, jstring jWndName, + jlong parent, jlong visualID, jboolean bIsUndecorated, + jint jx, jint jy, jint defaultWidth, jint defaultHeight) +{ + HWND parentWindow = (HWND) (intptr_t) parent; + const TCHAR* wndClassName = NULL; + const TCHAR* wndName = NULL; + DWORD windowStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_TABSTOP; + int x=(int)jx, y=(int)jy; + int width=(int)defaultWidth, height=(int)defaultHeight; + HWND window = NULL; + +#ifdef UNICODE + wndClassName = NewtCommon_GetNullTerminatedStringChars(env, jWndClassName); + wndName = NewtCommon_GetNullTerminatedStringChars(env, jWndName); +#else + wndClassName = (*env)->GetStringUTFChars(env, jWndClassName, NULL); + wndName = (*env)->GetStringUTFChars(env, jWndName, NULL); +#endif + + if(NULL!=parentWindow) { + if (!IsWindow(parentWindow)) { + DBG_PRINT("*** WindowsWindow: CreateWindow failure: Passed parentWindow %p is invalid\n", parentWindow); + return 0; + } + windowStyle |= WS_CHILD ; + } else if (bIsUndecorated) { + windowStyle |= WS_POPUP | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX; + } else { + windowStyle |= WS_OVERLAPPEDWINDOW; + x = CW_USEDEFAULT; + y = 0; + } + + (void) visualID; // FIXME: use the visualID .. + + window = CreateWindow(wndClassName, wndName, windowStyle, + x, y, width, height, + parentWindow, NULL, + (HINSTANCE) (intptr_t) hInstance, + NULL); + + DBG_PRINT("*** WindowsWindow: CreateWindow thread 0x%X, parent %p, window %p, %d/%d %dx%d\n", + (int)GetCurrentThreadId(), parentWindow, window, x, y, width, height); + + if (NULL == window) { + int lastError = (int) GetLastError(); + DBG_PRINT("*** WindowsWindow: CreateWindow failure: 0x%X %d\n", lastError, lastError); + return 0; + } else { + WindowUserData * wud = (WindowUserData *) malloc(sizeof(WindowUserData)); + wud->jinstance = (*env)->NewGlobalRef(env, obj); + wud->jenv = env; +#if !defined(__MINGW64__) && ( defined(UNDER_CE) || _MSC_VER <= 1200 ) + SetWindowLong(window, GWL_USERDATA, (intptr_t) wud); +#else + SetWindowLongPtr(window, GWLP_USERDATA, (intptr_t) wud); +#endif + + UpdateInsets(env, obj, window); + } + +#ifdef UNICODE + free((void*) wndClassName); + free((void*) wndName); +#else + (*env)->ReleaseStringUTFChars(env, jWndClassName, wndClassName); + (*env)->ReleaseStringUTFChars(env, jWndName, wndName); +#endif + + return (jlong) (intptr_t) window; +} + +/* + * Class: jogamp_newt_windows_WindowsWindow + * Method: MonitorFromWindow + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_windows_WindowsWindow_MonitorFromWindow0 + (JNIEnv *env, jobject obj, jlong window) +{ + #if (_WIN32_WINNT >= 0x0500 || _WIN32_WINDOWS >= 0x0410 || WINVER >= 0x0500) && !defined(_WIN32_WCE) + return (jlong) (intptr_t) MonitorFromWindow((HWND) (intptr_t) window, MONITOR_DEFAULTTOPRIMARY); + #else + return 0; + #endif +} + +/*** + * returns bits: 1: size change, 2: pos change + */ +int NewtWindow_setVisiblePosSize(JNIEnv *env, jobject obj, HWND hwnd, jboolean top, jboolean visible, + int x, int y, int width, int height) +{ + UINT flags; + HWND hWndInsertAfter; + BOOL bRes; + int iRes=0; + int wwidth = width; // final window width + int wheight = height; // final window height + + DBG_PRINT("*** WindowsWindow: NewtWindow_setVisiblePosSize %d/%d %dx%d, top %d, visible %d\n", + x, y, width, height, (int)top, (int)visible); + + if(JNI_TRUE == visible) { + flags = SWP_SHOWWINDOW; + } else { + flags = SWP_NOACTIVATE | SWP_NOZORDER; + } + + if(0>x || 0>y ) { + flags |= SWP_NOMOVE; + } else { + iRes |= 2; + } + if(0>=width || 0>=height ) { + flags |= SWP_NOSIZE; + } else { + iRes |= 1; + } + + if(JNI_TRUE == top) { + hWndInsertAfter = HWND_TOPMOST; + if ( 0 == ( flags & SWP_NOSIZE ) ) { + + // since width, height are the size of the client area, we need to add insets + RECT *pInsets = UpdateInsets(env, obj, hwnd); + + wwidth += pInsets->left + pInsets->right; + wheight += pInsets->top + pInsets->bottom; + } + DBG_PRINT("*** WindowsWindow: NewtWindow_setVisiblePosSize top size w/ insets: %d/%d %dx%d\n", x, y, wwidth, wheight); + } else { + hWndInsertAfter = HWND_TOP; + DBG_PRINT("*** WindowsWindow: NewtWindow_setVisiblePosSize client size: %d/%d %dx%d\n", x, y, wwidth, wheight); + } + + SetWindowPos(hwnd, hWndInsertAfter, x, y, wwidth, wheight, flags); + + InvalidateRect(hwnd, NULL, TRUE); + UpdateWindow(hwnd); + + // we report back the size of client area + (*env)->CallVoidMethod(env, obj, sizeChangedID, (jint) width, (jint) height, JNI_FALSE); + + return iRes; +} + +/* + * Class: jogamp_newt_windows_WindowsWindow + * Method: setVisible0 + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_windows_WindowsWindow_setVisible0 + (JNIEnv *env, jobject obj, jlong window, jboolean visible, jboolean top, jint x, jint y, jint width, jint height) +{ + HWND hwnd = (HWND) (intptr_t) window; + DBG_PRINT("*** WindowsWindow: setVisible window %p, visible: %d, top %d, %d/%d %dx%d\n", + hwnd, (int)visible, (int)top, x, y, width, height); + if (visible) { + NewtWindow_setVisiblePosSize(env, obj, hwnd, top, visible, x, y, width, height); + ShowWindow(hwnd, SW_SHOW); + } else { + ShowWindow(hwnd, SW_HIDE); + } +} + +static jboolean NewtWindows_setFullScreen(jboolean fullscreen) +{ + int flags = 0; + DEVMODE dm; + // initialize the DEVMODE structure + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (0 == EnumDisplaySettings(NULL /*current display device*/, ENUM_CURRENT_SETTINGS, &dm)) + { + return JNI_FALSE; + } + + flags = ( JNI_TRUE == fullscreen ) ? CDS_FULLSCREEN : CDS_RESET ; + + return ( DISP_CHANGE_SUCCESSFUL == ChangeDisplaySettings(&dm, flags) ) ? JNI_TRUE : JNI_FALSE; +} + +/* + * Class: jogamp_newt_windows_WindowsWindow + * Method: reconfigureWindow0 + * Signature: (JIIIIZZII)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_windows_WindowsWindow_reconfigureWindow0 + (JNIEnv *env, jobject obj, jlong parent, jlong window, jint x, jint y, jint width, jint height, + jboolean visible, jboolean parentChange, jint fullScreenChange, jint decorationChange) +{ + HWND hwndP = (HWND) (intptr_t) parent; + HWND hwnd = (HWND) (intptr_t) window; + DWORD windowStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN ; + BOOL styleChange = ( 0 != decorationChange || 0 != fullScreenChange || JNI_TRUE == parentChange ) ? TRUE : FALSE ; + UINT flags = SWP_SHOWWINDOW; + HWND hWndInsertAfter; + + DBG_PRINT("*** WindowsWindow: reconfigureWindow0 parent %p, window %p, %d/%d %dx%d, parentChange %d, fullScreenChange %d, visible %d, decorationChange %d -> styleChange %d\n", + parent, window, x, y, width, height, parentChange, fullScreenChange, visible, decorationChange, styleChange); + + if (!IsWindow(hwnd)) { + DBG_PRINT("*** WindowsWindow: reconfigureWindow0 failure: Passed window %p is invalid\n", (void*)hwnd); + return; + } + + if (NULL!=hwndP && !IsWindow(hwndP)) { + DBG_PRINT("*** WindowsWindow: reconfigureWindow0 failure: Passed parent window %p is invalid\n", (void*)hwndP); + return; + } + + if(JNI_TRUE == visible) { + windowStyle |= WS_VISIBLE ; + } + + if(fullScreenChange < 0) + { + NewtWindows_setFullScreen(JNI_FALSE); + } + + // order of call sequence: (MS documentation) + // TOP: SetParent(.., NULL); Clear WS_CHILD [, Set WS_POPUP] + // CHILD: Set WS_CHILD [, Clear WS_POPUP]; SetParent(.., PARENT) + // + if ( JNI_TRUE == parentChange && NULL == hwndP ) { + SetParent(hwnd, NULL); + } + + if(fullScreenChange > 0) + { + NewtWindows_setFullScreen(JNI_TRUE); + } + + if ( styleChange ) { + if(NULL!=hwndP) { + windowStyle |= WS_CHILD ; + } else if ( decorationChange < 0 || 0 < fullScreenChange ) { + windowStyle |= WS_POPUP | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX; + } else { + windowStyle |= WS_OVERLAPPEDWINDOW; + } + SetWindowLong(hwnd, GWL_STYLE, windowStyle); + SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER ); + } + + if ( JNI_TRUE == parentChange && NULL != hwndP ) { + SetParent(hwnd, hwndP ); + } + + NewtWindow_setVisiblePosSize(env, obj, hwnd, (NULL == hwndP) ? JNI_TRUE : JNI_FALSE /* top */, visible, + x, y, width, height); + + DBG_PRINT("*** WindowsWindow: reconfigureWindow0.X\n"); +} + +/* + * Class: jogamp_newt_windows_WindowsWindow + * Method: setTitle + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_windows_WindowsWindow_setTitle0 + (JNIEnv *env, jclass clazz, jlong window, jstring title) +{ + HWND hwnd = (HWND) (intptr_t) window; + if (title != NULL) { + jchar *titleString = NewtCommon_GetNullTerminatedStringChars(env, title); + if (titleString != NULL) { + SetWindowTextW(hwnd, titleString); + free(titleString); + } + } +} + +/* + * Class: jogamp_newt_windows_WindowsWindow + * Method: requestFocus + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_windows_WindowsWindow_requestFocus0 + (JNIEnv *env, jobject obj, jlong window, jboolean force) +{ + DBG_PRINT("*** WindowsWindow: RequestFocus0\n"); + NewtWindows_requestFocus ( env, obj, (HWND) (intptr_t) window, force) ; +} + + diff --git a/src/newt/native/X11Window.c b/src/newt/native/X11Window.c new file mode 100644 index 000000000..66b036ef5 --- /dev/null +++ b/src/newt/native/X11Window.c @@ -0,0 +1,1583 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +// Building on obsolete platform on SPARC right now +#ifdef __sparc + #include <inttypes.h> +#else + #include <stdint.h> +#endif +#include <unistd.h> +#include <errno.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/Xatom.h> + +#include <X11/extensions/Xrandr.h> + +#include "jogamp_newt_x11_X11Screen.h" +#include "jogamp_newt_x11_X11Display.h" +#include "jogamp_newt_x11_X11Window.h" + +#include "MouseEvent.h" +#include "InputEvent.h" +#include "KeyEvent.h" +#include "WindowEvent.h" +#include "ScreenMode.h" + +#include "NewtCommon.h" + +// #define VERBOSE_ON 1 + +#ifdef VERBOSE_ON + #define DBG_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) + + #define DUMP_VISUAL_INFO(a,b) _dumpVisualInfo((a),(b)) + + static void _dumpVisualInfo(const char * msg, XVisualInfo *pVisualQuery) { + if(pVisualQuery!=NULL) { + fprintf(stderr, "%s: screen %d, visual: %p, visual-id: 0x%X, depth: %d, class %d, cmap sz: %d, bpp: 3x%d, rgb 0x%X 0x%X 0x%X\n", + msg, + pVisualQuery->screen, + pVisualQuery->visual, + (int)pVisualQuery->visualid, + pVisualQuery->depth, + pVisualQuery->class, + pVisualQuery->colormap_size, + pVisualQuery->bits_per_rgb, + (int)pVisualQuery->red_mask, + (int)pVisualQuery->green_mask, + (int)pVisualQuery->blue_mask + ); + } else { + fprintf(stderr, "%s: NULL XVisualInfo\n", msg); + } + } + +#else + + #define DBG_PRINT(...) + + #define DUMP_VISUAL_INFO(a,b) + +#endif + +/** + * Keycode + */ + +#define IS_WITHIN(k,a,b) ((a)<=(k)&&(k)<=(b)) + +static jint X11KeySym2NewtVKey(KeySym keySym) { + if(IS_WITHIN(keySym,XK_F1,XK_F12)) + return (keySym-XK_F1)+J_VK_F1; + + switch(keySym) { + case XK_Alt_L: + case XK_Alt_R: + return J_VK_ALT; + + case XK_Left: + return J_VK_LEFT; + case XK_Right: + return J_VK_RIGHT; + case XK_Up: + return J_VK_UP; + case XK_Down: + return J_VK_DOWN; + case XK_Page_Up: + return J_VK_PAGE_UP; + case XK_Page_Down: + return J_VK_PAGE_DOWN; + case XK_Shift_L: + case XK_Shift_R: + return J_VK_SHIFT; + case XK_Control_L: + case XK_Control_R: + return J_VK_CONTROL; + case XK_Escape: + return J_VK_ESCAPE; + case XK_Delete: + return J_VK_DELETE; + } + return keySym; +} + +static jint X11InputState2NewtModifiers(unsigned int xstate) { + jint modifiers = 0; + if ((ControlMask & xstate) != 0) { + modifiers |= EVENT_CTRL_MASK; + } + if ((ShiftMask & xstate) != 0) { + modifiers |= EVENT_SHIFT_MASK; + } + if ((Mod1Mask & xstate) != 0) { + modifiers |= EVENT_ALT_MASK; + } + if ((Button1Mask & xstate) != 0) { + modifiers |= EVENT_BUTTON1_MASK; + } + if ((Button2Mask & xstate) != 0) { + modifiers |= EVENT_BUTTON2_MASK; + } + if ((Button3Mask & xstate) != 0) { + modifiers |= EVENT_BUTTON3_MASK; + } + + return modifiers; +} + +static const char * const ClazzNameNewtWindow = "com/jogamp/newt/Window"; + +static jclass newtWindowClz=NULL; + +static jmethodID sizeChangedID = NULL; +static jmethodID positionChangedID = NULL; +static jmethodID focusChangedID = NULL; +static jmethodID visibleChangedID = NULL; +static jmethodID windowDestroyNotifyID = NULL; +static jmethodID windowRepaintID = NULL; +static jmethodID windowReparentedID = NULL; +static jmethodID enqueueMouseEventID = NULL; +static jmethodID sendMouseEventID = NULL; +static jmethodID enqueueKeyEventID = NULL; +static jmethodID sendKeyEventID = NULL; +static jmethodID focusActionID = NULL; +static jmethodID enqueueRequestFocusID = NULL; + +static jmethodID displayCompletedID = NULL; + + +/** + * Display + */ + +static JNIEnv * x11ErrorHandlerJNIEnv = NULL; +static XErrorHandler origErrorHandler = NULL ; + +static int displayDispatchErrorHandler(Display *dpy, XErrorEvent *e) +{ + fprintf(stderr, "Warning: NEWT X11 Error: DisplayDispatch %p, Code 0x%X, errno %s\n", dpy, e->error_code, strerror(errno)); + + if (e->error_code == BadAtom) { + fprintf(stderr, " BadAtom (%p): Atom probably already removed\n", (void*)e->resourceid); + } else if (e->error_code == BadWindow) { + fprintf(stderr, " BadWindow (%p): Window probably already removed\n", (void*)e->resourceid); + } else { + NewtCommon_throwNewRuntimeException(x11ErrorHandlerJNIEnv, "NEWT X11 Error: Display %p, Code 0x%X, errno %s", + dpy, e->error_code, strerror(errno)); + } + + return 0; +} + +static void displayDispatchErrorHandlerEnable(int onoff, JNIEnv * env) { + if(onoff) { + if(NULL==origErrorHandler) { + x11ErrorHandlerJNIEnv = env; + origErrorHandler = XSetErrorHandler(displayDispatchErrorHandler); + } + } else { + if(NULL!=origErrorHandler) { + XSetErrorHandler(origErrorHandler); + origErrorHandler = NULL; + } + } +} + +/* + * Class: jogamp_newt_x11_X11Display + * Method: initIDs + * Signature: (Z)Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_x11_X11Display_initIDs0 + (JNIEnv *env, jclass clazz) +{ + jclass c; + + NewtCommon_init(env); + + displayCompletedID = (*env)->GetMethodID(env, clazz, "displayCompleted", "(JJ)V"); + if (displayCompletedID == NULL) { + return JNI_FALSE; + } + + if(NULL==newtWindowClz) { + c = (*env)->FindClass(env, ClazzNameNewtWindow); + if(NULL==c) { + NewtCommon_FatalError(env, "NEWT X11Window: can't find %s", ClazzNameNewtWindow); + } + newtWindowClz = (jclass)(*env)->NewGlobalRef(env, c); + (*env)->DeleteLocalRef(env, c); + if(NULL==newtWindowClz) { + NewtCommon_FatalError(env, "NEWT X11Window: can't use %s", ClazzNameNewtWindow); + } + } + + return JNI_TRUE; +} + +/* + * Class: jogamp_newt_x11_X11Display + * Method: CompleteDisplay + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_x11_X11Display_CompleteDisplay0 + (JNIEnv *env, jobject obj, jlong display) +{ + Display * dpy = (Display *)(intptr_t)display; + jlong javaObjectAtom; + jlong windowDeleteAtom; + + if(dpy==NULL) { + NewtCommon_FatalError(env, "invalid display connection.."); + } + + javaObjectAtom = (jlong) XInternAtom(dpy, "JOGL_JAVA_OBJECT", False); + if(None==javaObjectAtom) { + NewtCommon_throwNewRuntimeException(env, "could not create Atom JOGL_JAVA_OBJECT, bail out!"); + return; + } + + windowDeleteAtom = (jlong) XInternAtom(dpy, "WM_DELETE_WINDOW", False); + if(None==windowDeleteAtom) { + NewtCommon_throwNewRuntimeException(env, "could not create Atom WM_DELETE_WINDOW, bail out!"); + return; + } + + // XSetCloseDownMode(dpy, RetainTemporary); // Just a try .. + + DBG_PRINT("X11: X11Display_completeDisplay dpy %p\n", dpy); + + (*env)->CallVoidMethod(env, obj, displayCompletedID, javaObjectAtom, windowDeleteAtom); +} + +/** + * Window + */ + +static int putPtrIn32Long(unsigned long * dst, uintptr_t src) { + int i=0; + dst[i++] = (unsigned long) ( ( src >> 0 ) & 0xFFFFFFFF ) ; + if(sizeof(uintptr_t) == 8) { + dst[i++] = (unsigned long) ( ( src >> 32 ) & 0xFFFFFFFF ) ; + } + return i; +} + +static uintptr_t getPtrOut32Long(unsigned long * src) { + uintptr_t res = ( (uintptr_t) ( src[0] & 0xFFFFFFFF ) ) << 0 ; + if(sizeof(uintptr_t) == 8) { + res |= ( (uintptr_t) ( src[1] & 0xFFFFFFFF ) ) << 32 ; + } + return res; +} + +static void setJavaWindowProperty(JNIEnv *env, Display *dpy, Window window, jlong javaObjectAtom, jobject jwindow) { + unsigned long jogl_java_object_data[2]; // X11 is based on 'unsigned long' + int nitems_32 = putPtrIn32Long( jogl_java_object_data, (uintptr_t) jwindow); + + { + jobject test = (jobject) getPtrOut32Long(jogl_java_object_data); + if( ! (jwindow==test) ) { + NewtCommon_FatalError(env, "Internal Error .. Encoded Window ref not the same %p != %p !", jwindow, test); + } + } + + XChangeProperty( dpy, window, (Atom)javaObjectAtom, (Atom)javaObjectAtom, 32, PropModeReplace, + (unsigned char *)&jogl_java_object_data, nitems_32); +} + +static jobject getJavaWindowProperty(JNIEnv *env, Display *dpy, Window window, jlong javaObjectAtom, Bool showWarning) { + Atom actual_type_return; + int actual_format_return; + int nitems_32 = ( sizeof(uintptr_t) == 8 ) ? 2 : 1 ; + unsigned char * jogl_java_object_data_pp = NULL; + jobject jwindow; + + { + unsigned long nitems_return = 0; + unsigned long bytes_after_return = 0; + jobject jwindow = NULL; + int res; + + res = XGetWindowProperty(dpy, window, (Atom)javaObjectAtom, 0, nitems_32, False, + (Atom)javaObjectAtom, &actual_type_return, &actual_format_return, + &nitems_return, &bytes_after_return, &jogl_java_object_data_pp); + + if ( Success != res ) { + if(True==showWarning) { + fprintf(stderr, "Warning: NEWT X11Window: Could not fetch Atom JOGL_JAVA_OBJECT window property (res %d) nitems_return %ld, bytes_after_return %ld, result 0!\n", res, nitems_return, bytes_after_return); + } + return NULL; + } + + if(actual_type_return!=(Atom)javaObjectAtom || nitems_return<nitems_32 || NULL==jogl_java_object_data_pp) { + XFree(jogl_java_object_data_pp); + if(True==showWarning) { + fprintf(stderr, "Warning: NEWT X11Window: Fetched invalid Atom JOGL_JAVA_OBJECT window property (res %d) nitems_return %ld, bytes_after_return %ld, actual_type_return %ld, JOGL_JAVA_OBJECT %ld, result 0!\n", + res, nitems_return, bytes_after_return, (long)actual_type_return, javaObjectAtom); + } + return NULL; + } + } + + jwindow = (jobject) getPtrOut32Long( (unsigned long *) jogl_java_object_data_pp ) ; + XFree(jogl_java_object_data_pp); + +#ifdef VERBOSE_ON + if(JNI_FALSE == (*env)->IsInstanceOf(env, jwindow, newtWindowClz)) { + NewtCommon_throwNewRuntimeException(env, "fetched Atom JOGL_JAVA_OBJECT window is not a NEWT Window: javaWindow 0x%X !", jwindow); + } +#endif + return jwindow; +} + +/** @return zero if fails, non zero if OK */ +static Status NewtWindows_getRootAndParent (Display *dpy, Window w, Window * root_return, Window * parent_return) { + Window *children_return=NULL; + unsigned int nchildren_return=0; + + Status res = XQueryTree(dpy, w, root_return, parent_return, &children_return, &nchildren_return); + if(NULL!=children_return) { + XFree(children_return); + } + return res; +} +static Window NewtWindows_getRoot (Display *dpy, Window w) { + Window root_return; + Window parent_return; + if( 0 != NewtWindows_getRootAndParent(dpy, w, &root_return, &parent_return) ) { + return root_return; + } + return 0; +} +static Window NewtWindows_getParent (Display *dpy, Window w) { + Window root_return; + Window parent_return; + if( 0 != NewtWindows_getRootAndParent(dpy, w, &root_return, &parent_return) ) { + return parent_return; + } + return 0; +} + + +static void NewtWindows_requestFocus (JNIEnv *env, jobject window, Display *dpy, Window w, jboolean force) { + XWindowAttributes xwa; + Window focus_return; + int revert_to_return; + + XGetInputFocus(dpy, &focus_return, &revert_to_return); + if( JNI_TRUE==force || focus_return!=w) { + if( JNI_TRUE==force || JNI_FALSE == (*env)->CallBooleanMethod(env, window, focusActionID) ) { + XRaiseWindow(dpy, w); + // Avoid 'BadMatch' errors from XSetInputFocus, ie if window is not viewable + XGetWindowAttributes(dpy, w, &xwa); + if(xwa.map_state == IsViewable) { + XSetInputFocus(dpy, w, RevertToParent, CurrentTime); + } + } + } + XSync(dpy, False); +} + +#define MWM_HINTS_DECORATIONS (1L << 1) +#define PROP_MWM_HINTS_ELEMENTS 5 + +static void NewtWindows_setDecorations (Display *dpy, Window w, Bool decorated) { + unsigned long mwmhints[PROP_MWM_HINTS_ELEMENTS] = { 0, 0, 0, 0, 0 }; // flags, functions, decorations, input_mode, status + Atom _MOTIF_WM_HINTS_DECORATIONS = XInternAtom( dpy, "_MOTIF_WM_HINTS", False ); + Atom _NET_WM_WINDOW_TYPE = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE", False ); + Atom types[3]={0}; + int ntypes=0; + if(True==decorated) { + types[ntypes++] = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False ); + } else { + types[ntypes++] = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE_POPUP_MENU", False ); + types[ntypes++] = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False ); + } + + mwmhints[0] = MWM_HINTS_DECORATIONS; + mwmhints[2] = decorated ; + + XChangeProperty( dpy, w, _MOTIF_WM_HINTS_DECORATIONS, _MOTIF_WM_HINTS_DECORATIONS, 32, PropModeReplace, (unsigned char *)&mwmhints, PROP_MWM_HINTS_ELEMENTS); + XChangeProperty( dpy, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char *)&types, ntypes); +} + +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 + +static void NewtWindows_setFullscreen (Display *dpy, Window root, Window w, Bool fullscreen) { + Atom _NET_WM_STATE = XInternAtom( dpy, "_NET_WM_STATE", False ); + Atom _NET_WM_STATE_ABOVE = XInternAtom( dpy, "_NET_WM_STATE_ABOVE", False ); + Atom _NET_WM_STATE_FULLSCREEN = XInternAtom( dpy, "_NET_WM_STATE_FULLSCREEN", False ); + + Atom types[2]={0}; + int ntypes=0; + + types[ntypes++] = _NET_WM_STATE_FULLSCREEN; + types[ntypes++] = _NET_WM_STATE_ABOVE; + + XEvent xev; + memset ( &xev, 0, sizeof(xev) ); + + xev.type = ClientMessage; + xev.xclient.window = w; + xev.xclient.message_type = _NET_WM_STATE; + xev.xclient.format = 32; + + if(True==fullscreen) { + xev.xclient.data.l[0] = _NET_WM_STATE_ADD; + xev.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN; + xev.xclient.data.l[2] = _NET_WM_STATE_ABOVE; + xev.xclient.data.l[3] = 1; //source indication for normal applications + } else { + xev.xclient.data.l[0] = _NET_WM_STATE_REMOVE; + xev.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN; + xev.xclient.data.l[2] = _NET_WM_STATE_ABOVE; + xev.xclient.data.l[3] = 1; //source indication for normal applications + } + + XChangeProperty( dpy, w, _NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char *)&types, ntypes); + XSync(dpy, False); + XSendEvent (dpy, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev ); +} + +#define USE_SENDIO_DIRECT 1 + +/* + * Class: jogamp_newt_x11_X11Display + * Method: DispatchMessages + * Signature: (JIJJ)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_x11_X11Display_DispatchMessages0 + (JNIEnv *env, jobject obj, jlong display, jlong javaObjectAtom, jlong wmDeleteAtom) +{ + Display * dpy = (Display *) (intptr_t) display; + int num_events = 100; + + if ( NULL == dpy ) { + return; + } + + // Periodically take a break + while( num_events > 0 ) { + jobject jwindow = NULL; + XEvent evt; + KeySym keySym = 0; + jint modifiers = 0; + char keyChar = 0; + char text[255]; + + // num_events = XPending(dpy); // I/O Flush .. + // num_events = XEventsQueued(dpy, QueuedAfterFlush); // I/O Flush only of no already queued events are available + // num_events = XEventsQueued(dpy, QueuedAlready); // no I/O Flush at all, doesn't work on some cards (eg ATI) + if ( 0 >= XEventsQueued(dpy, QueuedAfterFlush) ) { + // DBG_PRINT( "X11: DispatchMessages 0x%X - Leave 1\n", dpy); + return; + } + + XNextEvent(dpy, &evt); + num_events--; + + if( 0==evt.xany.window ) { + NewtCommon_throwNewRuntimeException(env, "event window NULL, bail out!"); + return ; + } + + if(dpy!=evt.xany.display) { + NewtCommon_throwNewRuntimeException(env, "wrong display, bail out!"); + return ; + } + + // DBG_PRINT( "X11: DispatchMessages dpy %p, win %p, Event %d\n", (void*)dpy, (void*)evt.xany.window, evt.type); + + displayDispatchErrorHandlerEnable(1, env); + + jwindow = getJavaWindowProperty(env, dpy, evt.xany.window, javaObjectAtom, + #ifdef VERBOSE_ON + True + #else + False + #endif + ); + + displayDispatchErrorHandlerEnable(0, env); + + if(NULL==jwindow) { + fprintf(stderr, "Warning: NEWT X11 DisplayDispatch %p, Couldn't handle event %d for X11 window %p\n", + (void*)dpy, evt.type, (void*)evt.xany.window); + continue; + } + + switch(evt.type) { + case KeyRelease: + case KeyPress: + if(XLookupString(&evt.xkey,text,255,&keySym,0)==1) { + KeySym lower_return = 0, upper_return = 0; + keyChar=text[0]; + XConvertCase(keySym, &lower_return, &upper_return); + // always return upper case, set modifier masks (SHIFT, ..) + keySym = upper_return; + modifiers = X11InputState2NewtModifiers(evt.xkey.state); + } else { + keyChar=0; + } + break; + + case ButtonPress: + case ButtonRelease: + case MotionNotify: + modifiers = X11InputState2NewtModifiers(evt.xbutton.state); + break; + + default: + break; + } + + switch(evt.type) { + case ButtonPress: + (*env)->CallVoidMethod(env, jwindow, enqueueRequestFocusID, JNI_FALSE); + #ifdef USE_SENDIO_DIRECT + (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, + (jint) EVENT_MOUSE_PRESSED, + modifiers, + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0 /*rotation*/); + #else + (*env)->CallVoidMethod(env, jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_PRESSED, + modifiers, + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0 /*rotation*/); + #endif + break; + case ButtonRelease: + #ifdef USE_SENDIO_DIRECT + (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jint) EVENT_MOUSE_RELEASED, + modifiers, + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0 /*rotation*/); + #else + (*env)->CallVoidMethod(env, jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_RELEASED, + modifiers, + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0 /*rotation*/); + #endif + break; + case MotionNotify: + #ifdef USE_SENDIO_DIRECT + (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, (jint) EVENT_MOUSE_MOVED, + modifiers, + (jint) evt.xmotion.x, (jint) evt.xmotion.y, (jint) 0, 0 /*rotation*/); + #else + (*env)->CallVoidMethod(env, jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_MOVED, + modifiers, + (jint) evt.xmotion.x, (jint) evt.xmotion.y, (jint) 0, 0 /*rotation*/); + #endif + break; + case KeyPress: + #ifdef USE_SENDIO_DIRECT + (*env)->CallVoidMethod(env, jwindow, sendKeyEventID, (jint) EVENT_KEY_PRESSED, + modifiers, X11KeySym2NewtVKey(keySym), (jchar) keyChar); + #else + (*env)->CallVoidMethod(env, jwindow, enqueueKeyEventID, JNI_FALSE, (jint) EVENT_KEY_PRESSED, + modifiers, X11KeySym2NewtVKey(keySym), (jchar) keyChar); + #endif + + break; + case KeyRelease: + #ifdef USE_SENDIO_DIRECT + (*env)->CallVoidMethod(env, jwindow, sendKeyEventID, (jint) EVENT_KEY_RELEASED, + modifiers, X11KeySym2NewtVKey(keySym), (jchar) keyChar); + + (*env)->CallVoidMethod(env, jwindow, sendKeyEventID, (jint) EVENT_KEY_TYPED, + modifiers, (jint) -1, (jchar) keyChar); + #else + (*env)->CallVoidMethod(env, jwindow, enqueueKeyEventID, JNI_FALSE, (jint) EVENT_KEY_RELEASED, + modifiers, X11KeySym2NewtVKey(keySym), (jchar) keyChar); + + (*env)->CallVoidMethod(env, jwindow, enqueueKeyEventID, JNI_FALSE, (jint) EVENT_KEY_TYPED, + modifiers, (jint) -1, (jchar) keyChar); + #endif + + break; + case DestroyNotify: + DBG_PRINT( "X11: event . DestroyNotify call %p, parent %p, child-event: %d\n", + (void*)evt.xdestroywindow.window, (void*)evt.xdestroywindow.event, evt.xdestroywindow.window != evt.xdestroywindow.event); + if ( evt.xdestroywindow.window == evt.xdestroywindow.event ) { + // ignore child destroy notification + } + break; + case CreateNotify: + DBG_PRINT( "X11: event . CreateNotify call %p, parent %p, child-event: 1\n", + (void*)evt.xcreatewindow.window, (void*) evt.xcreatewindow.parent); + break; + case ConfigureNotify: + DBG_PRINT( "X11: event . ConfigureNotify call %p (parent %p, above %p) %d/%d %dx%d %d, child-event: %d\n", + (void*)evt.xconfigure.window, (void*)evt.xconfigure.event, (void*)evt.xconfigure.above, + evt.xconfigure.x, evt.xconfigure.y, evt.xconfigure.width, evt.xconfigure.height, + evt.xconfigure.override_redirect, evt.xconfigure.window != evt.xconfigure.event); + if ( evt.xconfigure.window == evt.xconfigure.event ) { + // ignore child window change notification + (*env)->CallVoidMethod(env, jwindow, sizeChangedID, + (jint) evt.xconfigure.width, (jint) evt.xconfigure.height, JNI_FALSE); + (*env)->CallVoidMethod(env, jwindow, positionChangedID, + (jint) evt.xconfigure.x, (jint) evt.xconfigure.y); + } + break; + case ClientMessage: + if (evt.xclient.send_event==True && evt.xclient.data.l[0]==(Atom)wmDeleteAtom) { + DBG_PRINT( "X11: event . ClientMessage call %p type 0x%X !!!\n", + (void*)evt.xclient.window, (unsigned int)evt.xclient.message_type); + (*env)->CallVoidMethod(env, jwindow, windowDestroyNotifyID); + // Called by Window.java: CloseWindow(); + } + break; + + case FocusIn: + DBG_PRINT( "X11: event . FocusIn call %p\n", (void*)evt.xvisibility.window); + (*env)->CallVoidMethod(env, jwindow, focusChangedID, JNI_TRUE); + break; + + case FocusOut: + DBG_PRINT( "X11: event . FocusOut call %p\n", (void*)evt.xvisibility.window); + (*env)->CallVoidMethod(env, jwindow, focusChangedID, JNI_FALSE); + break; + + case Expose: + DBG_PRINT( "X11: event . Expose call %p %d/%d %dx%d count %d\n", (void*)evt.xexpose.window, + evt.xexpose.x, evt.xexpose.y, evt.xexpose.width, evt.xexpose.height, evt.xexpose.count); + + if (evt.xexpose.count == 0 && evt.xexpose.width > 0 && evt.xexpose.height > 0) { + (*env)->CallVoidMethod(env, jwindow, windowRepaintID, + evt.xexpose.x, evt.xexpose.y, evt.xexpose.width, evt.xexpose.height); + } + break; + + case MapNotify: + DBG_PRINT( "X11: event . MapNotify call Event %p, Window %p, override_redirect %d, child-event: %d\n", + (void*)evt.xmap.event, (void*)evt.xmap.window, (int)evt.xmap.override_redirect, + evt.xmap.event!=evt.xmap.window); + if( evt.xmap.event == evt.xmap.window ) { + // ignore child window notification + (*env)->CallVoidMethod(env, jwindow, visibleChangedID, JNI_TRUE); + } + break; + + case UnmapNotify: + DBG_PRINT( "X11: event . UnmapNotify call Event %p, Window %p, from_configure %d, child-event: %d\n", + (void*)evt.xunmap.event, (void*)evt.xunmap.window, (int)evt.xunmap.from_configure, + evt.xunmap.event!=evt.xunmap.window); + if( evt.xunmap.event == evt.xunmap.window ) { + // ignore child window notification + (*env)->CallVoidMethod(env, jwindow, visibleChangedID, JNI_FALSE); + } + break; + + case ReparentNotify: + { + jlong parentResult; // 0 if root, otherwise proper value + Window winRoot, winTopParent; + #ifdef VERBOSE_ON + Window oldParentRoot, oldParentTopParent; + Window parentRoot, parentTopParent; + if( 0 == NewtWindows_getRootAndParent(dpy, evt.xreparent.event, &oldParentRoot, &oldParentTopParent) ) { + oldParentRoot=0; oldParentTopParent = 0; + } + if( 0 == NewtWindows_getRootAndParent(dpy, evt.xreparent.parent, &parentRoot, &parentTopParent) ) { + parentRoot=0; parentTopParent = 0; + } + #endif + if( 0 == NewtWindows_getRootAndParent(dpy, evt.xreparent.window, &winRoot, &winTopParent) ) { + winRoot=0; winTopParent = 0; + } + if(evt.xreparent.parent == winRoot) { + parentResult = 0; // our java indicator for root window + } else { + parentResult = (jlong) (intptr_t) evt.xreparent.parent; + } + #ifdef VERBOSE_ON + DBG_PRINT( "X11: event . ReparentNotify: call OldParent %p (root %p, top %p), NewParent %p (root %p, top %p), Window %p (root %p, top %p)\n", + (void*)evt.xreparent.event, (void*)oldParentRoot, (void*)oldParentTopParent, + (void*)evt.xreparent.parent, (void*)parentRoot, (void*)parentTopParent, + (void*)evt.xreparent.window, (void*)winRoot, (void*)winTopParent); + #endif + + (*env)->CallVoidMethod(env, jwindow, windowReparentedID, parentResult); + } + break; + + // unhandled events .. yet .. + + default: + DBG_PRINT("X11: event . unhandled %d 0x%X call %p\n", (int)evt.type, (unsigned int)evt.type, (void*)evt.xunmap.window); + } + } +} + + +/** + * Screen + */ + +/* + * Class: jogamp_newt_x11_X11Screen + * Method: GetScreen + * Signature: (JI)J + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_x11_X11Screen_GetScreen0 + (JNIEnv *env, jclass clazz, jlong display, jint screen_index) +{ + Display * dpy = (Display *)(intptr_t)display; + Screen * scrn= NULL; + + DBG_PRINT("X11: X11Screen_GetScreen0 dpy %p START\n", dpy); + + if(dpy==NULL) { + NewtCommon_FatalError(env, "invalid display connection.."); + } + + scrn = ScreenOfDisplay(dpy,screen_index); + if(scrn==NULL) { + scrn=DefaultScreenOfDisplay(dpy); + } + if(scrn==NULL) { + fprintf(stderr, "couldn't get screen ..\n"); + } + DBG_PRINT("X11: X11Screen_GetScreen0 scrn %p DONE\n", scrn); + return (jlong) (intptr_t) scrn; +} + +JNIEXPORT jint JNICALL Java_jogamp_newt_x11_X11Screen_getWidth0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display * dpy = (Display *) (intptr_t) display; + return (jint) XDisplayWidth( dpy, scrn_idx); +} + +JNIEXPORT jint JNICALL Java_jogamp_newt_x11_X11Screen_getHeight0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display * dpy = (Display *) (intptr_t) display; + return (jint) XDisplayHeight( dpy, scrn_idx); +} + + +static Bool NewtScreen_getRANDRVersion(Display *dpy, int *major, int *minor) { + if( 0 == XRRQueryVersion(dpy, major, minor) ) { + return False; + } + return True; +} + +static Bool NewtScreen_hasRANDR(Display *dpy) { + int major, minor; + return NewtScreen_getRANDRVersion(dpy, &major, &minor); +} + +static int NewtScreen_XRotation2Degree(JNIEnv *env, int xrotation) { + int rot; + if(xrotation == RR_Rotate_0) { + rot = 0; + } + else if(xrotation == RR_Rotate_90) { + rot = 90; + } + else if(xrotation == RR_Rotate_180) { + rot = 180; + } + else if(xrotation == RR_Rotate_270) { + rot = 270; + } else { + NewtCommon_throwNewRuntimeException(env, "invalid native rotation: %d", xrotation); + } + return rot; +} + +/* + * Class: jogamp_newt_x11_X11Screen + * Method: getAvailableScreenModeRotations0 + * Signature: (JI)I + */ +JNIEXPORT jintArray JNICALL Java_jogamp_newt_x11_X11Screen_getAvailableScreenModeRotations0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + int num_rotations = 0; + Rotation cur_rotation, rotations_supported; + int rotations[4]; + int major, minor; + + if(False == NewtScreen_getRANDRVersion(dpy, &major, &minor)) { + fprintf(stderr, "RANDR not available\n"); + return (*env)->NewIntArray(env, 0); + } + + rotations_supported = XRRRotations (dpy, (int)scrn_idx, &cur_rotation); + + if(0 != (rotations_supported & RR_Rotate_0)) { + rotations[num_rotations++] = 0; + } + if(0 != (rotations_supported & RR_Rotate_90)) { + rotations[num_rotations++] = 90; + } + if(0 != (rotations_supported & RR_Rotate_180)) { + rotations[num_rotations++] = 180; + } + if(0 != (rotations_supported & RR_Rotate_270)) { + rotations[num_rotations++] = 270; + } + + jintArray properties = NULL; + + if(num_rotations>0) { + properties = (*env)->NewIntArray(env, num_rotations); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", num_rotations); + } + + // move from the temp structure to the java structure + (*env)->SetIntArrayRegion(env, properties, 0, num_rotations, rotations); + } + + return properties; +} + +/* + * Class: jogamp_newt_x11_X11Screen + * Method: getNumScreenModeResolution0 + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_jogamp_newt_x11_X11Screen_getNumScreenModeResolutions0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_jogamp_newt_x11_X11Screen_getNumScreenModeResolutions0: RANDR not available\n"); + return 0; + } + + int num_sizes; + XRRScreenSize *xrrs = XRRSizes(dpy, (int)scrn_idx, &num_sizes); //get possible screen resolutions + + return num_sizes; +} + +/* + * Class: jogamp_newt_x11_X11Screen + * Method: getScreenModeResolutions0 + * Signature: (JII)[I + */ +JNIEXPORT jintArray JNICALL Java_jogamp_newt_x11_X11Screen_getScreenModeResolution0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx, jint resMode_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_jogamp_newt_x11_X11Screen_getScreenModeResolution0: RANDR not available\n"); + return (*env)->NewIntArray(env, 0); + } + + int num_sizes; + XRRScreenSize *xrrs = XRRSizes(dpy, (int)scrn_idx, &num_sizes); //get possible screen resolutions + + if( 0 > resMode_idx || resMode_idx >= num_sizes ) { + NewtCommon_throwNewRuntimeException(env, "Invalid resolution index: ! 0 < %d < %d", resMode_idx, num_sizes); + } + + // Fill the properties in temp jint array + int propIndex = 0; + jint prop[4]; + + prop[propIndex++] = xrrs[(int)resMode_idx].width; + prop[propIndex++] = xrrs[(int)resMode_idx].height; + prop[propIndex++] = xrrs[(int)resMode_idx].mwidth; + prop[propIndex++] = xrrs[(int)resMode_idx].mheight; + + jintArray properties = (*env)->NewIntArray(env, 4); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", 4); + } + + // move from the temp structure to the java structure + (*env)->SetIntArrayRegion(env, properties, 0, 4, prop); + + return properties; +} + +/* + * Class: jogamp_newt_x11_X11Screen + * Method: getScreenModeRates0 + * Signature: (JII)[I + */ +JNIEXPORT jintArray JNICALL Java_jogamp_newt_x11_X11Screen_getScreenModeRates0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx, jint resMode_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_jogamp_newt_x11_X11Screen_getScreenModeRates0: RANDR not available\n"); + return (*env)->NewIntArray(env, 0); + } + + int num_sizes; + XRRScreenSize *xrrs = XRRSizes(dpy, (int)scrn_idx, &num_sizes); //get possible screen resolutions + + if( 0 > resMode_idx || resMode_idx >= num_sizes ) { + NewtCommon_throwNewRuntimeException(env, "Invalid resolution index: ! 0 < %d < %d", resMode_idx, num_sizes); + } + + int num_rates; + short *rates = XRRRates(dpy, (int)scrn_idx, (int)resMode_idx, &num_rates); + + jint prop[num_rates]; + int i; + for(i=0; i<num_rates; i++) { + prop[i] = (int) rates[i]; + /** fprintf(stderr, "rate[%d, %d, %d/%d]: %d\n", (int)scrn_idx, resMode_idx, i, num_rates, prop[i]); */ + } + + jintArray properties = (*env)->NewIntArray(env, num_rates); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", num_rates); + } + + // move from the temp structure to the java structure + (*env)->SetIntArrayRegion(env, properties, 0, num_rates, prop); + + return properties; +} + +/* + * Class: jogamp_newt_x11_X11Screen + * Method: getCurrentScreenRate0 + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_jogamp_newt_x11_X11Screen_getCurrentScreenRate0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_jogamp_newt_x11_X11Screen_getCurrentScreenRate0: RANDR not available\n"); + return -1; + } + + // get current resolutions and frequencies + XRRScreenConfiguration *conf = XRRGetScreenInfo(dpy, root); + short original_rate = XRRConfigCurrentRate(conf); + + //free + XRRFreeScreenConfigInfo(conf); + + return (jint) original_rate; +} + +/* + * Class: jogamp_newt_x11_X11Screen + * Method: getCurrentScreenRotation0 + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_jogamp_newt_x11_X11Screen_getCurrentScreenRotation0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_jogamp_newt_x11_X11Screen_getCurrentScreenRotation0: RANDR not available\n"); + return -1; + } + + //get current resolutions and frequencies + XRRScreenConfiguration *conf = XRRGetScreenInfo(dpy, root); + + Rotation rotation; + XRRConfigCurrentConfiguration(conf, &rotation); + + //free + XRRFreeScreenConfigInfo(conf); + + return NewtScreen_XRotation2Degree(env, rotation); +} + + +/* + * Class: jogamp_newt_x11_X11Screen + * Method: getCurrentScreenResolutionIndex0 + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_jogamp_newt_x11_X11Screen_getCurrentScreenResolutionIndex0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_jogamp_newt_x11_X11Screen_getCurrentScreenResolutionIndex0: RANDR not available\n"); + return -1; + } + + // get current resolutions and frequency configuration + XRRScreenConfiguration *conf = XRRGetScreenInfo(dpy, root); + short original_rate = XRRConfigCurrentRate(conf); + + Rotation original_rotation; + SizeID original_size_id = XRRConfigCurrentConfiguration(conf, &original_rotation); + + //free + XRRFreeScreenConfigInfo(conf); + + return (jint)original_size_id; +} + +/* + * Class: jogamp_newt_x11_X11Screen + * Method: setCurrentScreenModeStart0 + * Signature: (JIIII)Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_x11_X11Screen_setCurrentScreenModeStart0 + (JNIEnv *env, jclass clazz, jlong display, jint screen_idx, jint resMode_idx, jint freq, jint rotation) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)screen_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_jogamp_newt_x11_X11Screen_setCurrentScreenModeStart0: RANDR not available\n"); + return JNI_FALSE; + } + + int num_sizes; + XRRScreenSize *xrrs = XRRSizes(dpy, (int)screen_idx, &num_sizes); //get possible screen resolutions + XRRScreenConfiguration *conf; + int rot; + + if( 0 > resMode_idx || resMode_idx >= num_sizes ) { + NewtCommon_throwNewRuntimeException(env, "Invalid resolution index: ! 0 < %d < %d", resMode_idx, num_sizes); + } + + conf = XRRGetScreenInfo(dpy, root); + + switch(rotation) { + case 0: + rot = RR_Rotate_0; + break; + case 90: + rot = RR_Rotate_90; + break; + case 180: + rot = RR_Rotate_180; + break; + case 270: + rot = RR_Rotate_270; + break; + default: + NewtCommon_throwNewRuntimeException(env, "Invalid rotation: %d", rotation); + } + + DBG_PRINT("X11Screen.setCurrentScreenMode0: CHANGED TO %d: %d x %d PIXELS, %d Hz, %d degree\n", + resMode_idx, xrrs[resMode_idx].width, xrrs[resMode_idx].height, (int)freq, rotation); + + XRRSelectInput (dpy, root, RRScreenChangeNotifyMask); + + XSync(dpy, False); + XRRSetScreenConfigAndRate(dpy, conf, root, (int)resMode_idx, rot, (short)freq, CurrentTime); + XSync(dpy, False); + + //free + XRRFreeScreenConfigInfo(conf); + XSync(dpy, False); + + return JNI_TRUE; +} + +/* + * Class: jogamp_newt_x11_X11Screen + * Method: setCurrentScreenModePollEnd0 + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_x11_X11Screen_setCurrentScreenModePollEnd0 + (JNIEnv *env, jclass clazz, jlong display, jint screen_idx, jint resMode_idx, jint freq, jint rotation) +{ + Display *dpy = (Display *) (intptr_t) display; + int randr_event_base, randr_error_base; + XEvent evt; + XRRScreenChangeNotifyEvent * scn_event = (XRRScreenChangeNotifyEvent *) &evt; + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_jogamp_newt_x11_X11Screen_setCurrentScreenModePollEnd0: RANDR not available\n"); + return JNI_FALSE; + } + + int num_sizes; + XRRScreenSize *xrrs = XRRSizes(dpy, (int)screen_idx, &num_sizes); //get possible screen resolutions + XRRScreenConfiguration *conf; + + if( 0 > resMode_idx || resMode_idx >= num_sizes ) { + NewtCommon_throwNewRuntimeException(env, "Invalid resolution index: ! 0 < %d < %d", resMode_idx, num_sizes); + } + + XRRQueryExtension(dpy, &randr_event_base, &randr_error_base); + + int done = 0; + int rot; + do { + if ( 0 >= XEventsQueued(dpy, QueuedAfterFlush) ) { + return; + } + XNextEvent(dpy, &evt); + + switch (evt.type - randr_event_base) { + case RRScreenChangeNotify: + rot = NewtScreen_XRotation2Degree(env, (int)scn_event->rotation); + DBG_PRINT( "XRANDR: event . RRScreenChangeNotify call %p (root %p) resIdx %d rot %d %dx%d\n", + (void*)scn_event->window, (void*)scn_event->root, + (int)scn_event->size_index, rot, + scn_event->width, scn_event->height); + // done = scn_event->size_index == resMode_idx; // not reliable .. + done = rot == rotation && + scn_event->width == xrrs[resMode_idx].width && + scn_event->height == xrrs[resMode_idx].height; + break; + default: + DBG_PRINT("RANDR: event . unhandled %d 0x%X call %p\n", (int)evt.type, (int)evt.type, (void*)evt.xany.window); + } + XRRUpdateConfiguration(&evt); + } while(!done); + + XSync(dpy, False); + +} + +/** + * Window + */ + +/* + * Class: jogamp_newt_x11_X11Window + * Method: initIDs + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_x11_X11Window_initIDs0 + (JNIEnv *env, jclass clazz) +{ + sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(IIZ)V"); + positionChangedID = (*env)->GetMethodID(env, clazz, "positionChanged", "(II)V"); + focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(Z)V"); + visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(Z)V"); + windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "()V"); + windowRepaintID = (*env)->GetMethodID(env, clazz, "windowRepaint", "(IIII)V"); + windowReparentedID = (*env)->GetMethodID(env, clazz, "windowReparented", "(J)V"); + enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZIIIIII)V"); + sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIII)V"); + enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZIIIC)V"); + sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); + enqueueRequestFocusID = (*env)->GetMethodID(env, clazz, "enqueueRequestFocus", "(Z)V"); + focusActionID = (*env)->GetMethodID(env, clazz, "focusAction", "()Z"); + + if (sizeChangedID == NULL || + positionChangedID == NULL || + focusChangedID == NULL || + visibleChangedID == NULL || + windowDestroyNotifyID == NULL || + windowRepaintID == NULL || + windowReparentedID == NULL || + enqueueMouseEventID == NULL || + sendMouseEventID == NULL || + enqueueKeyEventID == NULL || + sendKeyEventID == NULL || + focusActionID == NULL || + enqueueRequestFocusID == NULL) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +/* + * Class: jogamp_newt_x11_X11Window + * Method: CreateWindow + * Signature: (JJIJIIII)J + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_x11_X11Window_CreateWindow0 + (JNIEnv *env, jobject obj, jlong parent, jlong display, jint screen_index, + jlong visualID, + jlong javaObjectAtom, jlong windowDeleteAtom, + jint x, jint y, jint width, jint height, + jboolean undecorated) +{ + Display * dpy = (Display *)(intptr_t)display; + int scrn_idx = (int)screen_index; + Window windowParent = (Window) parent; + Window window = 0; + + XVisualInfo visualTemplate; + XVisualInfo *pVisualQuery = NULL; + Visual *visual = NULL; + int depth; + + XSetWindowAttributes xswa; + unsigned long attrMask; + int n; + + Screen* scrn; + Atom wm_delete_atom; + + if(dpy==NULL) { + NewtCommon_FatalError(env, "invalid display connection.."); + } + + if(visualID<0) { + NewtCommon_throwNewRuntimeException(env, "invalid VisualID .."); + return 0; + } + + XSync(dpy, False); + + scrn = ScreenOfDisplay(dpy, scrn_idx); + if(0==windowParent) { + windowParent = XRootWindowOfScreen(scrn); + } + if( XRootWindowOfScreen(scrn) != XRootWindow(dpy, scrn_idx) ) { + NewtCommon_FatalError(env, "XRoot Malfunction: %p != %p"+XRootWindowOfScreen(scrn), XRootWindow(dpy, scrn_idx)); + } + DBG_PRINT( "X11: CreateWindow dpy %p, parent %p, %x/%d %dx%d, undeco %d\n", + (void*)dpy, (void*)windowParent, x, y, width, height, undecorated); + + // try given VisualID on screen + memset(&visualTemplate, 0, sizeof(XVisualInfo)); + visualTemplate.screen = scrn_idx; + visualTemplate.visualid = (VisualID)visualID; + pVisualQuery = XGetVisualInfo(dpy, VisualIDMask|VisualScreenMask, &visualTemplate,&n); + DUMP_VISUAL_INFO("Given VisualID,ScreenIdx", pVisualQuery); + if(pVisualQuery!=NULL) { + visual = pVisualQuery->visual; + depth = pVisualQuery->depth; + visualID = (jlong)pVisualQuery->visualid; + XFree(pVisualQuery); + pVisualQuery=NULL; + } + DBG_PRINT( "X11: [CreateWindow] trying given (dpy %p, screen %d, visualID: %d, parent %p) found: %p\n", + dpy, scrn_idx, (int)visualID, (void*)windowParent, visual); + + if (visual==NULL) + { + NewtCommon_throwNewRuntimeException(env, "could not query Visual by given VisualID, bail out!"); + return 0; + } + + if(pVisualQuery!=NULL) { + XFree(pVisualQuery); + pVisualQuery=NULL; + } + + attrMask = ( CWBackingStore | CWBackingPlanes | CWBackingPixel | CWBackPixmap | + CWBorderPixel | CWColormap | CWOverrideRedirect ) ; + + memset(&xswa, 0, sizeof(xswa)); + xswa.override_redirect = False; // use the window manager, always + xswa.border_pixel = 0; + xswa.background_pixmap = None; + xswa.backing_store=NotUseful; /* NotUseful, WhenMapped, Always */ + xswa.backing_planes=0; /* planes to be preserved if possible */ + xswa.backing_pixel=0; /* value to use in restoring planes */ + + xswa.colormap = XCreateColormap(dpy, + windowParent, + visual, + AllocNone); + + window = XCreateWindow(dpy, + windowParent, + x, y, + width, height, + 0, // border width + depth, + InputOutput, + visual, + attrMask, + &xswa); + + if(0==window) { + NewtCommon_throwNewRuntimeException(env, "could not create Window, bail out!"); + return 0; + } + + wm_delete_atom = (Atom)windowDeleteAtom; + XSetWMProtocols(dpy, window, &wm_delete_atom, 1); + + setJavaWindowProperty(env, dpy, window, javaObjectAtom, (*env)->NewGlobalRef(env, obj)); + + // XClearWindow(dpy, window); + XSync(dpy, False); + + { + long xevent_mask = 0; + xevent_mask |= ButtonPressMask | ButtonReleaseMask | PointerMotionMask ; + xevent_mask |= KeyPressMask | KeyReleaseMask ; + xevent_mask |= FocusChangeMask | SubstructureNotifyMask | StructureNotifyMask | ExposureMask ; + + XSelectInput(dpy, window, xevent_mask); + } + + NewtWindows_setDecorations(dpy, window, ( JNI_TRUE == undecorated ) ? False : True ); + XSync(dpy, False); + + DBG_PRINT( "X11: [CreateWindow] created window %p on display %p\n", (void*)window, dpy); + return (jlong) window; +} + +/* + * Class: jogamp_newt_x11_X11Window + * Method: CloseWindow + * Signature: (JJ)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_x11_X11Window_CloseWindow0 + (JNIEnv *env, jobject obj, jlong display, jlong window, jlong javaObjectAtom, jlong wmDeleteAtom) +{ + Display * dpy = (Display *) (intptr_t) display; + Window w = (Window)window; + jobject jwindow; + + if(dpy==NULL) { + NewtCommon_FatalError(env, "invalid display connection.."); + } + + DBG_PRINT( "X11: CloseWindow START dpy %p, win %p\n", (void*)dpy, (void*)w); + + jwindow = getJavaWindowProperty(env, dpy, w, javaObjectAtom, True); + if(NULL==jwindow) { + NewtCommon_throwNewRuntimeException(env, "could not fetch Java Window object, bail out!"); + return; + } + if ( JNI_FALSE == (*env)->IsSameObject(env, jwindow, obj) ) { + NewtCommon_throwNewRuntimeException(env, "Internal Error .. Window global ref not the same!"); + return; + } + + XSync(dpy, False); + XSelectInput(dpy, w, 0); + XUnmapWindow(dpy, w); + + // Drain all events related to this window .. + Java_jogamp_newt_x11_X11Display_DispatchMessages0(env, obj, display, javaObjectAtom, wmDeleteAtom); + + XDestroyWindow(dpy, w); + XSync(dpy, False); + + (*env)->DeleteGlobalRef(env, jwindow); + + DBG_PRINT( "X11: CloseWindow END\n"); +} + +static void NewtWindows_setPosSize(Display *dpy, Window w, jint x, jint y, jint width, jint height) +{ + if(width>0 && height>0 || x>=0 && y>=0) { // resize/position if requested + XWindowChanges xwc; + unsigned int mod_flags = ( (x>=0)?CWX:0 ) | ( (y>=0)?CWY:0 ) | + ( (width>0)?CWWidth:0 ) | ( (height>0)?CWHeight:0 ) ; + DBG_PRINT( "X11: reconfigureWindow0 pos/size mod: 0x%X\n", mod_flags); + memset(&xwc, 0, sizeof(XWindowChanges)); + xwc.x=x; + xwc.y=y; + xwc.width=width; + xwc.height=height; + XConfigureWindow(dpy, w, mod_flags, &xwc); + XSync(dpy, False); + } +} + +/* + * Class: jogamp_newt_x11_X11Window + * Method: setVisible0 + * Signature: (JJZIIII)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_x11_X11Window_setVisible0 + (JNIEnv *env, jobject obj, jlong display, jlong window, jboolean visible, jint x, jint y, jint width, jint height) +{ + Display * dpy = (Display *) (intptr_t) display; + Window w = (Window)window; + DBG_PRINT( "X11: setVisible0 vis %d\n", visible); + + if(dpy==NULL) { + NewtCommon_FatalError(env, "invalid display connection.."); + } + + if(visible==JNI_TRUE) { + XMapRaised(dpy, w); + } else { + XUnmapWindow(dpy, w); + } + XSync(dpy, False); + + NewtWindows_setPosSize(dpy, w, x, y, width, height); +} + +/* + * Class: jogamp_newt_x11_X11Window + * Method: reconfigureWindow0 + * Signature: (JIJJIIIIZZII)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_x11_X11Window_reconfigureWindow0 + (JNIEnv *env, jobject obj, jlong jdisplay, jint screen_index, jlong jparent, jlong jwindow, + jint x, jint y, jint width, jint height, jboolean isVisible, jboolean parentChange, jint fullscreenChange, jint decorationChange) +{ + Display * dpy = (Display *) (intptr_t) jdisplay; + Screen * scrn = ScreenOfDisplay(dpy, (int)screen_index); + Window w = (Window)jwindow; + Window root = XRootWindowOfScreen(scrn); + Window parent = (0!=jparent)?(Window)jparent:root; + Window topParentParent; + Window topParentWindow; + Bool moveIntoParent = False; + + displayDispatchErrorHandlerEnable(1, env); + + topParentParent = NewtWindows_getParent (dpy, parent); + topParentWindow = NewtWindows_getParent (dpy, w); + + DBG_PRINT( "X11: reconfigureWindow0 dpy %p, scrn %d/%p, parent %p/%p (top %p), win %p (top %p), %d/%d %dx%d visible %d, parentChange %d, fullscreenChange %d, decorationChange %d\n", + (void*)dpy, screen_index, (void*)scrn, (void*) jparent, (void*)parent, (void*) topParentParent, (void*)w, (void*)topParentWindow, + x, y, width, height, isVisible, parentChange, fullscreenChange, decorationChange); + + if(parentChange && JNI_TRUE == isVisible) { // unmap window if visible, reduce X11 internal signaling (WM unmap) + XUnmapWindow(dpy, w); + XSync(dpy, False); + } + + if(0 > fullscreenChange ) { // FS off + NewtWindows_setFullscreen(dpy, root, w, False ); + XSync(dpy, False); + } + + if(parentChange) { + if(0 != jparent) { // move into parent .. + moveIntoParent = True; + NewtWindows_setDecorations (dpy, w, False); + XSync(dpy, False); + } + XReparentWindow( dpy, w, parent, x, y ); // actual reparent call + XSync(dpy, False); + } + + if(!moveIntoParent && 0!=decorationChange) { + NewtWindows_setDecorations (dpy, w, (0 < decorationChange) ? True : False); + XSync(dpy, False); + } + + NewtWindows_setPosSize(dpy, w, x, y, width, height); + + if(0 < fullscreenChange ) { // FS on + NewtWindows_setFullscreen(dpy, root, w, True ); + XSync(dpy, False); + } + + if(parentChange && JNI_TRUE == isVisible) { // map window + XMapRaised(dpy, w); + XSync(dpy, False); + } + + displayDispatchErrorHandlerEnable(0, env); + + DBG_PRINT( "X11: reconfigureWindow0 X\n"); +} + +/* + * Class: jogamp_newt_x11_X11Window + * Method: requestFocus0 + * Signature: (JJ)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_x11_X11Window_requestFocus0 + (JNIEnv *env, jobject obj, jlong display, jlong window, jboolean force) +{ + NewtWindows_requestFocus ( env, obj, (Display *) (intptr_t) display, (Window)window, force ) ; +} + +/* + * Class: Java_jogamp_newt_x11_X11Window + * Method: setTitle0 + * Signature: (JJLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_x11_X11Window_setTitle0 + (JNIEnv *env, jclass clazz, jlong display, jlong window, jstring title) +{ + Display * dpy = (Display *) (intptr_t) display; + Window w = (Window)window; + +#if 1 + const char* title_str; + if (NULL != title) { + title_str = (*env)->GetStringUTFChars(env, title, NULL); + if(NULL != title_str) { + DBG_PRINT( "X11: setTitle: <%s> SET\n", title_str); + XStoreName(dpy, w, title_str); + (*env)->ReleaseStringUTFChars(env, title, title_str); + } else { + DBG_PRINT( "X11: setTitle: NULL - NOT SET (1)\n"); + } + } else { + DBG_PRINT( "X11: setTitle: NULL TITLE\n"); + } +#else + char *str_list[] = { NULL }; + XTextProperty text_prop; + if (NULL != title) { + str_list[0] = (char *) NewtCommon_GetNullTerminatedStringChars(env, title); + if (str_list[0] != NULL) { + memset(&text_prop, 0, sizeof(XTextProperty)); + if ( Success != XmbTextListToTextProperty(dpy, str_list, 1, XStringStyle, &text_prop) ) { + DBG_PRINT( "X11: setTitle.XmbTextListToTextProperty not completly successfull\n"); + fprintf(stderr, "X11: setTitle.XmbTextListToTextProperty not completly successfull\n"); + } + if(NULL!=text_prop.value) { + DBG_PRINT( "X11: setTitle: <%s> SET\n", str_list[0]); + XSetWMName(dpy, w, &text_prop); + XFree(text_prop.value); + } else { + DBG_PRINT( "X11: setTitle: <%s> NOT SET (1)\n", str_list[0]); + } + free(str_list[0]); + } else { + DBG_PRINT( "X11: setTitle: NULL\n"); + } + } +#endif +} + |