Implement touch control
authorliyuqian <liyuqian@google.com>
Tue, 17 May 2016 19:44:20 +0000 (12:44 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 17 May 2016 19:44:20 +0000 (12:44 -0700)
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1982643004

Review-Url: https://codereview.chromium.org/1982643004

gyp/viewer.gyp
platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerActivity.java
tools/viewer/Viewer.cpp
tools/viewer/Viewer.h
tools/viewer/sk_app/Window.cpp
tools/viewer/sk_app/Window.h
tools/viewer/sk_app/android/Window_android.cpp
tools/viewer/sk_app/android/surface_glue_android.cpp
tools/viewer/sk_app/android/surface_glue_android.h

index d8082ef..84daa8d 100644 (file)
@@ -18,6 +18,7 @@
       'include_dirs': [
         '../bench',
         '../gm',
+        '../include/views',
         '../include/private',
         '../src/core',
         '../src/effects',
@@ -28,6 +29,7 @@
       ],
       'sources': [
         '../gm/gm.cpp',
+        '../src/views/SkTouchGesture.cpp',
         '<!@(python find.py ../tools/viewer "*.cpp")',
       ],
       'dependencies': [
index d6fe710..49f711d 100644 (file)
@@ -32,6 +32,7 @@ public class ViewerActivity
     private native void onSurfaceChanged(long handle, Surface surface);
     private native void onSurfaceDestroyed(long handle);
     private native void onKeyPressed(long handle, int keycode);
+    private native void onTouched(long handle, int owner, int state, float x, float y);
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
@@ -96,6 +97,14 @@ public class ViewerActivity
 
     @Override
     public boolean onTouch(View v, MotionEvent event) {
-        return false; // TODO pass the touch event to native code
+        int count = event.getPointerCount();
+        for (int i = 0; i < count; i++) {
+            final float x = event.getX(i);
+            final float y = event.getY(i);
+            final int owner = event.getPointerId(i);
+            int action = event.getAction() & MotionEvent.ACTION_MASK;
+            onTouched(mApplication.getNativeHandle(), owner, action, x, y);
+        }
+        return true;
     }
 }
index 644dd22..7f18652 100644 (file)
@@ -28,6 +28,13 @@ static void on_paint_handler(SkCanvas* canvas, void* userData) {
     return vv->onPaint(canvas);
 }
 
+static bool on_touch_handler(int owner, Window::InputState state, float x, float y, void* userData)
+{
+    Viewer* viewer = reinterpret_cast<Viewer*>(userData);
+
+    return viewer->onTouch(owner, state, x, y);
+}
+
 DEFINE_bool2(fullscreen, f, true, "Run fullscreen.");
 DEFINE_string(key, "", "Space-separated key/value pairs to add to JSON identifying this builder.");
 DEFINE_string2(match, m, nullptr,
@@ -71,6 +78,7 @@ Viewer::Viewer(int argc, char** argv, void* platformData)
     // register callbacks
     fCommands.attach(fWindow);
     fWindow->registerPaintFunc(on_paint_handler, this);
+    fWindow->registerTouchFunc(on_touch_handler, this);
 
     // add key-bindings
     fCommands.addCommand('s', "Overlays", "Toggle stats display", [this]() {
@@ -134,7 +142,6 @@ Viewer::Viewer(int argc, char** argv, void* platformData)
     // set up first frame
     fCurrentSlide = 0;
     setupCurrentSlide(-1);
-    updateMatrix();
 
     fWindow->show();
 }
@@ -228,10 +235,9 @@ void Viewer::changeZoomLevel(float delta) {
     } else {
         fZoomScale = SK_Scalar1;
     }
-    this->updateMatrix();
 }
 
-void Viewer::updateMatrix(){
+SkMatrix Viewer::computeMatrix() {
     SkMatrix m;
     m.reset();
 
@@ -247,12 +253,10 @@ void Viewer::updateMatrix(){
         m.postTranslate(cx, cy);
     }
 
-    // TODO: add gesture support
-    // Apply any gesture matrix
-    //m.preConcat(fGesture.localM());
-    //m.preConcat(fGesture.globalM());
+    m.preConcat(fGesture.localM());
+    m.preConcat(fGesture.globalM());
 
-    fLocalMatrix = m;
+    return m;
 }
 
 void Viewer::onPaint(SkCanvas* canvas) {
@@ -273,7 +277,7 @@ void Viewer::onPaint(SkCanvas* canvas) {
         matrix.setRectToRect(slideBounds, contentRect, SkMatrix::kCenter_ScaleToFit);
         canvas->concat(matrix);
     }
-    canvas->concat(fLocalMatrix);
+    canvas->concat(computeMatrix());
 
     fSlides[fCurrentSlide]->draw(canvas);
     canvas->restoreToCount(count);
@@ -284,6 +288,26 @@ void Viewer::onPaint(SkCanvas* canvas) {
     fCommands.drawHelp(canvas);
 }
 
+bool Viewer::onTouch(int owner, Window::InputState state, float x, float y) {
+    void* castedOwner = reinterpret_cast<void*>(owner);
+    switch (state) {
+        case Window::kUp_InputState: {
+            fGesture.touchEnd(castedOwner);
+            break;
+        }
+        case Window::kDown_InputState: {
+            fGesture.touchBegin(castedOwner, x, y);
+            break;
+        }
+        case Window::kMove_InputState: {
+            fGesture.touchMoved(castedOwner, x, y);
+            break;
+        }
+    }
+    fWindow->inval();
+    return true;
+}
+
 void Viewer::drawStats(SkCanvas* canvas) {
     static const float kPixelPerMS = 2.0f;
     static const int kDisplayWidth = 130;
index 3579e4d..c785cff 100644 (file)
@@ -24,6 +24,7 @@ public:
 
     void onPaint(SkCanvas* canvas);
     void onIdle(double ms) override;
+    bool onTouch(int owner, sk_app::Window::InputState state, float x, float y);
 
 private:
     void initSlides();
@@ -33,7 +34,7 @@ private:
     void drawStats(SkCanvas* canvas);
 
     void changeZoomLevel(float delta);
-    void updateMatrix();
+    SkMatrix computeMatrix();
 
     sk_app::Window*        fWindow;
 
@@ -50,13 +51,14 @@ private:
     sk_app::Window::BackendType fBackendType;
 
     // transform data
-    SkMatrix               fLocalMatrix;
     SkScalar               fZoomCenterX;
     SkScalar               fZoomCenterY;
     SkScalar               fZoomLevel;
     SkScalar               fZoomScale;
 
     sk_app::CommandSet     fCommands;
+
+    SkTouchGesture         fGesture;
 };
 
 
index dc0bf99..0a7bcf8 100644 (file)
@@ -27,11 +27,17 @@ static bool default_mouse_func(int x, int y, Window::InputState state, uint32_t
     return false;
 }
 
+static bool default_touch_func(int owner, Window::InputState state, float x, float y,
+                               void* userData) {
+    return false;
+}
+
 static void default_paint_func(SkCanvas*, void* userData) {}
 
 Window::Window() : fCharFunc(default_char_func)
                  , fKeyFunc(default_key_func)
                  , fMouseFunc(default_mouse_func)
+                 , fTouchFunc(default_touch_func)
                  , fPaintFunc(default_paint_func) {
 }
 
@@ -52,6 +58,10 @@ bool Window::onMouse(int x, int y, InputState state, uint32_t modifiers) {
     return fMouseFunc(x, y, state, modifiers, fMouseUserData);
 }
 
+bool Window::onTouch(int owner, InputState state, float x, float y) {
+    return fTouchFunc(owner, state, x, y, fTouchUserData);
+}
+
 void Window::onPaint() {
     sk_sp<SkSurface> backbuffer = fWindowContext->getBackbufferSurface();
     if (backbuffer) {
index 5b41f87..63d5e19 100644 (file)
@@ -9,8 +9,9 @@
 #define Window_DEFINED
 
 #include "DisplayParams.h"
-#include "SkTypes.h"
 #include "SkRect.h"
+#include "SkTouchGesture.h"
+#include "SkTypes.h"
 
 class SkCanvas;
 
@@ -104,6 +105,7 @@ public:
     typedef bool(*OnCharFunc)(SkUnichar c, uint32_t modifiers, void* userData);
     typedef bool(*OnKeyFunc)(Key key, InputState state, uint32_t modifiers, void* userData);
     typedef bool(*OnMouseFunc)(int x, int y, InputState state, uint32_t modifiers, void* userData);
+    typedef bool(*OnTouchFunc)(int owner, InputState state, float x, float y, void* userData);
     typedef void(*OnPaintFunc)(SkCanvas*, void* userData);
 
     void registerCharFunc(OnCharFunc func, void* userData) {
@@ -126,9 +128,15 @@ public:
         fPaintUserData = userData;
     }
 
+    void registerTouchFunc(OnTouchFunc func, void* userData) {
+        fTouchFunc = func;
+        fTouchUserData = userData;
+    }
+
     bool onChar(SkUnichar c, uint32_t modifiers);
     bool onKey(Key key, InputState state, uint32_t modifiers);
     bool onMouse(int x, int y, InputState state, uint32_t modifiers);
+    bool onTouch(int owner, InputState state, float x, float y);  // multi-owner = multi-touch
     void onPaint();
     void onResize(uint32_t width, uint32_t height);
 
@@ -150,10 +158,12 @@ protected:
     void*        fKeyUserData;
     OnMouseFunc  fMouseFunc;
     void*        fMouseUserData;
+    OnTouchFunc  fTouchFunc;
+    void*        fTouchUserData;
     OnPaintFunc  fPaintFunc;
     void*        fPaintUserData;
 
-    WindowContext* fWindowContext;
+    WindowContext* fWindowContext = nullptr;
 };
 
 }   // namespace sk_app
index 21998d5..106c40b 100644 (file)
@@ -64,6 +64,8 @@ void Window_android::onDisplayDestroyed() {
     detach();
 }
 
-void Window_android::inval() { fSkiaAndroidApp->postMessage(Message(kContentInvalidated)); }
+void Window_android::inval() {
+    fSkiaAndroidApp->inval();
+}
 
 }   // namespace sk_app
index 3d8617f..e871558 100644 (file)
@@ -13,6 +13,7 @@
 #include <unistd.h>
 #include <unordered_map>
 
+#include <android/input.h>
 #include <android/keycodes.h>
 #include <android/looper.h>
 #include <android/native_window_jni.h>
@@ -31,6 +32,15 @@ static const std::unordered_map<int, Window::Key> ANDROID_TO_WINDOW_KEYMAP({
     {AKEYCODE_SOFT_RIGHT, Window::Key::kRight}
 });
 
+static const std::unordered_map<int, Window::InputState> ANDROID_TO_WINDOW_STATEMAP({
+    {AMOTION_EVENT_ACTION_DOWN, Window::kDown_InputState},
+    {AMOTION_EVENT_ACTION_POINTER_DOWN, Window::kDown_InputState},
+    {AMOTION_EVENT_ACTION_UP, Window::kUp_InputState},
+    {AMOTION_EVENT_ACTION_POINTER_UP, Window::kUp_InputState},
+    {AMOTION_EVENT_ACTION_MOVE, Window::kMove_InputState},
+    {AMOTION_EVENT_ACTION_CANCEL, Window::kUp_InputState},
+});
+
 SkiaAndroidApp::SkiaAndroidApp(JNIEnv* env, jobject androidApp) {
     env->GetJavaVM(&fJavaVM);
     fAndroidApp = env->NewGlobalRef(androidApp);
@@ -76,6 +86,14 @@ void SkiaAndroidApp::readMessage(Message* message) const {
     SkASSERT(readSize == sizeof(Message));
 }
 
+void SkiaAndroidApp::inval() {
+    SkAutoMutexAcquire ama(fMutex);
+    if (!fIsContentInvalidated) {
+        postMessage(Message(kContentInvalidated));
+        fIsContentInvalidated = true;
+    }
+}
+
 int SkiaAndroidApp::message_callback(int fd, int events, void* data) {
     auto skiaAndroidApp = (SkiaAndroidApp*)data;
     Message message;
@@ -90,6 +108,8 @@ int SkiaAndroidApp::message_callback(int fd, int events, void* data) {
             return 0;
         }
         case kContentInvalidated: {
+            SkAutoMutexAcquire ama(skiaAndroidApp->fMutex);
+            skiaAndroidApp->fIsContentInvalidated = false;
             skiaAndroidApp->paintIfNeeded();
             break;
         }
@@ -121,13 +141,20 @@ int SkiaAndroidApp::message_callback(int fd, int events, void* data) {
             break;
         }
         case kKeyPressed: {
-            auto it = ANDROID_TO_WINDOW_KEYMAP.find(message.keycode);
+            auto it = ANDROID_TO_WINDOW_KEYMAP.find(message.fKeycode);
             SkASSERT(it != ANDROID_TO_WINDOW_KEYMAP.end());
             // No modifier is supported so far
             skiaAndroidApp->fWindow->onKey(it->second, Window::kDown_InputState, 0);
             skiaAndroidApp->fWindow->onKey(it->second, Window::kUp_InputState, 0);
             break;
         }
+        case kTouched: {
+            auto it = ANDROID_TO_WINDOW_STATEMAP.find(message.fTouchState);
+            SkASSERT(it != ANDROID_TO_WINDOW_STATEMAP.end());
+            skiaAndroidApp->fWindow->onTouch(message.fTouchOwner, it->second, message.fTouchX,
+                                             message.fTouchY);
+            break;
+        }
         default: {
             // do nothing
         }
@@ -203,7 +230,18 @@ extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onKeyPress
                                                                                    jint keycode) {
     auto skiaAndroidApp = (SkiaAndroidApp*)handle;
     Message message(kKeyPressed);
-    message.keycode = keycode;
+    message.fKeycode = keycode;
+    skiaAndroidApp->postMessage(message);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onTouched(
+    JNIEnv* env, jobject activity, jlong handle, jint owner, jfloat x, jfloat y, jint state) {
+    auto skiaAndroidApp = (SkiaAndroidApp*)handle;
+    Message message(kTouched);
+    message.fTouchOwner = owner;
+    message.fTouchState = state;
+    message.fTouchX = x;
+    message.fTouchY = y;
     skiaAndroidApp->postMessage(message);
 }
 
index 1ce0667..7ffba30 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <android/native_window_jni.h>
 
+#include "../private/SkMutex.h"
 #include "../Application.h"
 #include "../Window.h"
 
@@ -24,13 +25,16 @@ enum MessageType {
     kSurfaceDestroyed,
     kDestroyApp,
     kContentInvalidated,
-    kKeyPressed
+    kKeyPressed,
+    kTouched
 };
 
 struct Message {
     MessageType fType = kUndefined;
     ANativeWindow* fNativeWindow = nullptr;
-    int keycode = 0;
+    int fKeycode = 0;
+    int fTouchOwner, fTouchState;
+    float fTouchX, fTouchY;
 
     Message() {}
     Message(MessageType t) : fType(t) {}
@@ -49,6 +53,10 @@ struct SkiaAndroidApp {
 
     // This must be called in SkiaAndroidApp's own pthread because the JNIEnv is thread sensitive
     void setTitle(const char* title) const;
+
+    // This posts a kContentInvalidated message if there's no such message currently in the queue
+    void inval();
+
 private:
     pthread_t fThread;
     ANativeWindow* fNativeWindow;
@@ -57,6 +65,9 @@ private:
     JNIEnv* fPThreadEnv;
     jmethodID fSetTitleMethodID;
 
+    bool fIsContentInvalidated = false;  // use this to avoid duplicate invalidate events
+    SkMutex fMutex;
+
     // This must be called in SkiaAndroidApp's own pthread because the JNIEnv is thread sensitive
     ~SkiaAndroidApp();