From 5d91c011a6a5b404d980420f1c95239024b0f16a Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Mon, 9 Dec 2019 01:43:37 +1000 Subject: [PATCH] Android: Basic touchscreen controller implementation --- .../app/src/cpp/android_host_interface.cpp | 62 +++++++ android/app/src/cpp/android_host_interface.h | 17 ++ android/app/src/main/AndroidManifest.xml | 4 +- .../duckstation/AndroidHostInterface.java | 5 + .../duckstation/EmulationActivity.java | 12 ++ .../TouchscreenControllerButtonView.java | 160 ++++++++++++++++++ .../TouchscreenControllerView.java | 80 +++++++++ .../drawable/ic_controller_circle_button.xml | 19 +++ .../ic_controller_circle_button_pressed.xml | 19 +++ .../drawable/ic_controller_cross_button.xml | 27 +++ .../ic_controller_cross_button_pressed.xml | 27 +++ .../drawable/ic_controller_down_button.xml | 14 ++ .../ic_controller_down_button_pressed.xml | 14 ++ .../drawable/ic_controller_left_button.xml | 14 ++ .../ic_controller_left_button_pressed.xml | 14 ++ .../drawable/ic_controller_right_button.xml | 14 ++ .../ic_controller_right_button_pressed.xml | 14 ++ .../drawable/ic_controller_square_button.xml | 20 +++ .../ic_controller_square_button_pressed.xml | 20 +++ .../ic_controller_triangle_button.xml | 19 +++ .../ic_controller_triangle_button_pressed.xml | 19 +++ .../res/drawable/ic_controller_up_button.xml | 14 ++ .../ic_controller_up_button_pressed.xml | 14 ++ .../main/res/layout/activity_emulation.xml | 1 + android/app/src/main/res/values/strings.xml | 4 + 25 files changed, 625 insertions(+), 2 deletions(-) create mode 100644 android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerButtonView.java create mode 100644 android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerView.java create mode 100644 android/app/src/main/res/drawable/ic_controller_circle_button.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_circle_button_pressed.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_cross_button.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_cross_button_pressed.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_down_button.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_down_button_pressed.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_left_button.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_left_button_pressed.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_right_button.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_right_button_pressed.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_square_button.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_square_button_pressed.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_triangle_button.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_triangle_button_pressed.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_up_button.xml create mode 100644 android/app/src/main/res/drawable/ic_controller_up_button_pressed.xml diff --git a/android/app/src/cpp/android_host_interface.cpp b/android/app/src/cpp/android_host_interface.cpp index 460427b44..a020472e8 100644 --- a/android/app/src/cpp/android_host_interface.cpp +++ b/android/app/src/cpp/android_host_interface.cpp @@ -4,6 +4,7 @@ #include "YBaseLib/String.h" #include "android_audio_stream.h" #include "android_gles_host_display.h" +#include "core/controller.h" #include "core/gpu.h" #include "core/game_list.h" #include "core/host_display.h" @@ -330,6 +331,51 @@ void AndroidHostInterface::SurfaceChanged(ANativeWindow* window, int format, int m_display->ChangeRenderWindow(window); } +void AndroidHostInterface::SetControllerType(u32 index, std::string_view type_name) +{ + if (!IsEmulationThreadRunning()) + { + m_controller_type_names[index] = type_name; + return; + } + + std::string type_name_copy(type_name); + RunOnEmulationThread([this, index, type_name_copy]() { + Log_InfoPrintf("Changing controller slot %d to %s", index, type_name_copy.c_str()); + m_controller_type_names[index] = std::move(type_name_copy); + + m_controllers[index] = Controller::Create(m_controller_type_names[index]); + m_system->SetController(index, m_controllers[index]); + }, false); +} + +void AndroidHostInterface::SetControllerButtonState(u32 index, s32 button_code, bool pressed) +{ + if (!IsEmulationThreadRunning()) + return; + + RunOnEmulationThread([this, index, button_code, pressed]() { + if (!m_controllers[index]) + return; + + m_controllers[index]->SetButtonState(button_code, pressed); + }, false); +} + +void AndroidHostInterface::ConnectControllers() +{ + for (u32 i = 0; i < NUM_CONTROLLERS; i++) + { + if (m_controller_type_names[i].empty()) + continue; + + Log_InfoPrintf("Connecting controller '%s' to port %u", m_controller_type_names[i].c_str(), i); + m_controllers[i] = Controller::Create(m_controller_type_names[i]); + if (m_controllers[i]) + m_system->SetController(i, m_controllers[i]); + } +} + extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) { Log::GetInstance().SetDebugOutputParams(true, nullptr, LOGLEVEL_DEV); @@ -433,6 +479,22 @@ DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_surfaceChanged, jobject obj, j [hi, native_surface, format, width, height]() { hi->SurfaceChanged(native_surface, format, width, height); }, true); } +DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_setControllerType, jobject obj, jint index, jstring controller_type) +{ + GetNativeClass(env, obj)->SetControllerType(index, JStringToString(env, controller_type)); +} + +DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_setControllerButtonState, jobject obj, jint index, jint button_code, jboolean pressed) +{ + GetNativeClass(env, obj)->SetControllerButtonState(index, button_code, pressed); +} + +DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getControllerButtonCode, jobject unused, jstring controller_type, jstring button_name) +{ + std::optional code = Controller::GetButtonCodeByName(JStringToString(env, controller_type), JStringToString(env, button_name)); + return code.value_or(-1); +} + DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_cache_path, jstring j_redump_dat_path, jarray j_search_directories, jboolean search_recursively) { const std::string cache_path = JStringToString(env, j_cache_path); diff --git a/android/app/src/cpp/android_host_interface.h b/android/app/src/cpp/android_host_interface.h index 58e567259..c18115928 100644 --- a/android/app/src/cpp/android_host_interface.h +++ b/android/app/src/cpp/android_host_interface.h @@ -2,13 +2,17 @@ #include "YBaseLib/Event.h" #include "YBaseLib/Timer.h" #include "core/host_interface.h" +#include #include #include #include +#include #include struct ANativeWindow; +class Controller; + class AndroidHostInterface final : public HostInterface { public: @@ -26,7 +30,15 @@ public: void SurfaceChanged(ANativeWindow* window, int format, int width, int height); + void SetControllerType(u32 index, std::string_view type_name); + void SetControllerButtonState(u32 index, s32 button_code, bool pressed); + private: + enum : u32 + { + NUM_CONTROLLERS = 2 + }; + void EmulationThreadEntryPoint(ANativeWindow* initial_surface, std::string initial_filename, std::string initial_state_filename); @@ -36,6 +48,8 @@ private: void DrawFPSWindow(); + void ConnectControllers() override; + jobject m_java_object = {}; std::mutex m_callback_mutex; @@ -45,4 +59,7 @@ private: std::atomic_bool m_emulation_thread_stop_request{false}; std::atomic_bool m_emulation_thread_start_result{false}; Event m_emulation_thread_started; + + std::array m_controller_type_names; + std::array, NUM_CONTROLLERS> m_controllers; }; diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 715f01421..addabb317 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -9,10 +9,10 @@ android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" + android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/AppTheme" - android:requestLegacyExternalStorage="true"> + android:theme="@style/AppTheme"> %d", buttonName, code)); + + if (code < 0) { + Log.e("TouchscreenController", String.format("Unknown button name '%s' " + + "for '%s'", buttonName, mControllerType)); + } + } + } + + + @Override + public void onButtonStateChanged(TouchscreenControllerButtonView view, boolean pressed) { + if (mHostInterface == null || view.getButtonCode() < 0) + return; + + mHostInterface.setControllerButtonState(mControllerIndex, view.getButtonCode(), pressed); + } +} diff --git a/android/app/src/main/res/drawable/ic_controller_circle_button.xml b/android/app/src/main/res/drawable/ic_controller_circle_button.xml new file mode 100644 index 000000000..c078d372c --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_circle_button.xml @@ -0,0 +1,19 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_controller_circle_button_pressed.xml b/android/app/src/main/res/drawable/ic_controller_circle_button_pressed.xml new file mode 100644 index 000000000..a68b83f98 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_circle_button_pressed.xml @@ -0,0 +1,19 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_controller_cross_button.xml b/android/app/src/main/res/drawable/ic_controller_cross_button.xml new file mode 100644 index 000000000..c0b10a80a --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_cross_button.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/android/app/src/main/res/drawable/ic_controller_cross_button_pressed.xml b/android/app/src/main/res/drawable/ic_controller_cross_button_pressed.xml new file mode 100644 index 000000000..9f1766ec1 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_cross_button_pressed.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/android/app/src/main/res/drawable/ic_controller_down_button.xml b/android/app/src/main/res/drawable/ic_controller_down_button.xml new file mode 100644 index 000000000..b397fd762 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_down_button.xml @@ -0,0 +1,14 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_controller_down_button_pressed.xml b/android/app/src/main/res/drawable/ic_controller_down_button_pressed.xml new file mode 100644 index 000000000..ecc4d7ae7 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_down_button_pressed.xml @@ -0,0 +1,14 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_controller_left_button.xml b/android/app/src/main/res/drawable/ic_controller_left_button.xml new file mode 100644 index 000000000..0dd3138b1 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_left_button.xml @@ -0,0 +1,14 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_controller_left_button_pressed.xml b/android/app/src/main/res/drawable/ic_controller_left_button_pressed.xml new file mode 100644 index 000000000..11907981a --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_left_button_pressed.xml @@ -0,0 +1,14 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_controller_right_button.xml b/android/app/src/main/res/drawable/ic_controller_right_button.xml new file mode 100644 index 000000000..586a11b79 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_right_button.xml @@ -0,0 +1,14 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_controller_right_button_pressed.xml b/android/app/src/main/res/drawable/ic_controller_right_button_pressed.xml new file mode 100644 index 000000000..fabb570ae --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_right_button_pressed.xml @@ -0,0 +1,14 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_controller_square_button.xml b/android/app/src/main/res/drawable/ic_controller_square_button.xml new file mode 100644 index 000000000..9ce49e148 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_square_button.xml @@ -0,0 +1,20 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_controller_square_button_pressed.xml b/android/app/src/main/res/drawable/ic_controller_square_button_pressed.xml new file mode 100644 index 000000000..731d2eed9 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_square_button_pressed.xml @@ -0,0 +1,20 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_controller_triangle_button.xml b/android/app/src/main/res/drawable/ic_controller_triangle_button.xml new file mode 100644 index 000000000..7645a89e7 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_triangle_button.xml @@ -0,0 +1,19 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_controller_triangle_button_pressed.xml b/android/app/src/main/res/drawable/ic_controller_triangle_button_pressed.xml new file mode 100644 index 000000000..0bab1c10a --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_triangle_button_pressed.xml @@ -0,0 +1,19 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_controller_up_button.xml b/android/app/src/main/res/drawable/ic_controller_up_button.xml new file mode 100644 index 000000000..ad440f992 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_up_button.xml @@ -0,0 +1,14 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_controller_up_button_pressed.xml b/android/app/src/main/res/drawable/ic_controller_up_button_pressed.xml new file mode 100644 index 000000000..416454f5a --- /dev/null +++ b/android/app/src/main/res/drawable/ic_controller_up_button_pressed.xml @@ -0,0 +1,14 @@ + + + diff --git a/android/app/src/main/res/layout/activity_emulation.xml b/android/app/src/main/res/layout/activity_emulation.xml index 56df99a8d..ca8d77394 100644 --- a/android/app/src/main/res/layout/activity_emulation.xml +++ b/android/app/src/main/res/layout/activity_emulation.xml @@ -1,6 +1,7 @@ EmulationActivity Dummy Button DUMMY\nCONTENT + + + Hello blank fragment + TestControllerActivity