From 06045df2be8e9991e8ad2c44ce7e8b8720f50eff Mon Sep 17 00:00:00 2001 From: benfry Date: Mon, 3 Sep 2012 22:49:43 +0000 Subject: [PATCH] fix event handling bugs --- android/core/src/processing/core/PApplet.java | 350 +++++++++++------- .../core/src/processing/event/KeyEvent.java | 41 ++ .../core/src/processing/event/MouseEvent.java | 40 ++ .../core/src/processing/event/TouchEvent.java | 40 ++ core/src/processing/core/PApplet.java | 47 +-- 5 files changed, 351 insertions(+), 167 deletions(-) create mode 100644 android/core/src/processing/event/KeyEvent.java create mode 100644 android/core/src/processing/event/MouseEvent.java create mode 100644 android/core/src/processing/event/TouchEvent.java diff --git a/android/core/src/processing/core/PApplet.java b/android/core/src/processing/core/PApplet.java index 033ad8c44..5a92b48d0 100644 --- a/android/core/src/processing/core/PApplet.java +++ b/android/core/src/processing/core/PApplet.java @@ -560,15 +560,6 @@ public class PApplet extends Activity implements PConstants, Runnable { redraw = true; // draw this guy once firstMotion = true; - // these need to be inited before setup - sizeMethods = new RegisteredMethods(); - preMethods = new RegisteredMethods(); - drawMethods = new RegisteredMethods(); - postMethods = new RegisteredMethods(); - mouseEventMethods = new RegisteredMethods(); - keyEventMethods = new RegisteredMethods(); - disposeMethods = new RegisteredMethods(); - Context context = getApplicationContext(); sketchPath = context.getFilesDir().getAbsolutePath(); @@ -584,7 +575,7 @@ public class PApplet extends Activity implements PConstants, Runnable { @Override public void onConfigurationChanged(Configuration newConfig) { - System.out.println("configuration changed: " + newConfig); + if (DEBUG) System.out.println("configuration changed: " + newConfig); super.onConfigurationChanged(newConfig); } @@ -597,6 +588,7 @@ public class PApplet extends Activity implements PConstants, Runnable { // surfaceView.onResume(); if (DEBUG) System.out.println("PApplet.onResume() called"); paused = false; + handleMethods("resume"); //start(); // kick the thread back on resume(); // surfaceView.onResume(); @@ -607,10 +599,10 @@ public class PApplet extends Activity implements PConstants, Runnable { protected void onPause() { super.onPause(); - // TODO need to save all application state here! // System.out.println("PApplet.onPause() called"); paused = true; + handleMethods("pause"); pause(); // handler for others to write // synchronized (this) { // paused = true; @@ -1094,51 +1086,69 @@ public class PApplet extends Activity implements PConstants, Runnable { ////////////////////////////////////////////////////////////// - protected RegisteredMethods sizeMethods; - protected RegisteredMethods preMethods, drawMethods, postMethods; - protected RegisteredMethods mouseEventMethods, keyEventMethods; - protected RegisteredMethods disposeMethods; - public class RegisteredMethods { + /** Map of registered methods, stored by name. */ + HashMap registerMap = + new HashMap(); + + + class RegisteredMethods { int count; - Object objects[]; - Method methods[]; + Object[] objects; + // Because the Method comes from the class being called, + // it will be unique for most, if not all, objects. + Method[] methods; + Object[] emptyArgs = new Object[] { }; - // convenience version for no args - public void handle() { - handle(new Object[] { }); + void handle() { + handle(emptyArgs); } - public void handle(Object oargs[]) { + + void handle(Object[] args) { for (int i = 0; i < count; i++) { try { - //System.out.println(objects[i] + " " + args); - methods[i].invoke(objects[i], oargs); + methods[i].invoke(objects[i], args); } catch (Exception e) { - e.printStackTrace(); + // check for wrapped exception, get root exception + Throwable t; + if (e instanceof InvocationTargetException) { + InvocationTargetException ite = (InvocationTargetException) e; + t = ite.getCause(); + } else { + t = e; + } + // check for RuntimeException, and allow to bubble up + if (t instanceof RuntimeException) { + // re-throw exception + throw (RuntimeException) t; + } else { + // trap and print as usual + t.printStackTrace(); + } } } } - public void add(Object object, Method method) { - if (objects == null) { - objects = new Object[5]; - methods = new Method[5]; + + void add(Object object, Method method) { + if (findIndex(object) == -1) { + if (objects == null) { + objects = new Object[5]; + methods = new Method[5]; + + } else if (count == objects.length) { + objects = (Object[]) PApplet.expand(objects); + methods = (Method[]) PApplet.expand(methods); + } + objects[count] = object; + methods[count] = method; + count++; + } else { + die(method.getName() + "() already added for this instance of " + + object.getClass().getName()); } - if (count == objects.length) { - objects = (Object[]) PApplet.expand(objects); - methods = (Method[]) PApplet.expand(methods); -// Object otemp[] = new Object[count << 1]; -// System.arraycopy(objects, 0, otemp, 0, count); -// objects = otemp; -// Method mtemp[] = new Method[count << 1]; -// System.arraycopy(methods, 0, mtemp, 0, count); -// methods = mtemp; - } - objects[count] = object; - methods[count] = method; - count++; } @@ -1147,8 +1157,10 @@ public class PApplet extends Activity implements PConstants, Runnable { * must be called multiple times if object is registered multiple times). * Does not shrink array afterwards, silently returns if method not found. */ - public void remove(Object object, Method method) { - int index = findIndex(object, method); +// public void remove(Object object, Method method) { +// int index = findIndex(object, method); + public void remove(Object object) { + int index = findIndex(object); if (index != -1) { // shift remaining methods by one to preserve ordering count--; @@ -1162,9 +1174,12 @@ public class PApplet extends Activity implements PConstants, Runnable { } } - protected int findIndex(Object object, Method method) { + +// protected int findIndex(Object object, Method method) { + protected int findIndex(Object object) { for (int i = 0; i < count; i++) { - if (objects[i] == object && methods[i].equals(method)) { + if (objects[i] == object) { +// if (objects[i] == object && methods[i].equals(method)) { //objects[i].equals() might be overridden, so use == for safety // since here we do care about actual object identity //methods[i]==method is never true even for same method, so must use @@ -1177,126 +1192,177 @@ public class PApplet extends Activity implements PConstants, Runnable { } + /** + * Register a built-in event so that it can be fired for libraries, etc. + * Supported events include: + *
    + *
  • pre – at the very top of the draw() method (safe to draw) + *
  • draw – at the end of the draw() method (safe to draw) + *
  • post – after draw() has exited (not safe to draw) + *
  • pause() – called when the sketch is paused + *
  • resume() – called when the sketch is resumed + *
  • dispose – when the sketch is shutting down (definitely not safe to draw) + *
      + * In addition, the new (for 2.0) processing.event classes are passed to + * the following event types: + *
        + *
      • mouseEvent + *
      • keyEvent + *
      • touchEvent + *
      + * The older java.awt events are no longer supported. + * See the Library Wiki page for more details. + * @param methodName name of the method to be called + * @param target the target object that should receive the event + */ + public void registerMethod(String methodName, Object target) { + if (methodName.equals("mouseEvent")) { + registerWithArgs("mouseEvent", target, new Class[] { processing.event.MouseEvent.class }); + + } else if (methodName.equals("keyEvent")) { + registerWithArgs("keyEvent", target, new Class[] { processing.event.KeyEvent.class }); + + } else if (methodName.equals("touchEvent")) { + registerWithArgs("touchEvent", target, new Class[] { processing.event.TouchEvent.class }); + + } else { + registerNoArgs(methodName, target); + } + } + + + private void registerNoArgs(String name, Object o) { + RegisteredMethods meth = registerMap.get(name); + if (meth == null) { + meth = new RegisteredMethods(); + registerMap.put(name, meth); + } + Class c = o.getClass(); + try { + Method method = c.getMethod(name, new Class[] {}); + meth.add(o, method); + + } catch (NoSuchMethodException nsme) { + die("There is no public " + name + "() method in the class " + + o.getClass().getName()); + + } catch (Exception e) { + die("Could not register " + name + " + () for " + o, e); + } + } + + + private void registerWithArgs(String name, Object o, Class cargs[]) { + RegisteredMethods meth = registerMap.get(name); + if (meth == null) { + meth = new RegisteredMethods(); + registerMap.put(name, meth); + } + Class c = o.getClass(); + try { + Method method = c.getMethod(name, cargs); + meth.add(o, method); + + } catch (NoSuchMethodException nsme) { + die("There is no public " + name + "() method in the class " + + o.getClass().getName()); + + } catch (Exception e) { + die("Could not register " + name + " + () for " + o, e); + } + } + + +// public void registerMethod(String methodName, Object target, Object... args) { +// registerWithArgs(methodName, target, args); +// } + + + public void unregisterMethod(String name, Object target) { + RegisteredMethods meth = registerMap.get(name); + if (meth == null) { + die("No registered methods with the name " + name + "() were found."); + } + try { +// Method method = o.getClass().getMethod(name, new Class[] {}); +// meth.remove(o, method); + meth.remove(target); + } catch (Exception e) { + die("Could not unregister " + name + "() for " + target, e); + } + + } + + + protected void handleMethods(String methodName) { + RegisteredMethods meth = registerMap.get(methodName); + if (meth != null) { + meth.handle(); + } + } + + + @Deprecated public void registerSize(Object o) { - Class methodArgs[] = new Class[] { Integer.TYPE, Integer.TYPE }; - registerWithArgs(sizeMethods, "size", o, methodArgs); + System.err.println("The registerSize() command is no longer supported."); +// Class methodArgs[] = new Class[] { Integer.TYPE, Integer.TYPE }; +// registerWithArgs(sizeMethods, "size", o, methodArgs); } + + @Deprecated public void registerPre(Object o) { - registerNoArgs(preMethods, "pre", o); + registerNoArgs("pre", o); } + + @Deprecated public void registerDraw(Object o) { - registerNoArgs(drawMethods, "draw", o); + registerNoArgs("draw", o); } + + @Deprecated public void registerPost(Object o) { - registerNoArgs(postMethods, "post", o); - } - - public void registerMouseEvent(Object o) { -// Class methodArgs[] = new Class[] { MouseEvent.class }; - // registerWithArgs(mouseEventMethods, "mouseEvent", o, methodArgs); + registerNoArgs("post", o); } - public void registerKeyEvent(Object o) { - Class methodArgs[] = new Class[] { KeyEvent.class }; - registerWithArgs(keyEventMethods, "keyEvent", o, methodArgs); - } - + @Deprecated public void registerDispose(Object o) { - //registerNoArgs(disposeMethods, "dispose", o); - } - - - protected void registerNoArgs(RegisteredMethods meth, - String name, Object o) { - Class c = o.getClass(); - try { - Method method = c.getMethod(name, new Class[] {}); - meth.add(o, method); - - } catch (NoSuchMethodException nsme) { - die("There is no public " + name + "() method in the class " + - o.getClass().getName()); - - } catch (Exception e) { - die("Could not register " + name + " + () for " + o, e); - } - } - - - protected void registerWithArgs(RegisteredMethods meth, - String name, Object o, Class cargs[]) { - Class c = o.getClass(); - try { - Method method = c.getMethod(name, cargs); - meth.add(o, method); - - } catch (NoSuchMethodException nsme) { - die("There is no public " + name + "() method in the class " + - o.getClass().getName()); - - } catch (Exception e) { - die("Could not register " + name + " + () for " + o, e); - } + registerNoArgs("dispose", o); } + @Deprecated public void unregisterSize(Object o) { - Class methodArgs[] = new Class[] { Integer.TYPE, Integer.TYPE }; - unregisterWithArgs(sizeMethods, "size", o, methodArgs); + System.err.println("The unregisterSize() command is no longer supported."); +// Class methodArgs[] = new Class[] { Integer.TYPE, Integer.TYPE }; +// unregisterWithArgs(sizeMethods, "size", o, methodArgs); } + + @Deprecated public void unregisterPre(Object o) { - unregisterNoArgs(preMethods, "pre", o); + unregisterMethod("pre", o); } + + @Deprecated public void unregisterDraw(Object o) { - unregisterNoArgs(drawMethods, "draw", o); + unregisterMethod("draw", o); } + + @Deprecated public void unregisterPost(Object o) { - unregisterNoArgs(postMethods, "post", o); + unregisterMethod("post", o); } - public void unregisterMouseEvent(Object o) { - // Class methodArgs[] = new Class[] { MouseEvent.class }; - // unregisterWithArgs(mouseEventMethods, "mouseEvent", o, methodArgs); - } - - public void unregisterKeyEvent(Object o) { - Class methodArgs[] = new Class[] { KeyEvent.class }; - unregisterWithArgs(keyEventMethods, "keyEvent", o, methodArgs); - } + @Deprecated public void unregisterDispose(Object o) { - unregisterNoArgs(disposeMethods, "dispose", o); - } - - - protected void unregisterNoArgs(RegisteredMethods meth, - String name, Object o) { - Class c = o.getClass(); - try { - Method method = c.getMethod(name, new Class[] {}); - meth.remove(o, method); - } catch (Exception e) { - die("Could not unregister " + name + "() for " + o, e); - } - } - - - protected void unregisterWithArgs(RegisteredMethods meth, - String name, Object o, Class cargs[]) { - Class c = o.getClass(); - try { - Method method = c.getMethod(name, cargs); - meth.remove(o, method); - } catch (Exception e) { - die("Could not unregister " + name + "() for " + o, e); - } + unregisterMethod("dispose", o); } @@ -1876,6 +1942,10 @@ public class PApplet extends Activity implements PConstants, Runnable { float instantaneousRate = (float) rate / 1000.0f; frameRate = (frameRate * 0.9f) + (instantaneousRate * 0.1f); + if (frameCount != 0) { + handleMethods("pre"); + } + // use dmouseX/Y as previous mouse pos, since this is the // last position the mouse was in during the previous draw. pmouseX = dmouseX; @@ -1900,13 +1970,17 @@ public class PApplet extends Activity implements PConstants, Runnable { dequeueMotionEvents(); dequeueKeyEvents(); + handleMethods("draw"); + redraw = false; // unset 'redraw' flag in case it was set // (only do this once draw() has run, not just setup()) - } - g.endDraw(); + if (frameCount != 0) { + handleMethods("post"); + } + frameRateLastNanos = now; frameCount++; } @@ -2746,7 +2820,7 @@ public class PApplet extends Activity implements PConstants, Runnable { // call to shut down renderer, in case it needs it (pdf does) if (g != null) g.dispose(); - disposeMethods.handle(); + handleMethods("dispose"); } diff --git a/android/core/src/processing/event/KeyEvent.java b/android/core/src/processing/event/KeyEvent.java new file mode 100644 index 000000000..885a8510e --- /dev/null +++ b/android/core/src/processing/event/KeyEvent.java @@ -0,0 +1,41 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.event; + + +public class KeyEvent { + Object nativeObject; + + char key; + int keyCode; + + + public KeyEvent(Object nativeObject) { + this.nativeObject = nativeObject; + } + + + public Object getNative() { + return nativeObject; + } +} \ No newline at end of file diff --git a/android/core/src/processing/event/MouseEvent.java b/android/core/src/processing/event/MouseEvent.java new file mode 100644 index 000000000..7a92e8e41 --- /dev/null +++ b/android/core/src/processing/event/MouseEvent.java @@ -0,0 +1,40 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.event; + + +public class MouseEvent { + Object nativeObject; + + int x, y; + + + public MouseEvent(Object nativeObject) { + this.nativeObject = nativeObject; + } + + + public Object getNative() { + return nativeObject; + } +} \ No newline at end of file diff --git a/android/core/src/processing/event/TouchEvent.java b/android/core/src/processing/event/TouchEvent.java new file mode 100644 index 000000000..ccfc00822 --- /dev/null +++ b/android/core/src/processing/event/TouchEvent.java @@ -0,0 +1,40 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.event; + + +public class TouchEvent { + Object nativeObject; + + float x, y; + + + public TouchEvent(Object nativeObject) { + this.nativeObject = nativeObject; + } + + + public Object getNative() { + return nativeObject; + } +} \ No newline at end of file diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 997734335..36844bb80 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -731,23 +731,6 @@ public class PApplet extends Applet Object pauseObject = new Object(); Thread thread; -// static final String[] registerNames = { -// "size", -// "pause", "resume", -// "pre", "draw", "post", -// "mouseEvent", "keyEvent", "touchEvent", -// "dispose" -// }; - HashMap registerMap = - new HashMap(); -// protected RegisteredMethods sizeMethods; -// protected RegisteredMethods pauseMethods, resumeMethods; -// protected RegisteredMethods preMethods, drawMethods, postMethods; - /** Old methods with AWT API that should not be used. */ - protected RegisteredMethods mouseEventMethods, keyEventMethods; -// protected RegisteredMethods disposeMethods; - - // messages to send if attached as an external vm /** @@ -1010,7 +993,6 @@ public class PApplet extends Applet paused = true; // causes animation thread to sleep pause(); -// pauseMethods.handle(); handleMethods("pause"); // actual pause will happen in the run() method @@ -1059,10 +1041,16 @@ public class PApplet extends Applet } - ////////////////////////////////////////////////////////////// + /** Map of registered methods, stored by name. */ + HashMap registerMap = + new HashMap(); + /** Old methods with AWT API that should not be used. */ + RegisteredMethods mouseEventMethods, keyEventMethods; + + class RegisteredMethods { int count; Object[] objects; @@ -1170,6 +1158,8 @@ public class PApplet extends Applet *
    • pre – at the very top of the draw() method (safe to draw) *
    • draw – at the end of the draw() method (safe to draw) *
    • post – after draw() has exited (not safe to draw) + *
    • pause() – called when the sketch is paused + *
    • resume() – called when the sketch is resumed *
    • dispose – when the sketch is shutting down (definitely not safe to draw) *
        * In addition, the new (for 2.0) processing.event classes are passed to @@ -2153,21 +2143,13 @@ public class PApplet extends Applet redraw = false; // unset 'redraw' flag in case it was set // (only do this once draw() has run, not just setup()) - } - g.endDraw(); if (recorder != null) { recorder.endDraw(); } - frameRateLastNanos = now; - frameCount++; - - // 1.2.1 version -// paint(); - // 1.5.1 version repaint(); getToolkit().sync(); // force repaint now (proper method) @@ -2175,6 +2157,9 @@ public class PApplet extends Applet if (frameCount != 0) { handleMethods("post"); } + + frameRateLastNanos = now; + frameCount++; } } @@ -2432,7 +2417,9 @@ public class PApplet extends Applet } } - mouseEventMethods.handle(new Object[] { event }); + if (mouseEventMethods != null) { + mouseEventMethods.handle(new Object[] { event }); + } // this used to only be called on mouseMoved and mouseDragged // change it back if people run into trouble @@ -2671,7 +2658,9 @@ public class PApplet extends Applet key = event.getKeyChar(); keyCode = event.getKeyCode(); - keyEventMethods.handle(new Object[] { event }); + if (keyEventMethods != null) { + keyEventMethods.handle(new Object[] { event }); + } switch (event.getID()) { case KeyEvent.KEY_PRESSED: