From 5fc27ebb143a13fcd3996b9d748012307f7f9db2 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Thu, 25 Jul 2019 08:11:48 +0200 Subject: [PATCH] fix some lambda ref, use global android_app variable instead of passing as argument, fix clipboard utf issue by using byte array --- android/src/cpp/main.cpp | 207 ++++++++---------- .../com/omixlab/panopainter/MainActivity.java | 31 +++ src/app.cpp | 12 +- src/app_events.cpp | 18 +- 4 files changed, 143 insertions(+), 125 deletions(-) diff --git a/android/src/cpp/main.cpp b/android/src/cpp/main.cpp index 82e9d53..7aeeadc 100644 --- a/android/src/cpp/main.cpp +++ b/android/src/cpp/main.cpp @@ -83,7 +83,7 @@ std::string utf8chr(int cp) // see https://stackoverflow.com/questions/21124051/receive-complete-android-unicode-input-in-c-c // see http://www.zedwood.com/article/cpp-utf8-char-to-codepoint -int GetUnicodeChar(struct android_app* app, int eventType, int keyCode, int metaState) +int GetUnicodeChar(int eventType, int keyCode, int metaState) { #define COMBINING_ACCENT 0x80000000 #define COMBINING_ACCENT_MASK 0x7fffffff @@ -128,49 +128,42 @@ void android_detach_jni() g_engine.app->activity->vm->DetachCurrentThread(); } -void android_async_lock(struct engine* engine) +void android_async_lock() { mutex.lock(); if (mutex_count == 0) - eglMakeCurrent(engine->display, engine->surface, engine->surface, engine->context); + eglMakeCurrent(g_engine.display, g_engine.surface, g_engine.surface, g_engine.context); mutex_count++; } -bool android_async_trylock(struct engine* engine) +bool android_async_trylock() { if (!mutex.try_lock()) return false; if (mutex_count == 0) - eglMakeCurrent(engine->display, engine->surface, engine->surface, engine->context); + eglMakeCurrent(g_engine.display, g_engine.surface, g_engine.surface, g_engine.context); mutex_count++; return true; } -void android_async_swap(struct engine* engine) +void android_async_swap() { - eglSwapBuffers(engine->display, engine->surface); + eglSwapBuffers(g_engine.display, g_engine.surface); } -void android_async_unlock(struct engine* engine) +void android_async_unlock() { mutex_count--; if (mutex_count == 0) - eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglMakeCurrent(g_engine.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); mutex.unlock(); } -struct locker -{ - struct engine* e; - locker(struct engine* _e){ e = _e; android_async_lock(e); } - ~locker(){ android_async_unlock(e); } -}; - // see https://groups.google.com/forum/#!topic/android-ndk/Tk3g00wLKhk -void displayKeyboard(android_app* mApplication, bool pShow) +void displayKeyboard(bool pShow) { // Retrieves NativeActivity. - jobject lNativeActivity = mApplication->activity->clazz; + jobject lNativeActivity = g_engine.app->activity->clazz; jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity); // Retrieves Context.INPUT_METHOD_SERVICE. @@ -284,42 +277,82 @@ JNIEXPORT void JNICALL Java_com_omixlab_panopainter_MainActivity_contentRectChan } } -void android_pick_file(android_app* mApplication, std::function callback) +void android_pick_file(std::function callback) { pick_file_callback = callback; - - // Retrieves NativeActivity. - jobject lNativeActivity = mApplication->activity->clazz; - jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity); - - jmethodID MethodPickFile = jni->GetMethodID(ClassNativeActivity, "pickFile", "()V"); - jni->CallVoidMethod(lNativeActivity, MethodPickFile); + jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz); + jmethodID method = jni->GetMethodID(clazz, "pickFile", "()V"); + jni->CallVoidMethod(g_engine.app->activity->clazz, method); } -float get_display_density(android_app* mApplication) +float get_display_density() { - // Retrieves NativeActivity. - jobject lNativeActivity = mApplication->activity->clazz; - jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity); - - jmethodID MethodPickFile = jni->GetMethodID(ClassNativeActivity, "getDensity", "()F"); - float density = jni->CallFloatMethod(lNativeActivity, MethodPickFile); - return density; + jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz); + jmethodID method = jni->GetMethodID(clazz, "getDensity", "()F"); + return jni->CallFloatMethod(g_engine.app->activity->clazz, method); } -std::string get_data_path(android_app* mApplication) +std::string get_data_path() { - // Retrieves NativeActivity. - jobject lNativeActivity = mApplication->activity->clazz; - jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity); + jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz); + jmethodID method = jni->GetMethodID(clazz, "getDataPath", "()Ljava/lang/String;"); + jstring js = (jstring)jni->CallObjectMethod(g_engine.app->activity->clazz, method); - jmethodID MethodPickFile = jni->GetMethodID(ClassNativeActivity, "getDataPath", "()Ljava/lang/String;"); - jstring path = (jstring)jni->CallObjectMethod(lNativeActivity, MethodPickFile); + const char* utf = jni->GetStringUTFChars(js, nullptr); + std::string str = utf; // create a copy + jni->ReleaseStringUTFChars(js, utf); + return str; +} - const char* path_utf = jni->GetStringUTFChars(path, nullptr); - std::string file_path = path_utf; // create a copy - jni->ReleaseStringUTFChars(path, path_utf); - return file_path; +// source: https://github.com/opencollab/giws/issues/4 +jstring JniStringFromUTF8(const std::string& s) +{ + int len = s.size(); + jbyteArray bytes = jni->NewByteArray(len); + if (bytes == 0) + { + LOG("jni->NewByteArray failed"); + return 0; + } + + jni->SetByteArrayRegion(bytes, 0, len, (jbyte *)s.c_str()); + + jclass string_class = jni->FindClass("java/lang/String"); + jmethodID stringConstructor = jni->GetMethodID(string_class, "", "([B)V" ); + if (stringConstructor == NULL) + { + LOG("JniStringFromUTF8 new String(byte[]) failed"); + jni->DeleteLocalRef(bytes); + return 0; + } + + jstring result = (jstring)jni->NewObject(string_class, stringConstructor, bytes); + jni->DeleteLocalRef(bytes); + + return result; //NOTE: jstring must be freed using: curEnv->DeleteLocalRef(result); +} + +std::string android_get_clipboard() +{ + jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz); + jmethodID method = jni->GetMethodID(clazz, "getClipboardText", "()Ljava/lang/String;"); + jstring js = (jstring)jni->CallObjectMethod(g_engine.app->activity->clazz, method); + + const char* utf = jni->GetStringUTFChars(js, nullptr); + std::string str = utf; // create a copy + jni->ReleaseStringUTFChars(js, utf); + return str; +} + +bool android_set_clipboard(const std::string& s) +{ + jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz); + jmethodID method = jni->GetMethodID(clazz, "setClipboardText", "(Ljava/lang/String;)Z"); + jstring js = JniStringFromUTF8(s); + if (!js) return false; + jboolean success = jni->CallBooleanMethod(g_engine.app->activity->clazz, method, js); + jni->DeleteLocalRef(js); + return success; } /** @@ -511,6 +544,9 @@ static int engine_init_display(struct engine* engine) { { LOG("RESUME APP"); App::I->and_app = engine->app; + LOG("release egl context"); + eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + mutex.unlock(); return 0; } @@ -610,7 +646,7 @@ static int engine_init_display(struct engine* engine) { //std::string base_path = engine->app->activity->externalDataPath ? // engine->app->activity->externalDataPath : get_data_path(engine->app); if (App::I->data_path.empty() || App::I->data_path == ".") - App::I->data_path = get_data_path(engine->app); + App::I->data_path = get_data_path(); LOG("data_path %s", App::I->data_path.c_str()); @@ -625,7 +661,7 @@ static int engine_init_display(struct engine* engine) { App::I->has_vr = true; App::I->vr_only = true; #else - float density = get_display_density(engine->app); + float density = get_display_density(); LOG("density %f", density); App::I->zoom = density;// / 1.5; App::I->width = w; @@ -644,59 +680,13 @@ static int engine_init_display(struct engine* engine) { return 0; } -/** - * Just the current frame in the display. - */ -static void engine_draw_frame(struct engine* engine) { - return; - static auto start = std::chrono::high_resolution_clock::now(); - static float elapsed = 0; - static float elapsed_1s = 0; - static int rendered_frames = 0; - locker _lock(engine); - - if (engine->display == EGL_NO_DISPLAY) - return; - - auto now = std::chrono::high_resolution_clock::now(); - auto dt = std::chrono::duration(now - start); - start = now; - elapsed += dt.count(); - elapsed_1s += dt.count(); - App::I->tick(dt.count()); - - if (elapsed_1s > 1.f) - { - //LOG("vr %d fps", rendered_frames); - elapsed_1s = 0; - rendered_frames = 0; - } - - const float fps60 = 1.f / 60.f; - if (elapsed < fps60) - return; - - rendered_frames++; - -#ifdef __QUEST__ - App::I->update(elapsed); - oculus_draw(dt.count()); -#else - if (!(App::I->redraw || App::I->animate)) - return; - - App::I->clear(); - App::I->update(elapsed); - - eglSwapBuffers(engine->display, engine->surface); -#endif - elapsed = 0; -} - /** * Tear down the EGL context currently associated with the display. */ static void engine_term_display(struct engine* engine) { + LOG("flush render thread"); + App::I->render_sync(); + mutex.lock(); if (engine->display != EGL_NO_DISPLAY) { eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); // if (engine->context != EGL_NO_CONTEXT) { @@ -794,10 +784,10 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) p0.pos.x = AMotionEvent_getX(event, 0); p0.pos.y = AMotionEvent_getY(event, 0); //LOG("second down"); - App::I->ui_task_async([=] { - if (tracked == 1) + App::I->ui_task_async([t=tracked, _p0=p0, _p1=p1] { + if (t == 1) App::I->mouse_cancel(0); - App::I->gesture_start(p0.pos, p1.pos); + App::I->gesture_start(_p0.pos, _p1.pos); }); tracked = 2; } @@ -875,8 +865,8 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) float y = AMotionEvent_getY(event, 1); p1.pos = {x, y}; } - App::I->ui_task_async([=] { - App::I->gesture_move(p0.pos, p1.pos); + App::I->ui_task_async([_p0=p0, _p1=p1] { + App::I->gesture_move(_p0.pos, _p1.pos); }); } return 1; @@ -894,7 +884,7 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) int32_t key_val = AKeyEvent_getKeyCode(event); int key = AKeyEvent_getKeyCode(event); int metaState = AKeyEvent_getMetaState(event); - int uniValue = GetUnicodeChar(app, action, key, metaState); + int uniValue = GetUnicodeChar(action, key, metaState); switch (action) { case AKEY_EVENT_ACTION_MULTIPLE: @@ -945,10 +935,8 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd) { break; case APP_CMD_INIT_WINDOW: // The window is being shown, get it ready. - if (engine->app->window != NULL) { + if (engine->app->window != NULL) engine_init_display(engine); - engine_draw_frame(engine); - } break; case APP_CMD_TERM_WINDOW: // The window is being hidden or closed, clean it up. @@ -980,7 +968,6 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd) { } // Also stop animating. engine->animating = 0; - engine_draw_frame(engine); */ break; case APP_CMD_WINDOW_REDRAW_NEEDED: @@ -1033,7 +1020,7 @@ void android_main(struct android_app* state) { #endif // Note that AttachCurrentThread will reset the thread name. - prctl(PR_SET_NAME, (long)"PanoPainter Main", 0, 0, 0); + prctl(PR_SET_NAME, (long)"PP Main", 0, 0, 0); // Prepare to monitor accelerometer /* @@ -1091,10 +1078,8 @@ void android_main(struct android_app* state) { if (g_engine.display == EGL_NO_DISPLAY || ident == ALOOPER_POLL_CALLBACK) continue; - if (ident == ALOOPER_POLL_TIMEOUT){ + if (ident == ALOOPER_POLL_TIMEOUT) App::I->redraw = true; - engine_draw_frame(&g_engine); - } // Check if we are exiting. if (state->destroyRequested != 0) { @@ -1111,10 +1096,6 @@ void android_main(struct android_app* state) { pick_file_callback_context(); pick_file_callback_context = nullptr; } - - // Drawing is throttled to the screen update rate, so there - // is no need to do timing here. - engine_draw_frame(&g_engine); } } } diff --git a/android/src/java/com/omixlab/panopainter/MainActivity.java b/android/src/java/com/omixlab/panopainter/MainActivity.java index 1c4f0c7..63ab78f 100644 --- a/android/src/java/com/omixlab/panopainter/MainActivity.java +++ b/android/src/java/com/omixlab/panopainter/MainActivity.java @@ -2,6 +2,8 @@ package com.omixlab.panopainter; import android.Manifest; import android.app.NativeActivity; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Rect; @@ -23,9 +25,12 @@ public class MainActivity extends NativeActivity { System.loadLibrary("native-lib"); } + private ClipboardManager clipboard; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); checkPermissionReadStorage(); getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @@ -192,6 +197,32 @@ public class MainActivity extends NativeActivity { return getResources().getDisplayMetrics().density; } + public String getClipboardText() + { + try + { + return clipboard.hasPrimaryClip() ? clipboard.getPrimaryClip().getItemAt(0).getText().toString() : ""; + } + catch (Exception e) + { + return ""; + } + } + + public boolean setClipboardText(String s) + { + try + { + clipboard.setPrimaryClip(ClipData.newPlainText("PanoPainter text", s)); + return true; + } + catch (Exception e) + { + // fail silently + return false; + } + } + public void pickFile() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); diff --git a/src/app.cpp b/src/app.cpp index c92b135..52b74c8 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -12,9 +12,9 @@ #include "settings.h" #ifdef __ANDROID__ -void android_async_lock(struct engine* engine); -void android_async_swap(struct engine* engine); -void android_async_unlock(struct engine* engine); +void android_async_lock(); +void android_async_swap(); +void android_async_unlock(); void android_attach_jni(); void android_detach_jni(); #elif _WIN32 @@ -418,7 +418,7 @@ void App::async_start() #elif __IOS__ [ios_view async_lock]; #elif __ANDROID__ - android_async_lock(and_engine); + android_async_lock(); #elif _WIN32 async_lock(); glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -438,7 +438,7 @@ void App::async_end() #elif __IOS__ [ios_view async_unlock]; #elif __ANDROID__ - android_async_unlock(and_engine); + android_async_unlock(); #elif _WIN32 async_unlock(); #endif @@ -451,7 +451,7 @@ void App::async_swap() #elif __IOS__ [ios_view async_swap]; #elif __ANDROID__ - android_async_swap(and_engine); + android_async_swap(); #elif _WIN32 win32_async_swap(); #endif diff --git a/src/app_events.cpp b/src/app_events.cpp index dafa03c..c5d84b7 100644 --- a/src/app_events.cpp +++ b/src/app_events.cpp @@ -2,8 +2,10 @@ #include "app.h" #ifdef __ANDROID__ -void displayKeyboard(android_app* mApplication, bool pShow); -void android_pick_file(android_app* mApplication, std::function callback); +void displayKeyboard(bool pShow); +void android_pick_file(std::function callback); +std::string android_get_clipboard(); +bool android_set_clipboard(const std::string& s); #elif _WIN32 std::string win32_open_file(const char* filter); std::string win32_open_dir(); @@ -21,6 +23,8 @@ std::string App::clipboard_get_text() return [ios_view clipboard_get_string]; #elif __OSX__ return [osx_view clipboard_get_string]; +#elif __ANDROID__ + return android_get_clipboard(); #endif } @@ -32,6 +36,8 @@ bool App::clipboard_set_text(const std::string& s) return [ios_view clipboard_set_string:s]; #elif __OSX__ return [osx_view clipboard_set_string:s]; +#elif __ANDROID__ + return android_set_clipboard(s); #endif } @@ -93,7 +99,7 @@ void App::showKeyboard() [ios_view becomeFirstResponder]; }); #elif __ANDROID__ - displayKeyboard(and_app, true); + displayKeyboard(true); #endif } @@ -107,7 +113,7 @@ void App::hideKeyboard() // [ios_view unregisterForKeyboardNotifications]; }); #elif __ANDROID__ - displayKeyboard(and_app, false); + displayKeyboard(false); #endif } @@ -126,7 +132,7 @@ void App::pick_image(std::function callback) callback(path); }); #elif __ANDROID__ - android_pick_file(and_app, callback); + android_pick_file(callback); #elif _WIN32 std::string path = win32_open_file("Image Files (*.jpg, *.png)\0*.jpg;*.png"); if (!path.empty()) @@ -154,7 +160,7 @@ void App::pick_file(std::vector types, std::function