'include_dirs': [
'../bench',
'../gm',
+ '../include/views',
'../include/private',
'../src/core',
'../src/effects',
],
'sources': [
'../gm/gm.cpp',
+ '../src/views/SkTouchGesture.cpp',
'<!@(python find.py ../tools/viewer "*.cpp")',
],
'dependencies': [
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) {
@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;
}
}
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,
// 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]() {
// set up first frame
fCurrentSlide = 0;
setupCurrentSlide(-1);
- updateMatrix();
fWindow->show();
}
} else {
fZoomScale = SK_Scalar1;
}
- this->updateMatrix();
}
-void Viewer::updateMatrix(){
+SkMatrix Viewer::computeMatrix() {
SkMatrix m;
m.reset();
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) {
matrix.setRectToRect(slideBounds, contentRect, SkMatrix::kCenter_ScaleToFit);
canvas->concat(matrix);
}
- canvas->concat(fLocalMatrix);
+ canvas->concat(computeMatrix());
fSlides[fCurrentSlide]->draw(canvas);
canvas->restoreToCount(count);
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;
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();
void drawStats(SkCanvas* canvas);
void changeZoomLevel(float delta);
- void updateMatrix();
+ SkMatrix computeMatrix();
sk_app::Window* fWindow;
sk_app::Window::BackendType fBackendType;
// transform data
- SkMatrix fLocalMatrix;
SkScalar fZoomCenterX;
SkScalar fZoomCenterY;
SkScalar fZoomLevel;
SkScalar fZoomScale;
sk_app::CommandSet fCommands;
+
+ SkTouchGesture fGesture;
};
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) {
}
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) {
#define Window_DEFINED
#include "DisplayParams.h"
-#include "SkTypes.h"
#include "SkRect.h"
+#include "SkTouchGesture.h"
+#include "SkTypes.h"
class SkCanvas;
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) {
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);
void* fKeyUserData;
OnMouseFunc fMouseFunc;
void* fMouseUserData;
+ OnTouchFunc fTouchFunc;
+ void* fTouchUserData;
OnPaintFunc fPaintFunc;
void* fPaintUserData;
- WindowContext* fWindowContext;
+ WindowContext* fWindowContext = nullptr;
};
} // namespace sk_app
detach();
}
-void Window_android::inval() { fSkiaAndroidApp->postMessage(Message(kContentInvalidated)); }
+void Window_android::inval() {
+ fSkiaAndroidApp->inval();
+}
} // namespace sk_app
#include <unistd.h>
#include <unordered_map>
+#include <android/input.h>
#include <android/keycodes.h>
#include <android/looper.h>
#include <android/native_window_jni.h>
{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);
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;
return 0;
}
case kContentInvalidated: {
+ SkAutoMutexAcquire ama(skiaAndroidApp->fMutex);
+ skiaAndroidApp->fIsContentInvalidated = false;
skiaAndroidApp->paintIfNeeded();
break;
}
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
}
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);
}
#include <android/native_window_jni.h>
+#include "../private/SkMutex.h"
#include "../Application.h"
#include "../Window.h"
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) {}
// 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;
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();