aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--make/scripts/tests.sh6
-rw-r--r--src/newt/classes/com/jogamp/newt/Window.java62
-rw-r--r--src/newt/classes/com/jogamp/newt/event/DoubleTapScrollGesture.java346
-rw-r--r--src/newt/classes/com/jogamp/newt/event/GestureHandler.java143
-rw-r--r--src/newt/classes/com/jogamp/newt/event/InputEvent.java8
-rw-r--r--src/newt/classes/com/jogamp/newt/event/MouseEvent.java238
-rw-r--r--src/newt/classes/com/jogamp/newt/event/NEWTEvent.java1
-rw-r--r--src/newt/classes/com/jogamp/newt/event/PinchToZoomGesture.java217
-rw-r--r--src/newt/classes/com/jogamp/newt/opengl/GLWindow.java34
-rw-r--r--src/newt/classes/jogamp/newt/WindowImpl.java555
-rw-r--r--src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java301
-rw-r--r--src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java15
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java124
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java6
14 files changed, 1531 insertions, 525 deletions
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh
index fa44190c0..84f487c38 100644
--- a/make/scripts/tests.sh
+++ b/make/scripts/tests.sh
@@ -187,7 +187,7 @@ function jrun() {
#D_ARGS="-Dnewt.debug.Window -Djogl.debug.Animator -Dnewt.debug.Screen"
#D_ARGS="-Dnativewindow.debug.JAWT -Dnewt.debug.Window"
#D_ARGS="-Dnewt.debug.Window.KeyEvent"
- #D_ARGS="-Dnewt.debug.Window.MouseEvent"
+ D_ARGS="-Dnewt.debug.Window.MouseEvent"
#D_ARGS="-Dnewt.debug.Window.MouseEvent -Dnewt.debug.Window.KeyEvent"
#D_ARGS="-Dnewt.debug.Window -Dnativewindow.debug=all"
#D_ARGS="-Dnewt.debug.Window -Dnativewindow.debug.JAWT -Djogl.debug.Animator"
@@ -299,7 +299,7 @@ function testawtswt() {
#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT $*
#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelAWT $*
#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.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.TestLandscapeES2NEWT $*
@@ -479,7 +479,7 @@ function testawtswt() {
#testawt com.jogamp.opengl.test.junit.jogl.awt.TestBug461PBufferSupersamplingSwingAWT
#testawt com.jogamp.opengl.test.junit.jogl.glu.TestBug463ScaleImageMemoryAWT $*
#testawt com.jogamp.opengl.test.junit.jogl.glu.TestBug694ScaleImageUnpackBufferSizeAWT $*
-testawt com.jogamp.opengl.test.junit.jogl.glu.TestBug365TextureGenerateMipMaps $*
+#testawt com.jogamp.opengl.test.junit.jogl.glu.TestBug365TextureGenerateMipMaps $*
#testawt com.jogamp.opengl.test.junit.jogl.awt.TestAWTCardLayoutAnimatorStartStopBug532 $*
#testawt com.jogamp.opengl.test.junit.jogl.awt.TestGLCanvasAWTActionDeadlock00AWT $*
#testawt com.jogamp.opengl.test.junit.jogl.awt.TestGLCanvasAWTActionDeadlock01AWT $*
diff --git a/src/newt/classes/com/jogamp/newt/Window.java b/src/newt/classes/com/jogamp/newt/Window.java
index 8a43ef153..02727353f 100644
--- a/src/newt/classes/com/jogamp/newt/Window.java
+++ b/src/newt/classes/com/jogamp/newt/Window.java
@@ -30,12 +30,14 @@ package com.jogamp.newt;
import java.util.List;
+import com.jogamp.newt.event.GestureHandler;
import com.jogamp.newt.event.WindowEvent;
import com.jogamp.newt.event.WindowListener;
import com.jogamp.newt.event.KeyListener;
import com.jogamp.newt.event.KeyEvent;
import com.jogamp.newt.event.InputEvent;
import com.jogamp.newt.event.MouseListener;
+
import jogamp.newt.Debug;
import jogamp.newt.WindowImpl;
@@ -553,15 +555,12 @@ public interface Window extends NativeWindow, WindowClosingProtocol {
//
/**
- *
- * Appends the given {@link com.jogamp.newt.event.MouseListener} to the end of
- * the list.
+ * Appends the given {@link MouseListener} to the end of the list.
*/
void addMouseListener(MouseListener l);
/**
- *
- * Inserts the given {@link com.jogamp.newt.event.MouseListener} at the
+ * Inserts the given {@link MouseListener} at the
* specified position in the list.<br>
*
* @param index Position where the listener will be inserted.
@@ -572,10 +571,61 @@ public interface Window extends NativeWindow, WindowClosingProtocol {
*/
void addMouseListener(int index, MouseListener l);
+ /**
+ * Removes the given {@link MouseListener} from the list.
+ */
void removeMouseListener(MouseListener l);
+ /**
+ * Returns the {@link MouseListener} from the list at the given index.
+ */
MouseListener getMouseListener(int index);
+ /**
+ * Returns all {@link MouseListener}
+ */
MouseListener[] getMouseListeners();
-
+
+ /** Enable or disable default {@link GestureHandler}. Default is enabled. */
+ void setDefaultGesturesEnabled(boolean enable);
+ /** Return true if default {@link GestureHandler} are enabled. */
+ boolean areDefaultGesturesEnabled();
+ /**
+ * Appends the given {@link GestureHandler} to the end of the list.
+ */
+ void addGestureHandler(GestureHandler gh);
+ /**
+ * Inserts the given {@link GestureHandler} at the
+ * specified position in the list.<br>
+ *
+ * @param index Position where the listener will be inserted.
+ * Should be within (0 <= index && index <= size()).
+ * An index value of -1 is interpreted as the end of the list, size().
+ * @param l The listener object to be inserted
+ * @throws IndexOutOfBoundsException If the index is not within (0 <= index && index <= size()), or -1
+ */
+ void addGestureHandler(int index, GestureHandler gh);
+ /**
+ * Removes the given {@link GestureHandler} from the list.
+ */
+ void removeGestureHandler(GestureHandler gh);
+ /**
+ * Appends the given {@link GestureHandler.GestureListener} to the end of the list.
+ */
+ void addGestureListener(GestureHandler.GestureListener gl);
+ /**
+ * Inserts the given {@link GestureHandler.GestureListener} at the
+ * specified position in the list.<br>
+ *
+ * @param index Position where the listener will be inserted.
+ * Should be within (0 <= index && index <= size()).
+ * An index value of -1 is interpreted as the end of the list, size().
+ * @param l The listener object to be inserted
+ * @throws IndexOutOfBoundsException If the index is not within (0 <= index && index <= size()), or -1
+ */
+ void addGestureListener(int index, GestureHandler.GestureListener gl);
+ /**
+ * Removes the given {@link GestureHandler.GestureListener} from the list.
+ */
+ void removeGestureListener(GestureHandler.GestureListener gl);
}
diff --git a/src/newt/classes/com/jogamp/newt/event/DoubleTapScrollGesture.java b/src/newt/classes/com/jogamp/newt/event/DoubleTapScrollGesture.java
new file mode 100644
index 000000000..bc67cbee6
--- /dev/null
+++ b/src/newt/classes/com/jogamp/newt/event/DoubleTapScrollGesture.java
@@ -0,0 +1,346 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.newt.event;
+
+import jogamp.newt.Debug;
+
+/**
+ * 2 pointer scroll/rotate gesture handler processing {@link MouseEvent}s
+ * while producing {@link MouseEvent#EVENT_MOUSE_WHEEL_MOVED} events if gesture is completed.
+ * <p>
+ * Criteria related to parameters:
+ * <pre>
+ * - doubleTapSlop (scaled in pixels):
+ * - Max 2 finger distance to start 'scroll' mode
+ * - Max. distance diff of current 2-pointer middle and initiated 2-pointer middle.
+ *
+ * - touchSlop (scaled in pixels):
+ * - Min. movement w/ 2 pointer within ScaledDoubleTapSlop starting 'scroll' mode
+ *
+ * - Avoid computation if not within gesture, especially for MOVE/DRAG
+ *
+ * - Only allow gesture to start with PRESS
+ *
+ * - Leave gesture completely with RELEASE of both/all fingers, or dist-diff exceeds doubleTapSlop
+ *
+ * - Tolerate temporary lift 1 of 2 pointer
+ *
+ * - Always validate pointer-id
+ * </pre>
+ * </p>
+ * Implementation uses a n-state to get detect gesture:
+ * <p>
+ * <table border="1">
+ * <tr><th>from</th> <th>to</th> <th>action</th></tr>
+ * <tr><td>NONE</td> <td>1PRESS</td> <td>1-pointer-pressed</td></tr>
+ * <tr><td>1PRESS</td> <td>2PRESS_T</td> <td>2-pointer-pressed within doubleTapSlope</td></tr>
+ * <tr><td>2PRESS_T</td> <td>SCROLL</td> <td>2-pointer dragged, dist-diff within doubleTapSlop and scrollLen >= scrollSlop</td></tr>
+ * <tr><td>2PRESS_C</td> <td>SCROLL</td> <td>2-pointer dragged, dist-diff within doubleTapSlop</td></tr>
+ * <tr><td>SCROLL</td> <td>SCROLL</td> <td>2-pointer dragged, dist-diff within doubleTapSlop</td></tr>
+ * </table>
+ * State ST_2PRESS_C merely exist to pick up gesture after one pointer has been lost temporarily.
+ * </p>
+ * <p>
+ * {@link #isWithinGesture()} returns gestureState >= 2PRESS_C
+ * </p>
+ */
+public class DoubleTapScrollGesture implements GestureHandler {
+ /** Scroll threshold in pixels (fallback), defaults to 16 pixels. Can be overriden by integer property <code>newt.event.scroll_slop_pixel</code>.*/
+ public static final int SCROLL_SLOP_PIXEL;
+ /** Two pointer 'double tap' slop in pixels (fallback), defaults to 104 pixels. Can be overriden by integer property <code>newt.event.double_tap_slop_pixel</code>.*/
+ public static final int DOUBLE_TAP_SLOP_PIXEL;
+
+ /** Scroll threshold in millimeter, defaults to 3 mm. Can be overriden by integer property <code>newt.event.scroll_slop_mm</code>.*/
+ public static final float SCROLL_SLOP_MM;
+ /** Two pointer 'double tap' slop in millimeter, defaults to 20 mm. Can be overriden by integer property <code>newt.event.double_tap_slop_mm</code>.*/
+ public static final float DOUBLE_TAP_SLOP_MM;
+
+ static {
+ Debug.initSingleton();
+
+ SCROLL_SLOP_PIXEL = Debug.getIntProperty("newt.event.scroll_slop_pixel", true, 16);
+ DOUBLE_TAP_SLOP_PIXEL = Debug.getIntProperty("newt.event.double_tap_slop_pixel", true, 104);
+ SCROLL_SLOP_MM = Debug.getIntProperty("newt.event.scroll_slop_mm", true, 3);
+ DOUBLE_TAP_SLOP_MM = Debug.getIntProperty("newt.event.double_tap_slop_mm", true, 20);
+ }
+
+ private static final int ST_NONE = 0;
+ private static final int ST_1PRESS = 1;
+ private static final int ST_2PRESS_T = 2;
+ private static final int ST_2PRESS_C = 3;
+ private static final int ST_SCROLL = 4;
+
+ private final int scrollSlop, scrollSlopSquare, doubleTapSlop, doubleTapSlopSquare;
+ private final float[] scrollDistance = new float[] { 0f, 0f };
+ private int[] pIds = new int[] { -1, -1 };
+ /** See class docu */
+ private int gestureState;
+ private int sqStartDist;
+ private int lastX, lastY;
+ private int pointerDownCount;
+ private MouseEvent hitGestureEvent;
+
+ private static final int getSquareDistance(float x1, float y1, float x2, float y2) {
+ final int deltaX = (int) x1 - (int) x2;
+ final int deltaY = (int) y1 - (int) y2;
+ return deltaX * deltaX + deltaY * deltaY;
+ }
+
+ private int gesturePointers(final MouseEvent e, final int excludeIndex) {
+ int j = 0;
+ for(int i=e.getPointerCount()-1; i>=0; i--) {
+ if( excludeIndex != i ) {
+ final int id = e.getPointerId(i);
+ if( pIds[0] == id || pIds[1] == id ) {
+ j++;
+ }
+ }
+ }
+ return j;
+ }
+
+ /**
+ * scaledScrollSlop < scaledDoubleTapSlop
+ * @param scaledScrollSlop Distance a pointer can wander before we think the user is scrolling in <i>pixels</i>.
+ * @param scaledDoubleTapSlop Distance in <i>pixels</i> between the first touch and second touch to still be considered a double tap.
+ */
+ public DoubleTapScrollGesture(int scaledScrollSlop, int scaledDoubleTapSlop) {
+ scrollSlop = scaledScrollSlop;
+ scrollSlopSquare = scaledScrollSlop * scaledScrollSlop;
+ doubleTapSlop = scaledDoubleTapSlop;
+ doubleTapSlopSquare = scaledDoubleTapSlop * scaledDoubleTapSlop;
+ pointerDownCount = 0;
+ clear(true);
+ if(DEBUG) {
+ System.err.println("DoubleTapScroll scrollSlop (scaled) "+scrollSlop);
+ System.err.println("DoubleTapScroll doubleTapSlop (scaled) "+doubleTapSlop);
+ }
+ }
+
+ public String toString() {
+ return "DoubleTapScroll[state "+gestureState+", in "+isWithinGesture()+", has "+(null!=hitGestureEvent)+", pc "+pointerDownCount+"]";
+ }
+
+ @Override
+ public void clear(boolean clearStarted) {
+ scrollDistance[0] = 0f;
+ scrollDistance[1] = 0f;
+ hitGestureEvent = null;
+ if( clearStarted ) {
+ gestureState = ST_NONE;
+ sqStartDist = 0;
+ pIds[0] = -1;
+ pIds[1] = -1;
+ lastX = 0;
+ lastY = 0;
+ }
+ }
+
+ @Override
+ public boolean isWithinGesture() {
+ return ST_2PRESS_C <= gestureState;
+ }
+
+ @Override
+ public boolean hasGesture() {
+ return null != hitGestureEvent;
+ }
+
+ @Override
+ public InputEvent getGestureEvent() {
+ if( null != hitGestureEvent ) {
+ final MouseEvent ge = hitGestureEvent;
+ int modifiers = ge.getModifiers();
+ final float[] rotationXYZ = ge.getRotation();
+ rotationXYZ[0] = scrollDistance[0] / scrollSlop;
+ rotationXYZ[1] = scrollDistance[1] / scrollSlop;
+ if( rotationXYZ[0]*rotationXYZ[0] > rotationXYZ[1]*rotationXYZ[1] ) {
+ // Horizontal scroll -> SHIFT
+ modifiers |= com.jogamp.newt.event.InputEvent.SHIFT_MASK;
+ }
+ return new MouseEvent(MouseEvent.EVENT_MOUSE_WHEEL_MOVED, ge.getSource(), ge.getWhen(), modifiers,
+ ge.getAllPointerTypes(), ge.getAllPointerIDs(),
+ ge.getAllX(), ge.getAllY(), ge.getAllPressures(), ge.getMaxPressure(),
+ ge.getButton(), ge.getClickCount(), rotationXYZ, scrollSlop);
+ }
+ return null;
+ }
+
+ public final float[] getScrollDistanceXY() {
+ return scrollDistance;
+ }
+
+ @Override
+ public boolean process(final InputEvent in) {
+ if( null != hitGestureEvent || !(in instanceof MouseEvent) ) {
+ return true;
+ }
+ final MouseEvent pe = (MouseEvent)in;
+ if( pe.getPointerType(0).getPointerClass() != MouseEvent.PointerClass.Onscreen ) {
+ return false;
+ }
+ pointerDownCount = pe.getPointerCount();
+ final int eventType = pe.getEventType();
+ final int x0 = pe.getX(0);
+ final int y0 = pe.getY(0);
+ switch ( eventType ) {
+ case MouseEvent.EVENT_MOUSE_PRESSED: {
+ int gPtr = 0;
+ if( ST_NONE == gestureState && 1 == pointerDownCount ) {
+ pIds[0] = pe.getPointerId(0);
+ pIds[1] = -1;
+ gestureState = ST_1PRESS;
+ } else if( ST_NONE < gestureState && 2 == pointerDownCount && 1 == gesturePointers(pe, 0) /* w/o pressed pointer */ ) {
+ final int x1 = pe.getX(1);
+ final int y1 = pe.getY(1);
+ final int xm = (x0+x1)/2;
+ final int ym = (y0+y1)/2;
+
+ if( ST_1PRESS == gestureState ) {
+ final int sqDist = getSquareDistance(x0, y0, x1, y1);
+ final boolean isDistWithinDoubleTapSlop = sqDist < doubleTapSlopSquare;
+ if( isDistWithinDoubleTapSlop ) {
+ // very first 2-finger touch-down
+ gPtr = 2;
+ pIds[0] = pe.getPointerId(0);
+ pIds[1] = pe.getPointerId(1);
+ lastX = xm;
+ lastY = ym;
+ sqStartDist = sqDist;
+ gestureState = ST_2PRESS_T;
+ }
+ if(DEBUG) {
+ final int dist = (int)Math.round(Math.sqrt(sqDist));
+ System.err.println(this+".pressed.1: dist "+dist+", gPtr "+gPtr+", distWithin2DTSlop "+isDistWithinDoubleTapSlop+", last "+lastX+"/"+lastY+", "+pe);
+ }
+ } else if( ST_2PRESS_C == gestureState ) { // pick up gesture after temp loosing one pointer
+ gPtr = gesturePointers(pe, -1);
+ if( 2 == gPtr ) {
+ // same pointers re-touch-down
+ lastX = xm;
+ lastY = ym;
+ } else {
+ // other 2 pointers .. should rarely happen!
+ clear(true);
+ }
+ }
+ }
+ if(DEBUG) {
+ System.err.println(this+".pressed: gPtr "+gPtr+", this "+lastX+"/"+lastY+", "+pe);
+ }
+ } break;
+
+ case MouseEvent.EVENT_MOUSE_RELEASED: {
+ pointerDownCount--; // lifted
+ final int gPtr = gesturePointers(pe, 0); // w/o lifted pointer
+ if ( 1 == gPtr ) {
+ // tolerate lifting 1 of 2 gesture pointers temporary
+ gestureState = ST_2PRESS_C;
+ } else if( 0 == gPtr ) {
+ // all lifted
+ clear(true);
+ }
+ if(DEBUG) {
+ System.err.println(this+".released: gPtr "+gPtr+", "+pe);
+ }
+ } break;
+
+ case MouseEvent.EVENT_MOUSE_DRAGGED: {
+ if( 2 == pointerDownCount && ST_1PRESS < gestureState ) {
+ final int gPtr = gesturePointers(pe, -1);
+ if( 2 == gPtr ) {
+ // same pointers
+ final int x1 = pe.getX(1);
+ final int y1 = pe.getY(1);
+ final int xm = (x0+x1)/2;
+ final int ym = (y0+y1)/2;
+ final int sqDist = getSquareDistance(x0, y0, x1, y1);
+ final boolean isDistDiffWithinDoubleTapSlop = Math.abs(sqDist - sqStartDist) <= doubleTapSlopSquare;
+ if( isDistDiffWithinDoubleTapSlop ) {
+ switch( gestureState ) {
+ case ST_2PRESS_T: {
+ final int sqScrollLen = getSquareDistance(lastX, lastY, xm, ym);
+ if( sqScrollLen > scrollSlopSquare ) { // min. scrolling threshold reached
+ gestureState = ST_SCROLL;
+ }
+ } break;
+
+ case ST_2PRESS_C:
+ gestureState = ST_SCROLL;
+ break;
+
+ case ST_SCROLL:
+ scrollDistance[0] = lastX - xm;
+ scrollDistance[1] = lastY - ym;
+ hitGestureEvent = pe;
+ break;
+ }
+ if(DEBUG) {
+ final boolean isDistWithinDoubleTapSlop = sqDist < doubleTapSlopSquare;
+ final int dist = (int)Math.round(Math.sqrt(sqDist));
+ final int sqScrollLen = getSquareDistance(lastX, lastY, xm, ym);
+ final int scrollLen = (int)Math.round(Math.sqrt(sqScrollLen));
+ System.err.println(this+".dragged.1: pDist "+dist+", scrollLen "+scrollLen+", gPtr "+gPtr+" ["+pIds[0]+", "+pIds[1]+"]"+
+ ", diffDistWithinTapSlop "+isDistDiffWithinDoubleTapSlop+
+ ", distWithin2DTSlop "+isDistWithinDoubleTapSlop+
+ ", this "+xm+"/"+ym+", last "+lastX+"/"+lastY+", d "+scrollDistance[0]+"/"+scrollDistance[1]);
+ }
+ } else {
+ // distance too big ..
+ if(DEBUG) {
+ final boolean isDistWithinDoubleTapSlop = sqDist < doubleTapSlopSquare;
+ final int dist = (int)Math.round(Math.sqrt(sqDist));
+ final int startDist = (int)Math.round(Math.sqrt(sqStartDist));
+ System.err.println(this+".dragged.X1: pDist "+dist+", distStart "+startDist+", gPtr "+gPtr+" ["+pIds[0]+", "+pIds[1]+"]"+
+ ", diffDistWithinTapSlop "+isDistDiffWithinDoubleTapSlop+
+ ", distWithin2DTSlop "+isDistWithinDoubleTapSlop+
+ ", this "+xm+"/"+ym+", last "+lastX+"/"+lastY+", d "+scrollDistance[0]+"/"+scrollDistance[1]);
+ }
+ clear(true);
+ }
+ if( ST_2PRESS_T < gestureState ) {
+ // state ST_2PRESS_T waits for min scroll threshold !
+ lastX = xm;
+ lastY = ym;
+ }
+ } else {
+ // other 2 pointers .. should rarely happen!
+ if(DEBUG) {
+ System.err.println(this+".dragged.X2: gPtr "+gPtr+" ["+pIds[0]+", "+pIds[1]+"]"+
+ ", last "+lastX+"/"+lastY+", d "+scrollDistance[0]+"/"+scrollDistance[1]);
+ }
+ clear(true);
+ }
+ }
+ } break;
+
+ default:
+ }
+ return null != hitGestureEvent;
+ }
+}
diff --git a/src/newt/classes/com/jogamp/newt/event/GestureHandler.java b/src/newt/classes/com/jogamp/newt/event/GestureHandler.java
new file mode 100644
index 000000000..2c8f29bb7
--- /dev/null
+++ b/src/newt/classes/com/jogamp/newt/event/GestureHandler.java
@@ -0,0 +1,143 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.newt.event;
+
+import jogamp.newt.Debug;
+
+/**
+ * Generic gesture handler interface designed to allow pass-through
+ * filtering of {@link InputEvent}s.
+ * <p>
+ * To avoid negative impact on event processing,
+ * implementation shall restrict computation as much as possible
+ * and only within it's appropriate gesture states.
+ * </p>
+ * <p>
+ * To allow custom user events, other than the <i>normal</i> {@link InputEvent}s,
+ * a user may return a {@link GestureEvent} in it's implementation.
+ * </p>
+ */
+public interface GestureHandler {
+ public static final boolean DEBUG = Debug.debug("Window.MouseEvent");
+
+ /** A custom gesture event */
+ @SuppressWarnings("serial")
+ public static class GestureEvent extends InputEvent {
+ /** A gesture has been detected. */
+ public static final short EVENT_GESTURE_DETECTED = 400;
+
+ private final GestureHandler handler;
+
+ /**
+ * Creates a gesture event with default type {@link #EVENT_GESTURE_DETECTED}.
+ *
+ * @param source
+ * @param when
+ * @param modifiers
+ * @param handler
+ */
+ public GestureEvent(Object source, long when, int modifiers, GestureHandler handler) {
+ super(EVENT_GESTURE_DETECTED, source, when, modifiers);
+ this.handler = handler;
+ }
+
+ /**
+ * Creates a gesture event with custom <i>event_type</i> !
+ * @param event_type must lie within [400..599]
+ * @param source
+ * @param when
+ * @param modifiers
+ * @param handler
+ */
+ public GestureEvent(short event_type, Object source, long when, int modifiers, GestureHandler handler) {
+ super(event_type, source, when, modifiers);
+ this.handler = handler;
+ }
+
+ /** Return the {@link GestureHandler}, which produced the event. */
+ public final GestureHandler getHandler() { return handler; }
+ }
+
+ /**
+ * Listener for {@link GestureEvent}s.
+ *
+ * @see GestureEvent
+ */
+ public static interface GestureListener extends NEWTEventListener
+ {
+ /** {@link GestureHandler} {@link GestureHandler#hasGesture() has detected} the gesture. */
+ public void gestureDetected(GestureEvent gh);
+ }
+
+ /**
+ * Clears state of handler, i.e. resets all states incl. previous detected gesture.
+ * @param clearStarted if true, also clears {@link #isWithinGesture() started} state,
+ * otherwise stay within gesture - if appropriate.
+ * Staying within a gesture allows fluent continuous gesture sequence,
+ * e.g. for scrolling.
+ */
+ public void clear(boolean clearStarted);
+
+ /**
+ * Returns true if a previous {@link #process(InputEvent)} command produced a gesture,
+ * which has not been {@link #clear(boolean) cleared}.
+ * Otherwise returns false.
+ */
+ public boolean hasGesture();
+
+ /**
+ * Returns the corresponding {@link InputEvent} for the gesture as detected by
+ * a previous {@link #process(InputEvent)}, which has not been {@link #clear(boolean) cleared}.
+ * Otherwise returns null.
+ * <p>
+ * Only implemented for gestures mapping to {@link InputEvent}s.
+ * </p>
+ */
+ public InputEvent getGestureEvent();
+
+ /**
+ * Returns true if within a gesture as detected by a previous {@link #process(InputEvent)} command,
+ * which has not been {@link #clear(boolean) cleared}.
+ * Otherwise returns false.
+ */
+ public boolean isWithinGesture();
+
+ /**
+ * Process the given {@link InputEvent} and returns true if it produced the gesture.
+ * Otherwise returns false.
+ * <p>
+ * If a gesture was already detected previously and has not been cleared,
+ * method does not process the event and returns true.
+ * </p>
+ * <p>
+ * Besides validation of the event's details,
+ * the handler may also validate the {@link InputEvent.InputClass} and/or {@link InputEvent.InputType}.
+ * </p>
+ */
+ public boolean process(InputEvent e);
+}
diff --git a/src/newt/classes/com/jogamp/newt/event/InputEvent.java b/src/newt/classes/com/jogamp/newt/event/InputEvent.java
index b04ebc1af..7712b77e7 100644
--- a/src/newt/classes/com/jogamp/newt/event/InputEvent.java
+++ b/src/newt/classes/com/jogamp/newt/event/InputEvent.java
@@ -39,6 +39,14 @@ import com.jogamp.newt.Window;
@SuppressWarnings("serial")
public abstract class InputEvent extends NEWTEvent
{
+ /** Interface marking class of input types */
+ public static interface InputClass {
+ }
+
+ /** Interface marking type of input devices */
+ public static interface InputType {
+ }
+
public static final int SHIFT_MASK = 1 << 0;
public static final int CTRL_MASK = 1 << 1;
public static final int META_MASK = 1 << 2;
diff --git a/src/newt/classes/com/jogamp/newt/event/MouseEvent.java b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java
index 8533a37c6..2c137ab77 100644
--- a/src/newt/classes/com/jogamp/newt/event/MouseEvent.java
+++ b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java
@@ -40,8 +40,8 @@ package com.jogamp.newt.event;
* http://www.w3.org/Submission/pointer-events/#pointerevent-interface
* </p>
* <p>
- * In case an instance represents multi-touch events, i.e. {@link #getPointerCount()} is &gt; 1,
- * the first data element represents the pointer which triggered the action if individual to one pointer.<br/>
+ * In case an instance represents a multiple-pointer event, i.e. {@link #getPointerCount()} is &gt; 1,
+ * the first data element of the multiple-pointer fields represents the pointer which triggered the action.<br/>
* For example {@link #getX(int) e.getX(0)} at {@link #EVENT_MOUSE_PRESSED} returns the data of the pressed pointer, etc.
* </p>
*/
@@ -49,12 +49,12 @@ package com.jogamp.newt.event;
public class MouseEvent extends InputEvent
{
/** Class of pointer types */
- public static enum PointerClass{
+ public static enum PointerClass implements InputEvent.InputClass {
Offscreen, Onscreen, Undefined;
}
/** Type of pointer devices */
- public static enum PointerType{
+ public static enum PointerType implements InputEvent.InputType {
/** {@link PointerClass#Offscreen} mouse. */
Mouse(PointerClass.Offscreen),
/** {@link PointerClass#Offscreen} touch pad, usually using fingers. */
@@ -110,7 +110,7 @@ public class MouseEvent extends InputEvent
return 300;
}
- /** Constructor for tradition 1-pointer mouse events. */
+ /** Constructor for traditional one-pointer event. */
public MouseEvent(short eventType, Object source, long when,
int modifiers, int x, int y, short clickCount, short button,
float[] rotationXYZ, float rotationScale)
@@ -120,25 +120,48 @@ public class MouseEvent extends InputEvent
this.y = new int[]{y};
this.pressure = constMousePressure;
this.maxPressure= 1.0f;
- this.pointerIDs = constMousePointerIDs;
+ this.pointerID = constMousePointerIDs;
this.clickCount=clickCount;
this.button=button;
this.rotationXYZ = rotationXYZ;
this.rotationScale = rotationScale;
- this.pointerTypes = constMousePointerTypes;
+ this.pointerType = constMousePointerTypes;
}
- /** Constructor for multi-touch pointer events. */
- public MouseEvent(short eventType, Object source, long when,
- int modifiers, int[] x, int[] y, float[] pressure, float maxPressure, PointerType pointerTypes[], short[] pointerids, short clickCount,
- short button, float[] rotationXYZ, float rotationScale)
+ /**
+ * Constructor for a multiple-pointer event.
+ * <p>
+ * First element of multiple-pointer arrays represents the pointer which triggered the event!
+ * </p>
+ *
+ * @param eventType
+ * @param source
+ * @param when
+ * @param modifiers
+ * @param pointerType PointerType for each pointer (multiple pointer)
+ * @param pointerID Pointer ID for each pointer (multiple pointer)
+ * @param x X-axis for each pointer (multiple pointer)
+ * @param y Y-axis for each pointer (multiple pointer)
+ * @param pressure Pressure for each pointer (multiple pointer)
+ * @param maxPressure Maximum pointer pressure for all pointer
+ * @param button Corresponding mouse-button
+ * @param clickCount Mouse-button click-count
+ * @param rotationXYZ Rotation of all axis
+ * @param rotationScale Rotation scale
+ */
+ public MouseEvent(short eventType, Object source, long when, int modifiers,
+ PointerType pointerType[], short[] pointerID,
+ int[] x, int[] y, float[] pressure, float maxPressure,
+ short button, short clickCount, float[] rotationXYZ, float rotationScale)
{
super(eventType, source, when, modifiers);
this.x = x;
this.y = y;
- if(pointerids.length != pressure.length ||
- pointerids.length != x.length ||
- pointerids.length != y.length) {
+ final int pointerCount = pointerType.length;
+ if(pointerCount != pointerID.length ||
+ pointerCount != x.length ||
+ pointerCount != y.length ||
+ pointerCount != pressure.length) {
throw new IllegalArgumentException("All multiple pointer arrays must be of same size");
}
if( 0.0f >= maxPressure ) {
@@ -146,55 +169,148 @@ public class MouseEvent extends InputEvent
}
this.pressure = pressure;
this.maxPressure= maxPressure;
- this.pointerIDs = pointerids;
+ this.pointerID = pointerID;
this.clickCount=clickCount;
this.button=button;
this.rotationXYZ = rotationXYZ;
this.rotationScale = rotationScale;
- this.pointerTypes = pointerTypes;
+ this.pointerType = pointerType;
+ }
+
+ public MouseEvent createVariant(short newEventType) {
+ return new MouseEvent(newEventType, source, getWhen(), getModifiers(), pointerType, pointerID,
+ x, y, pressure, maxPressure, button, clickCount, rotationXYZ, rotationScale);
+ }
+
+ /**
+ * Factory for a multiple-pointer event.
+ * <p>
+ * The index for the element of multiple-pointer arrays represents the pointer which triggered the event
+ * is passed via <i>actionIdx</i>.
+ * </p>
+ *
+ * @param eventType
+ * @param source
+ * @param when
+ * @param modifiers
+ * @param actionIdx index of multiple-pointer arrays representing the pointer which triggered the event
+ * @param pointerType PointerType for each pointer (multiple pointer)
+ * @param pointerID Pointer ID for each pointer (multiple pointer)
+ * @param x X-axis for each pointer (multiple pointer)
+ * @param y Y-axis for each pointer (multiple pointer)
+ * @param pressure Pressure for each pointer (multiple pointer)
+ * @param maxPressure Maximum pointer pressure for all pointer
+ * @param button Corresponding mouse-button
+ * @param clickCount Mouse-button click-count
+ * @param rotationXYZ Rotation of all axis
+ * @param rotationScale Rotation scale
+ */
+ public static MouseEvent create(short eventType, Object source, long when, int modifiers,
+ int actionIdx, PointerType pointerType[], short[] pointerID,
+ int[] x, int[] y, float[] pressure, float maxPressure,
+ short button, short clickCount, float[] rotationXYZ, float rotationScale) {
+ if( 0 <= actionIdx && actionIdx < pointerType.length) {
+ if( 0 < actionIdx ) {
+ {
+ final PointerType aType = pointerType[actionIdx];
+ pointerType[actionIdx] = pointerType[0];
+ pointerType[0] = aType;
+ }
+ {
+ final short s = pointerID[actionIdx];
+ pointerID[actionIdx] = pointerID[0];
+ pointerID[0] = s;
+ }
+ {
+ int s = x[actionIdx];
+ x[actionIdx] = x[0];
+ x[0] = s;
+ s = y[actionIdx];
+ y[actionIdx] = y[0];
+ y[0] = s;
+ }
+ {
+ final float aPress = pressure[actionIdx];
+ pressure[actionIdx] = pressure[0];
+ pressure[0] = aPress;
+ }
+ }
+ return new MouseEvent(eventType, source, when, modifiers,
+ pointerType, pointerID, x, y, pressure, maxPressure,
+ button, clickCount, rotationXYZ, rotationScale);
+ }
+ throw new IllegalArgumentException("actionIdx out of bounds [0.."+(pointerType.length-1)+"]");
}
/**
* @return the count of pointers involved in this event
*/
- public int getPointerCount() {
- return x.length;
+ public final int getPointerCount() {
+ return pointerType.length;
}
/**
* @return the {@link PointerType} for the data at index.
* return null if index not available.
*/
- public PointerType getPointerType(int index) {
- if(index >= pointerIDs.length) {
+ public final PointerType getPointerType(int index) {
+ if(0 > index || index >= pointerType.length) {
return null;
}
- return pointerTypes[index];
+ return pointerType[index];
+ }
+
+ /**
+ * @return array of all {@link PointerType}s for all pointers
+ */
+ public final PointerType[] getAllPointerTypes() {
+ return pointerType;
}
/**
- * @return the pointer id for the data at index.
+ * @return the pointer id for the given index.
* return -1 if index not available.
*/
- public short getPointerId(int index) {
- if(index >= pointerIDs.length) {
+ public final short getPointerId(int index) {
+ if(0 > index || index >= pointerID.length) {
return -1;
}
- return pointerIDs[index];
+ return pointerID[index];
}
- public short getButton() {
+ /**
+ * @return the pointer index for the given pointer id.
+ * return -1 if id not available.
+ */
+ public final int getPointerIdx(short id) {
+ for(int i=pointerID.length-1; i>=0; i--) {
+ if( pointerID[i] == id ) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * @return array of all pointer IDs for all pointers
+ */
+ public final short[] getAllPointerIDs() {
+ return pointerID;
+ }
+
+ public final short getButton() {
return button;
}
- public short getClickCount() {
+ public final short getClickCount() {
return clickCount;
}
- public int getX() {
+
+ public final int getX() {
return x[0];
}
- public int getY() {
+ public final int getY() {
return y[0];
}
@@ -203,7 +319,7 @@ public class MouseEvent extends InputEvent
* @return X-Coord associated with the pointer-index.
* @see getPointerId(index)
*/
- public int getX(int index) {
+ public final int getX(int index) {
return x[index];
}
@@ -212,20 +328,41 @@ public class MouseEvent extends InputEvent
* @return Y-Coord associated with the pointer-index.
* @see getPointerId(index)
*/
- public int getY(int index) {
+ public final int getY(int index) {
return y[index];
}
/**
+ * @return array of all X-Coords for all pointers
+ */
+ public final int[] getAllX() {
+ return x;
+ }
+
+ /**
+ * @return array of all Y-Coords for all pointers
+ */
+ public final int[] getAllY() {
+ return y;
+ }
+
+ /**
* @param normalized if true, method returns the normalized pressure, i.e. <code>pressure / maxPressure</code>
* @return The pressure associated with the pointer-index 0.
* The value of zero is return if not available.
* @see #getMaxPressure()
*/
- public float getPressure(boolean normalized){
+ public final float getPressure(boolean normalized){
return normalized ? pressure[0] / maxPressure : pressure[0];
}
+ /**
+ * @return array of all raw, un-normalized pressures for all pointers
+ */
+ public final float[] getAllPressures() {
+ return pressure;
+ }
+
/**
* Returns the maximum pressure known for the input device generating this event.
* <p>
@@ -239,7 +376,7 @@ public class MouseEvent extends InputEvent
* </ul>
* </p>
*/
- public float getMaxPressure() {
+ public final float getMaxPressure() {
return maxPressure;
}
@@ -250,7 +387,7 @@ public class MouseEvent extends InputEvent
* The value of zero is return if not available.
* @see #getMaxPressure()
*/
- public float getPressure(int index, boolean normalized){
+ public final float getPressure(int index, boolean normalized){
return normalized ? pressure[index] / maxPressure : pressure[index];
}
@@ -294,7 +431,7 @@ public class MouseEvent extends InputEvent
* see {@link #getRotationScale()} for semantics.
* </p>
*/
- public float[] getRotation() {
+ public final float[] getRotation() {
return rotationXYZ;
}
@@ -311,15 +448,15 @@ public class MouseEvent extends InputEvent
* Hence <code>scale * rotation</code> reproduces the screen distance in pixels the finger[s] have moved.
* </p>
*/
- public float getRotationScale() {
+ public final float getRotationScale() {
return rotationScale;
}
- public String toString() {
+ public final String toString() {
return toString(null).toString();
}
- public StringBuilder toString(StringBuilder sb) {
+ public final StringBuilder toString(StringBuilder sb) {
if(null == sb) {
sb = new StringBuilder();
}
@@ -327,13 +464,13 @@ public class MouseEvent extends InputEvent
.append(", ").append(x).append("/").append(y)
.append(", button ").append(button).append(", count ")
.append(clickCount).append(", rotation [").append(rotationXYZ[0]).append(", ").append(rotationXYZ[1]).append(", ").append(rotationXYZ[2]).append("] * ").append(rotationScale);
- if(pointerIDs.length>0) {
- sb.append(", pointer<").append(pointerIDs.length).append(">[");
- for(int i=0; i<pointerIDs.length; i++) {
+ if(pointerID.length>0) {
+ sb.append(", pointer<").append(pointerID.length).append(">[");
+ for(int i=0; i<pointerID.length; i++) {
if(i>0) {
sb.append(", ");
}
- sb.append(pointerIDs[i]).append("/").append(pointerTypes[i]).append(": ")
+ sb.append(pointerID[i]).append("/").append(pointerType[i]).append(": ")
.append(x[i]).append("/").append(y[i]).append(", ")
.append("p[").append(pressure[i]).append("/").append(maxPressure).append("=").append(pressure[i]/maxPressure).append("]");
}
@@ -356,15 +493,24 @@ public class MouseEvent extends InputEvent
default: return "unknown (" + type + ")";
}
}
- private final int x[], y[];
+
+ /** PointerType for each pointer (multiple pointer) */
+ private final PointerType pointerType[];
+ /** Pointer-ID for each pointer (multiple pointer) */
+ private final short pointerID[];
+ /** X-axis for each pointer (multiple pointer) */
+ private final int x[];
+ /** Y-axis for each pointer (multiple pointer) */
+ private final int y[];
+ /** Pressure for each pointer (multiple pointer) */
+ private final float pressure[];
// private final short tiltX[], tiltY[]; // TODO: A generic way for pointer axis information, see Android MotionEvent!
private final short clickCount, button;
+ /** Rotation around the X, Y and X axis */
private final float[] rotationXYZ;
+ /** Rotation scale */
private final float rotationScale;
- private final float pressure[];
private final float maxPressure;
- private final short pointerIDs[];
- private final PointerType pointerTypes[];
private static final float[] constMousePressure = new float[]{0f};
private static final short[] constMousePointerIDs = new short[]{0};
diff --git a/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java b/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java
index c1bc791d8..02bb4f929 100644
--- a/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java
+++ b/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java
@@ -44,6 +44,7 @@ package com.jogamp.newt.event;
* <li> WindowEvent <code>100..10x</code></li>
* <li> MouseEvent <code>200..20x</code></li>
* <li> KeyEvent <code>300..30x</code></li>
+ * <li> GestureEvent <code>400..5xx</code></li>
* <li> MonitorEvent <code>600..60x</code></li>
* </ul><br>
*/
diff --git a/src/newt/classes/com/jogamp/newt/event/PinchToZoomGesture.java b/src/newt/classes/com/jogamp/newt/event/PinchToZoomGesture.java
new file mode 100644
index 000000000..3a34c6253
--- /dev/null
+++ b/src/newt/classes/com/jogamp/newt/event/PinchToZoomGesture.java
@@ -0,0 +1,217 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.newt.event;
+
+import javax.media.nativewindow.NativeSurface;
+
+import jogamp.newt.Debug;
+
+/**
+ * 2 pointer zoom, a.k.a. <i>pinch to zoom</i>, gesture handler processing {@link MouseEvent}s
+ * while producing {@link ZoomEvent}s if gesture is completed.
+ * <p>
+ * Zoom value lies within [0..2], with 1 as <i>1:1</i>.
+ * </p>
+ * <pre>
+ * - choosing the smallest surface edge (width/height -> x/y)
+ * - tolerating other fingers to be pressed and hence user to add functionality (scale, ..)
+ * </pre>
+ */
+public class PinchToZoomGesture implements GestureHandler {
+ public static final boolean DEBUG = Debug.debug("Window.MouseEvent");
+
+ /** A {@link GestureHandler.GestureEvent} denominating zoom. */
+ @SuppressWarnings("serial")
+ public static class ZoomEvent extends GestureEvent {
+ private final MouseEvent pe;
+ private final float zoom;
+ public ZoomEvent(Object source, long when, int modifiers, GestureHandler handler, MouseEvent pe, float zoom) {
+ super(source, when, modifiers, handler);
+ this.pe = pe;
+ this.zoom = zoom;
+ }
+ /** Triggering {@link MouseEvent} */
+ public final MouseEvent getTrigger() { return pe; }
+ /** Zoom value lies within [0..2], with 1 as <i>1:1</i>. */
+ public final float getZoom() { return zoom; }
+ }
+
+ private final NativeSurface surface;
+ private float zoom;
+ private int zoomLastEdgeDist;
+ private boolean zoomFirstTouch;
+ private boolean zoomMode;
+ private ZoomEvent zoomEvent;
+ private short[] pIds = new short[] { -1, -1 };
+
+ public PinchToZoomGesture(NativeSurface surface) {
+ clear(true);
+ this.surface = surface;
+ this.zoom = 1f;
+ }
+
+ public String toString() {
+ return "PinchZoom[1stTouch "+zoomFirstTouch+", in "+isWithinGesture()+", has "+(null!=zoomEvent)+", zoom "+zoom+"]";
+ }
+
+ private int gesturePointers(final MouseEvent e, final int excludeIndex) {
+ int j = 0;
+ for(int i=e.getPointerCount()-1; i>=0; i--) {
+ if( excludeIndex != i ) {
+ final int id = e.getPointerId(i);
+ if( pIds[0] == id || pIds[1] == id ) {
+ j++;
+ }
+ }
+ }
+ return j;
+ }
+
+ @Override
+ public void clear(boolean clearStarted) {
+ zoomEvent = null;
+ if( clearStarted ) {
+ zoomLastEdgeDist = 0;
+ zoomFirstTouch = true;
+ zoomMode = false;
+ pIds[0] = -1;
+ pIds[1] = -1;
+ }
+ }
+
+ @Override
+ public boolean isWithinGesture() {
+ return zoomMode;
+ }
+
+ @Override
+ public boolean hasGesture() {
+ return null != zoomEvent;
+ }
+
+ @Override
+ public InputEvent getGestureEvent() {
+ return zoomEvent;
+ }
+
+ /** Zoom value lies within [0..2], with 1 as <i>1:1</i>. */
+ public final float getZoom() {
+ return zoom;
+ }
+ /** Set zoom value within [0..2], with 1 as <i>1:1</i>. */
+ public final void setZoom(float zoom) {
+ this.zoom=zoom;
+ }
+
+ @Override
+ public boolean process(final InputEvent in) {
+ if( null != zoomEvent || !(in instanceof MouseEvent) ) {
+ return true;
+ }
+ final MouseEvent pe = (MouseEvent)in;
+ if( pe.getPointerType(0).getPointerClass() != MouseEvent.PointerClass.Onscreen ) {
+ return false;
+ }
+
+ final int pointerDownCount = pe.getPointerCount();
+ final int eventType = pe.getEventType();
+ final boolean useY = surface.getWidth() >= surface.getHeight(); // use smallest dimension
+ switch ( eventType ) {
+ case MouseEvent.EVENT_MOUSE_PRESSED: {
+ if( 1 == pointerDownCount ) {
+ pIds[0] = pe.getPointerId(0);
+ pIds[1] = -1;
+ } else if ( 2 <= pointerDownCount ) { // && 1 == gesturePointers(pe, 0) /* w/o pressed pointer */) {
+ pIds[0] = pe.getPointerId(0);
+ pIds[1] = pe.getPointerId(1);
+ }
+ if(DEBUG) {
+ System.err.println("XXX1: id0 "+pIds[0]+" -> idx0 "+0+", id1 "+pIds[1]+" -> idx1 "+1);
+ System.err.println(this+".pressed: down "+pointerDownCount+", gPtr "+gesturePointers(pe, -1)+", event "+pe);
+ }
+ } break;
+
+ case MouseEvent.EVENT_MOUSE_RELEASED: {
+ final int gPtr = gesturePointers(pe, 0); // w/o lifted pointer
+ if ( 1 == gPtr ) {
+ zoomFirstTouch = true;
+ zoomMode = false;
+ } else if( 0 == gPtr ) {
+ // all lifted
+ clear(true);
+ }
+ if(DEBUG) {
+ System.err.println(this+".released: down "+pointerDownCount+", gPtr "+gPtr+", event "+pe);
+ }
+ } break;
+
+ case MouseEvent.EVENT_MOUSE_DRAGGED: {
+ if( 2 <= pointerDownCount ) {
+ final int gPtr = gesturePointers(pe, -1);
+ if( 2 == gPtr ) {
+ // same pointers
+ final int p0Idx = pe.getPointerIdx(pIds[0]);
+ final int p1Idx = pe.getPointerIdx(pIds[1]);
+ final int edge0 = useY ? pe.getY(p0Idx) : pe.getX(p0Idx);
+ final int edge1 = useY ? pe.getY(p1Idx) : pe.getX(p1Idx);
+ // Diff. 1:1 Zoom: finger-distance to screen-coord
+ if(zoomFirstTouch) {
+ zoomLastEdgeDist = Math.abs(edge0-edge1);
+ zoomFirstTouch=false;
+ zoomMode = true;
+ } else if( zoomMode ) {
+ final int d = Math.abs(edge0-edge1);
+ final int dd = d - zoomLastEdgeDist;
+ final float screenEdge = useY ? surface.getHeight() : surface.getWidth();
+ final float incr = (float)dd / screenEdge; // [-1..1]
+ if(DEBUG) {
+ System.err.println("XXX2: id0 "+pIds[0]+" -> idx0 "+p0Idx+", id1 "+pIds[1]+" -> idx1 "+p1Idx);
+ System.err.println("XXX3: d "+d+", ld "+zoomLastEdgeDist+", dd "+dd+", screen "+screenEdge+" -> incr "+incr+", zoom "+zoom+" -> "+(zoom+incr));
+ }
+ zoom += incr;
+ // clip value
+ if( 2f < zoom ) {
+ zoom = 2f;
+ } else if( 0 > zoom ) {
+ zoom = 0;
+ }
+ zoomLastEdgeDist = d;
+ zoomEvent = new ZoomEvent(pe.getSource(), pe.getWhen(), pe.getModifiers(), this, pe, zoom);
+ }
+ }
+ if(DEBUG) {
+ System.err.println(this+".dragged: down "+pointerDownCount+", gPtr "+gPtr+", event "+pe);
+ }
+ }
+ } break;
+
+ default:
+ }
+ return null != zoomEvent;
+ }
+}
diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java
index eace0f2af..cae1a06a2 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.event.GestureHandler;
import com.jogamp.newt.event.KeyListener;
import com.jogamp.newt.event.MouseListener;
import com.jogamp.newt.event.NEWTEvent;
@@ -774,6 +775,39 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind
return window.getMouseListeners();
}
+ @Override
+ public void setDefaultGesturesEnabled(boolean enable) {
+ window.setDefaultGesturesEnabled(enable);
+ }
+ @Override
+ public boolean areDefaultGesturesEnabled() {
+ return window.areDefaultGesturesEnabled();
+ }
+ @Override
+ public final void addGestureHandler(GestureHandler gh) {
+ window.addGestureHandler(gh);
+ }
+ @Override
+ public final void addGestureHandler(int index, GestureHandler gh) {
+ window.addGestureHandler(index, gh);
+ }
+ @Override
+ public final void removeGestureHandler(GestureHandler gh) {
+ window.removeGestureHandler(gh);
+ }
+ @Override
+ public final void addGestureListener(GestureHandler.GestureListener gl) {
+ window.addGestureListener(-1, gl);
+ }
+ @Override
+ public final void addGestureListener(int index, GestureHandler.GestureListener gl) {
+ window.addGestureListener(index, gl);
+ }
+ @Override
+ public final void removeGestureListener(GestureHandler.GestureListener gl) {
+ window.removeGestureListener(gl);
+ }
+
//----------------------------------------------------------------------
// NativeWindow completion
//
diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java
index b7357863f..66ce46ed0 100644
--- a/src/newt/classes/jogamp/newt/WindowImpl.java
+++ b/src/newt/classes/jogamp/newt/WindowImpl.java
@@ -48,6 +48,8 @@ import com.jogamp.newt.Screen;
import com.jogamp.newt.Window;
import com.jogamp.common.util.locks.LockFactory;
import com.jogamp.common.util.locks.RecursiveLock;
+import com.jogamp.newt.event.DoubleTapScrollGesture;
+import com.jogamp.newt.event.GestureHandler;
import com.jogamp.newt.event.InputEvent;
import com.jogamp.newt.event.KeyEvent;
import com.jogamp.newt.event.KeyListener;
@@ -60,6 +62,7 @@ import com.jogamp.newt.event.MonitorModeListener;
import com.jogamp.newt.event.WindowEvent;
import com.jogamp.newt.event.WindowListener;
import com.jogamp.newt.event.WindowUpdateEvent;
+import com.jogamp.newt.event.MouseEvent.PointerType;
import javax.media.nativewindow.AbstractGraphicsConfiguration;
import javax.media.nativewindow.AbstractGraphicsDevice;
@@ -71,6 +74,7 @@ import javax.media.nativewindow.NativeWindowException;
import javax.media.nativewindow.NativeWindowFactory;
import javax.media.nativewindow.SurfaceUpdatedListener;
import javax.media.nativewindow.WindowClosingProtocol;
+import javax.media.nativewindow.util.DimensionImmutable;
import javax.media.nativewindow.util.Insets;
import javax.media.nativewindow.util.InsetsImmutable;
import javax.media.nativewindow.util.Point;
@@ -176,13 +180,41 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
private ArrayList<NativeWindow> childWindows = new ArrayList<NativeWindow>();
private ArrayList<MouseListener> mouseListeners = new ArrayList<MouseListener>();
- private short mouseButtonPressed = (short)0; // current pressed mouse button number
- private int mouseButtonModMask = 0; // current pressed mouse button modifier mask
- private long lastMousePressed = 0; // last time when a mouse button was pressed
- private short lastMouseClickCount = (short)0; // last mouse button click count
- private boolean mouseInWindow = false;// mouse entered window - is inside the window (may be synthetic)
- private Point lastMousePosition = new Point();
-
+
+ private static class PointerState0 {
+ /** current pressed mouse button number */
+ private short buttonPressed = (short)0;
+ /** current pressed mouse button modifier mask */
+ private int buttonModMask = 0;
+ /** last time when a mouse button was pressed */
+ private long lastButtonPressTime = 0;
+ /** last mouse button click count */
+ private short lastButtonClickCount = (short)0;
+ /** mouse entered window - is inside the window (may be synthetic) */
+ private boolean insideWindow = false;
+ /** last mouse-move position */
+ private Point lastMovePosition = new Point();
+ }
+ private static class PointerState1 {
+ /** current pressed mouse button number */
+ private short buttonPressed = (short)0;
+ /** last time when a mouse button was pressed */
+ private long lastButtonPressTime = 0;
+ /** mouse entered window - is inside the window (may be synthetic) */
+ private boolean insideWindow = false;
+ /** last mouse-move position */
+ private Point lastMovePosition = new Point();
+ }
+ /** doMouseEvent */
+ private PointerState0 pState0 = new PointerState0();
+ /** consumeMouseEvent */
+ private PointerState1 pState1 = new PointerState1();
+ private boolean defaultGestureHandlerEnabled = true;
+ private DoubleTapScrollGesture gesture2PtrTouchScroll = null;
+ private ArrayList<GestureHandler> pointerGestureHandler = new ArrayList<GestureHandler>();
+
+ private ArrayList<GestureHandler.GestureListener> gestureListeners = new ArrayList<GestureHandler.GestureListener>();
+
private ArrayList<KeyListener> keyListeners = new ArrayList<KeyListener>();
private ArrayList<WindowListener> windowListeners = new ArrayList<WindowListener>();
@@ -1754,6 +1786,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
for (int i = 0; i < mouseListeners.size(); i++ ) {
sb.append(mouseListeners.get(i)+", ");
}
+ sb.append("], PointerGestures default "+defaultGestureHandlerEnabled+", custom "+pointerGestureHandler.size()+" [");
+ for (int i = 0; i < pointerGestureHandler.size(); i++ ) {
+ sb.append(pointerGestureHandler.get(i)+", ");
+ }
sb.append("], KeyListeners num "+keyListeners.size()+" [");
for (int i = 0; i < keyListeners.size(); i++ ) {
sb.append(keyListeners.get(i)+", ");
@@ -2166,7 +2202,6 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
private final MonitorModeListenerImpl monitorModeListenerImpl = new MonitorModeListenerImpl();
-
//----------------------------------------------------------------------
// Child Window Management
//
@@ -2286,17 +2321,22 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
//
// MouseListener/Event Support
//
+
+ //
+ // Native MouseEvents pre-processed to be enqueued or consumed directly
+ //
+
public final void sendMouseEvent(short eventType, int modifiers,
int x, int y, short button, float rotation) {
- doMouseEvent(false, false, eventType, modifiers, x, y, button, rotation);
+ doMouseEvent(false, false, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
}
public final void enqueueMouseEvent(boolean wait, short eventType, int modifiers,
int x, int y, short button, float rotation) {
- doMouseEvent(true, wait, eventType, modifiers, x, y, button, rotation);
+ doMouseEvent(true, wait, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
}
protected final void doMouseEvent(boolean enqueue, boolean wait, short eventType, int modifiers,
int x, int y, short button, float rotation) {
- this.doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
+ doMouseEvent(enqueue, wait, eventType, modifiers, x, y, button, MouseEvent.getRotationXYZ(rotation, modifiers), 1f);
}
/**
public final void sendMouseEvent(short eventType, int modifiers,
@@ -2309,57 +2349,73 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
} */
protected void doMouseEvent(boolean enqueue, boolean wait, short eventType, int modifiers,
int x, int y, short button, float[] rotationXYZ, float rotationScale) {
- if( eventType == MouseEvent.EVENT_MOUSE_ENTERED || eventType == MouseEvent.EVENT_MOUSE_EXITED ) {
- if( eventType == MouseEvent.EVENT_MOUSE_EXITED && x==-1 && y==-1 ) {
- x = lastMousePosition.getX();
- y = lastMousePosition.getY();
- }
- // clip coordinates to window dimension
- x = Math.min(Math.max(x, 0), getWidth()-1);
- y = Math.min(Math.max(y, 0), getHeight()-1);
- mouseInWindow = eventType == MouseEvent.EVENT_MOUSE_ENTERED;
- // clear states
- lastMousePressed = 0;
- lastMouseClickCount = (short)0;
- mouseButtonPressed = 0;
- mouseButtonModMask = 0;
- }
- if( x < 0 || y < 0 || x >= getWidth() || y >= getHeight() ) {
- return; // .. invalid ..
- }
- if(DEBUG_MOUSE_EVENT) {
- System.err.println("doMouseEvent: enqueue "+enqueue+", wait "+wait+", "+MouseEvent.getEventTypeString(eventType)+
- ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+lastMousePosition);
+ if( 0 > button || button > MouseEvent.BUTTON_NUMBER ) {
+ throw new NativeWindowException("Invalid mouse button number" + button);
}
+
+ //
+ // Remove redundant events, determine ENTERED state and reset states if applicable
+ //
final long when = System.currentTimeMillis();
- MouseEvent eEntered = null;
- if(eventType == MouseEvent.EVENT_MOUSE_MOVED) {
- if(!mouseInWindow) {
- mouseInWindow = true;
- eEntered = new MouseEvent(MouseEvent.EVENT_MOUSE_ENTERED, this, when,
- modifiers, x, y, (short)0, (short)0, rotationXYZ, rotationScale);
+ switch( eventType ) {
+ case MouseEvent.EVENT_MOUSE_EXITED:
+ if( x==-1 && y==-1 ) {
+ x = pState0.lastMovePosition.getX();
+ y = pState0.lastMovePosition.getY();
+ }
+ // Fall through intended!
+
+ case MouseEvent.EVENT_MOUSE_ENTERED:
+ // clip coordinates to window dimension
+ x = Math.min(Math.max(x, 0), getWidth()-1);
+ y = Math.min(Math.max(y, 0), getHeight()-1);
+ pState0.insideWindow = eventType == MouseEvent.EVENT_MOUSE_ENTERED;
// clear states
- lastMousePressed = 0;
- lastMouseClickCount = (short)0;
- mouseButtonPressed = 0;
- mouseButtonModMask = 0;
- } else if( lastMousePosition.getX() == x && lastMousePosition.getY()==y ) {
- if(DEBUG_MOUSE_EVENT) {
- System.err.println("doMouseEvent: skip EVENT_MOUSE_MOVED w/ same position: "+lastMousePosition);
+ pState0.lastButtonPressTime = 0;
+ pState0.lastButtonClickCount = (short)0;
+ pState0.buttonPressed = 0;
+ pState0.buttonModMask = 0;
+ break;
+
+ case MouseEvent.EVENT_MOUSE_MOVED:
+ case MouseEvent.EVENT_MOUSE_DRAGGED:
+ if( pState0.insideWindow && pState0.lastMovePosition.getX() == x && pState0.lastMovePosition.getY() == y ) {
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("doMouseEvent: skip EVENT_MOUSE_MOVED w/ same position: "+pState0.lastMovePosition);
+ }
+ return; // skip same position
}
- return; // skip same position
+ pState0.lastMovePosition.setX(x);
+ pState0.lastMovePosition.setY(y);
+
+ // Fall through intended !
+
+ default:
+ if(!pState0.insideWindow) {
+ pState0.insideWindow = true;
+
+ // clear states
+ pState0.lastButtonPressTime = 0;
+ pState0.lastButtonClickCount = (short)0;
+ pState0.buttonPressed = 0;
+ pState0.buttonModMask = 0;
+ }
+ }
+
+ if( x < 0 || y < 0 || x >= getWidth() || y >= getHeight() ) {
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("doMouseEvent: drop: "+MouseEvent.getEventTypeString(eventType)+
+ ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+pState0.lastMovePosition);
}
- lastMousePosition.setX(x);
- lastMousePosition.setY(y);
+ return; // .. invalid ..
}
- if( 0 > button || button > MouseEvent.BUTTON_NUMBER ) {
- throw new NativeWindowException("Invalid mouse button number" + button);
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("doMouseEvent: enqueue "+enqueue+", wait "+wait+", "+MouseEvent.getEventTypeString(eventType)+
+ ", mod "+modifiers+", pos "+x+"/"+y+", button "+button+", lastMousePosition: "+pState0.lastMovePosition);
}
+
modifiers |= InputEvent.getButtonMask(button); // Always add current button to modifier mask (Bug 571)
- modifiers |= mouseButtonModMask; // Always add currently pressed mouse buttons to modifier mask
-
- MouseEvent eClicked = null;
- MouseEvent e = null;
+ modifiers |= pState0.buttonModMask; // Always add currently pressed mouse buttons to modifier mask
if( isPointerConfined() ) {
modifiers |= InputEvent.CONFINED_MASK;
@@ -2368,64 +2424,86 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
modifiers |= InputEvent.INVISIBLE_MASK;
}
- if( MouseEvent.EVENT_MOUSE_PRESSED == eventType ) {
- if( when - lastMousePressed < MouseEvent.getClickTimeout() ) {
- lastMouseClickCount++;
- } else {
- lastMouseClickCount=(short)1;
- }
- lastMousePressed = when;
- mouseButtonPressed = button;
- mouseButtonModMask |= MouseEvent.getButtonMask(button);
- e = new MouseEvent(eventType, this, when,
- modifiers, x, y, lastMouseClickCount, button, rotationXYZ, rotationScale);
- } else if( MouseEvent.EVENT_MOUSE_RELEASED == eventType ) {
- e = new MouseEvent(eventType, this, when,
- modifiers, x, y, lastMouseClickCount, button, rotationXYZ, rotationScale);
- if( when - lastMousePressed < MouseEvent.getClickTimeout() ) {
- eClicked = new MouseEvent(MouseEvent.EVENT_MOUSE_CLICKED, this, when,
- modifiers, x, y, lastMouseClickCount, button, rotationXYZ, rotationScale);
- } else {
- lastMouseClickCount = (short)0;
- lastMousePressed = 0;
- }
- mouseButtonPressed = 0;
- mouseButtonModMask &= ~MouseEvent.getButtonMask(button);
- } else if( MouseEvent.EVENT_MOUSE_MOVED == eventType ) {
- if ( mouseButtonPressed > 0 ) {
- e = new MouseEvent(MouseEvent.EVENT_MOUSE_DRAGGED, this, when,
- modifiers, x, y, (short)1, mouseButtonPressed, rotationXYZ, rotationScale);
- } else {
+ final MouseEvent e;
+
+ switch( eventType ) {
+ case MouseEvent.EVENT_MOUSE_PRESSED:
+ if( when - pState0.lastButtonPressTime < MouseEvent.getClickTimeout() ) {
+ pState0.lastButtonClickCount++;
+ } else {
+ pState0.lastButtonClickCount=(short)1;
+ }
+ pState0.lastButtonPressTime = when;
+ pState0.buttonPressed = button;
+ pState0.buttonModMask |= MouseEvent.getButtonMask(button);
e = new MouseEvent(eventType, this, when,
- modifiers, x, y, (short)0, button, rotationXYZ, rotationScale);
- }
- } else if( MouseEvent.EVENT_MOUSE_WHEEL_MOVED == eventType ) {
- e = new MouseEvent(eventType, this, when, modifiers, x, y, (short)0, button, rotationXYZ, rotationScale);
- } else {
- e = new MouseEvent(eventType, this, when, modifiers, x, y, (short)0, button, rotationXYZ, rotationScale);
- }
- if( null != eEntered ) {
- if(DEBUG_MOUSE_EVENT) {
- System.err.println("doMouseEvent: synthesized MOUSE_ENTERED event: "+eEntered);
- }
- doEvent(enqueue, wait, eEntered);
+ modifiers, x, y, pState0.lastButtonClickCount, button, rotationXYZ, rotationScale);
+ break;
+ case MouseEvent.EVENT_MOUSE_RELEASED:
+ e = new MouseEvent(eventType, this, when,
+ modifiers, x, y, pState0.lastButtonClickCount, button, rotationXYZ, rotationScale);
+ if( when - pState0.lastButtonPressTime >= MouseEvent.getClickTimeout() ) {
+ pState0.lastButtonClickCount = (short)0;
+ pState0.lastButtonPressTime = 0;
+ }
+ pState0.buttonPressed = 0;
+ pState0.buttonModMask &= ~MouseEvent.getButtonMask(button);
+ break;
+ case MouseEvent.EVENT_MOUSE_MOVED:
+ if ( pState0.buttonPressed > 0 ) {
+ e = new MouseEvent(MouseEvent.EVENT_MOUSE_DRAGGED, this, when,
+ modifiers, x, y, (short)1, pState0.buttonPressed, rotationXYZ, rotationScale);
+ } else {
+ e = new MouseEvent(eventType, this, when,
+ modifiers, x, y, (short)0, button, rotationXYZ, rotationScale);
+ }
+ break;
+ default:
+ e = new MouseEvent(eventType, this, when, modifiers, x, y, (short)0, button, rotationXYZ, rotationScale);
}
doEvent(enqueue, wait, e); // actual mouse event
- if( null != eClicked ) {
- if(DEBUG_MOUSE_EVENT) {
- System.err.println("doMouseEvent: synthesized MOUSE_CLICKED event: "+eClicked);
- }
- doEvent(enqueue, wait, eClicked);
- }
}
+ /**
+ * Send multiple-pointer event directly to be consumed
+ * <p>
+ * The index for the element of multiple-pointer arrays represents the pointer which triggered the event
+ * is passed via <i>actionIdx</i>.
+ * </p>
+ *
+ * @param eventType
+ * @param source
+ * @param when
+ * @param modifiers
+ * @param actionIdx index of multiple-pointer arrays representing the pointer which triggered the event
+ * @param pointerType PointerType for each pointer (multiple pointer)
+ * @param pointerID Pointer ID for each pointer (multiple pointer)
+ * @param x X-axis for each pointer (multiple pointer)
+ * @param y Y-axis for each pointer (multiple pointer)
+ * @param pressure Pressure for each pointer (multiple pointer)
+ * @param maxPressure Maximum pointer pressure for all pointer
+ * @param button Corresponding mouse-button
+ * @param clickCount Mouse-button click-count
+ * @param rotationXYZ Rotation of all axis
+ * @param rotationScale Rotation scale
+ */
+ public void sendMouseEvent(short eventType, Object source, long when, int modifiers,
+ int actionIdx, PointerType pointerType[], short[] pointerID,
+ int[] x, int[] y, float[] pressure, float maxPressure,
+ short button, short clickCount, float[] rotationXYZ, float rotationScale) {
+ final MouseEvent pe = MouseEvent.create(eventType, source, when, modifiers, actionIdx,
+ pointerType, pointerID, x, y, pressure, maxPressure, button, clickCount,
+ rotationXYZ, rotationScale);
+ consumeMouseEvent(pe);
+ }
+
@Override
- public void addMouseListener(MouseListener l) {
+ public final void addMouseListener(MouseListener l) {
addMouseListener(-1, l);
}
@Override
- public void addMouseListener(int index, MouseListener l) {
+ public final void addMouseListener(int index, MouseListener l) {
if(l == null) {
return;
}
@@ -2439,7 +2517,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
}
@Override
- public void removeMouseListener(MouseListener l) {
+ public final void removeMouseListener(MouseListener l) {
if (l == null) {
return;
}
@@ -2450,7 +2528,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
}
@Override
- public MouseListener getMouseListener(int index) {
+ public final MouseListener getMouseListener(int index) {
@SuppressWarnings("unchecked")
ArrayList<MouseListener> clonedListeners = (ArrayList<MouseListener>) mouseListeners.clone();
if(0>index) {
@@ -2460,14 +2538,273 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
}
@Override
- public MouseListener[] getMouseListeners() {
+ public final MouseListener[] getMouseListeners() {
return mouseListeners.toArray(new MouseListener[mouseListeners.size()]);
}
- protected void consumeMouseEvent(MouseEvent e) {
+ @Override
+ public final void setDefaultGesturesEnabled(boolean enable) {
+ defaultGestureHandlerEnabled = enable;
+ }
+ @Override
+ public final boolean areDefaultGesturesEnabled() {
+ return defaultGestureHandlerEnabled;
+ }
+
+ @Override
+ public final void addGestureHandler(GestureHandler gh) {
+ addGestureHandler(-1, gh);
+ }
+ @Override
+ public final void addGestureHandler(int index, GestureHandler gh) {
+ if(gh == null) {
+ return;
+ }
+ @SuppressWarnings("unchecked")
+ ArrayList<GestureHandler> cloned = (ArrayList<GestureHandler>) pointerGestureHandler.clone();
+ if(0>index) {
+ index = cloned.size();
+ }
+ cloned.add(index, gh);
+ pointerGestureHandler = cloned;
+ }
+ @Override
+ public final void removeGestureHandler(GestureHandler gh) {
+ if (gh == null) {
+ return;
+ }
+ @SuppressWarnings("unchecked")
+ ArrayList<GestureHandler> cloned = (ArrayList<GestureHandler>) pointerGestureHandler.clone();
+ cloned.remove(gh);
+ pointerGestureHandler = cloned;
+ }
+ @Override
+ public final void addGestureListener(GestureHandler.GestureListener gl) {
+ addGestureListener(-1, gl);
+ }
+ @Override
+ public final void addGestureListener(int index, GestureHandler.GestureListener gl) {
+ if(gl == null) {
+ return;
+ }
+ @SuppressWarnings("unchecked")
+ ArrayList<GestureHandler.GestureListener> cloned = (ArrayList<GestureHandler.GestureListener>) gestureListeners.clone();
+ if(0>index) {
+ index = cloned.size();
+ }
+ cloned.add(index, gl);
+ gestureListeners = cloned;
+ }
+ @Override
+ public final void removeGestureListener(GestureHandler.GestureListener gl) {
+ if (gl == null) {
+ return;
+ }
+ @SuppressWarnings("unchecked")
+ ArrayList<GestureHandler.GestureListener> cloned = (ArrayList<GestureHandler.GestureListener>) gestureListeners.clone();
+ cloned.remove(gl);
+ gestureListeners= cloned;
+ }
+
+ private static int step(int lower, int edge, int value) {
+ return value < edge ? lower : value;
+ }
+
+ /**
+ * Consume the {@link MouseEvent}, i.e.
+ * <pre>
+ * - validate
+ * - handle gestures
+ * - synthesize events if applicable (like gestures)
+ * - dispatch event to listener
+ * </pre>
+ */
+ protected void consumeMouseEvent(MouseEvent pe) {
+ final int x = pe.getX();
+ final int y = pe.getY();
+
+ if( x < 0 || y < 0 || x >= getWidth() || y >= getHeight() ) {
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("consumeMouseEvent.drop: "+pe);
+ }
+ return; // .. invalid ..
+ }
if(DEBUG_MOUSE_EVENT) {
- System.err.println("consumeMouseEvent: event: "+e);
+ System.err.println("consumeMouseEvent.in: "+pe);
}
+
+ //
+ // - Remove redundant events
+ // - Determine ENTERED/EXITED state
+ // - synthesize ENTERED event
+ // - fix MOVED/DRAGGED event
+ // - Reset states if applicable
+ //
+ final long when = pe.getWhen();
+ int eventType = pe.getEventType();
+ final MouseEvent eEntered;
+ switch( eventType ) {
+ case MouseEvent.EVENT_MOUSE_EXITED:
+ case MouseEvent.EVENT_MOUSE_ENTERED:
+ pState1.insideWindow = eventType == MouseEvent.EVENT_MOUSE_ENTERED;
+ // clear states
+ pState1.lastButtonPressTime = 0;
+ pState1.buttonPressed = 0;
+ eEntered = null;
+ break;
+
+ case MouseEvent.EVENT_MOUSE_MOVED:
+ if ( pState1.buttonPressed > 0 ) {
+ pe = pe.createVariant(MouseEvent.EVENT_MOUSE_DRAGGED);
+ eventType = pe.getEventType();
+ }
+ // Fall through intended !
+ case MouseEvent.EVENT_MOUSE_DRAGGED:
+ if( pState1.insideWindow && pState1.lastMovePosition.getX() == x && pState1.lastMovePosition.getY() == y ) {
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("consumeMouseEvent: skip EVENT_MOUSE_MOVED w/ same position: "+pState1.lastMovePosition);
+ }
+ return; // skip same position
+ }
+ pState1.lastMovePosition.setX(x);
+ pState1.lastMovePosition.setY(y);
+ // Fall through intended !
+ default:
+ if(!pState1.insideWindow) {
+ pState1.insideWindow = true;
+ eEntered = pe.createVariant(MouseEvent.EVENT_MOUSE_ENTERED);
+ // clear states
+ pState1.lastButtonPressTime = 0;
+ pState1.buttonPressed = 0;
+ } else {
+ eEntered = null;
+ }
+ }
+ if( null != eEntered ) {
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("consumeMouseEvent.send.0: "+eEntered);
+ }
+ dispatchMouseEvent(eEntered);
+ }
+
+ //
+ // Handle Default Gestures
+ //
+ if( defaultGestureHandlerEnabled &&
+ pe.getPointerType(0).getPointerClass() == MouseEvent.PointerClass.Onscreen )
+ {
+ if( null == gesture2PtrTouchScroll ) {
+ final int scaledScrollSlop;
+ final int scaledDoubleTapSlop;
+ final MonitorDevice monitor = getMainMonitor();
+ if ( null != monitor ) {
+ final DimensionImmutable mm = monitor.getSizeMM();
+ final float pixWPerMM = (float)monitor.getCurrentMode().getRotatedWidth() / (float)mm.getWidth();
+ final float pixHPerMM = (float)monitor.getCurrentMode().getRotatedHeight() / (float)mm.getHeight();
+ final float pixPerMM = Math.min(pixHPerMM, pixWPerMM);
+ scaledScrollSlop = Math.round(DoubleTapScrollGesture.SCROLL_SLOP_MM * pixPerMM);
+ scaledDoubleTapSlop = Math.round(DoubleTapScrollGesture.DOUBLE_TAP_SLOP_MM * pixPerMM);
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("consumeMouseEvent.gscroll: scrollSlop "+scaledScrollSlop+", doubleTapSlop "+scaledDoubleTapSlop+", pixPerMM "+pixPerMM+", "+monitor);
+ }
+ } else {
+ scaledScrollSlop = DoubleTapScrollGesture.SCROLL_SLOP_PIXEL;
+ scaledDoubleTapSlop = DoubleTapScrollGesture.DOUBLE_TAP_SLOP_PIXEL;
+ }
+ gesture2PtrTouchScroll = new DoubleTapScrollGesture(step(DoubleTapScrollGesture.SCROLL_SLOP_PIXEL, DoubleTapScrollGesture.SCROLL_SLOP_PIXEL/2, scaledScrollSlop),
+ step(DoubleTapScrollGesture.DOUBLE_TAP_SLOP_PIXEL, DoubleTapScrollGesture.DOUBLE_TAP_SLOP_PIXEL/2, scaledDoubleTapSlop));
+ }
+ if( gesture2PtrTouchScroll.process(pe) ) {
+ pe = (MouseEvent) gesture2PtrTouchScroll.getGestureEvent();
+ gesture2PtrTouchScroll.clear(false);
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("consumeMouseEvent.gscroll: "+pe);
+ }
+ dispatchMouseEvent(pe);
+ return;
+ }
+ if( gesture2PtrTouchScroll.isWithinGesture() ) {
+ return; // within gesture .. need more input ..
+ }
+ }
+ //
+ // Handle Custom Gestures
+ //
+ {
+ final int pointerGestureHandlerCount = pointerGestureHandler.size();
+ if( pointerGestureHandlerCount > 0 ) {
+ boolean withinGesture = false;
+ for(int i = 0; !pe.isConsumed() && i < pointerGestureHandlerCount; i++ ) {
+ final GestureHandler gh = pointerGestureHandler.get(i);
+ if( gh.process(pe) ) {
+ final InputEvent ieG = gh.getGestureEvent();
+ gh.clear(false);
+ if( ieG instanceof MouseEvent ) {
+ dispatchMouseEvent((MouseEvent)ieG);
+ } else if( ieG instanceof GestureHandler.GestureEvent) {
+ final GestureHandler.GestureEvent ge = (GestureHandler.GestureEvent) ieG;
+ for(int j = 0; !ge.isConsumed() && j < gestureListeners.size(); j++ ) {
+ gestureListeners.get(j).gestureDetected(ge);
+ }
+ }
+ return;
+ }
+ withinGesture |= gh.isWithinGesture();
+ }
+ if( withinGesture ) {
+ return;
+ }
+ }
+ }
+
+ //
+ // Synthesize mouse click
+ //
+ final MouseEvent eClicked;
+ switch( eventType ) {
+ case MouseEvent.EVENT_MOUSE_PRESSED:
+ if( 1 == pe.getPointerCount() ) {
+ pState1.lastButtonPressTime = when;
+ }
+ pState1.buttonPressed = pe.getButton();
+ eClicked = null;
+ break;
+ case MouseEvent.EVENT_MOUSE_RELEASED:
+ if( 1 == pe.getPointerCount() && when - pState1.lastButtonPressTime < MouseEvent.getClickTimeout() ) {
+ eClicked = pe.createVariant(MouseEvent.EVENT_MOUSE_CLICKED);
+ } else {
+ eClicked = null;
+ pState1.lastButtonPressTime = 0;
+ }
+ pState1.buttonPressed = 0;
+ break;
+ case MouseEvent.EVENT_MOUSE_CLICKED:
+ // ignore - synthesized here ..
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("consumeMouseEvent: drop recv'ed (synth here) "+pe);
+ }
+ pe = null;
+ eClicked = null;
+ break;
+ default:
+ eClicked = null;
+ }
+
+ if( null != pe ) {
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("consumeMouseEvent.send.1: "+pe);
+ }
+ dispatchMouseEvent(pe); // actual mouse event
+ }
+ if( null != eClicked ) {
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("consumeMouseEvent.send.2: "+eClicked);
+ }
+ dispatchMouseEvent(eClicked);
+ }
+ }
+
+ private final void dispatchMouseEvent(MouseEvent e) {
for(int i = 0; !e.isConsumed() && i < mouseListeners.size(); i++ ) {
MouseListener l = mouseListeners.get(i);
switch(e.getEventType()) {
@@ -2537,12 +2874,12 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer
public void sendKeyEvent(short eventType, int modifiers, short keyCode, short keySym, char keyChar) {
// Always add currently pressed mouse buttons to modifier mask
- consumeKeyEvent( KeyEvent.create(eventType, this, System.currentTimeMillis(), modifiers | mouseButtonModMask, keyCode, keySym, keyChar) );
+ consumeKeyEvent( KeyEvent.create(eventType, this, System.currentTimeMillis(), modifiers | pState0.buttonModMask, keyCode, keySym, keyChar) );
}
public void enqueueKeyEvent(boolean wait, short eventType, int modifiers, short keyCode, short keySym, char keyChar) {
// Always add currently pressed mouse buttons to modifier mask
- enqueueEvent(wait, KeyEvent.create(eventType, this, System.currentTimeMillis(), modifiers | mouseButtonModMask, keyCode, keySym, keyChar) );
+ enqueueEvent(wait, KeyEvent.create(eventType, this, System.currentTimeMillis(), modifiers | pState0.buttonModMask, keyCode, keySym, keyChar) );
}
@Override
diff --git a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java
index 364a348ee..0e76db374 100644
--- a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java
+++ b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventFactory.java
@@ -59,19 +59,16 @@ public class AndroidNewtEventFactory {
private static final short aMotionEventType2Newt(int aType) {
switch( aType ) {
case android.view.MotionEvent.ACTION_DOWN:
+ case android.view.MotionEvent.ACTION_POINTER_DOWN:
return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED;
case android.view.MotionEvent.ACTION_UP:
+ case android.view.MotionEvent.ACTION_POINTER_UP:
+ case android.view.MotionEvent.ACTION_CANCEL:
return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED;
case android.view.MotionEvent.ACTION_MOVE:
return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_DRAGGED;
- case android.view.MotionEvent.ACTION_CANCEL:
- return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED;
case android.view.MotionEvent.ACTION_OUTSIDE:
return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_MOVED;
- case android.view.MotionEvent.ACTION_POINTER_DOWN:
- return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_PRESSED;
- case android.view.MotionEvent.ACTION_POINTER_UP:
- return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED;
// case ACTION_HOVER_MOVE
case ACTION_SCROLL: // API Level 12 !
return com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_WHEEL_MOVED;
@@ -244,18 +241,15 @@ public class AndroidNewtEventFactory {
return maxPressure;
}
- private final int touchSlop, touchSlopSquare, doubleTapSlop, doubleTapSlopSquare;
-
+ private final int touchSlop;
public AndroidNewtEventFactory(android.content.Context context, android.os.Handler handler) {
final android.view.ViewConfiguration configuration = android.view.ViewConfiguration.get(context);
touchSlop = configuration.getScaledTouchSlop();
- touchSlopSquare = touchSlop * touchSlop;
- doubleTapSlop = configuration.getScaledDoubleTapSlop();
- doubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
+ final int doubleTapSlop = configuration.getScaledDoubleTapSlop();
if(DEBUG_MOUSE_EVENT) {
- System.err.println("GestureListener touchSlop (scaled) "+touchSlop);
- System.err.println("GestureListener doubleTapSlop (scaled) "+doubleTapSlop);
- }
+ System.err.println("AndroidNewtEventFactory scrollSlop (scaled) "+touchSlop);
+ System.err.println("AndroidNewtEventFactory doubleTapSlop (scaled) "+doubleTapSlop);
+ }
}
private static void collectPointerData(MotionEvent e, int eIdx, int dIdx, final int[] x, final int[] y, final float[] pressure, short[] pointerIds, final com.jogamp.newt.event.MouseEvent.PointerType[] pointerTypes) {
@@ -272,8 +266,8 @@ public class AndroidNewtEventFactory {
}
}
- public com.jogamp.newt.event.MouseEvent[] createMouseEvents(boolean isOnTouchEvent,
- android.view.MotionEvent event, com.jogamp.newt.Window newtSource) {
+ public com.jogamp.newt.event.MouseEvent createMouseEvents(boolean isOnTouchEvent,
+ android.view.MotionEvent event, com.jogamp.newt.Window newtSource) {
if(DEBUG_MOUSE_EVENT) {
System.err.println("createMouseEvent: isOnTouchEvent "+isOnTouchEvent+", "+event);
}
@@ -285,67 +279,17 @@ public class AndroidNewtEventFactory {
//
// Prefilter Android Event (Gesture, ..) and determine final type
//
- final int aType;
- final short nType;
+ final int aType = event.getActionMasked();
+ final short nType = aMotionEventType2Newt(aType);
final float rotationScale = touchSlop;
final float[] rotationXYZ = new float[] { 0f, 0f, 0f };
- int rotationSource = 0; // 1 - Gesture, 2 - ACTION_SCROLL
- {
- final int aType0 = event.getActionMasked();
- if( isOnTouchEvent ) {
- switch ( aType0 ) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN:
- gesture2FingerScrl.onDown(event);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_UP:
- gesture2FingerScrl.onUp(event);
- break;
- case MotionEvent.ACTION_MOVE:
- gesture2FingerScrl.onMove(event);
- break;
- }
- }
-
- if( gesture2FingerScrl.gestureStarted() ) {
- if( gesture2FingerScrl.hasGesture(true) ) {
- final float[] rot = gesture2FingerScrl.getScrollDistanceXY();
- rotationXYZ[0] = rot[0] / rotationScale;
- rotationXYZ[1] = rot[1] / rotationScale;
- aType = ACTION_SCROLL; // 8
- rotationSource = 1;
- } else {
- return new com.jogamp.newt.event.MouseEvent[0]; // skip, but cont. sending events
- }
- } else {
- aType = aType0;
- }
- nType = aMotionEventType2Newt(aType);
- }
if( (short)0 != nType ) {
final short clickCount = 1;
int modifiers = 0;
- if( 0 == rotationSource && AndroidVersion.SDK_INT >= 12 && ACTION_SCROLL == aType ) { // API Level 12
- rotationXYZ[0] = event.getAxisValue(android.view.MotionEvent.AXIS_X) / rotationScale;
- rotationXYZ[1] = event.getAxisValue(android.view.MotionEvent.AXIS_Y) / rotationScale;
- rotationSource = 2;
- }
-
- if( 0 != rotationSource ) {
- if( rotationXYZ[0]*rotationXYZ[0] > rotationXYZ[1]*rotationXYZ[1] ) {
- // Horizontal
- modifiers |= com.jogamp.newt.event.InputEvent.SHIFT_MASK;
- }
- if(DEBUG_MOUSE_EVENT) {
- System.err.println("createMouseEvent: Gesture2FingerScrl Scroll "+rotationXYZ[0]+"/"+rotationXYZ[1]+", "+rotationScale+", mods "+modifiers+", source "+rotationSource);
- }
- }
-
//
- // Determine newt-button and whether dedicated pointer is pressed
+ // Determine SDK 12 SCROLL, newt-button and whether dedicated pointer is pressed
//
final int pIndex;
final short button;
@@ -361,6 +305,22 @@ public class AndroidNewtEventFactory {
}
}
break;
+
+ case ACTION_SCROLL:
+ if( AndroidVersion.SDK_INT >= 12 ) { // API Level 12
+ rotationXYZ[0] = event.getAxisValue(android.view.MotionEvent.AXIS_X) / rotationScale;
+ rotationXYZ[1] = event.getAxisValue(android.view.MotionEvent.AXIS_Y) / rotationScale;
+
+ if( rotationXYZ[0]*rotationXYZ[0] > rotationXYZ[1]*rotationXYZ[1] ) {
+ // Horizontal
+ modifiers |= com.jogamp.newt.event.InputEvent.SHIFT_MASK;
+ }
+ if(DEBUG_MOUSE_EVENT) {
+ System.err.println("createMouseEvent: SDK-12 Scroll "+rotationXYZ[0]+"/"+rotationXYZ[1]+", "+rotationScale+", mods "+modifiers);
+ }
+ }
+ // Fall through intended!
+
default: {
pIndex = 0;
button = com.jogamp.newt.event.MouseEvent.BUTTON1;
@@ -378,9 +338,9 @@ public class AndroidNewtEventFactory {
final com.jogamp.newt.event.MouseEvent.PointerType[] pointerTypes = new com.jogamp.newt.event.MouseEvent.PointerType[pCount];
if( 0 < pCount ) {
if(DEBUG_MOUSE_EVENT) {
- System.err.println("createMouseEvent: collect ptr-data [0.."+(pCount-1)+", count "+pCount+", action "+pIndex+"], aType "+aType+", button "+button+", twoFingerScrollGesture "+gesture2FingerScrl);
+ System.err.println("createMouseEvent: collect ptr-data [0.."+(pCount-1)+", count "+pCount+", action "+pIndex+"], aType "+aType+", button "+button);
}
- int j = 0;
+ int j = 0;
// Always put action-pointer data at index 0
collectPointerData(event, pIndex, j++, x, y, pressure, pointerIds, pointerTypes);
for(int i=0; i < pCount; i++) {
@@ -402,202 +362,11 @@ public class AndroidNewtEventFactory {
final Object src = (null==newtSource)?null:(Object)newtSource;
final long unixTime = System.currentTimeMillis() + ( event.getEventTime() - android.os.SystemClock.uptimeMillis() );
- final com.jogamp.newt.event.MouseEvent me1 = new com.jogamp.newt.event.MouseEvent(
- nType, src, unixTime,
- modifiers, x, y, pressure, maxPressure, pointerTypes, pointerIds,
- clickCount, button, rotationXYZ, rotationScale);
-
- if( com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_RELEASED == nType ) {
- return new com.jogamp.newt.event.MouseEvent[] { me1,
- new com.jogamp.newt.event.MouseEvent(
- com.jogamp.newt.event.MouseEvent.EVENT_MOUSE_CLICKED,
- src, unixTime, modifiers, x, y, pressure, maxPressure, pointerTypes, pointerIds,
- clickCount, button, rotationXYZ, rotationScale) };
- } else {
- return new com.jogamp.newt.event.MouseEvent[] { me1 };
- }
- }
+ return new com.jogamp.newt.event.MouseEvent(nType, src, unixTime,
+ modifiers, pointerTypes, pointerIds, x, y, pressure, maxPressure,
+ button, clickCount, rotationXYZ, rotationScale);
+ }
return null; // no mapping ..
}
-
- static interface GestureHandler {
- /**
- * Returns true if last on* command produced a gesture, otherwise false.
- * @param clear if true, method clears the gesture flag.
- */
- public boolean hasGesture(boolean clear);
- /** Returns true if the gesture has started */
- public boolean gestureStarted();
- /** Returns distance of the last consecutive double-tab scrolling. */
- public float[] getScrollDistanceXY();
- public void onDown(android.view.MotionEvent e);
- public void onUp(android.view.MotionEvent e);
- public void onMove(android.view.MotionEvent e);
- }
-
- /**
- * Criteria related to Android parameter:
- * - ScaledDoubleTapSlop:
- * - Max 2 finger distance to start 'scroll' mode
- *
- * - ScaledTouchSlop:
- * - Min. movement w/ 2 pointer withing ScaledDoubleTapSlop starting 'scroll' mode
- * - Max. distance growth in respect to initiated 2-finger distance.
- *
- * - Tolerate temporary lift of 1/2 pointer
- *
- * - Always validate pointer-id
- */
- private final GestureHandler gesture2FingerScrl = new GestureHandler() {
- private final float[] scrollDistance = new float[] { 0f, 0f };
- private int[] pIds = new int[] { -1, -1 };
- private int startDist = -1;
- private float downY = 0;
- private float downX = 0;
- private float lastY = 0;
- private float lastX = 0;
- private int pointerDownCount = 0;
- private boolean withinGesture = false;
- private boolean hasGesture = false;
-
- public String toString() {
- return "Gesture2FingerScrl[in "+withinGesture+", has "+hasGesture+", pc "+pointerDownCount+"]";
- }
-
- private void clear() {
- downX = 0f;
- downY = 0f;
- lastX = 0f;
- lastY = 0f;
- startDist = -1;
- withinGesture = false;
- hasGesture = false;
- pIds[0] = -1;
- pIds[1] = -1;
- }
-
- private final int getSquareDistance(float x1, float y1, float x2, float y2) {
- final int deltaX = (int) x1 - (int) x2;
- final int deltaY = (int) y1 - (int) y2;
- return deltaX * deltaX + deltaY * deltaY;
- }
-
- private int gesturePointers(final android.view.MotionEvent e, final int excludeIndex) {
- int j = 0;
- for(int i=e.getPointerCount()-1; i>=0; i--) {
- if( excludeIndex != i ) {
- final int id = e.getPointerId(i);
- if( pIds[0] == id || pIds[1] == id ) {
- j++;
- }
- }
- }
- return j;
- }
-
- @Override
- public boolean gestureStarted() {
- return 0 <= startDist && withinGesture;
- }
-
- @Override
- public boolean hasGesture(boolean clear) {
- final boolean r = hasGesture;
- if( clear ) {
- hasGesture = false;
- }
- return r;
- }
-
- @Override
- public final float[] getScrollDistanceXY() {
- return scrollDistance;
- }
-
- @Override
- public void onDown(android.view.MotionEvent e) {
- pointerDownCount = e.getPointerCount();
- final int gPtr = gesturePointers(e, -1);
- if( 2 <= gPtr ) { // pick-up dLast coordinate to cont. gesture after temp loosing 1/2 pointers
- lastX = e.getX(0);
- lastY = e.getY(0);
- }
- if(DEBUG_MOUSE_EVENT) {
- System.err.println(this+".onDown: gPtr "+gPtr+", "+e);
- }
- }
-
- @Override
- public void onUp(android.view.MotionEvent e) {
- pointerDownCount = e.getPointerCount();
- final int gPtr = gesturePointers(e, e.getActionIndex()); // w/o lifted pointer
- if( 1 > gPtr ) { // tolerate lifting 1/2 gesture pointers temporary
- clear();
- }
- pointerDownCount--; // lifted now!
- if(DEBUG_MOUSE_EVENT) {
- System.err.println(this+".onUp: gPtr "+gPtr+", "+e);
- }
- }
-
- @Override
- public void onMove(android.view.MotionEvent e) {
- pointerDownCount = e.getPointerCount();
- if( 2 <= pointerDownCount ) {
- final float x0 = e.getX(0);
- final float y0 = e.getY(0);
- final int sqDist = getSquareDistance(x0, y0, e.getX(1), e.getY(1));
- final boolean isDistWithinDoubleTapSlop = sqDist < doubleTapSlopSquare;
- final int dist = (int)Math.sqrt(sqDist);
- if( !withinGesture ) {
- int gPtr = 0;
- if( isDistWithinDoubleTapSlop ) {
- if( 0 > startDist ) {
- gPtr = 2;
- pIds[0] = e.getPointerId(0);
- pIds[1] = e.getPointerId(1);
- downX = x0;
- downY = y0;
- lastX = x0;
- lastY = y0;
- startDist = dist;
- } else {
- gPtr = gesturePointers(e, -1);
- if( 2 <= gPtr ) {
- final int dX = (int) (x0 - downX);
- final int dY = (int) (y0 - downY);
- final int d = (dX * dX) + (dY * dY);
- withinGesture = d > touchSlopSquare;
- }
- }
- }
- if(DEBUG_MOUSE_EVENT) {
- final double dX = x0 - downX;
- final double dY = y0 - downY;
- final double d = Math.sqrt( (dX * dX) + (dY * dY) );
- System.err.println(this+".onMove.0: mDist "+d+", pStartDist "+dist+", gPtr "+gPtr+", distWithin2DTSlop "+isDistWithinDoubleTapSlop+", dLast "+lastX+"/"+lastY+", "+e);
- }
- }
- if( withinGesture ) {
- final int gPtr = gesturePointers(e, -1);
- final boolean isDistGrowthWithinTouchSlop = dist - startDist <= touchSlop;
- if( 2 > gPtr || !isDistGrowthWithinTouchSlop ) {
- clear();
- } else {
- scrollDistance[0] = lastX - x0;
- scrollDistance[1] = lastY - y0;
- lastX = x0;
- lastY = y0;
- hasGesture = true;
- }
- if(DEBUG_MOUSE_EVENT) {
- System.err.println(this+".onMove.1: pStartDist "+startDist+", pDist "+dist+", gPtr "+gPtr+" ["+pIds[0]+", "+pIds[1]+"]"+
- ", distWithin2DTSlop "+isDistWithinDoubleTapSlop+", distGrowthWithinTSlop "+isDistGrowthWithinTouchSlop+
- ", dLast "+lastX+"/"+lastY+", d "+scrollDistance[0]+"/"+scrollDistance[1]+", "+e);
- }
- }
- }
- }
- };
}
diff --git a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java
index 93735863e..df52208df 100644
--- a/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java
+++ b/src/newt/classes/jogamp/newt/driver/android/event/AndroidNewtEventTranslator.java
@@ -13,13 +13,16 @@ public class AndroidNewtEventTranslator implements View.OnKeyListener, View.OnTo
}
private final boolean processTouchMotionEvents(View v, android.view.MotionEvent event, boolean isOnTouchEvent) {
- final com.jogamp.newt.event.MouseEvent[] newtEvents = factory.createMouseEvents(isOnTouchEvent, event, newtWindow);
- if(null != newtEvents) {
- newtWindow.focusChanged(false, true);
- for(int i=0; i<newtEvents.length; i++) {
- newtWindow.enqueueEvent(false, newtEvents[i]);
+ final com.jogamp.newt.event.MouseEvent newtEvent = factory.createMouseEvents(isOnTouchEvent, event, newtWindow);
+ if(null != newtEvent) {
+ switch( event.getActionMasked() ) {
+ case android.view.MotionEvent.ACTION_DOWN:
+ case android.view.MotionEvent.ACTION_POINTER_DOWN:
+ newtWindow.focusChanged(false, true);
+ break;
}
- try { Thread.sleep((long) (1000.0F/30.0F)); }
+ newtWindow.enqueueEvent(false, newtEvent);
+ try { Thread.sleep((long) (100.0F/3.0F)); } // 33 ms
catch(InterruptedException e) { }
return true; // consumed/handled, further interest in events
}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
index bea761a35..50037ebf3 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
@@ -22,11 +22,14 @@ package com.jogamp.opengl.test.junit.jogl.demos.es2;
import com.jogamp.common.nio.Buffers;
import com.jogamp.newt.Window;
+import com.jogamp.newt.event.GestureHandler;
import com.jogamp.newt.event.KeyAdapter;
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.PinchToZoomGesture;
+import com.jogamp.newt.event.GestureHandler.GestureEvent;
import com.jogamp.opengl.JoglVersion;
import com.jogamp.opengl.test.junit.jogl.demos.GearsObject;
import com.jogamp.opengl.util.PMVMatrix;
@@ -59,7 +62,6 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
private GLUniformData colorU = null;
private float view_rotx = 20.0f, view_roty = 30.0f, view_rotz = 0.0f;
private float panX = 0.0f, panY = 0.0f, panZ=0.0f;
- private int drawableHeight = 1;
private GearsObjectES2 gear1=null, gear2=null, gear3=null;
private FloatBuffer gear1Color=GearsObject.red, gear2Color=GearsObject.green, gear3Color=GearsObject.blue;
private float angle = 0.0f;
@@ -76,6 +78,9 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
private float[] clearColor = null;
private boolean clearBuffers = true;
private boolean verbose = true;
+
+ private PinchToZoomGesture pinchToZoomGesture = null;
+
public GearsES2(int swapInterval) {
this.swapInterval = swapInterval;
@@ -235,15 +240,28 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
final Window window = (Window) upstreamWidget;
window.addMouseListener(gearsMouse);
window.addKeyListener(gearsKeys);
+ window.addGestureListener(pinchToZoomListener);
+ pinchToZoomGesture = new PinchToZoomGesture(drawable.getNativeSurface());
+ window.addGestureHandler(pinchToZoomGesture);
} else if (GLProfile.isAWTAvailable() && upstreamWidget instanceof java.awt.Component) {
final java.awt.Component comp = (java.awt.Component) upstreamWidget;
new com.jogamp.newt.event.awt.AWTMouseAdapter(gearsMouse).addTo(comp);
new com.jogamp.newt.event.awt.AWTKeyAdapter(gearsKeys).addTo(comp);
}
+
st.useProgram(gl, false);
System.err.println(Thread.currentThread()+" GearsES2.init FIN");
}
+
+ private final GestureHandler.GestureListener pinchToZoomListener = new GestureHandler.GestureListener() {
+ @Override
+ public void gestureDetected(GestureEvent gh) {
+ final PinchToZoomGesture.ZoomEvent ze = (PinchToZoomGesture.ZoomEvent) gh;
+ final float zoom = ze.getZoom() * ( ze.getTrigger().getPointerCount() - 1 );
+ panZ = zoom * 30f - 30f; // [0 .. 2] -> [-30f .. 30f]
+ }
+ };
@Override
public void reshape(GLAutoDrawable glad, int x, int y, int width, int height) {
@@ -266,7 +284,6 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
void reshapeImpl(GL2ES2 gl, int tileX, int tileY, int tileWidth, int tileHeight, int imageWidth, int imageHeight) {
final boolean msaa = gl.getContext().getGLDrawable().getChosenGLCapabilities().getSampleBuffers();
System.err.println(Thread.currentThread()+" GearsES2.reshape "+tileX+"/"+tileY+" "+tileWidth+"x"+tileHeight+" of "+imageWidth+"x"+imageHeight+", swapInterval "+swapInterval+", drawable 0x"+Long.toHexString(gl.getContext().getGLDrawable().getHandle())+", msaa "+msaa+", tileRendererInUse "+tileRendererInUse);
- drawableHeight = imageHeight;
if( !gl.hasGLSL() ) {
return;
@@ -326,6 +343,9 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
final Window window = (Window) upstreamWidget;
window.removeMouseListener(gearsMouse);
window.removeKeyListener(gearsKeys);
+ window.removeGestureHandler(pinchToZoomGesture);
+ pinchToZoomGesture = null;
+ window.removeGestureListener(pinchToZoomListener);
}
final GL2ES2 gl = drawable.getGL().getGL2ES2();
if( !gl.hasGLSL() ) {
@@ -432,69 +452,6 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
}
}
- interface GestureHandler {
- /** Returns true if within the gesture */
- public boolean isWithinGesture();
- /** Returns true if within the gesture */
- public boolean onReleased(MouseEvent e);
- /** Returns true if within the gesture */
- public boolean onDragged(MouseEvent e);
- }
- final GestureHandler gesture2PtrZoom = new GestureHandler() {
- private int zoomLastYDist;
- private boolean zoomFirstTouch = true;
- private boolean zoomMode = false;
-
- @Override
- public boolean isWithinGesture() {
- return zoomMode;
- }
-
- @Override
- public boolean onReleased(MouseEvent e) {
- if( zoomMode && e.getPointerCount()==1 ) {
- zoomFirstTouch = true;
- zoomMode = false;
- System.err.println("panZ.X: "+e);
- }
- return zoomMode;
- }
-
- @Override
- public boolean onDragged(MouseEvent e) {
- if( e.getPointerCount() >=2 ) {
- // 2 pointers zoom .. [ -15 .. 15 ], range 30
- /**
- // Simple 1:1 Zoom: finger-distance to screen-coord
- final int dy = Math.abs(e.getY(0)-e.getY(1));
- float scale = (float)dy / (float)drawableHeight;
- panZ = 30f * scale - 15f;
- System.err.println("panZ: scale "+scale+" ["+dy+"/"+drawableHeight+"] -> "+panZ);
- */
- // Diff. 1:1 Zoom: finger-distance to screen-coord
- if(zoomFirstTouch) {
- zoomLastYDist = Math.abs(e.getY(0)-e.getY(1));
- zoomFirstTouch=false;
- zoomMode = true;
- System.err.println("panZ: 1st pinch "+zoomLastYDist+", "+e);
- } else if( zoomMode ) {
- final int dy = Math.abs(e.getY(0)-e.getY(1));
- final int ddy = dy - zoomLastYDist;
-
- final float incr = ( (float)ddy / (float)drawableHeight ) * 15.0f;
- panZ += incr;
- if( e.getPointerCount() > 2 ) {
- panZ += incr;
- }
- System.err.println("panZ.1: ddy "+ddy+", incr "+incr+" ["+dy+"/"+drawableHeight+"], dblZoom "+(e.getPointerCount() > 2)+" -> "+panZ);
-
- zoomLastYDist = dy;
- }
- }
- return zoomMode;
- }
- };
-
class GearsMouseAdapter implements MouseListener{
private int prevMouseX, prevMouseY;
@@ -526,40 +483,33 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
}
public void mousePressed(MouseEvent e) {
- if( !gesture2PtrZoom.isWithinGesture() ) {
- if( e.getPointerCount()==1 ) {
- prevMouseX = e.getX();
- prevMouseY = e.getY();
- } else if( e.getPointerCount() == 4 ) {
- final Object src = e.getSource();
- if( e.getPressure(true) > 0.7f && src instanceof Window) { // show Keyboard
- ((Window) src).setKeyboardVisible(true);
- }
+ if( e.getPointerCount()==1 ) {
+ prevMouseX = e.getX();
+ prevMouseY = e.getY();
+ } else if( e.getPointerCount() == 4 ) {
+ final Object src = e.getSource();
+ if( e.getPressure(true) > 0.7f && src instanceof Window) { // show Keyboard
+ ((Window) src).setKeyboardVisible(true);
}
}
}
public void mouseReleased(MouseEvent e) {
- gesture2PtrZoom.onReleased(e);
}
public void mouseMoved(MouseEvent e) {
- if( !gesture2PtrZoom.isWithinGesture() && e.getPointerCount()==1 ) {
- if( e.isConfined() ) {
- navigate(e);
- } else {
- // track prev. position so we don't have 'jumps'
- // in case we move to confined navigation.
- prevMouseX = e.getX();
- prevMouseY = e.getY();
- }
+ if( e.isConfined() ) {
+ navigate(e);
+ } else {
+ // track prev. position so we don't have 'jumps'
+ // in case we move to confined navigation.
+ prevMouseX = e.getX();
+ prevMouseY = e.getY();
}
}
public void mouseDragged(MouseEvent e) {
- if( !gesture2PtrZoom.onDragged(e) && e.getPointerCount()==1 ) {
- navigate(e);
- }
+ navigate(e);
}
private void navigate(MouseEvent e) {
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 fe2c0f31a..74be176da 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
@@ -266,8 +266,10 @@ public class TestGearsES2NEWT extends UITestCase {
});
glWindow.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
- glWindow.setFullscreen(!glWindow.isFullscreen());
- System.err.println("setFullscreen: "+glWindow.isFullscreen());
+ if(e.getClickCount() == 2 && e.getPointerCount() == 1) {
+ glWindow.setFullscreen(!glWindow.isFullscreen());
+ System.err.println("setFullscreen: "+glWindow.isFullscreen());
+ }
}
});