Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
surface_glue_android.cpp
Go to the documentation of this file.
1/*
2* Copyright 2016 Google Inc.
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
9
10#include <jni.h>
11#include <pthread.h>
12#include <stdio.h>
13#include <unistd.h>
14#include <unordered_map>
15
16#include <android/asset_manager.h>
17#include <android/asset_manager_jni.h>
18#include <android/input.h>
19#include <android/keycodes.h>
20#include <android/looper.h>
21#include <android/native_window_jni.h>
22
25#include "src/base/SkUTF.h"
29
30
31namespace sk_app {
32
33static void config_resource_mgr(JNIEnv* env, jobject assetManager) {
34 static AAssetManager* gAAssetManager = nullptr;
35 SkASSERT(assetManager);
36 gAAssetManager = AAssetManager_fromJava(env, assetManager);
37 SkASSERT(gAAssetManager);
38 gResourceFactory = [](const char* resource) -> sk_sp<SkData> {
39 if (!gAAssetManager) {
40 return nullptr;
41 }
42 SkString path = SkStringPrintf("resources/%s", resource);
43 AAsset* asset = AAssetManager_open(gAAssetManager, path.c_str(), AASSET_MODE_STREAMING);
44 if (!asset) {
45 return nullptr;
46 }
47 size_t size = SkToSizeT(AAsset_getLength(asset));
49 (void)AAsset_read(asset, data->writable_data(), size);
50 AAsset_close(asset);
51 return data;
52 };
53}
54
55static const int LOOPER_ID_MESSAGEPIPE = 1;
56
57static const std::unordered_map<int, skui::Key> ANDROID_TO_WINDOW_KEYMAP({
58 {AKEYCODE_SOFT_LEFT, skui::Key::kLeft },
59 {AKEYCODE_SOFT_RIGHT, skui::Key::kRight}
60});
61
62static const std::unordered_map<int, skui::InputState> ANDROID_TO_WINDOW_STATEMAP({
63 {AMOTION_EVENT_ACTION_DOWN, skui::InputState::kDown },
64 {AMOTION_EVENT_ACTION_POINTER_DOWN, skui::InputState::kDown },
65 {AMOTION_EVENT_ACTION_UP, skui::InputState::kUp },
66 {AMOTION_EVENT_ACTION_POINTER_UP, skui::InputState::kUp },
67 {AMOTION_EVENT_ACTION_MOVE, skui::InputState::kMove },
68 {AMOTION_EVENT_ACTION_CANCEL, skui::InputState::kUp },
69});
70
71SkiaAndroidApp::SkiaAndroidApp(JNIEnv* env, jobject androidApp) {
72 env->GetJavaVM(&fJavaVM);
73 fAndroidApp = env->NewGlobalRef(androidApp);
74 jclass cls = env->GetObjectClass(fAndroidApp);
75 fSetTitleMethodID = env->GetMethodID(cls, "setTitle", "(Ljava/lang/String;)V");
76 fSetStateMethodID = env->GetMethodID(cls, "setState", "(Ljava/lang/String;)V");
77 fNativeWindow = nullptr;
78 pthread_create(&fThread, nullptr, pthread_main, this);
79}
80
81SkiaAndroidApp::~SkiaAndroidApp() {
82 fPThreadEnv->DeleteGlobalRef(fAndroidApp);
83 if (fWindow) {
84 fWindow->detach();
85 }
86 if (fNativeWindow) {
87 ANativeWindow_release(fNativeWindow);
88 fNativeWindow = nullptr;
89 }
90 if (fApp) {
91 delete fApp;
92 }
93}
94
95void SkiaAndroidApp::setTitle(const char* title) const {
96 jstring titleString = fPThreadEnv->NewStringUTF(title);
97 fPThreadEnv->CallVoidMethod(fAndroidApp, fSetTitleMethodID, titleString);
98 fPThreadEnv->DeleteLocalRef(titleString);
99}
100
101void SkiaAndroidApp::setUIState(const char* state) const {
102 jstring jstr = fPThreadEnv->NewStringUTF(state);
103 fPThreadEnv->CallVoidMethod(fAndroidApp, fSetStateMethodID, jstr);
104 fPThreadEnv->DeleteLocalRef(jstr);
105}
106
108 SkDEBUGCODE(auto writeSize =) write(fPipes[1], &message, sizeof(message));
109 SkASSERT(writeSize == sizeof(message));
110}
111
113 SkDEBUGCODE(auto readSize =) read(fPipes[0], message, sizeof(Message));
114 SkASSERT(readSize == sizeof(Message));
115}
116
117int SkiaAndroidApp::message_callback(int fd, int events, void* data) {
118 auto skiaAndroidApp = (SkiaAndroidApp*)data;
120 skiaAndroidApp->readMessage(&message);
121 SkASSERT(message.fType != kUndefined);
122
123 switch (message.fType) {
124 case kDestroyApp: {
125 delete skiaAndroidApp;
126 pthread_exit(nullptr);
127 }
128 case kContentInvalidated: {
129 ((Window_android*)skiaAndroidApp->fWindow)->paintIfNeeded();
130 break;
131 }
132 case kSurfaceCreated: {
133 SkASSERT(!skiaAndroidApp->fNativeWindow && message.fNativeWindow);
134 skiaAndroidApp->fNativeWindow = message.fNativeWindow;
135 auto window_android = (Window_android*)skiaAndroidApp->fWindow;
136 window_android->initDisplay(skiaAndroidApp->fNativeWindow);
137 ((Window_android*)skiaAndroidApp->fWindow)->paintIfNeeded();
138 break;
139 }
140 case kSurfaceChanged: {
141 SkASSERT(message.fNativeWindow);
142 int width = ANativeWindow_getWidth(skiaAndroidApp->fNativeWindow);
143 int height = ANativeWindow_getHeight(skiaAndroidApp->fNativeWindow);
144 auto window_android = (Window_android*)skiaAndroidApp->fWindow;
145 if (message.fNativeWindow != skiaAndroidApp->fNativeWindow) {
146 window_android->onDisplayDestroyed();
147 ANativeWindow_release(skiaAndroidApp->fNativeWindow);
148 skiaAndroidApp->fNativeWindow = message.fNativeWindow;
149 window_android->initDisplay(skiaAndroidApp->fNativeWindow);
150 }
151 window_android->onResize(width, height);
152 window_android->paintIfNeeded();
153 break;
154 }
155 case kSurfaceDestroyed: {
156 if (skiaAndroidApp->fNativeWindow) {
157 auto window_android = (Window_android*)skiaAndroidApp->fWindow;
158 window_android->onDisplayDestroyed();
159 ANativeWindow_release(skiaAndroidApp->fNativeWindow);
160 skiaAndroidApp->fNativeWindow = nullptr;
161 }
162 break;
163 }
164 case kKeyPressed: {
165 auto it = ANDROID_TO_WINDOW_KEYMAP.find(message.fKeycode);
167 // No modifier is supported so far
168 skiaAndroidApp->fWindow->onKey(it->second, skui::InputState::kDown, skui::ModifierKey::kNone);
169 skiaAndroidApp->fWindow->onKey(it->second, skui::InputState::kUp, skui::ModifierKey::kNone);
170 break;
171 }
172 case kTouched: {
173 auto it = ANDROID_TO_WINDOW_STATEMAP.find(message.fTouchState);
174 if (it != ANDROID_TO_WINDOW_STATEMAP.end()) {
175 skiaAndroidApp->fWindow->onTouch(message.fTouchOwner, it->second, message.fTouchX,
176 message.fTouchY);
177 } else {
178 SkDebugf("Unknown Touch State: %d\n", message.fTouchState);
179 }
180 break;
181 }
182 case kUIStateChanged: {
183 skiaAndroidApp->fWindow->onUIStateChanged(*message.stateName, *message.stateValue);
184 delete message.stateName;
185 delete message.stateValue;
186 break;
187 }
188 default: {
189 // do nothing
190 }
191 }
192
193 return 1; // continue receiving callbacks
194}
195
196void* SkiaAndroidApp::pthread_main(void* arg) {
197 SkDebugf("pthread_main begins");
198
199 auto skiaAndroidApp = (SkiaAndroidApp*)arg;
200
201 // Because JNIEnv is thread sensitive, we need AttachCurrentThread to set our fPThreadEnv
202 skiaAndroidApp->fJavaVM->AttachCurrentThread(&(skiaAndroidApp->fPThreadEnv), nullptr);
203
204 ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
205 pipe(skiaAndroidApp->fPipes);
206 ALooper_addFd(looper, skiaAndroidApp->fPipes[0], LOOPER_ID_MESSAGEPIPE, ALOOPER_EVENT_INPUT,
207 message_callback, skiaAndroidApp);
208
209 static const char* gCmdLine[] = {
210 "viewer",
211 // TODO: figure out how to use am start with extra params to pass in additional arguments at
212 // runtime. Or better yet make an in app switch to enable
213 // "--atrace",
214 };
215
216 skiaAndroidApp->fApp = Application::Create(std::size(gCmdLine),
217 const_cast<char**>(gCmdLine),
218 skiaAndroidApp);
219
220 while (true) {
221 const int ident = ALooper_pollAll(0, nullptr, nullptr, nullptr);
222
223 if (ident >= 0) {
224 SkDebugf("Unhandled ALooper_pollAll ident=%d !", ident);
225 } else {
226 skiaAndroidApp->fApp->onIdle();
227 }
228 }
229}
230
231extern "C" // extern "C" is needed for JNI (although the method itself is in C++)
232 JNIEXPORT jlong JNICALL
234 jobject application,
235 jobject assetManager) {
236 config_resource_mgr(env, assetManager);
237 SkiaAndroidApp* skiaAndroidApp = new SkiaAndroidApp(env, application);
238 return (jlong)((size_t)skiaAndroidApp);
239}
240
242 JNIEnv* env, jobject application, jlong handle) {
243 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
244 skiaAndroidApp->postMessage(Message(kDestroyApp));
245}
246
248 JNIEnv* env, jobject activity, jlong handle, jobject surface) {
249 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
251 message.fNativeWindow = ANativeWindow_fromSurface(env, surface);
252 skiaAndroidApp->postMessage(message);
253}
254
256 JNIEnv* env, jobject activity, jlong handle, jobject surface) {
257 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
259 message.fNativeWindow = ANativeWindow_fromSurface(env, surface);
260 skiaAndroidApp->postMessage(message);
261}
262
264 JNIEnv* env, jobject activity, jlong handle) {
265 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
266 skiaAndroidApp->postMessage(Message(kSurfaceDestroyed));
267}
268
269extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onKeyPressed(JNIEnv* env,
270 jobject activity,
271 jlong handle,
272 jint keycode) {
273 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
275 message.fKeycode = keycode;
276 skiaAndroidApp->postMessage(message);
277}
278
279extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onTouched(
280 JNIEnv* env, jobject activity, jlong handle, jint owner, jint state, jfloat x, jfloat y) {
281 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
283 message.fTouchOwner = owner;
284 message.fTouchState = state;
285 message.fTouchX = x;
286 message.fTouchY = y;
287 skiaAndroidApp->postMessage(message);
288}
289
291 JNIEnv* env, jobject activity, jlong handle, jstring stateName, jstring stateValue) {
292 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
294 const char* nameChars = env->GetStringUTFChars(stateName, nullptr);
295 const char* valueChars = env->GetStringUTFChars(stateValue, nullptr);
296 message.stateName = new SkString(nameChars);
297 message.stateValue = new SkString(valueChars);
298 skiaAndroidApp->postMessage(message);
299 env->ReleaseStringUTFChars(stateName, nameChars);
300 env->ReleaseStringUTFChars(stateValue, valueChars);
301}
302
303} // namespace sk_app
sk_sp< SkData >(* gResourceFactory)(const char *)
Definition Resources.cpp:21
#define SkASSERT(cond)
Definition SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static bool read(SkStream *stream, void *buffer, size_t amount)
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
constexpr size_t SkToSizeT(S x)
Definition SkTo.h:31
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition SkData.cpp:116
static Application * Create(int argc, char **argv, void *platformData)
void detach()
Definition Window.cpp:25
VkSurfaceKHR surface
Definition main.cc:49
AtkStateType state
Win32Message message
double y
double x
Definition __init__.py:1
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceChanged(JNIEnv *env, jobject activity, jlong handle, jobject surface)
static const int LOOPER_ID_MESSAGEPIPE
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onKeyPressed(JNIEnv *env, jobject activity, jlong handle, jint keycode)
static void config_resource_mgr(JNIEnv *env, jobject assetManager)
static const std::unordered_map< int, skui::InputState > ANDROID_TO_WINDOW_STATEMAP({ {AMOTION_EVENT_ACTION_DOWN, skui::InputState::kDown }, {AMOTION_EVENT_ACTION_POINTER_DOWN, skui::InputState::kDown }, {AMOTION_EVENT_ACTION_UP, skui::InputState::kUp }, {AMOTION_EVENT_ACTION_POINTER_UP, skui::InputState::kUp }, {AMOTION_EVENT_ACTION_MOVE, skui::InputState::kMove }, {AMOTION_EVENT_ACTION_CANCEL, skui::InputState::kUp }, })
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerApplication_destroyNativeApp(JNIEnv *env, jobject application, jlong handle)
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onUIStateChanged(JNIEnv *env, jobject activity, jlong handle, jstring stateName, jstring stateValue)
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceCreated(JNIEnv *env, jobject activity, jlong handle, jobject surface)
JNIEXPORT jlong JNICALL Java_org_skia_viewer_ViewerApplication_createNativeApp(JNIEnv *env, jobject application, jobject assetManager)
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onTouched(JNIEnv *env, jobject activity, jlong handle, jint owner, jint state, jfloat x, jfloat y)
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceDestroyed(JNIEnv *env, jobject activity, jlong handle)
static const std::unordered_map< int, skui::Key > ANDROID_TO_WINDOW_KEYMAP({ {AKEYCODE_SOFT_LEFT, skui::Key::kLeft }, {AKEYCODE_SOFT_RIGHT, skui::Key::kRight} })
int32_t height
int32_t width
void write(SkWStream *wStream, const T &text)
Definition skqp.cpp:188
void setUIState(const char *state) const
SkiaAndroidApp(JNIEnv *env, jobject androidApp)
void setTitle(const char *title) const
void readMessage(Message *message) const
void postMessage(const Message &message) const