diff options
author | Sven Gothel <[email protected]> | 2013-12-31 08:14:21 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2013-12-31 08:14:21 +0100 |
commit | e7ffa68bce9bb707005be72530b207c732f62c31 (patch) | |
tree | db1d2e73c89da355c8ff319febcf3c2cc2637a16 | |
parent | e7032ae9ca4b754bd9737f86d9496211e9155db4 (diff) |
Bug 934, Bug 935: NEWT: Add support for custom Application/Window and Pointer Icons
- Utilizing JOGL's PNG decoder for all icons, if available.
- Application/window icons:
- Providing default application/window icons in 16x16 and 32x32 size
- NewtFactory.setWindowIcons(..) or property 'newt.window.icons' maybe used to override default icons.
- Using icons at application/window instantiation
- Display.PointerIcons:
- NativeWindow Win32 WindowClass no more references a default cursor
in favor of fine grained cursor control [in NEWT]
- Display provides create/destroy methods,
where display destruction also releases open PointerIcon references.
- Window.setPointerIcon(..) sets custom PointerIcon
- Implemented Platforms
- X11
- Windows
- OSX
- Manual Test: TestGearsES2NEWT (Press 'c')
32 files changed, 1027 insertions, 43 deletions
diff --git a/make/build-jogl.xml b/make/build-jogl.xml index 28b738b10..4cdb93f4d 100644 --- a/make/build-jogl.xml +++ b/make/build-jogl.xml @@ -1833,7 +1833,7 @@ <fileset dir="${classes}" includes="${java.part.util} ${java.part.util.glsl} ${java.part.openal}" excludes="${java.part.util.awt} ${java.part.util.gldesktop} ${java.part.util.fixedfuncemu} ${java.part.util.graph}"/> - <fileset dir="resources/assets" includes="jogl/util/**" /> + <fileset dir="resources/assets" includes="jogl/util/data/**" /> </jar> <jar manifest="${build.jogl}/manifest.mf" destfile="${jogl-util-graph.jar}" filesonly="true"> <fileset dir="${classes}" diff --git a/make/build-newt.xml b/make/build-newt.xml index 960c7b533..9bc2e0e83 100644 --- a/make/build-newt.xml +++ b/make/build-newt.xml @@ -103,7 +103,7 @@ value="com/jogamp/newt/* com/jogamp/newt/event/* com/jogamp/newt/util/* jogamp/newt/* jogamp/newt/event/* jogamp/newt/driver/*"/> <property name="java.part.opengl" - value="com/jogamp/newt/opengl/**"/> + value="com/jogamp/newt/opengl/** jogamp/newt/driver/opengl/**"/> <property name="java.part.driver.awt" value="jogamp/newt/driver/awt/**"/> @@ -277,12 +277,14 @@ <linkerarg value="-L/usr/local/lib"/> <syslibset libs="X11"/> <syslibset libs="Xrandr"/> + <syslibset libs="Xcursor"/> </linker> <linker id="linker.cfg.freebsd.amd64.newt.x11" extends="linker.cfg.freebsd.amd64"> <linkerarg value="-L/usr/local/lib"/> <syslibset libs="X11"/> <syslibset libs="Xrandr"/> + <syslibset libs="Xcursor"/> </linker> <linker id="linker.cfg.linux.newt.bcm_egl" extends="linker.cfg.linux"> @@ -302,6 +304,7 @@ <linker id="linker.cfg.linux.newt.x11" extends="linker.cfg.linux"> <syslibset libs="X11"/> <syslibset libs="Xrandr"/> + <syslibset libs="Xcursor"/> <!--syslibset libs="xcb" /--> <!--syslibset libs="X11-xcb" /--> </linker> @@ -309,6 +312,7 @@ <linker id="linker.cfg.linux.x86.newt.x11" extends="linker.cfg.linux.x86"> <syslibset libs="X11"/> <syslibset libs="Xrandr"/> + <syslibset libs="Xcursor"/> <!--syslibset libs="xcb" /--> <!--syslibset libs="X11-xcb" /--> </linker> @@ -316,6 +320,7 @@ <linker id="linker.cfg.linux.amd64.newt.x11" extends="linker.cfg.linux.amd64"> <syslibset libs="X11"/> <syslibset libs="Xrandr"/> + <syslibset libs="Xcursor"/> <!--syslibset libs="xcb" /--> <!--syslibset libs="X11-xcb" /--> </linker> @@ -323,6 +328,7 @@ <linker id="linker.cfg.linux.armv6.newt.x11" extends="linker.cfg.linux.armv6"> <syslibset dir="${env.TARGET_PLATFORM_ROOT}/usr/lib" libs="X11" /> <syslibset dir="${env.TARGET_PLATFORM_ROOT}/usr/lib" libs="Xrandr" /> + <syslibset dir="${env.TARGET_PLATFORM_ROOT}/usr/lib" libs="Xcursor" /> </linker> <linker id="linker.cfg.android.newt" extends="linker.cfg.android"> @@ -332,18 +338,21 @@ <linker id="linker.cfg.solaris.newt.x11" extends="linker.cfg.solaris"> <syslibset libs="X11"/> <syslibset libs="Xrandr"/> + <syslibset libs="Xcursor"/> </linker> <linker id="linker.cfg.solaris.sparcv9.newt.x11" extends="linker.cfg.solaris.sparcv9"> <linkerarg value="-L/usr/lib/sparcv9"/> <syslibset libs="X11"/> <syslibset libs="Xrandr"/> + <syslibset libs="Xcursor"/> </linker> <linker id="linker.cfg.solaris.amd64.newt.x11" extends="linker.cfg.solaris.amd64"> <linkerarg value="-L/usr/lib/amd64"/> <syslibset libs="X11"/> <syslibset libs="Xrandr"/> + <syslibset libs="Xcursor"/> </linker> <linker id="linker.cfg.win32.mingw.newt" extends="linker.cfg.win32.mingw"> @@ -798,6 +807,7 @@ <jar manifest="${build.newt}/manifest.mf" destfile="${newt-core.jar}" filesonly="true"> <fileset dir="${classes}" includes="${java.part.core}"/> + <fileset dir="resources/assets" includes="newt/data/**" /> </jar> <jar manifest="${build.newt}/manifest.mf" destfile="${newt-event.jar}" filesonly="true"> <fileset dir="${classes}"> diff --git a/make/resources/assets-test/jogamp-pointer-64x64.png b/make/resources/assets-test/jogamp-pointer-64x64.png Binary files differnew file mode 100644 index 000000000..a965dcab1 --- /dev/null +++ b/make/resources/assets-test/jogamp-pointer-64x64.png diff --git a/make/resources/assets/newt/data/jogamp-16x16.png b/make/resources/assets/newt/data/jogamp-16x16.png Binary files differnew file mode 100644 index 000000000..02df8997f --- /dev/null +++ b/make/resources/assets/newt/data/jogamp-16x16.png diff --git a/make/resources/assets/newt/data/jogamp-32x32.png b/make/resources/assets/newt/data/jogamp-32x32.png Binary files differnew file mode 100644 index 000000000..ab21c6e1b --- /dev/null +++ b/make/resources/assets/newt/data/jogamp-32x32.png diff --git a/make/resources/misc/jogamp-48x48.png b/make/resources/misc/jogamp-48x48.png Binary files differnew file mode 100644 index 000000000..216f8c0b7 --- /dev/null +++ b/make/resources/misc/jogamp-48x48.png diff --git a/make/resources/misc/jogamp-64x64.png b/make/resources/misc/jogamp-64x64.png Binary files differnew file mode 100644 index 000000000..9936616ca --- /dev/null +++ b/make/resources/misc/jogamp-64x64.png diff --git a/make/scripts/tests-win.bat b/make/scripts/tests-win.bat index f5417bf7a..d95b0025f 100755 --- a/make/scripts/tests-win.bat +++ b/make/scripts/tests-win.bat @@ -6,7 +6,7 @@ REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.acore.TestMainVersion REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.demos.gl2.newt.TestGearsNewtAWTWrapper %* REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.demos.gl2.newt.TestGearsNEWT -time 30000 REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.demos.es1.newt.TestGearsES1NEWT %* -REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT %* +scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT %* REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT -vsync -time 4000 -x 10 -y 10 -width 100 -height 100 -screen 0 REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT -vsync -time 40000 -width 100 -height 100 -screen 0 %* REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.demos.gl2.awt.TestGearsAWT -time 5000 @@ -39,7 +39,7 @@ REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.tile.TestTiledPrintin REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.acore.TestGLCapabilities01NEWT %* REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.acore.TestShutdownCompleteNEWT %* -scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.acore.TestShutdownCompleteAWT %* +REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.acore.TestShutdownCompleteAWT %* REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.offscreen.TestOffscreen01GLPBufferNEWT -time 5000 REM scripts\java-win.bat com.jogamp.opengl.test.junit.jogl.acore.TestSharedContextListNEWT %* diff --git a/make/scripts/tests-x64-dbg.bat b/make/scripts/tests-x64-dbg.bat index 6e8407f3e..ce2df922c 100755 --- a/make/scripts/tests-x64-dbg.bat +++ b/make/scripts/tests-x64-dbg.bat @@ -55,9 +55,10 @@ REM set D_ARGS="-Djogl.debug.GLCanvas" "-Djogl.debug.Animator" "-Djogl.debug.GLC REM set D_ARGS="-Djogl.debug.GLCanvas" "-Djogl.debug.GLJPanel" "-Djogl.debug.TileRenderer" "-Djogl.debug.TileRenderer.PNG"
REM set D_ARGS="-Djogl.debug.GLCanvas" "-Djogl.debug.GLJPanel" "-Djogl.debug.TileRenderer"
REM set D_ARGS="-Djogl.gljpanel.noverticalflip"
+set D_ARGS="-Dnewt.debug=all"
REM set D_ARGS="-Dnewt.debug.Window"
REM set D_ARGS="-Dnewt.debug.Window.KeyEvent"
-set D_ARGS="-Dnewt.debug.Window" "-Dnewt.debug.Window.KeyEvent" "-Dnewt.debug.EDT"
+REM set D_ARGS="-Dnewt.debug.Window" "-Dnewt.debug.Window.KeyEvent" "-Dnewt.debug.EDT"
REM set D_ARGS="-Dnewt.debug.Window.MouseEvent"
REM set D_ARGS="-Dnewt.debug.Window.MouseEvent" "-Dnewt.debug.Window.KeyEvent"
REM set D_ARGS="-Dnewt.debug.Window" "-Dnewt.debug.Display"
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index 6cb1966d1..4c084d705 100644 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -312,7 +312,7 @@ function testawtswt() { #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestMainVersionGLWindowNEWT $* #testawt com.jogamp.opengl.test.junit.jogl.acore.TestMainVersionGLCanvasAWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLProfile00NEWT $* -testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLProfile01NEWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLProfile01NEWT $* # # demos (any TK, more user driven tests) @@ -325,7 +325,7 @@ testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLProfile01NEWT $* #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelsAWT $* #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasAWT $* #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestLandscapeES2NewtCanvasAWT $* -#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $* +testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestLandscapeES2NEWT $* #testawtswt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasSWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestElektronenMultipliziererNEWT $* @@ -636,6 +636,7 @@ testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLProfile01NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestJPEGTextureFromFileNEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGImage00NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGImage01NEWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.ToolPNG2CSource $* #testawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGTextureFromFileAWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGTextureFromFileNEWT $* #testawt com.jogamp.opengl.test.junit.jogl.util.texture.TestGLReadBufferUtilTextureIOWrite01AWT $* diff --git a/src/nativewindow/native/win32/GDImisc.c b/src/nativewindow/native/win32/GDImisc.c index b55988c11..25b98acf3 100644 --- a/src/nativewindow/native/win32/GDImisc.c +++ b/src/nativewindow/native/win32/GDImisc.c @@ -263,7 +263,7 @@ Java_jogamp_nativewindow_windows_GDIUtil_CreateWindowClass0 wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; - wc.hCursor = LoadCursor( NULL, IDC_ARROW); + wc.hCursor = NULL; wc.hbrBackground = NULL; // no background paint - GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = clazzName; diff --git a/src/newt/classes/com/jogamp/newt/Display.java b/src/newt/classes/com/jogamp/newt/Display.java index 4469189a8..ee6dfd080 100644 --- a/src/newt/classes/com/jogamp/newt/Display.java +++ b/src/newt/classes/com/jogamp/newt/Display.java @@ -28,15 +28,21 @@ package com.jogamp.newt; -import com.jogamp.newt.util.EDTUtil; -import jogamp.newt.Debug; - +import java.io.IOException; import java.lang.ref.WeakReference; -import java.util.*; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; +import jogamp.newt.Debug; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.newt.util.EDTUtil; + public abstract class Display { public static final boolean DEBUG = Debug.debug("Display"); @@ -56,6 +62,42 @@ public abstract class Display { } /** + * Native PointerIcon handle. + * <p> + * Instances can be created via {@link Display#createPointerIcon(com.jogamp.common.util.IOUtil.ClassResources, int, int)} + * and released via {@link Display#destroyPointerIcon(PointerIcon)}. + * </p> + * <p> + * PointerIcons can be used via {@link Window#setPointerIcon(PointerIcon)}. + * </p> + */ + public static interface PointerIcon { } + + /** + * Returns the created {@link PointerIcon} or <code>null</code> if not implemented on platform. + * + * @param pngResource PNG resource + * @param hotX pointer hotspot x-coord, origin is upper-left corner + * @param hotY pointer hotspot y-coord, origin is upper-left corner + * @throws MalformedURLException + * @throws InterruptedException + * @throws IOException + * + * @see PointerIcon + * @see #destroyPointerIcon(PointerIcon) + * @see Window#setPointerIcon(PointerIcon) + */ + + public abstract PointerIcon createPointerIcon(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException; + + /** + * @param pi + * + * @see #createPointerIcon(com.jogamp.common.util.IOUtil.ClassResources, int, int) + */ + public abstract void destroyPointerIcon(PointerIcon pi); + + /** * 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> diff --git a/src/newt/classes/com/jogamp/newt/NewtFactory.java b/src/newt/classes/com/jogamp/newt/NewtFactory.java index 157e2bb3c..92339ea73 100644 --- a/src/newt/classes/com/jogamp/newt/NewtFactory.java +++ b/src/newt/classes/com/jogamp/newt/NewtFactory.java @@ -36,6 +36,7 @@ package com.jogamp.newt; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Arrays; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; @@ -44,6 +45,8 @@ import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowFactory; +import com.jogamp.common.util.IOUtil; + import jogamp.newt.Debug; import jogamp.newt.DisplayImpl; import jogamp.newt.ScreenImpl; @@ -54,15 +57,45 @@ public class NewtFactory { public static final String DRIVER_DEFAULT_ROOT_PACKAGE = "jogamp.newt.driver"; + private static IOUtil.ClassResources defaultWindowIcons; + static { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { NativeWindowFactory.initSingleton(); // last resort .. + { + final String[] paths = Debug.getProperty("newt.window.icons", true, "newt/data/jogamp-16x16.png newt/data/jogamp-32x32.png").split("\\s"); + if( paths.length < 2 ) { + throw new IllegalArgumentException("Property 'newt.window.icons' did not specify at least two PNG icons, but "+Arrays.toString(paths)); + } + final Class<?> clazz = NewtFactory.class; + defaultWindowIcons = new IOUtil.ClassResources(clazz, paths); + } return null; } } ); } + /** + * Returns the application window icon resources to be used. + * <p> + * Property <code>newt.window.icons</code> may define a list of PNG icons separated by a whitespace character. + * Shall reference at least two PNG icons, from lower (16x16) to higher (>= 32x32) resolution. + * </p> + * <p> + * Users may also specify application window icons using {@link #setWindowIcons(com.jogamp.common.util.IOUtil.ClassResources)}. + * </p> + */ + public static IOUtil.ClassResources getWindowIcons() { return defaultWindowIcons; } + + /** + * Allow user to set custom window icons, only applicable at application start before creating any NEWT instance. + * <p> + * Shall reference at least two PNG icons, from lower (16x16) to higher (>= 32x32) resolution. + * </p> + */ + public static void setWindowIcons(IOUtil.ClassResources cres) { defaultWindowIcons = cres; } + public static Class<?> getCustomClass(String packageName, String classBaseName) { Class<?> clazz = null; if(packageName!=null && classBaseName!=null) { diff --git a/src/newt/classes/com/jogamp/newt/Window.java b/src/newt/classes/com/jogamp/newt/Window.java index 5c3bb7889..8e73ba1d2 100644 --- a/src/newt/classes/com/jogamp/newt/Window.java +++ b/src/newt/classes/com/jogamp/newt/Window.java @@ -30,6 +30,7 @@ package com.jogamp.newt; import java.util.List; +import com.jogamp.newt.Display.PointerIcon; import com.jogamp.newt.event.GestureHandler; import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowListener; @@ -307,6 +308,20 @@ public interface Window extends NativeWindow, WindowClosingProtocol { */ void setPointerVisible(boolean pointerVisible); + /** + * Returns the current {@link PointerIcon}, which maybe <code>null</code> for the default. + * @see #setPointerIcon(PointerIcon) + */ + PointerIcon getPointerIcon(); + + /** + * @param pi Valid {@link PointerIcon} reference or <code>null</code> to reset the pointer icon to default. + * + * @see PointerIcon + * @see Display#createPointerIcon(com.jogamp.common.util.IOUtil.ClassResources, int, int) + */ + void setPointerIcon(final PointerIcon pi); + /** @see #confinePointer(boolean) */ boolean isPointerConfined(); diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index 208602aa1..ca8ab6733 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -77,6 +77,7 @@ import com.jogamp.newt.MonitorDevice; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Screen; import com.jogamp.newt.Window; +import com.jogamp.newt.Display.PointerIcon; import com.jogamp.newt.event.GestureHandler; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.MouseListener; @@ -268,6 +269,16 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override + public final PointerIcon getPointerIcon() { + return window.getPointerIcon(); + } + + @Override + public final void setPointerIcon(final PointerIcon pi) { + window.setPointerIcon(pi); + } + + @Override public final boolean isPointerConfined() { return window.isPointerConfined(); } diff --git a/src/newt/classes/jogamp/newt/DisplayImpl.java b/src/newt/classes/jogamp/newt/DisplayImpl.java index 0c29d772a..4e9b8b441 100644 --- a/src/newt/classes/jogamp/newt/DisplayImpl.java +++ b/src/newt/classes/jogamp/newt/DisplayImpl.java @@ -34,14 +34,18 @@ package jogamp.newt; +import com.jogamp.common.util.IOUtil; import com.jogamp.newt.Display; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.event.NEWTEvent; import com.jogamp.newt.event.NEWTEventConsumer; import jogamp.newt.event.NEWTEventTask; + import com.jogamp.newt.util.EDTUtil; +import java.io.IOException; +import java.net.MalformedURLException; import java.util.ArrayList; import javax.media.nativewindow.AbstractGraphicsDevice; @@ -62,6 +66,67 @@ public abstract class DisplayImpl extends Display { }); } + public static class PointerIconImpl implements PointerIcon { + public final long handle; + public PointerIconImpl(long handle) { + this.handle=handle; + } + } + + private void addPointerIconToList(final PointerIcon pi) { + synchronized(pointerIconList) { + pointerIconList.add(pi); + } + } + private void delPointerIconFromList(final PointerIcon pi) { + synchronized(pointerIconList) { + pointerIconList.remove(pi); + } + } + private final ArrayList<PointerIcon> pointerIconList = new ArrayList<PointerIcon>(); + + /** Executed from EDT! */ + private void destroyAllPointerIconFromList(final long dpy) { + synchronized(pointerIconList) { + for( int i=0; i < pointerIconList.size(); i++ ) { + final PointerIcon item = pointerIconList.get(i); + if( null != item ) { + // destroy! + destroyPointerIconImpl(dpy, item); + } + } + pointerIconList.clear(); + } + } + + @Override + public final PointerIcon createPointerIcon(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException { + final PointerIcon res = createPointerIconImpl(pngResource, hotX, hotY); + addPointerIconToList(res); + return res; + } + protected PointerIcon createPointerIconImpl(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException { + return null; + } + + @Override + public final void destroyPointerIcon(final PointerIcon pi) { + delPointerIconFromList(pi); + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(long dpy) { + try { + destroyPointerIconImpl(dpy, pi); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + }); + } + /** Executed from EDT! */ + protected void destroyPointerIconImpl(final long displayHandle, final PointerIcon pi) { } + /** Ensure static init has been run. */ /* pp */static void initSingleton() { } @@ -283,6 +348,7 @@ public abstract class DisplayImpl extends Display { @Override public void run() { if ( null != f_aDevice ) { + f_dpy.destroyAllPointerIconFromList(f_aDevice.getHandle()); f_dpy.closeNativeImpl(f_aDevice); } } @@ -453,7 +519,7 @@ public abstract class DisplayImpl extends Display { /** Dispatch native Toolkit messageges */ protected abstract void dispatchMessagesNative(); - private Object eventsLock = new Object(); + private final Object eventsLock = new Object(); private ArrayList<NEWTEventTask> events = new ArrayList<NEWTEventTask>(); private volatile boolean haveEvents = false; diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 7a7e69e48..d078caa3b 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -42,6 +42,7 @@ import java.lang.reflect.Method; import com.jogamp.common.util.ArrayHashSet; import com.jogamp.common.util.IntBitfield; import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.newt.Display.PointerIcon; import com.jogamp.newt.MonitorDevice; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Display; @@ -83,6 +84,7 @@ import javax.media.nativewindow.util.Rectangle; import javax.media.nativewindow.util.RectangleImmutable; import jogamp.nativewindow.SurfaceUpdatedHelper; +import jogamp.newt.DisplayImpl.PointerIconImpl; public abstract class WindowImpl implements Window, NEWTEventConsumer { @@ -170,6 +172,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private String title = "Newt Window"; private boolean undecorated = false; private boolean alwaysOnTop = false; + private PointerIcon pointerIcon = null; private boolean pointerVisible = true; private boolean pointerConfined = false; private LifecycleHook lifecycleHook = null; @@ -436,6 +439,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer createNativeImpl(); screen.addMonitorModeListener(monitorModeListenerImpl); setTitleImpl(title); + if( null != pointerIcon ) { + setPointerIcon(pointerIcon); + } setPointerVisibleImpl(pointerVisible); confinePointerImpl(pointerConfined); setKeyboardVisible(keyboardVisible); @@ -725,6 +731,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer protected boolean setPointerVisibleImpl(boolean pointerVisible) { return false; } protected boolean confinePointerImpl(boolean confine) { return false; } protected void warpPointerImpl(int x, int y) { } + protected void setPointerIconImpl(final PointerIconImpl pi) { } //---------------------------------------------------------------------- // NativeSurface @@ -1675,6 +1682,21 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } + + @Override + public final PointerIcon getPointerIcon() { return pointerIcon; } + + @Override + public final void setPointerIcon(final PointerIcon pi) { + if( this.pointerIcon != pi ) { + setPointerIcon(pointerIcon); + if( isNativeValid() ) { + setPointerIconImpl((PointerIconImpl)pi); + } + this.pointerIcon = pi; + } + } + @Override public final boolean isPointerConfined() { return pointerConfined; diff --git a/src/newt/classes/jogamp/newt/driver/PNGIcon.java b/src/newt/classes/jogamp/newt/driver/PNGIcon.java new file mode 100644 index 000000000..c958f6ec2 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/PNGIcon.java @@ -0,0 +1,102 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.newt.driver; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.nio.ByteBuffer; + +import jogamp.newt.Debug; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.ReflectionUtil; + +public class PNGIcon { + private static final String err0 = "PNG decoder not implemented."; + + private static final boolean avail; + + static { + Debug.initSingleton(); + + final ClassLoader cl = PNGIcon.class.getClassLoader(); + avail = ReflectionUtil.isClassAvailable("jogamp.newt.driver.opengl.JoglUtilPNGIcon", cl) && + ReflectionUtil.isClassAvailable("com.jogamp.opengl.util.texture.spi.PNGImage", cl); + } + + /** Returns true if PNG decoder is available. */ + public static boolean isAvailable() { + return avail; + } + + /** + * Implemented for X11. + * @param resources + * @param data_size + * @param elem_bytesize + * + * @return BGRA8888 bytes with origin at upper-left corner where component B is located on the lowest 8-bit and component A is located on the highest 8-bit. + * + * @throws UnsupportedOperationException if not implemented + * @throws InterruptedException + * @throws IOException + * @throws MalformedURLException + */ + public static ByteBuffer arrayToX11BGRAImages(IOUtil.ClassResources resources, int[] data_size, int[] elem_bytesize) throws UnsupportedOperationException, InterruptedException, IOException, MalformedURLException { + if( avail ) { + return jogamp.newt.driver.opengl.JoglUtilPNGIcon.arrayToX11BGRAImages(resources, data_size, elem_bytesize); + } + throw new UnsupportedOperationException(err0); + } + + /** + * Implemented for Windows. + * @param resources + * @param toBGRA if true, arranges stores in BGRA888 order, otherwise RGBA888 + * @param width + * @param height + * @param data_size + * @param elem_bytesize + * @param resourcesIdx + * @return pixels with origin at upper-left corner. + * If storing RGBA8888, component R is located on the lowest 8-bit. + * If storing BGRA8888, component B is located on the lowest 8-bit. + * Component A is located on the highest 8-bit. + * + * @throws UnsupportedOperationException if not implemented + * @throws InterruptedException + * @throws IOException + * @throws MalformedURLException + */ + public static ByteBuffer singleToRGBAImage(IOUtil.ClassResources resources, int resourceIdx, boolean toBGRA, int[] width, int[] height, int[] data_size, int[] elem_bytesize) throws UnsupportedOperationException, InterruptedException, IOException, MalformedURLException { + if( avail ) { + return jogamp.newt.driver.opengl.JoglUtilPNGIcon.singleToRGBAImage(resources, resourceIdx, toBGRA, width, height, data_size, elem_bytesize); + } + throw new UnsupportedOperationException(err0); + } +} diff --git a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java index c44685dc8..86c8464a8 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java @@ -34,15 +34,26 @@ package jogamp.newt.driver.macosx; +import java.io.IOException; +import java.net.MalformedURLException; +import java.nio.Buffer; +import java.nio.ByteBuffer; + import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; +import com.jogamp.common.util.IOUtil; import com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice; +import com.jogamp.newt.NewtFactory; import jogamp.newt.DisplayImpl; import jogamp.newt.NEWTJNILibLoader; +import jogamp.newt.driver.PNGIcon; public class DisplayDriver extends DisplayImpl { + private static final int defaultIconWidth, defaultIconHeight; + private static final Buffer defaultIconData; + static { NEWTJNILibLoader.loadNEWT(); @@ -52,6 +63,25 @@ public class DisplayDriver extends DisplayImpl { if(!WindowDriver.initIDs0()) { throw new NativeWindowException("Failed to initialize jmethodIDs"); } + { + final int[] width = { 0 }, height = { 0 }, data_size = { 0 }, elem_bytesize = { 0 }; + Buffer data=null; + if( PNGIcon.isAvailable() ) { + try { + final IOUtil.ClassResources iconRes = NewtFactory.getWindowIcons(); + data = PNGIcon.singleToRGBAImage(iconRes, iconRes.resourceCount()-1, false /* toBGRA */, width, height, data_size, elem_bytesize); + } catch (Exception e) { + e.printStackTrace(); + } + } + defaultIconWidth = width[0]; + defaultIconHeight = height[0]; + defaultIconData = data; + if( null != defaultIconData ) { + DisplayDriver.setAppIcon0(defaultIconData, defaultIconWidth, defaultIconHeight); + } + } + if(DEBUG) { System.err.println("MacDisplay.init App and IDs OK "+Thread.currentThread().getName()); } @@ -79,6 +109,23 @@ public class DisplayDriver extends DisplayImpl { aDevice.close(); } + @Override + protected PointerIcon createPointerIconImpl(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException { + if( PNGIcon.isAvailable() ) { + final int[] width = { 0 }, height = { 0 }, data_size = { 0 }, elem_bytesize = { 0 }; + if( null != pngResource && 0 < pngResource.resourceCount() ) { + final ByteBuffer data = PNGIcon.singleToRGBAImage(pngResource, 0, true /* toBGRA */, width, height, data_size, elem_bytesize); + return new PointerIconImpl( createPointerIcon0(data, width[0], height[0], hotX, hotY) ); + } + } + return null; + } + + @Override + protected final void destroyPointerIconImpl(final long displayHandle, final PointerIcon pi) { + destroyPointerIcon0(((PointerIconImpl)pi).handle); + } + public static void runNSApplication() { runNSApplication0(); } @@ -89,5 +136,9 @@ public class DisplayDriver extends DisplayImpl { private static native boolean initNSApplication0(); private static native void runNSApplication0(); private static native void stopNSApplication0(); + /* pp */ static native void setAppIcon0(Object iconData, int iconWidth, int iconHeight); + private static native long createPointerIcon0(Object iconData, int iconWidth, int iconHeight, int hotX, int hotY); + private static native long destroyPointerIcon0(long handle); + } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java index 641d7437c..a55fa915a 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java @@ -46,6 +46,7 @@ import javax.media.nativewindow.util.PointImmutable; import jogamp.nativewindow.macosx.OSXUtil; import jogamp.newt.WindowImpl; +import jogamp.newt.DisplayImpl.PointerIconImpl; import jogamp.newt.driver.DriverClearFocus; import jogamp.newt.driver.DriverUpdatePosition; @@ -392,6 +393,15 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } @Override + protected void setPointerIconImpl(final PointerIconImpl pi) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + setPointerIcon0(getWindowHandle(), null != pi ? pi.handle : 0); + } } ); + } + + @Override protected boolean setPointerVisibleImpl(final boolean pointerVisible) { if( !isOffscreenInstance ) { return setPointerVisible0(getWindowHandle(), hasFocus(), pointerVisible); @@ -568,6 +578,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl private static native boolean setPointerVisible0(long windowHandle, boolean hasFocus, boolean visible); private static native boolean confinePointer0(long windowHandle, boolean confine); private static native void warpPointer0(long windowHandle, int x, int y); + private static native void setPointerIcon0(long windowHandle, long handle); // Window styles private static final int NSBorderlessWindowMask = 0; diff --git a/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java b/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java new file mode 100644 index 000000000..ea9029006 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java @@ -0,0 +1,139 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.newt.driver.opengl; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URLConnection; +import java.nio.ByteBuffer; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.util.texture.spi.PNGImage; + +public class JoglUtilPNGIcon { + + public static ByteBuffer arrayToX11BGRAImages(IOUtil.ClassResources resources, int[] data_size, int[] elem_bytesize) throws UnsupportedOperationException, InterruptedException, IOException, MalformedURLException { + final PNGImage[] images = new PNGImage[resources.resourceCount()]; + data_size[0] = 0; + for(int i=0; i<resources.resourceCount(); i++) { + final URLConnection urlConn = resources.resolve(i); + final PNGImage image = PNGImage.read(urlConn.getInputStream()); + data_size[0] += 2 + image.getWidth() * image.getHeight(); + images[i] = image; + } + final boolean is64Bit = Platform.is64Bit(); + elem_bytesize[0] = is64Bit ? Buffers.SIZEOF_LONG : Buffers.SIZEOF_INT; + final ByteBuffer buffer = Buffers.newDirectByteBuffer( data_size[0] * elem_bytesize[0] ); + + for(int i=0; i<images.length; i++) { + final PNGImage image1 = images[i]; + if( is64Bit ) { + buffer.putLong(image1.getWidth()); + buffer.putLong(image1.getHeight()); + } else { + buffer.putInt(image1.getWidth()); + buffer.putInt(image1.getHeight()); + } + final ByteBuffer bb = image1.getData(); + final int bpp = image1.getBytesPerPixel(); + final int stride = image1.getWidth() * bpp; + for(int y=0; y<image1.getHeight(); y++) { + int bbOff = image1.isGLOriented() ? ( image1.getHeight() - 1 - y ) * stride : y * stride; + for(int x=0; x<image1.getWidth(); x++) { + // Source: R G B A + // Dest: B G R A + long pixel; + pixel = ( 0xffL & bb.get(bbOff++) ) << 16; // R + pixel |= ( 0xffL & bb.get(bbOff++) ) << 8; // G + pixel |= ( 0xffL & bb.get(bbOff++) ); // B + if( 4 == bpp ) { + pixel |= ( 0xffL & bb.get(bbOff++) ) << 24; + } else { + pixel |= 0x00000000ff000000L; + } + if( is64Bit ) { + buffer.putLong(pixel); + } else { + buffer.putInt((int)pixel); + } + } + } + } + buffer.rewind(); + return buffer; + } + + public static ByteBuffer singleToRGBAImage(IOUtil.ClassResources resources, int resourceIdx, boolean toBGRA, int[] width, int[] height, int[] data_size, int[] elem_bytesize) throws UnsupportedOperationException, InterruptedException, IOException, MalformedURLException { + width[0] = 0; + height[0] = 0; + data_size[0] = 0; + final URLConnection urlConn = resources.resolve(resourceIdx); + final PNGImage image = PNGImage.read(urlConn.getInputStream()); + width[0] = image.getWidth(); + height[0] = image.getHeight(); + data_size[0] = image.getWidth() * image.getHeight(); + + elem_bytesize[0] = 4; // BGRA + final ByteBuffer buffer = Buffers.newDirectByteBuffer( data_size[0] * elem_bytesize[0] ); + + final ByteBuffer bb = image.getData(); + final int bpp = image.getBytesPerPixel(); + final int stride = image.getWidth() * bpp; + for(int y=0; y<image.getHeight(); y++) { + int bbOff = image.isGLOriented() ? ( image.getHeight() - 1 - y ) * stride : y * stride; + for(int x=0; x<image.getWidth(); x++) { + // Source: R G B A + final byte r, g, b, a; + r = bb.get(bbOff++); // R + g = bb.get(bbOff++); // G + b = bb.get(bbOff++); // B + if( 4 == bpp ) { + a = bb.get(bbOff++); // A + } else { + a = (byte)0xff; // A + } + if( toBGRA ) { + // Dest: B G R A + buffer.put(b); + buffer.put(g); + buffer.put(r); + } else { + // Dest: R G B A + buffer.put(r); + buffer.put(g); + buffer.put(b); + } + buffer.put(a); + } + } + buffer.rewind(); + return buffer; + } +} diff --git a/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java index c4cfd98b3..8d4d8972b 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java @@ -34,13 +34,22 @@ package jogamp.newt.driver.windows; +import java.io.IOException; +import java.net.MalformedURLException; +import java.nio.Buffer; +import java.nio.ByteBuffer; + import jogamp.nativewindow.windows.RegisteredClass; import jogamp.nativewindow.windows.RegisteredClassFactory; import jogamp.newt.DisplayImpl; import jogamp.newt.NEWTJNILibLoader; +import jogamp.newt.driver.PNGIcon; + import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.IOUtil; import com.jogamp.nativewindow.windows.WindowsGraphicsDevice; public class DisplayDriver extends DisplayImpl { @@ -92,9 +101,38 @@ public class DisplayDriver extends DisplayImpl { return sharedClass.getName(); } + @Override + protected PointerIcon createPointerIconImpl(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException { + if( PNGIcon.isAvailable() ) { + final int[] width = { 0 }, height = { 0 }, data_size = { 0 }, elem_bytesize = { 0 }; + if( null != pngResource && 0 < pngResource.resourceCount() ) { + final ByteBuffer data = PNGIcon.singleToRGBAImage(pngResource, 0, true /* toBGRA */, width, height, data_size, elem_bytesize); + return new PointerIconImpl( createBGRA8888Icon0(data, width[0], height[0], true, hotX, hotY) ); + } + } + return null; + } + + @Override + protected final void destroyPointerIconImpl(final long displayHandle, final PointerIcon pi) { + destroyIcon0(((PointerIconImpl)pi).handle); + } + //---------------------------------------------------------------------- // Internals only // private static native void DispatchMessages0(); + + static long createBGRA8888Icon0(Buffer data, int width, int height, boolean isCursor, int hotX, int hotY) { + if( null == data ) { + throw new IllegalArgumentException("data buffer/size"); + } + if( !Buffers.isDirect(data) ) { + throw new IllegalArgumentException("data buffer is not direct "+data); + } + return createBGRA8888Icon0(data, Buffers.getDirectBufferByteOffset(data), width, height, isCursor, hotX, hotY); + } + private static native long createBGRA8888Icon0(Object data, int data_offset, int width, int height, boolean isCursor, int hotX, int hotY); + private static native void destroyIcon0(long handle); } diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java index 6e8ac3efa..3239aab5b 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java @@ -34,9 +34,13 @@ package jogamp.newt.driver.windows; +import java.nio.ByteBuffer; + import jogamp.nativewindow.windows.GDI; import jogamp.nativewindow.windows.GDIUtil; import jogamp.newt.WindowImpl; +import jogamp.newt.DisplayImpl.PointerIconImpl; +import jogamp.newt.driver.PNGIcon; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.GraphicsConfigurationFactory; @@ -47,23 +51,46 @@ import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; import com.jogamp.common.os.Platform; +import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.VersionNumber; +import com.jogamp.newt.NewtFactory; import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.MouseEvent.PointerType; public class WindowDriver extends WindowImpl { + private static final long[] defaultIconHandles; + + static { + DisplayDriver.initSingleton(); + { + long[] _defaultIconHandle = { 0, 0 }; + if( PNGIcon.isAvailable() ) { + try { + final int[] width = { 0 }, height = { 0 }, data_size = { 0 }, elem_bytesize = { 0 }; + final IOUtil.ClassResources iconRes = NewtFactory.getWindowIcons(); + { + final ByteBuffer icon_data_small = PNGIcon.singleToRGBAImage(iconRes, 0, true /* toBGRA */, width, height, data_size, elem_bytesize); + _defaultIconHandle[0] = DisplayDriver.createBGRA8888Icon0(icon_data_small, width[0], height[0], false, 0, 0); + } + { + final ByteBuffer icon_data_big = PNGIcon.singleToRGBAImage(iconRes, iconRes.resourceCount()-1, true /* toBGRA */, width, height, data_size, elem_bytesize); + _defaultIconHandle[1] = DisplayDriver.createBGRA8888Icon0(icon_data_big, width[0], height[0], false, 0, 0); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + defaultIconHandles = _defaultIconHandle; + } + } private long hmon; private long hdc; private long hdc_old; private long windowHandleClose; - static { - DisplayDriver.initSingleton(); - } - public WindowDriver() { } @@ -142,7 +169,8 @@ public class WindowDriver extends WindowImpl { ( FLAG_IS_ALWAYSONTOP | FLAG_IS_UNDECORATED ) ; final long _windowHandle = CreateWindow0(DisplayDriver.getHInstance(), display.getWindowClassName(), display.getWindowClassName(), winVer.getMajor(), winVer.getMinor(), - getParentWindowHandle(), getX(), getY(), getWidth(), getHeight(), autoPosition(), flags); + getParentWindowHandle(), getX(), getY(), getWidth(), getHeight(), autoPosition(), flags, + defaultIconHandles[0], defaultIconHandles[1]); if ( 0 == _windowHandle ) { throw new NativeWindowException("Error creating window"); } @@ -159,8 +187,8 @@ public class WindowDriver extends WindowImpl { @Override protected void closeNativeImpl() { - if(windowHandleClose != 0) { - if (hdc != 0) { + if( 0 != windowHandleClose ) { + if ( 0 != hdc ) { try { GDI.ReleaseDC(windowHandleClose, hdc); } catch (Throwable t) { @@ -177,10 +205,9 @@ public class WindowDriver extends WindowImpl { Exception e = new Exception("Warning: closeNativeImpl failed - "+Thread.currentThread().getName(), t); e.printStackTrace(); } - } finally { - windowHandleClose = 0; } } + windowHandleClose = 0; hdc = 0; hdc_old = 0; } @@ -224,6 +251,11 @@ public class WindowDriver extends WindowImpl { } @Override + protected void setPointerIconImpl(final PointerIconImpl pi) { + setPointerIcon0(getWindowHandle(), null != pi ? pi.handle : 0); + } + + @Override protected boolean setPointerVisibleImpl(final boolean pointerVisible) { final boolean[] res = new boolean[] { false }; @@ -361,7 +393,8 @@ public class WindowDriver extends WindowImpl { protected static native boolean initIDs0(long hInstance); private native long CreateWindow0(long hInstance, String wndClassName, String wndName, int winMajor, int winMinor, - long parentWindowHandle, int x, int y, int width, int height, boolean autoPosition, int flags); + long parentWindowHandle, int x, int y, int width, int height, boolean autoPosition, int flags, + long iconSmallHandle, long iconBigHandle); private native long MonitorFromWindow0(long windowHandle); private native void reconfigureWindow0(long parentWindowHandle, long windowHandle, int x, int y, int width, int height, int flags); @@ -371,4 +404,7 @@ public class WindowDriver extends WindowImpl { private static native boolean setPointerVisible0(long windowHandle, boolean visible); private static native boolean confinePointer0(long windowHandle, boolean grab, int l, int t, int r, int b); private static native void warpPointer0(long windowHandle, int x, int y); + private static native ByteBuffer newDirectByteBuffer(long addr, long capacity); + + private static native void setPointerIcon0(long windowHandle, long iconHandle); } diff --git a/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java index 504839797..8170c2e3d 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java @@ -34,14 +34,22 @@ package jogamp.newt.driver.x11; +import java.io.IOException; +import java.net.MalformedURLException; +import java.nio.Buffer; +import java.nio.ByteBuffer; + import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.IOUtil; import com.jogamp.nativewindow.x11.X11GraphicsDevice; import jogamp.nativewindow.x11.X11Util; import jogamp.newt.DisplayImpl; import jogamp.newt.NEWTJNILibLoader; +import jogamp.newt.driver.PNGIcon; public class DisplayDriver extends DisplayImpl { @@ -120,6 +128,35 @@ public class DisplayDriver extends DisplayImpl { /** Returns <code>null</code> if !{@link #isNativeValid()}, otherwise the Boolean value of {@link X11GraphicsDevice#isXineramaEnabled()}. */ protected Boolean isXineramaEnabled() { return isNativeValid() ? Boolean.valueOf(((X11GraphicsDevice)aDevice).isXineramaEnabled()) : null; } + @Override + protected PointerIcon createPointerIconImpl(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException { + if( PNGIcon.isAvailable() ) { + final int[] width = { 0 }, height = { 0 }, data_size = { 0 }, elem_bytesize = { 0 }; + if( null != pngResource && 0 < pngResource.resourceCount() ) { + final ByteBuffer data = PNGIcon.singleToRGBAImage(pngResource, 0, false /* toBGRA */, width, height, data_size, elem_bytesize); + final long handle = runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Long>() { + @Override + public Long run(long dpy) { + long h = 0; + try { + h = createPointerIcon0(dpy, data, width[0], height[0], hotX, hotY); + } catch (Exception e) { + e.printStackTrace(); + } + return Long.valueOf(h); + } + }).longValue(); + return new PointerIconImpl(handle); + } + } + return null; + } + + @Override + protected final void destroyPointerIconImpl(final long displayHandle, final PointerIcon pi) { + destroyPointerIcon0(displayHandle, ((PointerIconImpl)pi).handle); + } + //---------------------------------------------------------------------- // Internals only // @@ -137,6 +174,15 @@ public class DisplayDriver extends DisplayImpl { private native void DispatchMessages0(long display, long javaObjectAtom, long windowDeleteAtom /* , long kbdHandle */); // XKB disabled for now + static long createPointerIcon0(long display, Buffer data, int width, int height, int hotX, int hotY) { + if( !Buffers.isDirect(data) ) { + throw new IllegalArgumentException("data buffer is not direct "+data); + } + return createPointerIcon0(display, data, Buffers.getDirectBufferByteOffset(data), width, height, hotX, hotY); + } + private static native long createPointerIcon0(long display, Object data, int data_offset, int width, int height, int hotX, int hotY); + static native void destroyPointerIcon0(long display, long handle); + /** X11 Window delete atom marker used on EDT */ private long windowDeleteAtom; diff --git a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java index f2a27b0c9..0ea2c5358 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java @@ -34,11 +34,16 @@ package jogamp.newt.driver.x11; +import java.nio.Buffer; + import jogamp.nativewindow.x11.X11Lib; import jogamp.nativewindow.x11.X11Util; import jogamp.newt.DisplayImpl; import jogamp.newt.DisplayImpl.DisplayRunnable; +import jogamp.newt.DisplayImpl.PointerIconImpl; import jogamp.newt.WindowImpl; +import jogamp.newt.driver.PNGIcon; + import javax.media.nativewindow.*; import javax.media.nativewindow.VisualIDHolder.VIDType; import javax.media.nativewindow.util.Insets; @@ -47,6 +52,7 @@ import javax.media.nativewindow.util.Point; import com.jogamp.nativewindow.x11.X11GraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsScreen; +import com.jogamp.newt.NewtFactory; import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.MouseEvent; @@ -58,8 +64,29 @@ public class WindowDriver extends WindowImpl { private static final int X11_WHEEL_TWO_UP_BUTTON = 6; private static final int X11_WHEEL_TWO_DOWN_BUTTON = 7; + private static final int defaultIconDataSize; + private static final Buffer defaultIconData; + static { ScreenDriver.initSingleton(); + + int _icon_data_size=0, _icon_elem_bytesize=0; + Buffer _icon_data=null; + if( PNGIcon.isAvailable() ) { + try { + final int[] data_size = { 0 }, elem_bytesize = { 0 }; + _icon_data = PNGIcon.arrayToX11BGRAImages(NewtFactory.getWindowIcons(), data_size, elem_bytesize); + _icon_data_size = data_size[0]; + _icon_elem_bytesize = elem_bytesize[0]; + } catch (Exception e) { + e.printStackTrace(); + } + } + defaultIconDataSize = _icon_data_size; + defaultIconData = _icon_data; + if(DEBUG_IMPLEMENTATION) { + System.err.println("Def. Icon: data_size "+defaultIconDataSize+" * elem_size "+_icon_elem_bytesize+" = data "+defaultIconData); + } } public WindowDriver() { @@ -100,7 +127,8 @@ public class WindowDriver extends WindowImpl { setWindowHandle(CreateWindow0(getParentWindowHandle(), edtDevice.getHandle(), screen.getIndex(), visualID, display.getJavaObjectAtom(), display.getWindowDeleteAtom(), - getX(), getY(), getWidth(), getHeight(), autoPosition(), flags)); + getX(), getY(), getWidth(), getHeight(), autoPosition(), flags, + defaultIconDataSize, defaultIconData)); } finally { edtDevice.unlock(); } @@ -246,6 +274,21 @@ public class WindowDriver extends WindowImpl { } @Override + protected void setPointerIconImpl(final PointerIconImpl pi) { + runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Object>() { + @Override + public Object run(long dpy) { + try { + setPointerIcon0(dpy, getWindowHandle(), null != pi ? pi.handle : 0); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + }); + } + + @Override protected boolean setPointerVisibleImpl(final boolean pointerVisible) { return runWithLockedDisplayDevice( new DisplayImpl.DisplayRunnable<Boolean>() { @Override @@ -381,13 +424,17 @@ public class WindowDriver extends WindowImpl { private native long CreateWindow0(long parentWindowHandle, long display, int screen_index, int visualID, long javaObjectAtom, long windowDeleteAtom, - int x, int y, int width, int height, boolean autoPosition, int flags); + int x, int y, int width, int height, boolean autoPosition, int flags, + int iconDataSize, Object iconData); private native void CloseWindow0(long display, long windowHandle, long javaObjectAtom, long windowDeleteAtom /*, long kbdHandle*/ ); // XKB disabled for now private native void reconfigureWindow0(long display, int screen_index, long parentWindowHandle, long windowHandle, long windowDeleteAtom, int x, int y, int width, int height, int flags); private native void requestFocus0(long display, long windowHandle, boolean force); private static native void setTitle0(long display, long windowHandle, String title); + + private static native void setPointerIcon0(long display, long windowHandle, long handle); + private static native long getParentWindow0(long display, long windowHandle); private static native boolean setPointerVisible0(long display, long windowHandle, boolean visible); private static native boolean confinePointer0(long display, long windowHandle, boolean grab); diff --git a/src/newt/native/MacWindow.m b/src/newt/native/MacWindow.m index 30d3458ad..16e9814ef 100644 --- a/src/newt/native/MacWindow.m +++ b/src/newt/native/MacWindow.m @@ -285,6 +285,76 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_stopNSApplic [pool release]; } +static NSImage * createNSImageFromData(JNIEnv *env, jobject jiconData, jint jiconWidth, jint jiconHeight) { + if( NULL != jiconData ) { + unsigned char * iconData = (unsigned char *) (*env)->GetDirectBufferAddress(env, jiconData); + NSInteger iconWidth = (NSInteger) jiconWidth; + NSInteger iconHeight = (NSInteger) jiconHeight; + const NSInteger bpc = 8 /* bits per component */, spp=4 /* RGBA */, bpp = bpc * spp; + const NSBitmapFormat bfmt = NSAlphaNonpremultipliedBitmapFormat; + const BOOL hasAlpha = YES; + + NSBitmapImageRep* bir = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: &iconData + pixelsWide: iconWidth + pixelsHigh: iconHeight + bitsPerSample: bpc + samplesPerPixel: spp + hasAlpha: hasAlpha + isPlanar: NO + colorSpaceName: NSCalibratedRGBColorSpace + bitmapFormat: bfmt + bytesPerRow: iconWidth*4 + bitsPerPixel: bpp]; + [bir autorelease]; + NSImage* nsImage = [[NSImage alloc] initWithCGImage: [bir CGImage] size:NSZeroSize]; + return nsImage; + } + return NULL; +} + +/* + * Class: jogamp_newt_driver_macosx_DisplayDriver + * Method: setAppIcon0 + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_setAppIcon0 + (JNIEnv *env, jobject unused, jobject jiconData, jint jiconWidth, jint jiconHeight) +{ + NSImage * nsImage = createNSImageFromData(env, jiconData, jiconWidth, jiconHeight); + if( NULL != nsImage ) { + [nsImage autorelease]; + [NSApp setApplicationIconImage: nsImage]; + } +} + +JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_createPointerIcon0 + (JNIEnv *env, jobject unused, jobject jiconData, jint jiconWidth, jint jiconHeight, jint hotX, jint hotY) +{ + NSImage * nsImage = createNSImageFromData(env, jiconData, jiconWidth, jiconHeight); + if( NULL != nsImage ) { + [nsImage autorelease]; + NSPoint hotP = { hotX, hotY }; + NSCursor * c = [[NSCursor alloc] initWithImage: nsImage hotSpot: hotP]; + return (jlong) (intptr_t) c; + } + return 0; +} + +JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_destroyPointerIcon0 + (JNIEnv *env, jobject unused, jlong handle) +{ + NSCursor * c = (NSCursor*) (intptr_t) handle ; + [c release]; +} + +JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setPointerIcon0 + (JNIEnv *env, jobject unused, jlong window, jlong handle) +{ + NewtMacWindow *mWin = (NewtMacWindow*) (intptr_t) window; + NSCursor * c = (NSCursor*) (intptr_t) handle ; + + [mWin setCustomCursor: c]; +} + static NSScreen * NewtScreen_getNSScreenByIndex(int screen_idx, BOOL cap) { NSArray *screens = [NSScreen screens]; if( screen_idx<0 || screen_idx>=[screens count] ) { @@ -632,7 +702,7 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_createView0 [pool release]; - return (jlong) ((intptr_t) myView); + return (jlong) (intptr_t) myView; } /** @@ -674,7 +744,6 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_createWindow backing: (NSBackingStoreType) bufferingType defer: YES isFullscreenWindow: fullscreen]; - // DBG_PRINT( "createWindow0.1 - %p, isVisible %d\n", myWindow, [myWindow isVisible]); DBG_PRINT( "createWindow0.X - %p, isVisible %d\n", myWindow, [myWindow isVisible]); diff --git a/src/newt/native/NewtMacWindow.h b/src/newt/native/NewtMacWindow.h index ba60b5665..1f907f30e 100644 --- a/src/newt/native/NewtMacWindow.h +++ b/src/newt/native/NewtMacWindow.h @@ -114,6 +114,7 @@ BOOL mouseVisible; BOOL mouseInside; BOOL cursorIsHidden; + NSCursor * customCursor; BOOL realized; BOOL modsDown[4]; // shift, ctrl, alt/option, win/command NSPoint lastInsideMousePosition; @@ -151,7 +152,8 @@ - (NSPoint) screenPos2NewtClientWinPos: (NSPoint) p; - (BOOL) isMouseInside; -- (void) cursorHide:(BOOL)v; +- (void) cursorHide:(BOOL)v enter:(int)enterState; +- (void) setCustomCursor:(NSCursor*)c; - (void) setMouseVisible:(BOOL)v hasFocus:(BOOL)focus; - (void) setMouseConfined:(BOOL)v; - (void) setMousePosition:(NSPoint)p; diff --git a/src/newt/native/NewtMacWindow.m b/src/newt/native/NewtMacWindow.m index 4b0198c7e..a7bab9b9d 100644 --- a/src/newt/native/NewtMacWindow.m +++ b/src/newt/native/NewtMacWindow.m @@ -41,6 +41,8 @@ #include <math.h> +#define PRINTF(...) NSLog(@ __VA_ARGS__) + static jfloat GetDelta(NSEvent *event, jint javaMods[]) { CGEventRef cgEvent = [event CGEvent]; CGFloat deltaY = 0.0; @@ -468,6 +470,7 @@ static UniChar CKCH_CharForKeyCode(jshort keyCode) { defaultPresentationOptions = 0; fullscreenPresentationOptions = 0; } + isFullscreenWindow = isfs; // Why is this necessary? Without it we don't get any of the // delegate methods like resizing and window movement. @@ -484,6 +487,7 @@ static UniChar CKCH_CharForKeyCode(jshort keyCode) { mouseVisible = YES; mouseInside = NO; cursorIsHidden = NO; + customCursor = NULL; realized = YES; DBG_PRINT("NewtWindow::create: %p, realized %d, hasPresentationSwitch %d[defaultOptions 0x%X, fullscreenOptions 0x%X], (refcnt %d)\n", res, realized, (int)hasPresentationSwitch, (int)defaultPresentationOptions, (int)fullscreenPresentationOptions, (int)[res retainCount]); @@ -678,13 +682,36 @@ static UniChar CKCH_CharForKeyCode(jshort keyCode) { DBG_PRINT( "setMouseVisible: confined %d, visible %d (current: %d), mouseInside %d, hasFocus %d\n", mouseConfined, mouseVisible, !cursorIsHidden, mouseInside, focus); if(YES == focus && YES == mouseInside) { - [self cursorHide: !mouseVisible]; + [self cursorHide: !mouseVisible enter: 0]; + } +} + +- (void) setCustomCursor:(NSCursor*)c +{ + if(YES == mouseInside) { + if( NULL != c ) { + DBG_PRINT( "setCustomCursor push: %p\n", c); + [c push]; + } else if( NULL != customCursor && [NSCursor currentCursor] == customCursor ) { + DBG_PRINT( "setCustomCursor pop: %p\n", customCursor); + [customCursor pop]; + } } + customCursor = c; } -- (void) cursorHide:(BOOL)v +- (void) cursorHide:(BOOL)v enter:(int)enterState { - DBG_PRINT( "cursorHide: %d -> %d\n", cursorIsHidden, v); + DBG_PRINT( "cursorHide: %d -> %d, enter %d\n", cursorIsHidden, v, enterState); + if( NULL != customCursor ) { + if( 1 == enterState && [NSCursor currentCursor] != customCursor ) { + DBG_PRINT( "cursorHide.customCursor push: %p\n", customCursor); + [customCursor push]; + } else if( -1 == enterState && [NSCursor currentCursor] == customCursor ) { + DBG_PRINT( "cursorHide.customCursor pop: %p\n", customCursor); + [customCursor pop]; + } + } if(v) { if(!cursorIsHidden) { [NSCursor hide]; @@ -941,7 +968,7 @@ static jint mods2JavaMods(NSUInteger mods) DBG_PRINT( "*************** windowDidBecomeKey\n"); mouseInside = [self isMouseInside]; if(YES == mouseInside) { - [self cursorHide: !mouseVisible]; + [self cursorHide: !mouseVisible enter: 0]; } [self focusChanged: YES]; } @@ -995,7 +1022,7 @@ static jint mods2JavaMods(NSUInteger mods) { DBG_PRINT( "mouseEntered: confined %d, visible %d\n", mouseConfined, mouseVisible); mouseInside = YES; - [self cursorHide: !mouseVisible]; + [self cursorHide: !mouseVisible enter: 1]; if(NO == mouseConfined) { [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_ENTERED]; } @@ -1006,7 +1033,7 @@ static jint mods2JavaMods(NSUInteger mods) DBG_PRINT( "mouseExited: confined %d, visible %d\n", mouseConfined, mouseVisible); if(NO == mouseConfined) { mouseInside = NO; - [self cursorHide: NO]; + [self cursorHide: NO enter: -1]; [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_EXITED]; } else { [self setMousePosition: lastInsideMousePosition]; @@ -1160,7 +1187,7 @@ static jint mods2JavaMods(NSUInteger mods) jboolean closed = JNI_FALSE; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - [self cursorHide: NO]; + [self cursorHide: NO enter: -1]; NSView* nsview = [self contentView]; if( ! [nsview isKindOfClass:[NewtView class]] ) { diff --git a/src/newt/native/WindowsWindow.c b/src/newt/native/WindowsWindow.c index dd0150e78..2a16dce57 100644 --- a/src/newt/native/WindowsWindow.c +++ b/src/newt/native/WindowsWindow.c @@ -139,7 +139,7 @@ #include "NewtCommon.h" -// #define VERBOSE_ON 1 +#define VERBOSE_ON 1 // #define DEBUG_KEYS 1 #ifdef VERBOSE_ON @@ -186,6 +186,10 @@ typedef struct { int height; /** Tristate: -1 HIDE, 0 NOP, 1 SHOW */ int setPointerVisible; + /** Tristate: -1 RESET, 0 NOP, 1 SET-NEW */ + int setPointerAction; + HCURSOR setPointerHandle; + HCURSOR defPointerHandle; /** Bool: 0 NOP, 1 FULLSCREEN */ int setFullscreen; int pointerCaptured; @@ -1023,10 +1027,22 @@ static LRESULT CALLBACK wndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lP } else /* -1 == wud->setPointerVisible */ { visibilityChangeSuccessful = SafeShowCursor(FALSE); } - useDefWindowProc = visibilityChangeSuccessful ? 1 : 0; DBG_PRINT("*** WindowsWindow: WM_SETCURSOR requested visibility: %d success: %d\n", wud->setPointerVisible, visibilityChangeSuccessful); wud->setPointerVisible = 0; - // own signal, consumed + // own signal, consumed, no further processing + useDefWindowProc = 0; + res = 1; + } else if( 0 != wud->setPointerAction ) { + if( -1 == wud->setPointerAction ) { + wud->setPointerHandle = wud->defPointerHandle; + } + HCURSOR preHandle = SetCursor(wud->setPointerHandle); + DBG_PRINT("*** WindowsWindow: WM_SETCURSOR requested change %d: pre %p -> set %p, def %p\n", + wud->setPointerAction, (void*)preHandle, (void*)wud->setPointerHandle, (void*)wud->defPointerHandle); + wud->setPointerAction = 0; + // own signal, consumed, no further processing + useDefWindowProc = 0; + res = 1; } else { useDefWindowProc = 1; // NOP for us, allow parent to act } @@ -1246,6 +1262,7 @@ static LRESULT CALLBACK wndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lP if( 0 == wud->pointerInside ) { wud->pointerInside = 1; NewtWindows_trackPointerLeave(wnd); + SetCursor(wud->setPointerHandle); } (*env)->CallVoidMethod(env, window, sendMouseEventID, (jshort) EVENT_MOUSE_MOVED, @@ -1997,7 +2014,8 @@ static void NewtWindow_setVisiblePosSize(HWND hwnd, BOOL atop, BOOL visible, JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_windows_WindowDriver_CreateWindow0 (JNIEnv *env, jobject obj, jlong hInstance, jstring jWndClassName, jstring jWndName, jint winMajor, jint winMinor, - jlong parent, jint jx, jint jy, jint defaultWidth, jint defaultHeight, jboolean autoPosition, jint flags) + jlong parent, jint jx, jint jy, jint defaultWidth, jint defaultHeight, jboolean autoPosition, jint flags, + jlong iconSmallHandle, jlong iconBigHandle) { HWND parentWindow = (HWND) (intptr_t) parent; const TCHAR* wndClassName = NULL; @@ -2054,6 +2072,9 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_windows_WindowDriver_CreateWindo wud->width = width; wud->height = height; wud->setPointerVisible = 0; + wud->setPointerAction = 0; + wud->defPointerHandle = LoadCursor( NULL, IDC_ARROW); + wud->setPointerHandle = wud->defPointerHandle; wud->setFullscreen = 0; wud->pointerCaptured = 0; wud->pointerInside = 0; @@ -2083,6 +2104,12 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_windows_WindowDriver_CreateWindo RECT rc; RECT * insets; + if( 0 != iconSmallHandle ) { + SendMessage(window, WM_SETICON, ICON_SMALL, (LPARAM) iconSmallHandle ); + } + if( 0 != iconBigHandle ) { + SendMessage(window, WM_SETICON, ICON_BIG, (LPARAM) iconBigHandle ); + } ShowWindow(window, SW_SHOW); // send insets before visibility, allowing java code a proper sync point! @@ -2321,3 +2348,83 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_windows_WindowDriver_warpPointer0 SetCursorPos(x, y); } +JNIEXPORT jlong JNICALL +Java_jogamp_newt_driver_windows_DisplayDriver_createBGRA8888Icon0(JNIEnv *env, jobject _unused, + jobject data, jint data_offset, jint width, jint height, jboolean isCursor, jint hotX, jint hotY) { + + const unsigned char * data_ptr = (const unsigned char *) (*env)->GetDirectBufferAddress(env, data) + data_offset; + const int bytes = 4 * width * height; // BGRA8888 + + DWORD dwWidth, dwHeight; + BITMAPV5HEADER bi; + HBITMAP hBitmap; + void *lpBits; + HICON handle = NULL; + + dwWidth = width; // width of cursor + dwHeight = height; // height of cursor + + ZeroMemory(&bi,sizeof(BITMAPV5HEADER)); + bi.bV5Size = sizeof(BITMAPV5HEADER); + bi.bV5Width = dwWidth; + bi.bV5Height = -1 * dwHeight; + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + // The following mask specification specifies a supported 32 BPP + // alpha format for Windows XP. + bi.bV5RedMask = 0x00FF0000; + bi.bV5GreenMask = 0x0000FF00; + bi.bV5BlueMask = 0x000000FF; + bi.bV5AlphaMask = 0xFF000000; + + HDC hdc; + hdc = GetDC(NULL); + + // Create the DIB section with an alpha channel. + hBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, + (void **)&lpBits, NULL, (DWORD)0); + + memcpy(lpBits, data_ptr, bytes); + + ReleaseDC(NULL,hdc); + + // Create an empty mask bitmap. + HBITMAP hMonoBitmap = CreateBitmap(dwWidth,dwHeight,1,1,NULL); + + ICONINFO ii; + ii.fIcon = isCursor ? FALSE : TRUE; + ii.xHotspot = hotX; + ii.yHotspot = hotY; + ii.hbmMask = hMonoBitmap; + ii.hbmColor = hBitmap; + + // Create the alpha cursor with the alpha DIB section. + handle = CreateIconIndirect(&ii); + + DeleteObject(hBitmap); + DeleteObject(hMonoBitmap); + + return (jlong) (intptr_t) handle; +} + +JNIEXPORT void JNICALL +Java_jogamp_newt_driver_windows_DisplayDriver_destroyIcon0(JNIEnv *env, jobject _unused, jlong jhandle) { + HICON handle = (HICON) (intptr_t) jhandle; + DestroyIcon(handle); +} + +JNIEXPORT void JNICALL +Java_jogamp_newt_driver_windows_WindowDriver_setPointerIcon0(JNIEnv *env, jobject _unused, jlong window, jlong iconHandle) { + HWND hwnd = (HWND) (intptr_t) window; + WindowUserData * wud; +#if !defined(__MINGW64__) && ( defined(UNDER_CE) || _MSC_VER <= 1200 ) + wud = (WindowUserData *) GetWindowLong(hwnd, GWL_USERDATA); +#else + wud = (WindowUserData *) GetWindowLongPtr(hwnd, GWLP_USERDATA); +#endif + wud->setPointerAction = 0 != iconHandle ? 1 : -1; + wud->setPointerHandle = (HCURSOR) (intptr_t) iconHandle; + SendMessage(hwnd, WM_SETCURSOR, 0, 0); +} + diff --git a/src/newt/native/X11Display.c b/src/newt/native/X11Display.c index e2392a113..19e733111 100644 --- a/src/newt/native/X11Display.c +++ b/src/newt/native/X11Display.c @@ -28,6 +28,8 @@ #include "X11Common.h" +#include <X11/Xcursor/Xcursor.h> + // #include <X11/XKBlib.h> // XKB disabled for now jclass X11NewtWindowClazz = NULL; @@ -670,4 +672,54 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_DispatchMessage } } +/* + * Class: Java_jogamp_newt_driver_x11_DisplayDriver + * Method: createPointerIcon0 + * Signature: (JJILjava/lang/Object;I)V + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_createPointerIcon0 + (JNIEnv *env, jclass clazz, jlong display, jobject data, jint data_offset, jint width, jint height, jint hotX, jint hotY) +{ + Cursor c; + + if( 0 != data ) { + Display * dpy = (Display *) (intptr_t) display; + char * data_ptr = (char *) (*env)->GetDirectBufferAddress(env, data) + data_offset; + XcursorImage ci; + ci.version = 1; // XCURSOR_IMAGE_VERSION; + ci.size = width; // nominal size (assume square ..) + ci.width = width; + ci.height = height; + ci.xhot = hotX; + ci.yhot = hotY; + ci.delay = 0; + ci.pixels = (XcursorPixel *)(intptr_t)data_ptr; + + c = XcursorImageLoadCursor (dpy, &ci); + + DBG_PRINT( "X11: createPointerIcon0: %p %dx%d %d/%d -> %p\n", data_ptr, width, height, hotX, hotY, (void *)c); + + } else { + c = 0; + } + return (jlong) (intptr_t) c; +} + +/* + * Class: Java_jogamp_newt_driver_x11_DisplayDriver + * Method: destroyPointerIcon0 + * Signature: (JJILjava/lang/Object;I)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_destroyPointerIcon0 + (JNIEnv *env, jclass clazz, jlong display, jlong handle) +{ + Display * dpy = (Display *) (intptr_t) display; + + if( 0 != handle ) { + Cursor c = (Cursor) (intptr_t) handle; + DBG_PRINT( "X11: destroyPointerIcon0: %p\n", (void *)c); + XFreeCursor(dpy, c); + } +} + diff --git a/src/newt/native/X11Window.c b/src/newt/native/X11Window.c index 5f5dddbe0..da2778004 100644 --- a/src/newt/native/X11Window.c +++ b/src/newt/native/X11Window.c @@ -497,6 +497,12 @@ static void NewtWindows_setPosSize(Display *dpy, Window w, jint x, jint y, jint } } +static void NewtWindows_setIcon(Display *dpy, Window w, int data_size, const unsigned char * data_ptr) { + Atom _NET_WM_ICON = XInternAtom(dpy, "_NET_WM_ICON", False); + Atom CARDINAL = XInternAtom(dpy, "CARDINAL", False); + XChangeProperty(dpy, w, _NET_WM_ICON, CARDINAL, 32, PropModeReplace, data_ptr, data_size); +} + /* * Class: jogamp_newt_driver_x11_WindowDriver * Method: CreateWindow @@ -505,7 +511,8 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_x11_WindowDriver_CreateWindow0 (JNIEnv *env, jobject obj, jlong parent, jlong display, jint screen_index, jint visualID, jlong javaObjectAtom, jlong windowDeleteAtom, - jint x, jint y, jint width, jint height, jboolean autoPosition, int flags) + jint x, jint y, jint width, jint height, jboolean autoPosition, int flags, + jint iconDataSize, jobject iconData) { Display * dpy = (Display *)(intptr_t)display; Atom wm_delete_atom = (Atom)windowDeleteAtom; @@ -626,6 +633,11 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_x11_WindowDriver_CreateWindow0 XEvent event; int left=0, right=0, top=0, bottom=0; + if( 0 < iconDataSize && NULL != iconData ) { + const unsigned char * iconDataPtr = (const unsigned char *) (*env)->GetDirectBufferAddress(env, iconData); + NewtWindows_setIcon(dpy, window, (int)iconDataSize, iconDataPtr); + } + XMapWindow(dpy, window); XIfEvent( dpy, &event, WaitForMapNotify, (XPointer) window ); // wait to get proper insets values @@ -946,6 +958,27 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_WindowDriver_setTitle0 /* * Class: Java_jogamp_newt_driver_x11_WindowDriver + * Method: setPointerIcon0 + * Signature: (JJILjava/lang/Object;I)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_WindowDriver_setPointerIcon0 + (JNIEnv *env, jclass clazz, jlong display, jlong window, jlong handle) +{ + Display * dpy = (Display *) (intptr_t) display; + Window w = (Window)window; + + if( 0 == handle ) { + DBG_PRINT( "X11: setPointerIcon0: reset\n"); + XUndefineCursor(dpy, w); + } else { + Cursor c = (Cursor) (intptr_t) handle; + DBG_PRINT( "X11: setPointerIcon0: %p\n", (void*)c); + XDefineCursor(dpy, w, c); + } +} + +/* + * Class: Java_jogamp_newt_driver_x11_WindowDriver * Method: setPointerVisible0 * Signature: (JJZ)Z */ diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java index b54a2cd19..efec961de 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java @@ -30,7 +30,9 @@ package com.jogamp.opengl.test.junit.jogl.demos.es2.newt; import java.io.IOException; +import com.jogamp.common.util.IOUtil; import com.jogamp.newt.Display; +import com.jogamp.newt.Display.PointerIcon; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Screen; import com.jogamp.newt.event.KeyAdapter; @@ -44,9 +46,7 @@ import com.jogamp.opengl.test.junit.util.AWTRobotUtil; import com.jogamp.opengl.test.junit.util.MiscUtils; import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.test.junit.util.QuitAdapter; - import com.jogamp.opengl.util.Animator; - import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import javax.media.nativewindow.NativeWindowFactory; @@ -54,7 +54,6 @@ import javax.media.nativewindow.util.Dimension; import javax.media.nativewindow.util.Point; import javax.media.nativewindow.util.PointImmutable; import javax.media.nativewindow.util.DimensionImmutable; - import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; @@ -171,6 +170,20 @@ public class TestGearsES2NEWT extends UITestCase { } }); + final PointerIcon pointerIconOne; + { + PointerIcon _pointerIconOne = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "jogamp-pointer-64x64.png" } ); + final Display disp = glWindow.getScreen().getDisplay(); + disp.createNative(); + try { + _pointerIconOne = disp.createPointerIcon(res, 32, 0); + } catch (Exception e) { + e.printStackTrace(); + } + pointerIconOne = _pointerIconOne; + } + glWindow.addKeyListener(new KeyAdapter() { @Override public void keyPressed(final KeyEvent e) { @@ -203,6 +216,16 @@ public class TestGearsES2NEWT extends UITestCase { System.err.println("[set alwaysontop post]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getWidth()+"x"+glWindow.getHeight()+", f "+glWindow.isFullscreen()+", a "+glWindow.isAlwaysOnTop()+", "+glWindow.getInsets()); glWindow.setExclusiveContextThread(t); } }.start(); + } else if(e.getKeyChar()=='c') { + new Thread() { + public void run() { + final Thread t = glWindow.setExclusiveContextThread(null); + System.err.println("[set pointer-icon pre]"); + final PointerIcon currentPI = glWindow.getPointerIcon(); + glWindow.setPointerIcon( currentPI == pointerIconOne ? null : pointerIconOne); + System.err.println("[set pointer-icon post] "+currentPI+" -> "+glWindow.getPointerIcon()); + glWindow.setExclusiveContextThread(t); + } }.start(); } else if(e.getKeyChar()=='d') { new Thread() { public void run() { |