From fcbc8022657b4408d86bbaf718ac2eeb9ad71b7b Mon Sep 17 00:00:00 2001 From: Anthony Rabine Date: Mon, 6 May 2024 23:33:53 +0200 Subject: [PATCH] fix TLV, add docs, editor: add title name and sound, new story player app in flutter --- .gitignore | 8 + docs/dev-stories.md | 42 +- docs/player-intro.md | 29 +- shared/library_manager.cpp | 29 +- shared/library_manager.h | 6 +- shared/resource_manager.cpp | 4 +- shared/resource_manager.h | 2 +- shared/story_project.h | 6 +- story-editor/src/gui.cpp | 1 - story-editor/src/main_window.cpp | 132 +- story-editor/src/main_window.h | 1 - story-player-raylib/.vscode/launch.json | 28 + .../CMakeLists.txt | 0 .../Inter-Regular.ttf | Bin .../build_android.sh | 0 {story-player => story-player-raylib}/cat.png | Bin .../custom_file_dialog.c | 0 .../gui_file_dialog.h | 0 .../logo-color2.png | Bin {story-player => story-player-raylib}/main.c | 0 .../raygui.h | 0 .../raygui_icons.h | 0 .../.gradle/8.0/checksums/md5-checksums.bin | Bin 0 -> 18597 bytes .../.gradle/8.0/checksums/sha1-checksums.bin | Bin 0 -> 18767 bytes .../8.0/dependencies-accessors/gc.properties | 0 .../8.0/executionHistory/executionHistory.bin | Bin 0 -> 891008 bytes .../.gradle/8.0/fileChanges/last-build.bin | Bin 0 -> 1 bytes .../.gradle/8.0/fileHashes/fileHashes.bin | Bin 0 -> 84565 bytes .../8.0/fileHashes/resourceHashesCache.bin | Bin 0 -> 19653 bytes story-player-sdl/.gradle/8.0/gc.properties | 0 .../buildOutputCleanup/cache.properties | 2 + .../buildOutputCleanup/outputFiles.bin | Bin 0 -> 23573 bytes story-player-sdl/.gradle/file-system.probe | Bin 0 -> 8 bytes story-player-sdl/.gradle/vcs-1/gc.properties | 0 story-player-sdl/CMakeLists.txt | 183 + story-player-sdl/LICENSE | 21 + story-player-sdl/app/.gitignore | 4 + story-player-sdl/app/CMakeLists.txt | 70 + story-player-sdl/app/build.gradle | 72 + story-player-sdl/app/demokey.jks | Bin 0 -> 1190 bytes story-player-sdl/app/proguard-rules.pro | 17 + .../app/src/main/AndroidManifest.xml | 77 + .../main/java/org/libsdl/app/HIDDevice.java | 21 + .../app/HIDDeviceBLESteamController.java | 645 ++++ .../java/org/libsdl/app/HIDDeviceManager.java | 682 ++++ .../java/org/libsdl/app/HIDDeviceUSB.java | 308 ++ .../app/src/main/java/org/libsdl/app/SDL.java | 86 + .../main/java/org/libsdl/app/SDLActivity.java | 2016 +++++++++++ .../java/org/libsdl/app/SDLAudioManager.java | 500 +++ .../org/libsdl/app/SDLControllerManager.java | 856 +++++ .../java/org/libsdl/app/SDLDummyEdit.java | 62 + .../org/libsdl/app/SDLInputConnection.java | 136 + .../main/java/org/libsdl/app/SDLSurface.java | 413 +++ .../storyplayer/StoryActivity.java | 92 + .../app/src/main/play_store_512.png | Bin 0 -> 38774 bytes .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 6221 bytes .../mipmap-hdpi/ic_launcher_background.png | Bin 0 -> 1935 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 6086 bytes .../mipmap-hdpi/ic_launcher_monochrome.png | Bin 0 -> 6086 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 3754 bytes .../mipmap-mdpi/ic_launcher_background.png | Bin 0 -> 1331 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 3446 bytes .../mipmap-mdpi/ic_launcher_monochrome.png | Bin 0 -> 3446 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 9233 bytes .../mipmap-xhdpi/ic_launcher_background.png | Bin 0 -> 2649 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 9244 bytes .../mipmap-xhdpi/ic_launcher_monochrome.png | Bin 0 -> 9244 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 15469 bytes .../mipmap-xxhdpi/ic_launcher_background.png | Bin 0 -> 4149 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 16565 bytes .../mipmap-xxhdpi/ic_launcher_monochrome.png | Bin 0 -> 16565 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 22381 bytes .../mipmap-xxxhdpi/ic_launcher_background.png | Bin 0 -> 6044 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 24414 bytes .../mipmap-xxxhdpi/ic_launcher_monochrome.png | Bin 0 -> 24414 bytes .../app/src/main/res/values/colors.xml | 6 + .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/styles.xml | 9 + story-player-sdl/build.gradle | 29 + story-player-sdl/gradle.properties | 19 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61608 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + story-player-sdl/gradlew | 244 ++ story-player-sdl/gradlew.bat | 92 + story-player-sdl/local.properties | 9 + story-player-sdl/settings.gradle | 1 + story-player-sdl/src/gfx-engine/display.cpp | 177 + story-player-sdl/src/gfx-engine/display.h | 60 + story-player-sdl/src/gfx-engine/engine.cpp | 183 + story-player-sdl/src/gfx-engine/engine.h | 115 + story-player-sdl/src/gfx-engine/entity.cpp | 68 + story-player-sdl/src/gfx-engine/entity.h | 182 + story-player-sdl/src/gfx-engine/group.cpp | 61 + story-player-sdl/src/gfx-engine/group.h | 104 + story-player-sdl/src/gfx-engine/image.cpp | 278 ++ story-player-sdl/src/gfx-engine/image.h | 55 + story-player-sdl/src/gfx-engine/nanosvg.h | 3008 +++++++++++++++++ story-player-sdl/src/gfx-engine/nanosvgrast.h | 1452 ++++++++ story-player-sdl/src/gfx-engine/scene.cpp | 0 story-player-sdl/src/gfx-engine/scene.h | 106 + story-player-sdl/src/gfx-engine/xlog.h | 59 + story-player-sdl/src/main.cpp | 49 + story-player-sdl/story.cmake | 14 + story-player/.gitignore | 44 + story-player/.metadata | 30 + story-player/.vscode/launch.json | 61 +- story-player/README.md | 16 + story-player/analysis_options.yaml | 29 + story-player/android/.gitignore | 13 + story-player/android/app/build.gradle | 72 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 39 + .../example/flutter_launcher/MainActivity.kt | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + story-player/android/build.gradle | 31 + story-player/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + story-player/android/settings.gradle | 11 + story-player/assets/320x240.png | Bin 0 -> 4446 bytes story-player/lib/main.dart | 317 ++ story-player/lib/storyvm.dart | 204 ++ story-player/linux/.gitignore | 1 + story-player/linux/CMakeLists.txt | 150 + story-player/linux/flutter/CMakeLists.txt | 88 + .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + .../linux/flutter/generated_plugins.cmake | 23 + story-player/linux/main.cc | 6 + story-player/linux/my_application.cc | 104 + story-player/linux/my_application.h | 18 + story-player/pubspec.yaml | 102 + story-player/storyvm/CMakeLists.txt | 11 + story-player/storyvm/dlib_export.h | 18 + story-player/storyvm/storyvm.cpp | 188 ++ story-player/storyvm/storyvm.def | 3 + story-player/test/widget_test.dart | 30 + 146 files changed, 14558 insertions(+), 111 deletions(-) create mode 100644 story-player-raylib/.vscode/launch.json rename {story-player => story-player-raylib}/CMakeLists.txt (100%) rename {story-player => story-player-raylib}/Inter-Regular.ttf (100%) rename {story-player => story-player-raylib}/build_android.sh (100%) mode change 100644 => 100755 rename {story-player => story-player-raylib}/cat.png (100%) rename {story-player => story-player-raylib}/custom_file_dialog.c (100%) rename {story-player => story-player-raylib}/gui_file_dialog.h (100%) rename {story-player => story-player-raylib}/logo-color2.png (100%) rename {story-player => story-player-raylib}/main.c (100%) rename {story-player => story-player-raylib}/raygui.h (100%) rename {story-player => story-player-raylib}/raygui_icons.h (100%) create mode 100644 story-player-sdl/.gradle/8.0/checksums/md5-checksums.bin create mode 100644 story-player-sdl/.gradle/8.0/checksums/sha1-checksums.bin create mode 100644 story-player-sdl/.gradle/8.0/dependencies-accessors/gc.properties create mode 100644 story-player-sdl/.gradle/8.0/executionHistory/executionHistory.bin create mode 100644 story-player-sdl/.gradle/8.0/fileChanges/last-build.bin create mode 100644 story-player-sdl/.gradle/8.0/fileHashes/fileHashes.bin create mode 100644 story-player-sdl/.gradle/8.0/fileHashes/resourceHashesCache.bin create mode 100644 story-player-sdl/.gradle/8.0/gc.properties create mode 100644 story-player-sdl/.gradle/buildOutputCleanup/cache.properties create mode 100644 story-player-sdl/.gradle/buildOutputCleanup/outputFiles.bin create mode 100644 story-player-sdl/.gradle/file-system.probe create mode 100644 story-player-sdl/.gradle/vcs-1/gc.properties create mode 100644 story-player-sdl/CMakeLists.txt create mode 100644 story-player-sdl/LICENSE create mode 100644 story-player-sdl/app/.gitignore create mode 100644 story-player-sdl/app/CMakeLists.txt create mode 100644 story-player-sdl/app/build.gradle create mode 100644 story-player-sdl/app/demokey.jks create mode 100644 story-player-sdl/app/proguard-rules.pro create mode 100644 story-player-sdl/app/src/main/AndroidManifest.xml create mode 100644 story-player-sdl/app/src/main/java/org/libsdl/app/HIDDevice.java create mode 100644 story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java create mode 100644 story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceManager.java create mode 100644 story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java create mode 100644 story-player-sdl/app/src/main/java/org/libsdl/app/SDL.java create mode 100644 story-player-sdl/app/src/main/java/org/libsdl/app/SDLActivity.java create mode 100644 story-player-sdl/app/src/main/java/org/libsdl/app/SDLAudioManager.java create mode 100644 story-player-sdl/app/src/main/java/org/libsdl/app/SDLControllerManager.java create mode 100644 story-player-sdl/app/src/main/java/org/libsdl/app/SDLDummyEdit.java create mode 100644 story-player-sdl/app/src/main/java/org/libsdl/app/SDLInputConnection.java create mode 100644 story-player-sdl/app/src/main/java/org/libsdl/app/SDLSurface.java create mode 100644 story-player-sdl/app/src/main/java/org/openstoryteller/storyplayer/StoryActivity.java create mode 100644 story-player-sdl/app/src/main/play_store_512.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 story-player-sdl/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-hdpi/ic_launcher_background.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-mdpi/ic_launcher_background.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 story-player-sdl/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png create mode 100644 story-player-sdl/app/src/main/res/values/colors.xml create mode 100644 story-player-sdl/app/src/main/res/values/strings.xml create mode 100644 story-player-sdl/app/src/main/res/values/styles.xml create mode 100644 story-player-sdl/build.gradle create mode 100644 story-player-sdl/gradle.properties create mode 100644 story-player-sdl/gradle/wrapper/gradle-wrapper.jar create mode 100644 story-player-sdl/gradle/wrapper/gradle-wrapper.properties create mode 100755 story-player-sdl/gradlew create mode 100644 story-player-sdl/gradlew.bat create mode 100644 story-player-sdl/local.properties create mode 100644 story-player-sdl/settings.gradle create mode 100644 story-player-sdl/src/gfx-engine/display.cpp create mode 100644 story-player-sdl/src/gfx-engine/display.h create mode 100644 story-player-sdl/src/gfx-engine/engine.cpp create mode 100644 story-player-sdl/src/gfx-engine/engine.h create mode 100644 story-player-sdl/src/gfx-engine/entity.cpp create mode 100644 story-player-sdl/src/gfx-engine/entity.h create mode 100644 story-player-sdl/src/gfx-engine/group.cpp create mode 100644 story-player-sdl/src/gfx-engine/group.h create mode 100644 story-player-sdl/src/gfx-engine/image.cpp create mode 100644 story-player-sdl/src/gfx-engine/image.h create mode 100644 story-player-sdl/src/gfx-engine/nanosvg.h create mode 100644 story-player-sdl/src/gfx-engine/nanosvgrast.h create mode 100644 story-player-sdl/src/gfx-engine/scene.cpp create mode 100644 story-player-sdl/src/gfx-engine/scene.h create mode 100644 story-player-sdl/src/gfx-engine/xlog.h create mode 100644 story-player-sdl/src/main.cpp create mode 100644 story-player-sdl/story.cmake create mode 100644 story-player/.gitignore create mode 100644 story-player/.metadata create mode 100644 story-player/README.md create mode 100644 story-player/analysis_options.yaml create mode 100644 story-player/android/.gitignore create mode 100644 story-player/android/app/build.gradle create mode 100644 story-player/android/app/src/debug/AndroidManifest.xml create mode 100644 story-player/android/app/src/main/AndroidManifest.xml create mode 100644 story-player/android/app/src/main/kotlin/com/example/flutter_launcher/MainActivity.kt create mode 100644 story-player/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 story-player/android/app/src/main/res/drawable/launch_background.xml create mode 100644 story-player/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 story-player/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 story-player/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 story-player/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 story-player/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 story-player/android/app/src/main/res/values-night/styles.xml create mode 100644 story-player/android/app/src/main/res/values/styles.xml create mode 100644 story-player/android/app/src/profile/AndroidManifest.xml create mode 100644 story-player/android/build.gradle create mode 100644 story-player/android/gradle.properties create mode 100644 story-player/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 story-player/android/settings.gradle create mode 100644 story-player/assets/320x240.png create mode 100644 story-player/lib/main.dart create mode 100644 story-player/lib/storyvm.dart create mode 100644 story-player/linux/.gitignore create mode 100644 story-player/linux/CMakeLists.txt create mode 100644 story-player/linux/flutter/CMakeLists.txt create mode 100644 story-player/linux/flutter/generated_plugin_registrant.cc create mode 100644 story-player/linux/flutter/generated_plugin_registrant.h create mode 100644 story-player/linux/flutter/generated_plugins.cmake create mode 100644 story-player/linux/main.cc create mode 100644 story-player/linux/my_application.cc create mode 100644 story-player/linux/my_application.h create mode 100644 story-player/pubspec.yaml create mode 100644 story-player/storyvm/CMakeLists.txt create mode 100644 story-player/storyvm/dlib_export.h create mode 100644 story-player/storyvm/storyvm.cpp create mode 100644 story-player/storyvm/storyvm.def create mode 100644 story-player/test/widget_test.dart diff --git a/.gitignore b/.gitignore index 403d3d8..97360a2 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,11 @@ story-editor/build-win32/ build-story-editor-Desktop_Qt_GCC_64bit-Debug/ firmware/build/ + +story-player/stories/ + +story-player/storyvm/build/ + +story-player-sdl/build/ + +story-player-raylib/build/ diff --git a/docs/dev-stories.md b/docs/dev-stories.md index 9fcb2da..d91b673 100644 --- a/docs/dev-stories.md +++ b/docs/dev-stories.md @@ -34,23 +34,35 @@ The assets directory must contains all the resource files for the story (sounds, This binary file is encoded using a simple TLV format (Type Lenght Value) supporting the following types: -| Type | encoding | -| ----- | ----- | -| Object | 0xE7 | -| Array | 0xAB | -| String | 0x3D | -| Integer | 0x77 | -| Real | 0xB8 | +| Type | encoding | Value size | +| ----- | ----- | ------- | +| Object | 0xE7 | Variable | +| Array | 0xAB | Variable | +| String | 0x3D | Variable | +| Integer | 0x77 | 4 bytes | +| Real | 0xB8 | 4 bytes | -Each Type is encoded on a byte. +Each Type is encoded on a byte. Serialization uses little endian fir bytes ordering. -The Length is encoded on two bytes, little endian. The length indicates the size of the Value. +The Length is encoded on two bytes. The length indicates the size of the following value. -## Application on the Index File Forma +## Stories index format V1 + +The Index file root type is an object containing the file format version and the list of stories. + +Stories are stored in an array, each entry is the description of one story. A story is located in a directory wich name is the uuid (lower case). + + + +- Object (2 elements) + - Integer (format version) + - Array (n elements) + - Object (6 elements) + - String (UUID, folder name of the story) + - String (title image file name) + - String (sound title file name) + - String (Story name or title) + - String (Story description) + - Integer (Story version) -- Array - - Object - - String (UUID, folder name of the story) - - String (title image file name) - - String (sound title file name) diff --git a/docs/player-intro.md b/docs/player-intro.md index 616a6a6..988ea86 100644 --- a/docs/player-intro.md +++ b/docs/player-intro.md @@ -18,7 +18,7 @@ It should be possible to run it everywhere where Raylib can run. Just open the C32 Virtual Machine binary file ("story.c32", typically), images and sounds must follow standard project organisation. -## How to build +## How to build for Desktop The source code is available in the `story-player` sub-directory. From the command line: @@ -28,6 +28,33 @@ cmake .. make ``` +## How to build for Android + +The source code is available in the `story-player` sub-directory. + +First, make sure the Android SDK is found: + +``` +export ANDROID_HOME=/mnt/work/android-sdk +``` + +Then, use the gradle script to build: + +``` +./gradlew assembleDebug +``` + +Finally, install the generated APK on your device: + +``` +adb -s a19a2a install -r app/build/outputs/apk/debug/app-debug-V1-20240503.apk +``` + +Comments: +- Change the option `-s a19a2a` with the corresponding device ID you are using. See it using `adb devices` to see connected phones. +- `app-debug-V1-20240503.apk` is the APK name containing the date of the build. Use the APK generated in this directory. + + ## TODO 1. Drag and drop zipped story archive diff --git a/shared/library_manager.cpp b/shared/library_manager.cpp index db0a6c1..cb9fdfa 100644 --- a/shared/library_manager.cpp +++ b/shared/library_manager.cpp @@ -114,10 +114,22 @@ void LibraryManager::Save() { Tlv tlv; - tlv.add_object(1); - tlv.add_string(GetVersion()); + tlv.add_object(2); // 2 éléments dans l'objet - tlv.add_array(m_projectsList.size()); + tlv.add_integer(1); // version du fichier (1 == V1) + + // On compte le nombre de projets sélecttionnés + uint16_t stories = 0; + for (auto &p : m_projectsList) + { + if (p->IsSelected()) + { + stories++; + } + } + + tlv.add_array(stories); // Nombre d'histoires + for (auto &p : m_projectsList) { if (p->IsSelected()) @@ -133,17 +145,6 @@ void LibraryManager::Save() } tlv.Save(IndexFileName()); - - /* - - - // Title image - std::string image = RemoveFileExtension(m_titleImage) + ".qoi"; - tlv.add_string(image.c_str(), image.size()); - - std::string sound = RemoveFileExtension(m_titleSound) + ".wav"; - tlv.add_string(sound.c_str(), sound.size()); // title sound -*/ } diff --git a/shared/library_manager.h b/shared/library_manager.h index b354f68..9e7a82a 100644 --- a/shared/library_manager.h +++ b/shared/library_manager.h @@ -20,8 +20,8 @@ public: std::string LibraryPath() const { return m_library_path; } static std::string GetVersion(); - std::vector>::const_iterator begin() const { return m_projectsList.begin(); } - std::vector>::const_iterator end() const { return m_projectsList.end(); } + std::list>::const_iterator begin() const { return m_projectsList.begin(); } + std::list>::const_iterator end() const { return m_projectsList.end(); } uint32_t ProjectsCount() const { return m_projectsList.size(); }; @@ -45,7 +45,7 @@ public: private: ILogger &m_log; std::string m_library_path; - std::vector> m_projectsList; + std::list> m_projectsList; std::string m_storeUrl; std::thread m_copyWorker; }; diff --git a/shared/resource_manager.cpp b/shared/resource_manager.cpp index 1868653..77ed83c 100644 --- a/shared/resource_manager.cpp +++ b/shared/resource_manager.cpp @@ -36,7 +36,7 @@ std::string ResourceManager::ExtentionInfo(std::string extension, int info_type) } } -void ResourceManager::ConvertResources(const std::filesystem::path &assetsPath, const std::string &destAssetsPath) +void ResourceManager::ConvertResources(const std::filesystem::path &assetsPath, const std::filesystem::path &destAssetsPath) { auto [b, e] = Items(); for (auto it = b; it != e; ++it) @@ -77,7 +77,7 @@ void ResourceManager::ConvertResources(const std::filesystem::path &assetsPath, // Log("Convertered file: " + inputfile); // Conversion success, now copy to output directory if necessary (ie: to an external device) - if ((destAssetsPath != assetsPath.generic_string()) && (std::filesystem::exists(destAssetsPath))) + if ((destAssetsPath != assetsPath) && (std::filesystem::exists(destAssetsPath))) { // destination filename auto destFile = destAssetsPath / outputfile.filename(); diff --git a/shared/resource_manager.h b/shared/resource_manager.h index 276dc8e..ffd9541 100644 --- a/shared/resource_manager.h +++ b/shared/resource_manager.h @@ -25,7 +25,7 @@ public: static std::string ExtentionInfo(std::string extension, int info_type); - void ConvertResources(const std::filesystem::path &assetsPath, const std::string &destAssetsPath); + void ConvertResources(const std::filesystem::path &assetsPath, const std::filesystem::path &destAssetsPath); ~ResourceManager() { diff --git a/shared/story_project.h b/shared/story_project.h index 575df39..bc29f2a 100644 --- a/shared/story_project.h +++ b/shared/story_project.h @@ -49,8 +49,8 @@ struct StoryProject : public IStoryProject { public: - enum ImageFormat { IMG_FORMAT_BMP_4BITS, IMG_FORMAT_QOIF, IMG_FORMAT_COUNT }; - enum SoundFormat { SND_FORMAT_WAV, SND_FORMAT_QOAF, SND_FORMAT_COUNT }; + enum ImageFormat { IMG_SAME_FORMAT, IMG_FORMAT_QOIF, IMG_FORMAT_COUNT }; + enum SoundFormat { SND_SAME_FORMAT, SND_FORMAT_WAV, SND_FORMAT_QOAF, SND_FORMAT_COUNT }; StoryProject(ILogger &log); ~StoryProject(); @@ -167,7 +167,7 @@ private: int m_display_w{320}; int m_display_h{240}; - ImageFormat m_imageFormat{IMG_FORMAT_BMP_4BITS}; + ImageFormat m_imageFormat{IMG_SAME_FORMAT}; SoundFormat m_soundFormat{SND_FORMAT_WAV}; diff --git a/story-editor/src/gui.cpp b/story-editor/src/gui.cpp index a227e89..bd88513 100644 --- a/story-editor/src/gui.cpp +++ b/story-editor/src/gui.cpp @@ -92,7 +92,6 @@ bool LoadTextureFromFile(const char* filename, Gui::Image &img) return true; } -#define MANOLAB_VERSION "1.0" std::string GetDirectory (const std::string& path) { diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp index 6303011..b8346c9 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -17,7 +17,7 @@ #pragma comment(lib, "iphlpapi.lib") #pragma comment(lib, "ws2_32.lib") #endif - +#include "IconsMaterialDesignIcons.h" #include "ImGuiFileDialog.h" MainWindow::MainWindow() @@ -267,9 +267,18 @@ void MainWindow::DrawMainMenuBar() { if (ImGui::BeginMenu("File")) { + if (ImGui::MenuItem("New project")) { - showNewProject = true; + CloseProject(); + + m_story = m_libraryManager.NewProject(); + + if (m_story) + { + SaveProject(); + OpenProject(m_story->GetUuid()); + } } /* if (ImGui::BeginMenu("Open Recent")) @@ -344,15 +353,6 @@ void MainWindow::DrawMainMenuBar() } } - if (showNewProject) - { - IGFD::FileDialogConfig config; - config.path = "."; - config.countSelectionMax = 1; - config.flags = ImGuiFileDialogFlags_Modal; - ImGuiFileDialog::Instance()->OpenDialog("ChooseDirDialog", "Choose a parent directory for your project", nullptr, config); - } - // Always center this window when appearing ImVec2 center = ImGui::GetMainViewport()->GetCenter(); //ImVec2 parent_pos = ImGui::GetWindowPos(); @@ -395,8 +395,6 @@ void MainWindow::Initialize() m_nodeEditorWindow.Initialize(); m_PropertiesWindow.Initialize(); m_libraryWindow.Initialize(); - - } @@ -431,39 +429,6 @@ bool MainWindow::ShowQuitConfirm() } -void MainWindow::NewProjectPopup() -{ - // Always center this window when appearing - ImVec2 center = ImGui::GetMainViewport()->GetCenter(); - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - - if (ImGuiFileDialog::Instance()->Display("ChooseDirDialog")) - { - // action if OK - if (ImGuiFileDialog::Instance()->IsOk()) - { - std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); - std::string projdir = ImGuiFileDialog::Instance()->GetCurrentPath(); - - if (!std::filesystem::is_directory(projdir)) - { - CloseProject(); - - m_story = m_libraryManager.NewProject(); - - if (m_story) - { - SaveProject(); - OpenProject(m_story->GetUuid()); - } - } - } - - // close - ImGuiFileDialog::Instance()->Close(); - } -} - void MainWindow::ProjectPropertiesPopup() { // Always center this window when appearing @@ -514,7 +479,7 @@ void MainWindow::ProjectPropertiesPopup() // Using the generic BeginCombo() API, you have full control over how to display the combo contents. // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively // stored in the object itself, etc.) - const char* image_items[] = { "BMP (compressed 4-bit palette)", "QOIF (Quite Ok Image Format" }; + const char* image_items[] = { "Native (no conversion)", "QOIF (Quite Ok Image Format" }; const char* image_combo_preview_value = image_items[image_item_current_idx]; // Pass in the preview value visible before opening the combo (it could be anything) if (ImGui::BeginCombo("##ComboImage", image_combo_preview_value, flags)) { @@ -539,7 +504,7 @@ void MainWindow::ProjectPropertiesPopup() // Using the generic BeginCombo() API, you have full control over how to display the combo contents. // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively // stored in the object itself, etc.) - const char* sound_items[] = { "WAV (16-bit stereo)", "QOAF (Quite Ok Audio Format" }; + const char* sound_items[] = { "Native (no conversion)", "WAV (16-bit stereo)", "QOAF (Quite Ok Audio Format" }; const char* sound_combo_preview_value = sound_items[sound_item_current_idx]; // Pass in the preview value visible before opening the combo (it could be anything) if (ImGui::BeginCombo("##ComboSound", sound_combo_preview_value, flags)) { @@ -558,9 +523,77 @@ void MainWindow::ProjectPropertiesPopup() } + ImGui::AlignTextToFramePadding(); + ImGui::Text("Image"); + ImGui::SameLine(); + + ImGui::Text("%s", m_story->GetTitleImage().c_str()); + + ImGui::SameLine(); + + static bool isImage = true; + if (ImGui::Button("Select...##image")) { + ImGui::OpenPopup("popup_button"); + isImage = true; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_MDI_CLOSE_BOX_OUTLINE "##delimage")) { + m_story->SetTitleImage(""); + } + + ImGui::AlignTextToFramePadding(); + ImGui::Text("Sound"); + ImGui::SameLine(); + + ImGui::Text("%s", m_story->GetTitleSound().c_str()); + + ImGui::SameLine(); + + if (ImGui::Button("Play " ICON_MDI_PLAY)) + { + PlaySoundFile(BuildFullAssetsPath(m_story->GetTitleSound())); + } + + ImGui::SameLine(); + + if (ImGui::Button("Select...##sound")) { + ImGui::OpenPopup("popup_button"); + isImage = false; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_MDI_CLOSE_BOX_OUTLINE "##delsound")) { + m_story->SetTitleSound(""); + } + + + if (ImGui::BeginPopup("popup_button")) { + ImGui::SeparatorText(isImage ? "Images" : "Sounds"); + + auto [filtreDebut, filtreFin] = isImage ? Images() : Sounds(); + int n = 0; + for (auto it = filtreDebut; it != filtreFin; ++it, n++) + { + if (ImGui::Selectable((*it)->file.c_str())) + { + if (isImage) + { + m_story->SetTitleImage((*it)->file); + } + else + { + m_story->SetTitleSound((*it)->file); + } + } + } + + ImGui::EndPopup(); // Note this does not do anything to the popup open/close state. It just terminates the content declaration. + } + + auto GetImageFormat = [](int idx) -> StoryProject::ImageFormat { - StoryProject::ImageFormat img{StoryProject::IMG_FORMAT_BMP_4BITS}; + StoryProject::ImageFormat img{StoryProject::IMG_SAME_FORMAT}; if (idx < StoryProject::IMG_FORMAT_COUNT) { img = static_cast(idx); } @@ -740,7 +773,6 @@ void MainWindow::Loop() m_PropertiesWindow.Draw(); } - NewProjectPopup(); ProjectPropertiesPopup(); if (aboutToClose) diff --git a/story-editor/src/main_window.h b/story-editor/src/main_window.h index d52e556..4868965 100644 --- a/story-editor/src/main_window.h +++ b/story-editor/src/main_window.h @@ -158,7 +158,6 @@ private: void DrawMainMenuBar(); bool ShowQuitConfirm(); - void NewProjectPopup(); void SaveProject(); void CloseProject(); void DrawStatusBar(); diff --git a/story-player-raylib/.vscode/launch.json b/story-player-raylib/.vscode/launch.json new file mode 100644 index 0000000..969687f --- /dev/null +++ b/story-player-raylib/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C/C++ debug", + "cwd": "${workspaceFolder}", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/story-player", // Binary to exec + "args": [ + "build/config.yml" + ], // Arguments passed + "stopAtEntry": false, + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "${defaultBuildTask}", + "miDebuggerPath": "/usr/bin/gdb" + } + ] +} \ No newline at end of file diff --git a/story-player/CMakeLists.txt b/story-player-raylib/CMakeLists.txt similarity index 100% rename from story-player/CMakeLists.txt rename to story-player-raylib/CMakeLists.txt diff --git a/story-player/Inter-Regular.ttf b/story-player-raylib/Inter-Regular.ttf similarity index 100% rename from story-player/Inter-Regular.ttf rename to story-player-raylib/Inter-Regular.ttf diff --git a/story-player/build_android.sh b/story-player-raylib/build_android.sh old mode 100644 new mode 100755 similarity index 100% rename from story-player/build_android.sh rename to story-player-raylib/build_android.sh diff --git a/story-player/cat.png b/story-player-raylib/cat.png similarity index 100% rename from story-player/cat.png rename to story-player-raylib/cat.png diff --git a/story-player/custom_file_dialog.c b/story-player-raylib/custom_file_dialog.c similarity index 100% rename from story-player/custom_file_dialog.c rename to story-player-raylib/custom_file_dialog.c diff --git a/story-player/gui_file_dialog.h b/story-player-raylib/gui_file_dialog.h similarity index 100% rename from story-player/gui_file_dialog.h rename to story-player-raylib/gui_file_dialog.h diff --git a/story-player/logo-color2.png b/story-player-raylib/logo-color2.png similarity index 100% rename from story-player/logo-color2.png rename to story-player-raylib/logo-color2.png diff --git a/story-player/main.c b/story-player-raylib/main.c similarity index 100% rename from story-player/main.c rename to story-player-raylib/main.c diff --git a/story-player/raygui.h b/story-player-raylib/raygui.h similarity index 100% rename from story-player/raygui.h rename to story-player-raylib/raygui.h diff --git a/story-player/raygui_icons.h b/story-player-raylib/raygui_icons.h similarity index 100% rename from story-player/raygui_icons.h rename to story-player-raylib/raygui_icons.h diff --git a/story-player-sdl/.gradle/8.0/checksums/md5-checksums.bin b/story-player-sdl/.gradle/8.0/checksums/md5-checksums.bin new file mode 100644 index 0000000000000000000000000000000000000000..aaea1f19d2659e4708f2a32575cd772447f41141 GIT binary patch literal 18597 zcmeI%u}VWR6adiJ!J!D&LFnorA{H%la}X;J5uJ-5#i4_%gP_0QAov0PfZ$d^5L`tk z(aBA}pp*)Vo6RGA6o0@$P9V9L8!k!CnkON%jb8x(d1Q(%fCA= b_FqqXwfe@O-+kYix!z12Us>uXALkuIZAeAX literal 0 HcmV?d00001 diff --git a/story-player-sdl/.gradle/8.0/checksums/sha1-checksums.bin b/story-player-sdl/.gradle/8.0/checksums/sha1-checksums.bin new file mode 100644 index 0000000000000000000000000000000000000000..463a1631f1af7c3c9b08e77411cb93048b6f911c GIT binary patch literal 18767 zcmeI%JxBvF7{>9mP=``b%89K@Cvi|g?N&c%p`eF|LlL1U1q)K06pHn0aOmQqiw+Lr zCW1I93c6cxQE-xiI9SoaN$ZIpgXYME4lW{2@(<+l^5k+a`K=chB2e?`3{e@im68ep z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R;Ywz@WT{S6wF1?6!;QoDi)(S$GyZ(>+L9YvT^TEY|i@I Rf?3a+>h-azdd(N@USBh8nD+nx literal 0 HcmV?d00001 diff --git a/story-player-sdl/.gradle/8.0/dependencies-accessors/gc.properties b/story-player-sdl/.gradle/8.0/dependencies-accessors/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/story-player-sdl/.gradle/8.0/executionHistory/executionHistory.bin b/story-player-sdl/.gradle/8.0/executionHistory/executionHistory.bin new file mode 100644 index 0000000000000000000000000000000000000000..029fde19a668c2be2d39517858894bb0bb9ca10d GIT binary patch literal 891008 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�z{hvo`Kr}E?}DXX2_YvbsaKw&^REpxtG+ot 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 OE~e=f7TF26jmAHmC+yMy literal 0 HcmV?d00001 diff --git a/story-player-sdl/.gradle/8.0/gc.properties b/story-player-sdl/.gradle/8.0/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/story-player-sdl/.gradle/buildOutputCleanup/cache.properties b/story-player-sdl/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000..d31859d --- /dev/null +++ b/story-player-sdl/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Thu May 02 21:39:15 CEST 2024 +gradle.version=8.0 diff --git a/story-player-sdl/.gradle/buildOutputCleanup/outputFiles.bin b/story-player-sdl/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 0000000000000000000000000000000000000000..f150f0351ec939f4d88261641d0a9ed1da79b2fa GIT binary patch literal 23573 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 literal 0 HcmV?d00001 diff --git a/story-player-sdl/.gradle/file-system.probe b/story-player-sdl/.gradle/file-system.probe new file mode 100644 index 0000000000000000000000000000000000000000..c4d59f364daa5232c041fcfea1e1f65f71369d9c GIT binary patch literal 8 PcmZQzVC;8E3v~qm1_}Yy literal 0 HcmV?d00001 diff --git a/story-player-sdl/.gradle/vcs-1/gc.properties b/story-player-sdl/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/story-player-sdl/CMakeLists.txt b/story-player-sdl/CMakeLists.txt new file mode 100644 index 0000000..4845fc2 --- /dev/null +++ b/story-player-sdl/CMakeLists.txt @@ -0,0 +1,183 @@ +cmake_minimum_required(VERSION 3.5) + +set(STORY_PLAYER_PROJECT story-player) + +project(${STORY_PLAYER_PROJECT} LANGUAGES C CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Version du projet +set(PROJECT_VERSION_MAJOR 1) +set(PROJECT_VERSION_MINOR 0) +set(PROJECT_VERSION_PATCH 0) + +# set(CMAKE_VERBOSE_MAKEFILE on) +if(POLICY CMP0072) + cmake_policy(SET CMP0072 NEW) +endif() + +find_package(OpenGL REQUIRED) + + +# set(OPENSSL_ROOT_DIR /libs/openssl) +# find_package(OpenSSL REQUIRED) + + +include(FetchContent) + +# ========================================================================================================================= +# SDL3 +# ========================================================================================================================= +FetchContent_Declare( + sdl3 + GIT_REPOSITORY https://github.com/libsdl-org/SDL.git + GIT_TAG prerelease-3.1.0 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +set(BUILD_SHARED_LIBS TRUE) +set(SDL_STATIC TRUE) +FetchContent_MakeAvailable(sdl3) +include_directories(${sdl3_SOURCE_DIR}/include) +# add_subdirectory(${sdl3_SOURCE_DIR}) + +# ========================================================================================================================= +# SDL3-Image +# ========================================================================================================================= +FetchContent_Declare( + sdl_image + GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git + GIT_TAG origin/main + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +set(SDL3IMAGE_INSTALL OFF) +set(BUILD_SHARED_LIBS FALSE) +FetchContent_MakeAvailable(sdl_image) +include_directories(${sdl_image_SOURCE_DIR}/include) + +set(ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +include(./story.cmake) + +if(WIN32) + list(APPEND + PROJECT_SOURCES + icon.rc + ) +endif() + +if(WIN32) + add_executable(${STORY_PLAYER_PROJECT} + WIN32 + ${PROJECT_SOURCES} + ) +else() + add_executable(${STORY_PLAYER_PROJECT} + ${PROJECT_SOURCES} + + ) +endif() + +target_include_directories(${STORY_PLAYER_PROJECT} PUBLIC + ${sdl3_SOURCE_DIR}/include + + ${PROJECT_INCLUDES} + +) + +add_definitions(-DIMGUI_USE_WCHAR32 -DVERSION_MAJOR=${PROJECT_VERSION_MAJOR} -DVERSION_MINOR=${PROJECT_VERSION_MINOR} -DVERSION_PATCH=${PROJECT_VERSION_PATCH}) +add_link_options(-static-libgcc -static-libstdc++) + +if (APPLE) +set_target_properties(${PROJECT_NAME} PROPERTIES + LINKER_LANGUAGE CXX + LINK_FLAGS "-Wl,-rpath,@executable_path" + MACOSX_RPATH TRUE + SKIP_BUILD_RPATH FALSE + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH_USE_LINK_PATH TRUE +) + +set_target_properties(${PROJECT_NAME} PROPERTIES + BUNDLE True + MACOSX_BUNDLE_GUI_IDENTIFIER eu.d8s.OpenStoryTeller.StoryEditor.${CUR_TARGET} + MACOSX_BUNDLE_BUNDLE_NAME ${CUR_TARGET} + MACOSX_BUNDLE_BUNDLE_VERSION "0.1" + MACOSX_BUNDLE_SHORT_VERSION_STRING "0.1" + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/bundle.plist.in +) + +endif() + +target_compile_definitions(${STORY_PLAYER_PROJECT} PUBLIC cimg_display=0) + +target_compile_definitions(${STORY_PLAYER_PROJECT} PUBLIC "$<$:DEBUG>") + +target_link_directories(${STORY_PLAYER_PROJECT} PUBLIC ${sdl3_BINARY_DIR} ${sdl_image_BINARY_DIR}) + +# On est obligé de passer par une variable pour injecter +# certaines informations à CPACK +set(SDL_BIN_DIR ${sdl3_BINARY_DIR}) + +if(UNIX) + target_link_libraries(${STORY_PLAYER_PROJECT} + pthread + OpenGL::GL + dl + SDL3::SDL3 + SDL3_image::SDL3_image + + # OpenSSL::SSL OpenSSL::Crypto + ) +elseif(WIN32) + target_link_libraries(${STORY_PLAYER_PROJECT} + OpenGL::GL + SDL3::SDL3 + SDL3_image::SDL3_image + libcurl_static + ws2_32.lib psapi.lib setupapi.lib cfgmgr32.lib advapi32.lib + ) +endif() + +# ========================================================================================================================= +# CPACK INSTALLER +# ========================================================================================================================= +install(TARGETS ${STORY_PLAYER_PROJECT} RUNTIME DESTINATION ".") + +# Personnaliser les options d'installation +set(CPACK_PACKAGE_NAME "Open-Story-Editor") +set(CPACK_PACKAGE_DESCRIPTION "Open Story Teller - Node based editor") +set(CPACK_PACKAGE_VENDOR "D8S") +set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") + +# install(DIRECTORY "${PROJECT_SOURCE_DIR}/assets/" DESTINATION "assets") +install(DIRECTORY "${PROJECT_SOURCE_DIR}/fonts/" DESTINATION "fonts") +install(DIRECTORY "${PROJECT_SOURCE_DIR}/scripts/" DESTINATION "scripts") +install_files("." FILES "${CMAKE_SOURCE_DIR}/LICENSE") +install_files("." FILES "${CMAKE_SOURCE_DIR}/tools/imgui.ini") + +if(WIN32) + install_files("." FILES "${SDL_BIN_DIR}/SDL3.dll") + install_files("." FILES "/usr/lib/gcc/x86_64-w64-mingw32/10-posix/libstdc++-6.dll") + install_files("." FILES "/usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll") + install_files("." FILES "/usr/lib/gcc/x86_64-w64-mingw32/10-posix/libgcc_s_seh-1.dll") + + # Personnaliser l'icône pour les installateurs Windows + set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/story-editor-logo.ico") +endif() + +if (APPLE) + set(CPACK_GENERATOR "DragNDrop") + set(MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/bundle.plist.in) + install_files("." FILES "${SDL_BIN_DIR}/libSDL2-2.0.0.dylib") +endif() + +include(CPack) diff --git a/story-player-sdl/LICENSE b/story-player-sdl/LICENSE new file mode 100644 index 0000000..652bdbb --- /dev/null +++ b/story-player-sdl/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Anthony Rabine + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/story-player-sdl/app/.gitignore b/story-player-sdl/app/.gitignore new file mode 100644 index 0000000..8f34d96 --- /dev/null +++ b/story-player-sdl/app/.gitignore @@ -0,0 +1,4 @@ +/build +.externalNativeBuild +*.iml +.cxx \ No newline at end of file diff --git a/story-player-sdl/app/CMakeLists.txt b/story-player-sdl/app/CMakeLists.txt new file mode 100644 index 0000000..d407947 --- /dev/null +++ b/story-player-sdl/app/CMakeLists.txt @@ -0,0 +1,70 @@ +# =========================================================================== +# CMAKE PRINCIPAL APPELÉ PAR GRADLE +# =========================================================================== + +cmake_minimum_required(VERSION 3.10) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_VERBOSE_MAKEFILE ON) + +project(main C CXX) + +set(ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) + +include(FetchContent) + + +#========================================================================================================================= +# APPLI +#========================================================================================================================= +include(../story.cmake) + +# ========================================================================================================================= +# SDL3 +# ========================================================================================================================= +FetchContent_Declare( + sdl3 + GIT_REPOSITORY https://github.com/libsdl-org/SDL.git + GIT_TAG prerelease-3.1.0 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +set(BUILD_SHARED_LIBS TRUE) +set(SDL_STATIC TRUE) +FetchContent_MakeAvailable(sdl3) +include_directories(${sdl3_SOURCE_DIR}/include) +# add_subdirectory(${sdl3_SOURCE_DIR}) + +# ========================================================================================================================= +# SDL3-Image +# ========================================================================================================================= +FetchContent_Declare( + sdl_image + GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git + GIT_TAG origin/main + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) + +set(SDL3IMAGE_INSTALL OFF) +FetchContent_MakeAvailable(sdl_image) +include_directories(${sdl_image_SOURCE_DIR}/include) + + +#========================================================================================================================= +# TARGET +#========================================================================================================================= +add_library(${PROJECT_NAME} SHARED ${PROJECT_SOURCES}) + + +target_include_directories(${PROJECT_NAME} PUBLIC ${sdl3_SOURCE_DIR}/include ${PROJECT_INCLUDES}) +target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${sdl3_BINARY_DIR}) + +target_link_libraries(${PROJECT_NAME} PUBLIC dl GLESv1_CM GLESv2 OpenSLES SDL3 SDL3_image::SDL3_image android log) + +target_compile_options(${PROJECT_NAME} PUBLIC -Wall -Wextra -Wdocumentation -Wdocumentation-unknown-command -Wmissing-prototypes -Wunreachable-code-break -Wunneeded-internal-declaration -Wmissing-variable-declarations + -Wfloat-conversion -Wshorten-64-to-32 -Wunreachable-code-return -Wshift-sign-overflow -Wstrict-prototypes -Wkeyword-macro -Wno-unused-parameter -Wno-sign-compare) + + diff --git a/story-player-sdl/app/build.gradle b/story-player-sdl/app/build.gradle new file mode 100644 index 0000000..ba13901 --- /dev/null +++ b/story-player-sdl/app/build.gradle @@ -0,0 +1,72 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 33 + buildToolsVersion "30.0.3" + ndkVersion "21.4.7075529" + + namespace 'org.openstoryteller.storyplayer' + + sourceSets { + main { + assets.srcDirs = ['../assets'] + } + } + + signingConfigs { + demokey { + storeFile file('demokey.jks') + storePassword "demokey" + keyAlias 'demokey' + keyPassword 'demokey' + } + } + defaultConfig { + applicationId "org.openstoryteller.storyplayer" + minSdkVersion 18 + targetSdkVersion 33 + versionCode 1 + versionName "1.0.0" + externalNativeBuild { + cmake { + arguments "-DANDROID_NATIVE_API_LEVEL=33", "-DANDROID_STL=c++_shared", "-DANDROID=true", "-DANDROID_TOOLCHAIN=clang", "-DCMAKE_VERBOSE_MAKEFILE=ON" + cppFlags "-std=c++11 -frtti -fexceptions" + abiFilters "arm64-v8a", "armeabi-v7a" //, "x86_64", "x86" + } + } + } + buildTypes { + debug { + minifyEnabled false + signingConfig signingConfigs.demokey + } + release { + minifyEnabled true + signingConfig signingConfigs.demokey + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + applicationVariants.all { variant -> + variant.outputs.all { + def fileName = project.name + '-' + variant.name + '-V' + + defaultConfig.versionCode + "-" + buildTime() + ".apk" + outputFileName = fileName + } + } + externalNativeBuild { + cmake { + version "3.27.0+" + path 'CMakeLists.txt' + } + } +} + +static def buildTime() { + return new Date().format("yyyyMMdd", TimeZone.getDefault()) +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'androidx.appcompat:appcompat:1.6.1' +} + \ No newline at end of file diff --git a/story-player-sdl/app/demokey.jks b/story-player-sdl/app/demokey.jks new file mode 100644 index 0000000000000000000000000000000000000000..22eb5ce5e847ba2937750f2eed904802a298092c GIT binary patch literal 1190 zcmezO_TO6u1_mY|W&~sQl+@h(?9@siKd5YPUMWy+w?Pxrb^|^(E^RhO7Dg>5K}JSa z29_qK#eT*+7rZ^aK!@G+#LmYDmn{Ca|9e=~=l`nrul{s*I-{dm72M*N+;{Mq#??xH zAt`q;!yOXgh0k|qC2s4~eRmmsi~0#`kxFmBhZQPv)|h znQ`3Kc$M~RR=_cyT;`G!#=jGVpXze(rC#|d8fyBXl__DNO+@6BhpttH_Sp_W0+YV_ zy{cT5lvcyDjO&C$y0ZP%2WCyIUHcV;_h*$qwZ1&FQLXnwt4YUG)$>+GlMn6Dw&(n@ zu%vLp=7k;ylTXghz3`y#(P`V%vnP8MJDD<`Up0O6?#|r>-%qbQ_`g}9xbphx$IG%$ z>b6hKX-v@HtL=X2`30#u-O`+UH-4N8z47SZ`Wf?ns#vTpUwt#+L8Hsvog&x7<<_;D z$L4#?bm4h(r`-2cNp_@=Z$(q)hh1{@0&Q=^9n2p@$xWP>|DnP7*OlD0f3G;*7XALL zb=|i1^o@#t?>rKfsn^oG@a})f0lUdvzkJ@k5!mi=Hs{-At>=6li9Hqv`O{CQep$lC z6?V?JvG%ZIyzPSPr(QNV+~a?DZK3Iz&AyRJQ}=EbIrZao0oF+P7a{r!%rYogGU))_PR-|HF zU^8DU$-HFe=G<3{wgmF7>HcAC@;ztAi)>G$1JdWS9*b>|lW^H@S9a#HWb=v7&)zM) z@t!GLaBlS_sm!gM=MCS6B>Ubt@^{;$tw+OdZZzP%HMyvv^^?Gb#A(MXRh3@w{NK7< z=Z{6i#4oEJH7o4y-md8RUH&s+iUIKu+4lvv8OkSfBE%K zbzIw9Px36%F8nOME?3KL^@0a&M{caV^*sM(`=QkJOw5c7jL6OddY&2Ru1QRi_oTM| zoA+XkiIP)s-rkA3cG#FrJvC81NuW54BkiwD*^yemWn2yOg462-ebZP28@O)G7nge( zD0fkkHK}oLMu1GqgzTwd+&><%kIZ(DLmv-A@+WY wq2)F8pH2J9`c9NKCfB{Tp7c}HEyMBl)Uz_$ZJ~w7nb>x{y0W+?al(ur0B@rekpKVy literal 0 HcmV?d00001 diff --git a/story-player-sdl/app/proguard-rules.pro b/story-player-sdl/app/proguard-rules.pro new file mode 100644 index 0000000..efad134 --- /dev/null +++ b/story-player-sdl/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in E:\androidsdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/story-player-sdl/app/src/main/AndroidManifest.xml b/story-player-sdl/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..dc009f5 --- /dev/null +++ b/story-player-sdl/app/src/main/AndroidManifest.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDevice.java b/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDevice.java new file mode 100644 index 0000000..f960953 --- /dev/null +++ b/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDevice.java @@ -0,0 +1,21 @@ +package org.libsdl.app; + +import android.hardware.usb.UsbDevice; + +interface HIDDevice +{ + public int getId(); + public int getVendorId(); + public int getProductId(); + public String getSerialNumber(); + public int getVersion(); + public String getManufacturerName(); + public String getProductName(); + public UsbDevice getDevice(); + public boolean open(); + public int writeReport(byte[] report, boolean feature); + public boolean readReport(byte[] report, boolean feature); + public void setFrozen(boolean frozen); + public void close(); + public void shutdown(); +} diff --git a/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java b/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java new file mode 100644 index 0000000..a7b85d0 --- /dev/null +++ b/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java @@ -0,0 +1,645 @@ +package org.libsdl.app; + +import android.content.Context; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothGattService; +import android.hardware.usb.UsbDevice; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.os.*; + +//import com.android.internal.util.HexDump; + +import java.lang.Runnable; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.UUID; + +class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice { + + private static final String TAG = "hidapi"; + private HIDDeviceManager mManager; + private BluetoothDevice mDevice; + private int mDeviceId; + private BluetoothGatt mGatt; + private boolean mIsRegistered = false; + private boolean mIsConnected = false; + private boolean mIsChromebook = false; + private boolean mIsReconnecting = false; + private boolean mFrozen = false; + private LinkedList mOperations; + GattOperation mCurrentOperation = null; + private Handler mHandler; + + private static final int TRANSPORT_AUTO = 0; + private static final int TRANSPORT_BREDR = 1; + private static final int TRANSPORT_LE = 2; + + private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000; + + static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3"); + static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3"); + static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3"); + static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 }; + + static class GattOperation { + private enum Operation { + CHR_READ, + CHR_WRITE, + ENABLE_NOTIFICATION + } + + Operation mOp; + UUID mUuid; + byte[] mValue; + BluetoothGatt mGatt; + boolean mResult = true; + + private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) { + mGatt = gatt; + mOp = operation; + mUuid = uuid; + } + + private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) { + mGatt = gatt; + mOp = operation; + mUuid = uuid; + mValue = value; + } + + public void run() { + // This is executed in main thread + BluetoothGattCharacteristic chr; + + switch (mOp) { + case CHR_READ: + chr = getCharacteristic(mUuid); + //Log.v(TAG, "Reading characteristic " + chr.getUuid()); + if (!mGatt.readCharacteristic(chr)) { + Log.e(TAG, "Unable to read characteristic " + mUuid.toString()); + mResult = false; + break; + } + mResult = true; + break; + case CHR_WRITE: + chr = getCharacteristic(mUuid); + //Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value)); + chr.setValue(mValue); + if (!mGatt.writeCharacteristic(chr)) { + Log.e(TAG, "Unable to write characteristic " + mUuid.toString()); + mResult = false; + break; + } + mResult = true; + break; + case ENABLE_NOTIFICATION: + chr = getCharacteristic(mUuid); + //Log.v(TAG, "Writing descriptor of " + chr.getUuid()); + if (chr != null) { + BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); + if (cccd != null) { + int properties = chr.getProperties(); + byte[] value; + if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) { + value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE; + } else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) { + value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE; + } else { + Log.e(TAG, "Unable to start notifications on input characteristic"); + mResult = false; + return; + } + + mGatt.setCharacteristicNotification(chr, true); + cccd.setValue(value); + if (!mGatt.writeDescriptor(cccd)) { + Log.e(TAG, "Unable to write descriptor " + mUuid.toString()); + mResult = false; + return; + } + mResult = true; + } + } + } + } + + public boolean finish() { + return mResult; + } + + private BluetoothGattCharacteristic getCharacteristic(UUID uuid) { + BluetoothGattService valveService = mGatt.getService(steamControllerService); + if (valveService == null) + return null; + return valveService.getCharacteristic(uuid); + } + + static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) { + return new GattOperation(gatt, Operation.CHR_READ, uuid); + } + + static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) { + return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value); + } + + static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) { + return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid); + } + } + + public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) { + mManager = manager; + mDevice = device; + mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier()); + mIsRegistered = false; + mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); + mOperations = new LinkedList(); + mHandler = new Handler(Looper.getMainLooper()); + + mGatt = connectGatt(); + // final HIDDeviceBLESteamController finalThis = this; + // mHandler.postDelayed(new Runnable() { + // @Override + // public void run() { + // finalThis.checkConnectionForChromebookIssue(); + // } + // }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL); + } + + public String getIdentifier() { + return String.format("SteamController.%s", mDevice.getAddress()); + } + + public BluetoothGatt getGatt() { + return mGatt; + } + + // Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead + // of TRANSPORT_LE. Let's force ourselves to connect low energy. + private BluetoothGatt connectGatt(boolean managed) { + if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) { + try { + return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE); + } catch (Exception e) { + return mDevice.connectGatt(mManager.getContext(), managed, this); + } + } else { + return mDevice.connectGatt(mManager.getContext(), managed, this); + } + } + + private BluetoothGatt connectGatt() { + return connectGatt(false); + } + + protected int getConnectionState() { + + Context context = mManager.getContext(); + if (context == null) { + // We are lacking any context to get our Bluetooth information. We'll just assume disconnected. + return BluetoothProfile.STATE_DISCONNECTED; + } + + BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE); + if (btManager == null) { + // This device doesn't support Bluetooth. We should never be here, because how did + // we instantiate a device to start with? + return BluetoothProfile.STATE_DISCONNECTED; + } + + return btManager.getConnectionState(mDevice, BluetoothProfile.GATT); + } + + public void reconnect() { + + if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) { + mGatt.disconnect(); + mGatt = connectGatt(); + } + + } + + protected void checkConnectionForChromebookIssue() { + if (!mIsChromebook) { + // We only do this on Chromebooks, because otherwise it's really annoying to just attempt + // over and over. + return; + } + + int connectionState = getConnectionState(); + + switch (connectionState) { + case BluetoothProfile.STATE_CONNECTED: + if (!mIsConnected) { + // We are in the Bad Chromebook Place. We can force a disconnect + // to try to recover. + Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect."); + mIsReconnecting = true; + mGatt.disconnect(); + mGatt = connectGatt(false); + break; + } + else if (!isRegistered()) { + if (mGatt.getServices().size() > 0) { + Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover."); + probeService(this); + } + else { + Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover."); + mIsReconnecting = true; + mGatt.disconnect(); + mGatt = connectGatt(false); + break; + } + } + else { + Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!"); + return; + } + break; + + case BluetoothProfile.STATE_DISCONNECTED: + Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover."); + + mIsReconnecting = true; + mGatt.disconnect(); + mGatt = connectGatt(false); + break; + + case BluetoothProfile.STATE_CONNECTING: + Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer."); + break; + } + + final HIDDeviceBLESteamController finalThis = this; + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + finalThis.checkConnectionForChromebookIssue(); + } + }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL); + } + + private boolean isRegistered() { + return mIsRegistered; + } + + private void setRegistered() { + mIsRegistered = true; + } + + private boolean probeService(HIDDeviceBLESteamController controller) { + + if (isRegistered()) { + return true; + } + + if (!mIsConnected) { + return false; + } + + Log.v(TAG, "probeService controller=" + controller); + + for (BluetoothGattService service : mGatt.getServices()) { + if (service.getUuid().equals(steamControllerService)) { + Log.v(TAG, "Found Valve steam controller service " + service.getUuid()); + + for (BluetoothGattCharacteristic chr : service.getCharacteristics()) { + if (chr.getUuid().equals(inputCharacteristic)) { + Log.v(TAG, "Found input characteristic"); + // Start notifications + BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); + if (cccd != null) { + enableNotification(chr.getUuid()); + } + } + } + return true; + } + } + + if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) { + Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us."); + mIsConnected = false; + mIsReconnecting = true; + mGatt.disconnect(); + mGatt = connectGatt(false); + } + + return false; + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + private void finishCurrentGattOperation() { + GattOperation op = null; + synchronized (mOperations) { + if (mCurrentOperation != null) { + op = mCurrentOperation; + mCurrentOperation = null; + } + } + if (op != null) { + boolean result = op.finish(); // TODO: Maybe in main thread as well? + + // Our operation failed, let's add it back to the beginning of our queue. + if (!result) { + mOperations.addFirst(op); + } + } + executeNextGattOperation(); + } + + private void executeNextGattOperation() { + synchronized (mOperations) { + if (mCurrentOperation != null) + return; + + if (mOperations.isEmpty()) + return; + + mCurrentOperation = mOperations.removeFirst(); + } + + // Run in main thread + mHandler.post(new Runnable() { + @Override + public void run() { + synchronized (mOperations) { + if (mCurrentOperation == null) { + Log.e(TAG, "Current operation null in executor?"); + return; + } + + mCurrentOperation.run(); + // now wait for the GATT callback and when it comes, finish this operation + } + } + }); + } + + private void queueGattOperation(GattOperation op) { + synchronized (mOperations) { + mOperations.add(op); + } + executeNextGattOperation(); + } + + private void enableNotification(UUID chrUuid) { + GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid); + queueGattOperation(op); + } + + public void writeCharacteristic(UUID uuid, byte[] value) { + GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value); + queueGattOperation(op); + } + + public void readCharacteristic(UUID uuid) { + GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid); + queueGattOperation(op); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////// BluetoothGattCallback overridden methods + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + public void onConnectionStateChange(BluetoothGatt g, int status, int newState) { + //Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState); + mIsReconnecting = false; + if (newState == 2) { + mIsConnected = true; + // Run directly, without GattOperation + if (!isRegistered()) { + mHandler.post(new Runnable() { + @Override + public void run() { + mGatt.discoverServices(); + } + }); + } + } + else if (newState == 0) { + mIsConnected = false; + } + + // Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent. + } + + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + //Log.v(TAG, "onServicesDiscovered status=" + status); + if (status == 0) { + if (gatt.getServices().size() == 0) { + Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack."); + mIsReconnecting = true; + mIsConnected = false; + gatt.disconnect(); + mGatt = connectGatt(false); + } + else { + probeService(this); + } + } + } + + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + //Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid()); + + if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) { + mManager.HIDDeviceReportResponse(getId(), characteristic.getValue()); + } + + finishCurrentGattOperation(); + } + + public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + //Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid()); + + if (characteristic.getUuid().equals(reportCharacteristic)) { + // Only register controller with the native side once it has been fully configured + if (!isRegistered()) { + Log.v(TAG, "Registering Steam Controller with ID: " + getId()); + mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0); + setRegistered(); + } + } + + finishCurrentGattOperation(); + } + + public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + // Enable this for verbose logging of controller input reports + //Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue())); + + if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) { + mManager.HIDDeviceInputReport(getId(), characteristic.getValue()); + } + } + + public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + //Log.v(TAG, "onDescriptorRead status=" + status); + } + + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + BluetoothGattCharacteristic chr = descriptor.getCharacteristic(); + //Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid()); + + if (chr.getUuid().equals(inputCharacteristic)) { + boolean hasWrittenInputDescriptor = true; + BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic); + if (reportChr != null) { + Log.v(TAG, "Writing report characteristic to enter valve mode"); + reportChr.setValue(enterValveMode); + gatt.writeCharacteristic(reportChr); + } + } + + finishCurrentGattOperation(); + } + + public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { + //Log.v(TAG, "onReliableWriteCompleted status=" + status); + } + + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { + //Log.v(TAG, "onReadRemoteRssi status=" + status); + } + + public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { + //Log.v(TAG, "onMtuChanged status=" + status); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + //////// Public API + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public int getId() { + return mDeviceId; + } + + @Override + public int getVendorId() { + // Valve Corporation + final int VALVE_USB_VID = 0x28DE; + return VALVE_USB_VID; + } + + @Override + public int getProductId() { + // We don't have an easy way to query from the Bluetooth device, but we know what it is + final int D0G_BLE2_PID = 0x1106; + return D0G_BLE2_PID; + } + + @Override + public String getSerialNumber() { + // This will be read later via feature report by Steam + return "12345"; + } + + @Override + public int getVersion() { + return 0; + } + + @Override + public String getManufacturerName() { + return "Valve Corporation"; + } + + @Override + public String getProductName() { + return "Steam Controller"; + } + + @Override + public UsbDevice getDevice() { + return null; + } + + @Override + public boolean open() { + return true; + } + + @Override + public int writeReport(byte[] report, boolean feature) { + if (!isRegistered()) { + Log.e(TAG, "Attempted writeReport before Steam Controller is registered!"); + if (mIsConnected) { + probeService(this); + } + return -1; + } + + if (feature) { + // We need to skip the first byte, as that doesn't go over the air + byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1); + //Log.v(TAG, "writeFeatureReport " + HexDump.dumpHexString(actual_report)); + writeCharacteristic(reportCharacteristic, actual_report); + return report.length; + } else { + //Log.v(TAG, "writeOutputReport " + HexDump.dumpHexString(report)); + writeCharacteristic(reportCharacteristic, report); + return report.length; + } + } + + @Override + public boolean readReport(byte[] report, boolean feature) { + if (!isRegistered()) { + Log.e(TAG, "Attempted readReport before Steam Controller is registered!"); + if (mIsConnected) { + probeService(this); + } + return false; + } + + if (feature) { + readCharacteristic(reportCharacteristic); + return true; + } else { + // Not implemented + return false; + } + } + + @Override + public void close() { + } + + @Override + public void setFrozen(boolean frozen) { + mFrozen = frozen; + } + + @Override + public void shutdown() { + close(); + + BluetoothGatt g = mGatt; + if (g != null) { + g.disconnect(); + g.close(); + mGatt = null; + } + mManager = null; + mIsRegistered = false; + mIsConnected = false; + mOperations.clear(); + } + +} + diff --git a/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceManager.java new file mode 100644 index 0000000..8b6d5ba --- /dev/null +++ b/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceManager.java @@ -0,0 +1,682 @@ +package org.libsdl.app; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.PendingIntent; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.os.Build; +import android.util.Log; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.hardware.usb.*; +import android.os.Handler; +import android.os.Looper; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +public class HIDDeviceManager { + private static final String TAG = "hidapi"; + private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION"; + + private static HIDDeviceManager sManager; + private static int sManagerRefCount = 0; + + public static HIDDeviceManager acquire(Context context) { + if (sManagerRefCount == 0) { + sManager = new HIDDeviceManager(context); + } + ++sManagerRefCount; + return sManager; + } + + public static void release(HIDDeviceManager manager) { + if (manager == sManager) { + --sManagerRefCount; + if (sManagerRefCount == 0) { + sManager.close(); + sManager = null; + } + } + } + + private Context mContext; + private HashMap mDevicesById = new HashMap(); + private HashMap mBluetoothDevices = new HashMap(); + private int mNextDeviceId = 0; + private SharedPreferences mSharedPreferences = null; + private boolean mIsChromebook = false; + private UsbManager mUsbManager; + private Handler mHandler; + private BluetoothManager mBluetoothManager; + private List mLastBluetoothDevices; + + private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) { + UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + handleUsbDeviceAttached(usbDevice); + } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) { + UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + handleUsbDeviceDetached(usbDevice); + } else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) { + UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)); + } + } + }; + + private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + // Bluetooth device was connected. If it was a Steam Controller, handle it + if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + Log.d(TAG, "Bluetooth device connected: " + device); + + if (isSteamController(device)) { + connectBluetoothDevice(device); + } + } + + // Bluetooth device was disconnected, remove from controller manager (if any) + if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + Log.d(TAG, "Bluetooth device disconnected: " + device); + + disconnectBluetoothDevice(device); + } + } + }; + + private HIDDeviceManager(final Context context) { + mContext = context; + + HIDDeviceRegisterCallback(); + + mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE); + mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); + +// if (shouldClear) { +// SharedPreferences.Editor spedit = mSharedPreferences.edit(); +// spedit.clear(); +// spedit.commit(); +// } +// else + { + mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0); + } + } + + public Context getContext() { + return mContext; + } + + public int getDeviceIDForIdentifier(String identifier) { + SharedPreferences.Editor spedit = mSharedPreferences.edit(); + + int result = mSharedPreferences.getInt(identifier, 0); + if (result == 0) { + result = mNextDeviceId++; + spedit.putInt("next_device_id", mNextDeviceId); + } + + spedit.putInt(identifier, result); + spedit.commit(); + return result; + } + + private void initializeUSB() { + mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE); + if (mUsbManager == null) { + return; + } + + /* + // Logging + for (UsbDevice device : mUsbManager.getDeviceList().values()) { + Log.i(TAG,"Path: " + device.getDeviceName()); + Log.i(TAG,"Manufacturer: " + device.getManufacturerName()); + Log.i(TAG,"Product: " + device.getProductName()); + Log.i(TAG,"ID: " + device.getDeviceId()); + Log.i(TAG,"Class: " + device.getDeviceClass()); + Log.i(TAG,"Protocol: " + device.getDeviceProtocol()); + Log.i(TAG,"Vendor ID " + device.getVendorId()); + Log.i(TAG,"Product ID: " + device.getProductId()); + Log.i(TAG,"Interface count: " + device.getInterfaceCount()); + Log.i(TAG,"---------------------------------------"); + + // Get interface details + for (int index = 0; index < device.getInterfaceCount(); index++) { + UsbInterface mUsbInterface = device.getInterface(index); + Log.i(TAG," ***** *****"); + Log.i(TAG," Interface index: " + index); + Log.i(TAG," Interface ID: " + mUsbInterface.getId()); + Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass()); + Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass()); + Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol()); + Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount()); + + // Get endpoint details + for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++) + { + UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi); + Log.i(TAG," ++++ ++++ ++++"); + Log.i(TAG," Endpoint index: " + epi); + Log.i(TAG," Attributes: " + mEndpoint.getAttributes()); + Log.i(TAG," Direction: " + mEndpoint.getDirection()); + Log.i(TAG," Number: " + mEndpoint.getEndpointNumber()); + Log.i(TAG," Interval: " + mEndpoint.getInterval()); + Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize()); + Log.i(TAG," Type: " + mEndpoint.getType()); + } + } + } + Log.i(TAG," No more devices connected."); + */ + + // Register for USB broadcasts and permission completions + IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + mContext.registerReceiver(mUsbBroadcast, filter, Context.RECEIVER_EXPORTED); + } else { + mContext.registerReceiver(mUsbBroadcast, filter); + } + + for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) { + handleUsbDeviceAttached(usbDevice); + } + } + + UsbManager getUSBManager() { + return mUsbManager; + } + + private void shutdownUSB() { + try { + mContext.unregisterReceiver(mUsbBroadcast); + } catch (Exception e) { + // We may not have registered, that's okay + } + } + + private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) { + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) { + return true; + } + if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) { + return true; + } + return false; + } + + private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) { + final int XB360_IFACE_SUBCLASS = 93; + final int XB360_IFACE_PROTOCOL = 1; // Wired + final int XB360W_IFACE_PROTOCOL = 129; // Wireless + final int[] SUPPORTED_VENDORS = { + 0x0079, // GPD Win 2 + 0x044f, // Thrustmaster + 0x045e, // Microsoft + 0x046d, // Logitech + 0x056e, // Elecom + 0x06a3, // Saitek + 0x0738, // Mad Catz + 0x07ff, // Mad Catz + 0x0e6f, // PDP + 0x0f0d, // Hori + 0x1038, // SteelSeries + 0x11c9, // Nacon + 0x12ab, // Unknown + 0x1430, // RedOctane + 0x146b, // BigBen + 0x1532, // Razer Sabertooth + 0x15e4, // Numark + 0x162e, // Joytech + 0x1689, // Razer Onza + 0x1949, // Lab126, Inc. + 0x1bad, // Harmonix + 0x20d6, // PowerA + 0x24c6, // PowerA + 0x2c22, // Qanba + 0x2dc8, // 8BitDo + 0x9886, // ASTRO Gaming + }; + + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC && + usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS && + (usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL || + usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) { + int vendor_id = usbDevice.getVendorId(); + for (int supportedVid : SUPPORTED_VENDORS) { + if (vendor_id == supportedVid) { + return true; + } + } + } + return false; + } + + private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) { + final int XB1_IFACE_SUBCLASS = 71; + final int XB1_IFACE_PROTOCOL = 208; + final int[] SUPPORTED_VENDORS = { + 0x03f0, // HP + 0x044f, // Thrustmaster + 0x045e, // Microsoft + 0x0738, // Mad Catz + 0x0e6f, // PDP + 0x0f0d, // Hori + 0x10f5, // Turtle Beach + 0x1532, // Razer Wildcat + 0x20d6, // PowerA + 0x24c6, // PowerA + 0x2dc8, // 8BitDo + 0x2e24, // Hyperkin + 0x3537, // GameSir + }; + + if (usbInterface.getId() == 0 && + usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC && + usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS && + usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) { + int vendor_id = usbDevice.getVendorId(); + for (int supportedVid : SUPPORTED_VENDORS) { + if (vendor_id == supportedVid) { + return true; + } + } + } + return false; + } + + private void handleUsbDeviceAttached(UsbDevice usbDevice) { + connectHIDDeviceUSB(usbDevice); + } + + private void handleUsbDeviceDetached(UsbDevice usbDevice) { + List devices = new ArrayList(); + for (HIDDevice device : mDevicesById.values()) { + if (usbDevice.equals(device.getDevice())) { + devices.add(device.getId()); + } + } + for (int id : devices) { + HIDDevice device = mDevicesById.get(id); + mDevicesById.remove(id); + device.shutdown(); + HIDDeviceDisconnected(id); + } + } + + private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) { + for (HIDDevice device : mDevicesById.values()) { + if (usbDevice.equals(device.getDevice())) { + boolean opened = false; + if (permission_granted) { + opened = device.open(); + } + HIDDeviceOpenResult(device.getId(), opened); + } + } + } + + private void connectHIDDeviceUSB(UsbDevice usbDevice) { + synchronized (this) { + int interface_mask = 0; + for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) { + UsbInterface usbInterface = usbDevice.getInterface(interface_index); + if (isHIDDeviceInterface(usbDevice, usbInterface)) { + // Check to see if we've already added this interface + // This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive + int interface_id = usbInterface.getId(); + if ((interface_mask & (1 << interface_id)) != 0) { + continue; + } + interface_mask |= (1 << interface_id); + + HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index); + int id = device.getId(); + mDevicesById.put(id, device); + HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol()); + } + } + } + } + + private void initializeBluetooth() { + Log.d(TAG, "Initializing Bluetooth"); + + if (Build.VERSION.SDK_INT >= 31 /* Android 12 */ && + mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH_CONNECT, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH_CONNECT"); + return; + } + + if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ && + mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH"); + return; + } + + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) { + Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE"); + return; + } + + // Find bonded bluetooth controllers and create SteamControllers for them + mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE); + if (mBluetoothManager == null) { + // This device doesn't support Bluetooth. + return; + } + + BluetoothAdapter btAdapter = mBluetoothManager.getAdapter(); + if (btAdapter == null) { + // This device has Bluetooth support in the codebase, but has no available adapters. + return; + } + + // Get our bonded devices. + for (BluetoothDevice device : btAdapter.getBondedDevices()) { + + Log.d(TAG, "Bluetooth device available: " + device); + if (isSteamController(device)) { + connectBluetoothDevice(device); + } + + } + + // NOTE: These don't work on Chromebooks, to my undying dismay. + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + mContext.registerReceiver(mBluetoothBroadcast, filter, Context.RECEIVER_EXPORTED); + } else { + mContext.registerReceiver(mBluetoothBroadcast, filter); + } + + if (mIsChromebook) { + mHandler = new Handler(Looper.getMainLooper()); + mLastBluetoothDevices = new ArrayList(); + + // final HIDDeviceManager finalThis = this; + // mHandler.postDelayed(new Runnable() { + // @Override + // public void run() { + // finalThis.chromebookConnectionHandler(); + // } + // }, 5000); + } + } + + private void shutdownBluetooth() { + try { + mContext.unregisterReceiver(mBluetoothBroadcast); + } catch (Exception e) { + // We may not have registered, that's okay + } + } + + // Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly. + // This function provides a sort of dummy version of that, watching for changes in the + // connected devices and attempting to add controllers as things change. + public void chromebookConnectionHandler() { + if (!mIsChromebook) { + return; + } + + ArrayList disconnected = new ArrayList(); + ArrayList connected = new ArrayList(); + + List currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT); + + for (BluetoothDevice bluetoothDevice : currentConnected) { + if (!mLastBluetoothDevices.contains(bluetoothDevice)) { + connected.add(bluetoothDevice); + } + } + for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) { + if (!currentConnected.contains(bluetoothDevice)) { + disconnected.add(bluetoothDevice); + } + } + + mLastBluetoothDevices = currentConnected; + + for (BluetoothDevice bluetoothDevice : disconnected) { + disconnectBluetoothDevice(bluetoothDevice); + } + for (BluetoothDevice bluetoothDevice : connected) { + connectBluetoothDevice(bluetoothDevice); + } + + final HIDDeviceManager finalThis = this; + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + finalThis.chromebookConnectionHandler(); + } + }, 10000); + } + + public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) { + Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice); + synchronized (this) { + if (mBluetoothDevices.containsKey(bluetoothDevice)) { + Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect"); + + HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice); + device.reconnect(); + + return false; + } + HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice); + int id = device.getId(); + mBluetoothDevices.put(bluetoothDevice, device); + mDevicesById.put(id, device); + + // The Steam Controller will mark itself connected once initialization is complete + } + return true; + } + + public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) { + synchronized (this) { + HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice); + if (device == null) + return; + + int id = device.getId(); + mBluetoothDevices.remove(bluetoothDevice); + mDevicesById.remove(id); + device.shutdown(); + HIDDeviceDisconnected(id); + } + } + + public boolean isSteamController(BluetoothDevice bluetoothDevice) { + // Sanity check. If you pass in a null device, by definition it is never a Steam Controller. + if (bluetoothDevice == null) { + return false; + } + + // If the device has no local name, we really don't want to try an equality check against it. + if (bluetoothDevice.getName() == null) { + return false; + } + + return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0); + } + + private void close() { + shutdownUSB(); + shutdownBluetooth(); + synchronized (this) { + for (HIDDevice device : mDevicesById.values()) { + device.shutdown(); + } + mDevicesById.clear(); + mBluetoothDevices.clear(); + HIDDeviceReleaseCallback(); + } + } + + public void setFrozen(boolean frozen) { + synchronized (this) { + for (HIDDevice device : mDevicesById.values()) { + device.setFrozen(frozen); + } + } + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + private HIDDevice getDevice(int id) { + synchronized (this) { + HIDDevice result = mDevicesById.get(id); + if (result == null) { + Log.v(TAG, "No device for id: " + id); + Log.v(TAG, "Available devices: " + mDevicesById.keySet()); + } + return result; + } + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////// JNI interface functions + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + public boolean initialize(boolean usb, boolean bluetooth) { + Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")"); + + if (usb) { + initializeUSB(); + } + if (bluetooth) { + initializeBluetooth(); + } + return true; + } + + public boolean openDevice(int deviceID) { + Log.v(TAG, "openDevice deviceID=" + deviceID); + HIDDevice device = getDevice(deviceID); + if (device == null) { + HIDDeviceDisconnected(deviceID); + return false; + } + + // Look to see if this is a USB device and we have permission to access it + UsbDevice usbDevice = device.getDevice(); + if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) { + HIDDeviceOpenPending(deviceID); + try { + final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31 + int flags; + if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { + flags = FLAG_MUTABLE; + } else { + flags = 0; + } + mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags)); + } catch (Exception e) { + Log.v(TAG, "Couldn't request permission for USB device " + usbDevice); + HIDDeviceOpenResult(deviceID, false); + } + return false; + } + + try { + return device.open(); + } catch (Exception e) { + Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); + } + return false; + } + + public int writeReport(int deviceID, byte[] report, boolean feature) { + try { + //Log.v(TAG, "writeReport deviceID=" + deviceID + " length=" + report.length); + HIDDevice device; + device = getDevice(deviceID); + if (device == null) { + HIDDeviceDisconnected(deviceID); + return -1; + } + + return device.writeReport(report, feature); + } catch (Exception e) { + Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); + } + return -1; + } + + public boolean readReport(int deviceID, byte[] report, boolean feature) { + try { + //Log.v(TAG, "readReport deviceID=" + deviceID); + HIDDevice device; + device = getDevice(deviceID); + if (device == null) { + HIDDeviceDisconnected(deviceID); + return false; + } + + return device.readReport(report, feature); + } catch (Exception e) { + Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); + } + return false; + } + + public void closeDevice(int deviceID) { + try { + Log.v(TAG, "closeDevice deviceID=" + deviceID); + HIDDevice device; + device = getDevice(deviceID); + if (device == null) { + HIDDeviceDisconnected(deviceID); + return; + } + + device.close(); + } catch (Exception e) { + Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); + } + } + + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////// Native methods + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + private native void HIDDeviceRegisterCallback(); + private native void HIDDeviceReleaseCallback(); + + native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol); + native void HIDDeviceOpenPending(int deviceID); + native void HIDDeviceOpenResult(int deviceID, boolean opened); + native void HIDDeviceDisconnected(int deviceID); + + native void HIDDeviceInputReport(int deviceID, byte[] report); + native void HIDDeviceReportResponse(int deviceID, byte[] report); +} diff --git a/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java b/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java new file mode 100644 index 0000000..5b5d201 --- /dev/null +++ b/story-player-sdl/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java @@ -0,0 +1,308 @@ +package org.libsdl.app; + +import android.hardware.usb.*; +import android.os.Build; +import android.util.Log; +import java.util.Arrays; + +class HIDDeviceUSB implements HIDDevice { + + private static final String TAG = "hidapi"; + + protected HIDDeviceManager mManager; + protected UsbDevice mDevice; + protected int mInterfaceIndex; + protected int mInterface; + protected int mDeviceId; + protected UsbDeviceConnection mConnection; + protected UsbEndpoint mInputEndpoint; + protected UsbEndpoint mOutputEndpoint; + protected InputThread mInputThread; + protected boolean mRunning; + protected boolean mFrozen; + + public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) { + mManager = manager; + mDevice = usbDevice; + mInterfaceIndex = interface_index; + mInterface = mDevice.getInterface(mInterfaceIndex).getId(); + mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier()); + mRunning = false; + } + + public String getIdentifier() { + return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex); + } + + @Override + public int getId() { + return mDeviceId; + } + + @Override + public int getVendorId() { + return mDevice.getVendorId(); + } + + @Override + public int getProductId() { + return mDevice.getProductId(); + } + + @Override + public String getSerialNumber() { + String result = null; + if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) { + try { + result = mDevice.getSerialNumber(); + } + catch (SecurityException exception) { + //Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage()); + } + } + if (result == null) { + result = ""; + } + return result; + } + + @Override + public int getVersion() { + return 0; + } + + @Override + public String getManufacturerName() { + String result = null; + if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) { + result = mDevice.getManufacturerName(); + } + if (result == null) { + result = String.format("%x", getVendorId()); + } + return result; + } + + @Override + public String getProductName() { + String result = null; + if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) { + result = mDevice.getProductName(); + } + if (result == null) { + result = String.format("%x", getProductId()); + } + return result; + } + + @Override + public UsbDevice getDevice() { + return mDevice; + } + + public String getDeviceName() { + return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")"; + } + + @Override + public boolean open() { + mConnection = mManager.getUSBManager().openDevice(mDevice); + if (mConnection == null) { + Log.w(TAG, "Unable to open USB device " + getDeviceName()); + return false; + } + + // Force claim our interface + UsbInterface iface = mDevice.getInterface(mInterfaceIndex); + if (!mConnection.claimInterface(iface, true)) { + Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName()); + close(); + return false; + } + + // Find the endpoints + for (int j = 0; j < iface.getEndpointCount(); j++) { + UsbEndpoint endpt = iface.getEndpoint(j); + switch (endpt.getDirection()) { + case UsbConstants.USB_DIR_IN: + if (mInputEndpoint == null) { + mInputEndpoint = endpt; + } + break; + case UsbConstants.USB_DIR_OUT: + if (mOutputEndpoint == null) { + mOutputEndpoint = endpt; + } + break; + } + } + + // Make sure the required endpoints were present + if (mInputEndpoint == null || mOutputEndpoint == null) { + Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName()); + close(); + return false; + } + + // Start listening for input + mRunning = true; + mInputThread = new InputThread(); + mInputThread.start(); + + return true; + } + + @Override + public int writeReport(byte[] report, boolean feature) { + if (feature) { + int res = -1; + int offset = 0; + int length = report.length; + boolean skipped_report_id = false; + byte report_number = report[0]; + + if (report_number == 0x0) { + ++offset; + --length; + skipped_report_id = true; + } + + res = mConnection.controlTransfer( + UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT, + 0x09/*HID set_report*/, + (3/*HID feature*/ << 8) | report_number, + mInterface, + report, offset, length, + 1000/*timeout millis*/); + + if (res < 0) { + Log.w(TAG, "writeFeatureReport() returned " + res + " on device " + getDeviceName()); + return -1; + } + + if (skipped_report_id) { + ++length; + } + return length; + } else { + int res = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000); + if (res != report.length) { + Log.w(TAG, "writeOutputReport() returned " + res + " on device " + getDeviceName()); + } + return res; + } + } + + @Override + public boolean readReport(byte[] report, boolean feature) { + int res = -1; + int offset = 0; + int length = report.length; + boolean skipped_report_id = false; + byte report_number = report[0]; + + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + ++offset; + --length; + skipped_report_id = true; + } + + res = mConnection.controlTransfer( + UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN, + 0x01/*HID get_report*/, + ((feature ? 3/*HID feature*/ : 1/*HID Input*/) << 8) | report_number, + mInterface, + report, offset, length, + 1000/*timeout millis*/); + + if (res < 0) { + Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName()); + return false; + } + + if (skipped_report_id) { + ++res; + ++length; + } + + byte[] data; + if (res == length) { + data = report; + } else { + data = Arrays.copyOfRange(report, 0, res); + } + mManager.HIDDeviceReportResponse(mDeviceId, data); + + return true; + } + + @Override + public void close() { + mRunning = false; + if (mInputThread != null) { + while (mInputThread.isAlive()) { + mInputThread.interrupt(); + try { + mInputThread.join(); + } catch (InterruptedException e) { + // Keep trying until we're done + } + } + mInputThread = null; + } + if (mConnection != null) { + UsbInterface iface = mDevice.getInterface(mInterfaceIndex); + mConnection.releaseInterface(iface); + mConnection.close(); + mConnection = null; + } + } + + @Override + public void shutdown() { + close(); + mManager = null; + } + + @Override + public void setFrozen(boolean frozen) { + mFrozen = frozen; + } + + protected class InputThread extends Thread { + @Override + public void run() { + int packetSize = mInputEndpoint.getMaxPacketSize(); + byte[] packet = new byte[packetSize]; + while (mRunning) { + int r; + try + { + r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000); + } + catch (Exception e) + { + Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e); + break; + } + if (r < 0) { + // Could be a timeout or an I/O error + } + if (r > 0) { + byte[] data; + if (r == packetSize) { + data = packet; + } else { + data = Arrays.copyOfRange(packet, 0, r); + } + + if (!mFrozen) { + mManager.HIDDeviceInputReport(mDeviceId, data); + } + } + } + } + } +} diff --git a/story-player-sdl/app/src/main/java/org/libsdl/app/SDL.java b/story-player-sdl/app/src/main/java/org/libsdl/app/SDL.java new file mode 100644 index 0000000..3e3ec94 --- /dev/null +++ b/story-player-sdl/app/src/main/java/org/libsdl/app/SDL.java @@ -0,0 +1,86 @@ +package org.libsdl.app; + +import android.content.Context; + +import java.lang.Class; +import java.lang.reflect.Method; + +/** + SDL library initialization +*/ +public class SDL { + + // This function should be called first and sets up the native code + // so it can call into the Java classes + public static void setupJNI() { + SDLActivity.nativeSetupJNI(); + SDLAudioManager.nativeSetupJNI(); + SDLControllerManager.nativeSetupJNI(); + } + + // This function should be called each time the activity is started + public static void initialize() { + setContext(null); + + SDLActivity.initialize(); + SDLAudioManager.initialize(); + SDLControllerManager.initialize(); + } + + // This function stores the current activity (SDL or not) + public static void setContext(Context context) { + SDLAudioManager.setContext(context); + mContext = context; + } + + public static Context getContext() { + return mContext; + } + + public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException { + + if (libraryName == null) { + throw new NullPointerException("No library name provided."); + } + + try { + // Let's see if we have ReLinker available in the project. This is necessary for + // some projects that have huge numbers of local libraries bundled, and thus may + // trip a bug in Android's native library loader which ReLinker works around. (If + // loadLibrary works properly, ReLinker will simply use the normal Android method + // internally.) + // + // To use ReLinker, just add it as a dependency. For more information, see + // https://github.com/KeepSafe/ReLinker for ReLinker's repository. + // + Class relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker"); + Class relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener"); + Class contextClass = mContext.getClassLoader().loadClass("android.content.Context"); + Class stringClass = mContext.getClassLoader().loadClass("java.lang.String"); + + // Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if + // they've changed during updates. + Method forceMethod = relinkClass.getDeclaredMethod("force"); + Object relinkInstance = forceMethod.invoke(null); + Class relinkInstanceClass = relinkInstance.getClass(); + + // Actually load the library! + Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass); + loadMethod.invoke(relinkInstance, mContext, libraryName, null, null); + } + catch (final Throwable e) { + // Fall back + try { + System.loadLibrary(libraryName); + } + catch (final UnsatisfiedLinkError ule) { + throw ule; + } + catch (final SecurityException se) { + throw se; + } + } + } + + protected static Context mContext; +} diff --git a/story-player-sdl/app/src/main/java/org/libsdl/app/SDLActivity.java b/story-player-sdl/app/src/main/java/org/libsdl/app/SDLActivity.java new file mode 100644 index 0000000..a560662 --- /dev/null +++ b/story-player-sdl/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -0,0 +1,2016 @@ +package org.libsdl.app; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.UiModeManager; +import android.content.ClipboardManager; +import android.content.ClipData; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.hardware.Sensor; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.SparseArray; +import android.view.Display; +import android.view.Gravity; +import android.view.InputDevice; +import android.view.KeyEvent; +import android.view.PointerIcon; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.Hashtable; +import java.util.Locale; + + +/** + SDL Activity +*/ +public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener { + private static final String TAG = "SDL"; + private static final int SDL_MAJOR_VERSION = 3; + private static final int SDL_MINOR_VERSION = 1; + private static final int SDL_MICRO_VERSION = 0; +/* + // Display InputType.SOURCE/CLASS of events and devices + // + // SDLActivity.debugSource(device.getSources(), "device[" + device.getName() + "]"); + // SDLActivity.debugSource(event.getSource(), "event"); + public static void debugSource(int sources, String prefix) { + int s = sources; + int s_copy = sources; + String cls = ""; + String src = ""; + int tst = 0; + int FLAG_TAINTED = 0x80000000; + + if ((s & InputDevice.SOURCE_CLASS_BUTTON) != 0) cls += " BUTTON"; + if ((s & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) cls += " JOYSTICK"; + if ((s & InputDevice.SOURCE_CLASS_POINTER) != 0) cls += " POINTER"; + if ((s & InputDevice.SOURCE_CLASS_POSITION) != 0) cls += " POSITION"; + if ((s & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) cls += " TRACKBALL"; + + + int s2 = s_copy & ~InputDevice.SOURCE_ANY; // keep class bits + s2 &= ~( InputDevice.SOURCE_CLASS_BUTTON + | InputDevice.SOURCE_CLASS_JOYSTICK + | InputDevice.SOURCE_CLASS_POINTER + | InputDevice.SOURCE_CLASS_POSITION + | InputDevice.SOURCE_CLASS_TRACKBALL); + + if (s2 != 0) cls += "Some_Unkown"; + + s2 = s_copy & InputDevice.SOURCE_ANY; // keep source only, no class; + + if (Build.VERSION.SDK_INT >= 23) { + tst = InputDevice.SOURCE_BLUETOOTH_STYLUS; + if ((s & tst) == tst) src += " BLUETOOTH_STYLUS"; + s2 &= ~tst; + } + + tst = InputDevice.SOURCE_DPAD; + if ((s & tst) == tst) src += " DPAD"; + s2 &= ~tst; + + tst = InputDevice.SOURCE_GAMEPAD; + if ((s & tst) == tst) src += " GAMEPAD"; + s2 &= ~tst; + + if (Build.VERSION.SDK_INT >= 21) { + tst = InputDevice.SOURCE_HDMI; + if ((s & tst) == tst) src += " HDMI"; + s2 &= ~tst; + } + + tst = InputDevice.SOURCE_JOYSTICK; + if ((s & tst) == tst) src += " JOYSTICK"; + s2 &= ~tst; + + tst = InputDevice.SOURCE_KEYBOARD; + if ((s & tst) == tst) src += " KEYBOARD"; + s2 &= ~tst; + + tst = InputDevice.SOURCE_MOUSE; + if ((s & tst) == tst) src += " MOUSE"; + s2 &= ~tst; + + if (Build.VERSION.SDK_INT >= 26) { + tst = InputDevice.SOURCE_MOUSE_RELATIVE; + if ((s & tst) == tst) src += " MOUSE_RELATIVE"; + s2 &= ~tst; + + tst = InputDevice.SOURCE_ROTARY_ENCODER; + if ((s & tst) == tst) src += " ROTARY_ENCODER"; + s2 &= ~tst; + } + tst = InputDevice.SOURCE_STYLUS; + if ((s & tst) == tst) src += " STYLUS"; + s2 &= ~tst; + + tst = InputDevice.SOURCE_TOUCHPAD; + if ((s & tst) == tst) src += " TOUCHPAD"; + s2 &= ~tst; + + tst = InputDevice.SOURCE_TOUCHSCREEN; + if ((s & tst) == tst) src += " TOUCHSCREEN"; + s2 &= ~tst; + + if (Build.VERSION.SDK_INT >= 18) { + tst = InputDevice.SOURCE_TOUCH_NAVIGATION; + if ((s & tst) == tst) src += " TOUCH_NAVIGATION"; + s2 &= ~tst; + } + + tst = InputDevice.SOURCE_TRACKBALL; + if ((s & tst) == tst) src += " TRACKBALL"; + s2 &= ~tst; + + tst = InputDevice.SOURCE_ANY; + if ((s & tst) == tst) src += " ANY"; + s2 &= ~tst; + + if (s == FLAG_TAINTED) src += " FLAG_TAINTED"; + s2 &= ~FLAG_TAINTED; + + if (s2 != 0) src += " Some_Unkown"; + + Log.v(TAG, prefix + "int=" + s_copy + " CLASS={" + cls + " } source(s):" + src); + } +*/ + + public static boolean mIsResumedCalled, mHasFocus; + public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */); + + // Cursor types + // private static final int SDL_SYSTEM_CURSOR_NONE = -1; + private static final int SDL_SYSTEM_CURSOR_ARROW = 0; + private static final int SDL_SYSTEM_CURSOR_IBEAM = 1; + private static final int SDL_SYSTEM_CURSOR_WAIT = 2; + private static final int SDL_SYSTEM_CURSOR_CROSSHAIR = 3; + private static final int SDL_SYSTEM_CURSOR_WAITARROW = 4; + private static final int SDL_SYSTEM_CURSOR_SIZENWSE = 5; + private static final int SDL_SYSTEM_CURSOR_SIZENESW = 6; + private static final int SDL_SYSTEM_CURSOR_SIZEWE = 7; + private static final int SDL_SYSTEM_CURSOR_SIZENS = 8; + private static final int SDL_SYSTEM_CURSOR_SIZEALL = 9; + private static final int SDL_SYSTEM_CURSOR_NO = 10; + private static final int SDL_SYSTEM_CURSOR_HAND = 11; + private static final int SDL_SYSTEM_CURSOR_WINDOW_TOPLEFT = 12; + private static final int SDL_SYSTEM_CURSOR_WINDOW_TOP = 13; + private static final int SDL_SYSTEM_CURSOR_WINDOW_TOPRIGHT = 14; + private static final int SDL_SYSTEM_CURSOR_WINDOW_RIGHT = 15; + private static final int SDL_SYSTEM_CURSOR_WINDOW_BOTTOMRIGHT = 16; + private static final int SDL_SYSTEM_CURSOR_WINDOW_BOTTOM = 17; + private static final int SDL_SYSTEM_CURSOR_WINDOW_BOTTOMLEFT = 18; + private static final int SDL_SYSTEM_CURSOR_WINDOW_LEFT = 19; + + protected static final int SDL_ORIENTATION_UNKNOWN = 0; + protected static final int SDL_ORIENTATION_LANDSCAPE = 1; + protected static final int SDL_ORIENTATION_LANDSCAPE_FLIPPED = 2; + protected static final int SDL_ORIENTATION_PORTRAIT = 3; + protected static final int SDL_ORIENTATION_PORTRAIT_FLIPPED = 4; + + protected static int mCurrentRotation; + protected static Locale mCurrentLocale; + + // Handle the state of the native layer + public enum NativeState { + INIT, RESUMED, PAUSED + } + + public static NativeState mNextNativeState; + public static NativeState mCurrentNativeState; + + /** If shared libraries (e.g. SDL or the native application) could not be loaded. */ + public static boolean mBrokenLibraries = true; + + // Main components + protected static SDLActivity mSingleton; + protected static SDLSurface mSurface; + protected static SDLDummyEdit mTextEdit; + protected static boolean mScreenKeyboardShown; + protected static ViewGroup mLayout; + protected static SDLClipboardHandler mClipboardHandler; + protected static Hashtable mCursors; + protected static int mLastCursorID; + protected static SDLGenericMotionListener_API12 mMotionListener; + protected static HIDDeviceManager mHIDDeviceManager; + + // This is what SDL runs in. It invokes SDL_main(), eventually + protected static Thread mSDLThread; + protected static boolean mSDLMainFinished = false; + protected static boolean mActivityCreated = false; + + protected static SDLGenericMotionListener_API12 getMotionListener() { + if (mMotionListener == null) { + if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) { + mMotionListener = new SDLGenericMotionListener_API26(); + } else if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { + mMotionListener = new SDLGenericMotionListener_API24(); + } else { + mMotionListener = new SDLGenericMotionListener_API12(); + } + } + + return mMotionListener; + } + + /** + * This method creates a Runnable which invokes SDL_main. The default implementation + * uses the getMainSharedObject() and getMainFunction() methods to invoke native + * code from the specified shared library. + */ + protected Runnable createSDLMainRunnable() { + return new SDLMain(); + } + + /** + * This method returns the name of the shared object with the application entry point + * It can be overridden by derived classes. + */ + protected String getMainSharedObject() { + String library; + String[] libraries = SDLActivity.mSingleton.getLibraries(); + if (libraries.length > 0) { + library = "lib" + libraries[libraries.length - 1] + ".so"; + } else { + library = "libmain.so"; + } + return getContext().getApplicationInfo().nativeLibraryDir + "/" + library; + } + + /** + * This method returns the name of the application entry point + * It can be overridden by derived classes. + */ + protected String getMainFunction() { + return "SDL_main"; + } + + /** + * This method is called by SDL before loading the native shared libraries. + * It can be overridden to provide names of shared libraries to be loaded. + * The default implementation returns the defaults. It never returns null. + * An array returned by a new implementation must at least contain "SDL3". + * Also keep in mind that the order the libraries are loaded may matter. + * @return names of shared libraries to be loaded (e.g. "SDL3", "main"). + */ + protected String[] getLibraries() { + return new String[] { + "SDL3", + // "SDL3_image", + // "SDL3_mixer", + // "SDL3_net", + // "SDL3_ttf", + "main" + }; + } + + // Load the .so + public void loadLibraries() { + for (String lib : getLibraries()) { + SDL.loadLibrary(lib); + } + } + + /** + * This method is called by SDL before starting the native application thread. + * It can be overridden to provide the arguments after the application name. + * The default implementation returns an empty array. It never returns null. + * @return arguments for the native application. + */ + protected String[] getArguments() { + return new String[0]; + } + + public static void initialize() { + // The static nature of the singleton and Android quirkyness force us to initialize everything here + // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values + mSingleton = null; + mSurface = null; + mTextEdit = null; + mLayout = null; + mClipboardHandler = null; + mCursors = new Hashtable(); + mLastCursorID = 0; + mSDLThread = null; + mIsResumedCalled = false; + mHasFocus = true; + mNextNativeState = NativeState.INIT; + mCurrentNativeState = NativeState.INIT; + } + + protected SDLSurface createSDLSurface(Context context) { + return new SDLSurface(context); + } + + // Setup + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.v(TAG, "Device: " + Build.DEVICE); + Log.v(TAG, "Model: " + Build.MODEL); + Log.v(TAG, "onCreate()"); + super.onCreate(savedInstanceState); + + + /* Control activity re-creation */ + if (mSDLMainFinished || mActivityCreated) { + boolean allow_recreate = SDLActivity.nativeAllowRecreateActivity(); + if (mSDLMainFinished) { + Log.v(TAG, "SDL main() finished"); + } + if (allow_recreate) { + Log.v(TAG, "activity re-created"); + } else { + Log.v(TAG, "activity finished"); + System.exit(0); + return; + } + } + + mActivityCreated = true; + + try { + Thread.currentThread().setName("SDLActivity"); + } catch (Exception e) { + Log.v(TAG, "modify thread properties failed " + e.toString()); + } + + // Load shared libraries + String errorMsgBrokenLib = ""; + try { + loadLibraries(); + mBrokenLibraries = false; /* success */ + } catch(UnsatisfiedLinkError e) { + System.err.println(e.getMessage()); + mBrokenLibraries = true; + errorMsgBrokenLib = e.getMessage(); + } catch(Exception e) { + System.err.println(e.getMessage()); + mBrokenLibraries = true; + errorMsgBrokenLib = e.getMessage(); + } + + if (!mBrokenLibraries) { + String expected_version = String.valueOf(SDL_MAJOR_VERSION) + "." + + String.valueOf(SDL_MINOR_VERSION) + "." + + String.valueOf(SDL_MICRO_VERSION); + String version = nativeGetVersion(); + if (!version.equals(expected_version)) { + mBrokenLibraries = true; + errorMsgBrokenLib = "SDL C/Java version mismatch (expected " + expected_version + ", got " + version + ")"; + } + } + + if (mBrokenLibraries) { + mSingleton = this; + AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this); + dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall." + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + "Error: " + errorMsgBrokenLib); + dlgAlert.setTitle("SDL Error"); + dlgAlert.setPositiveButton("Exit", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog,int id) { + // if this button is clicked, close current activity + SDLActivity.mSingleton.finish(); + } + }); + dlgAlert.setCancelable(false); + dlgAlert.create().show(); + + return; + } + + + /* Control activity re-creation */ + /* Robustness: check that the native code is run for the first time. + * (Maybe Activity was reset, but not the native code.) */ + { + int run_count = SDLActivity.nativeCheckSDLThreadCounter(); /* get and increment a native counter */ + if (run_count != 0) { + boolean allow_recreate = SDLActivity.nativeAllowRecreateActivity(); + if (allow_recreate) { + Log.v(TAG, "activity re-created // run_count: " + run_count); + } else { + Log.v(TAG, "activity finished // run_count: " + run_count); + System.exit(0); + return; + } + } + } + + // Set up JNI + SDL.setupJNI(); + + // Initialize state + SDL.initialize(); + + // So we can call stuff from static callbacks + mSingleton = this; + SDL.setContext(this); + + mClipboardHandler = new SDLClipboardHandler(); + + mHIDDeviceManager = HIDDeviceManager.acquire(this); + + // Set up the surface + mSurface = createSDLSurface(this); + + mLayout = new RelativeLayout(this); + mLayout.addView(mSurface); + + // Get our current screen orientation and pass it down. + SDLActivity.nativeSetNaturalOrientation(SDLActivity.getNaturalOrientation()); + mCurrentRotation = SDLActivity.getCurrentRotation(); + SDLActivity.onNativeRotationChanged(mCurrentRotation); + + try { + if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) { + mCurrentLocale = getContext().getResources().getConfiguration().locale; + } else { + mCurrentLocale = getContext().getResources().getConfiguration().getLocales().get(0); + } + } catch(Exception ignored) { + } + + switch (getContext().getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) { + case Configuration.UI_MODE_NIGHT_NO: + SDLActivity.onNativeDarkModeChanged(false); + break; + case Configuration.UI_MODE_NIGHT_YES: + SDLActivity.onNativeDarkModeChanged(true); + break; + } + + setContentView(mLayout); + + setWindowStyle(false); + + getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this); + + // Get filename from "Open with" of another application + Intent intent = getIntent(); + if (intent != null && intent.getData() != null) { + String filename = intent.getData().getPath(); + if (filename != null) { + Log.v(TAG, "Got filename: " + filename); + SDLActivity.onNativeDropFile(filename); + } + } + } + + protected void pauseNativeThread() { + mNextNativeState = NativeState.PAUSED; + mIsResumedCalled = false; + + if (SDLActivity.mBrokenLibraries) { + return; + } + + SDLActivity.handleNativeState(); + } + + protected void resumeNativeThread() { + mNextNativeState = NativeState.RESUMED; + mIsResumedCalled = true; + + if (SDLActivity.mBrokenLibraries) { + return; + } + + SDLActivity.handleNativeState(); + } + + // Events + @Override + protected void onPause() { + Log.v(TAG, "onPause()"); + super.onPause(); + + if (mHIDDeviceManager != null) { + mHIDDeviceManager.setFrozen(true); + } + if (!mHasMultiWindow) { + pauseNativeThread(); + } + } + + @Override + protected void onResume() { + Log.v(TAG, "onResume()"); + super.onResume(); + + if (mHIDDeviceManager != null) { + mHIDDeviceManager.setFrozen(false); + } + if (!mHasMultiWindow) { + resumeNativeThread(); + } + } + + @Override + protected void onStop() { + Log.v(TAG, "onStop()"); + super.onStop(); + if (mHasMultiWindow) { + pauseNativeThread(); + } + } + + @Override + protected void onStart() { + Log.v(TAG, "onStart()"); + super.onStart(); + if (mHasMultiWindow) { + resumeNativeThread(); + } + } + + public static int getNaturalOrientation() { + int result = SDL_ORIENTATION_UNKNOWN; + + Activity activity = (Activity)getContext(); + if (activity != null) { + Configuration config = activity.getResources().getConfiguration(); + Display display = activity.getWindowManager().getDefaultDisplay(); + int rotation = display.getRotation(); + if (((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) && + config.orientation == Configuration.ORIENTATION_LANDSCAPE) || + ((rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) && + config.orientation == Configuration.ORIENTATION_PORTRAIT)) { + result = SDL_ORIENTATION_LANDSCAPE; + } else { + result = SDL_ORIENTATION_PORTRAIT; + } + } + return result; + } + + public static int getCurrentRotation() { + int result = 0; + + Activity activity = (Activity)getContext(); + if (activity != null) { + Display display = activity.getWindowManager().getDefaultDisplay(); + switch (display.getRotation()) { + case Surface.ROTATION_0: + result = 0; + break; + case Surface.ROTATION_90: + result = 90; + break; + case Surface.ROTATION_180: + result = 180; + break; + case Surface.ROTATION_270: + result = 270; + break; + } + } + return result; + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + Log.v(TAG, "onWindowFocusChanged(): " + hasFocus); + + if (SDLActivity.mBrokenLibraries) { + return; + } + + mHasFocus = hasFocus; + if (hasFocus) { + mNextNativeState = NativeState.RESUMED; + SDLActivity.getMotionListener().reclaimRelativeMouseModeIfNeeded(); + + SDLActivity.handleNativeState(); + nativeFocusChanged(true); + + } else { + nativeFocusChanged(false); + if (!mHasMultiWindow) { + mNextNativeState = NativeState.PAUSED; + SDLActivity.handleNativeState(); + } + } + } + + @Override + public void onLowMemory() { + Log.v(TAG, "onLowMemory()"); + super.onLowMemory(); + + if (SDLActivity.mBrokenLibraries) { + return; + } + + SDLActivity.nativeLowMemory(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + Log.v(TAG, "onConfigurationChanged()"); + super.onConfigurationChanged(newConfig); + + if (SDLActivity.mBrokenLibraries) { + return; + } + + if (mCurrentLocale == null || !mCurrentLocale.equals(newConfig.locale)) { + mCurrentLocale = newConfig.locale; + SDLActivity.onNativeLocaleChanged(); + } + + switch (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) { + case Configuration.UI_MODE_NIGHT_NO: + SDLActivity.onNativeDarkModeChanged(false); + break; + case Configuration.UI_MODE_NIGHT_YES: + SDLActivity.onNativeDarkModeChanged(true); + break; + } + } + + @Override + protected void onDestroy() { + Log.v(TAG, "onDestroy()"); + + if (mHIDDeviceManager != null) { + HIDDeviceManager.release(mHIDDeviceManager); + mHIDDeviceManager = null; + } + + SDLAudioManager.release(this); + + if (SDLActivity.mBrokenLibraries) { + super.onDestroy(); + return; + } + + if (SDLActivity.mSDLThread != null) { + + // Send Quit event to "SDLThread" thread + SDLActivity.nativeSendQuit(); + + // Wait for "SDLThread" thread to end + try { + // Use a timeout because: + // C SDLmain() thread might have started (mSDLThread.start() called) + // while the SDL_Init() might not have been called yet, + // and so the previous QUIT event will be discarded by SDL_Init() and app is running, not exiting. + SDLActivity.mSDLThread.join(1000); + } catch(Exception e) { + Log.v(TAG, "Problem stopping SDLThread: " + e); + } + } + + SDLActivity.nativeQuit(); + + super.onDestroy(); + } + + @Override + public void onBackPressed() { + // Check if we want to block the back button in case of mouse right click. + // + // If we do, the normal hardware back button will no longer work and people have to use home, + // but the mouse right click will work. + // + boolean trapBack = SDLActivity.nativeGetHintBoolean("SDL_ANDROID_TRAP_BACK_BUTTON", false); + if (trapBack) { + // Exit and let the mouse handler handle this button (if appropriate) + return; + } + + // Default system back button behavior. + if (!isFinishing()) { + super.onBackPressed(); + } + } + + // Called by JNI from SDL. + public static void manualBackButton() { + mSingleton.pressBackButton(); + } + + // Used to get us onto the activity's main thread + public void pressBackButton() { + runOnUiThread(new Runnable() { + @Override + public void run() { + if (!SDLActivity.this.isFinishing()) { + SDLActivity.this.superOnBackPressed(); + } + } + }); + } + + // Used to access the system back behavior. + public void superOnBackPressed() { + super.onBackPressed(); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + + if (SDLActivity.mBrokenLibraries) { + return false; + } + + int keyCode = event.getKeyCode(); + // Ignore certain special keys so they're handled by Android + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || + keyCode == KeyEvent.KEYCODE_VOLUME_UP || + keyCode == KeyEvent.KEYCODE_CAMERA || + keyCode == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */ + keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */ + ) { + return false; + } + return super.dispatchKeyEvent(event); + } + + /* Transition to next state */ + public static void handleNativeState() { + + if (mNextNativeState == mCurrentNativeState) { + // Already in same state, discard. + return; + } + + // Try a transition to init state + if (mNextNativeState == NativeState.INIT) { + + mCurrentNativeState = mNextNativeState; + return; + } + + // Try a transition to paused state + if (mNextNativeState == NativeState.PAUSED) { + if (mSDLThread != null) { + nativePause(); + } + if (mSurface != null) { + mSurface.handlePause(); + } + mCurrentNativeState = mNextNativeState; + return; + } + + // Try a transition to resumed state + if (mNextNativeState == NativeState.RESUMED) { + if (mSurface.mIsSurfaceReady && (mHasFocus || mHasMultiWindow) && mIsResumedCalled) { + if (mSDLThread == null) { + // This is the entry point to the C app. + // Start up the C app thread and enable sensor input for the first time + // FIXME: Why aren't we enabling sensor input at start? + + mSDLThread = new Thread(SDLActivity.mSingleton.createSDLMainRunnable(), "SDLThread"); + mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true); + mSDLThread.start(); + + // No nativeResume(), don't signal Android_ResumeSem + } else { + nativeResume(); + } + mSurface.handleResume(); + + mCurrentNativeState = mNextNativeState; + } + } + } + + // Messages from the SDLMain thread + protected static final int COMMAND_CHANGE_TITLE = 1; + protected static final int COMMAND_CHANGE_WINDOW_STYLE = 2; + protected static final int COMMAND_TEXTEDIT_HIDE = 3; + protected static final int COMMAND_SET_KEEP_SCREEN_ON = 5; + protected static final int COMMAND_USER = 0x8000; + + protected static boolean mFullscreenModeActive; + + /** + * This method is called by SDL if SDL did not handle a message itself. + * This happens if a received message contains an unsupported command. + * Method can be overwritten to handle Messages in a different class. + * @param command the command of the message. + * @param param the parameter of the message. May be null. + * @return if the message was handled in overridden method. + */ + protected boolean onUnhandledMessage(int command, Object param) { + return false; + } + + /** + * A Handler class for Messages from native SDL applications. + * It uses current Activities as target (e.g. for the title). + * static to prevent implicit references to enclosing object. + */ + protected static class SDLCommandHandler extends Handler { + @Override + public void handleMessage(Message msg) { + Context context = SDL.getContext(); + if (context == null) { + Log.e(TAG, "error handling message, getContext() returned null"); + return; + } + switch (msg.arg1) { + case COMMAND_CHANGE_TITLE: + if (context instanceof Activity) { + ((Activity) context).setTitle((String)msg.obj); + } else { + Log.e(TAG, "error handling message, getContext() returned no Activity"); + } + break; + case COMMAND_CHANGE_WINDOW_STYLE: + if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) { + if (context instanceof Activity) { + Window window = ((Activity) context).getWindow(); + if (window != null) { + if ((msg.obj instanceof Integer) && ((Integer) msg.obj != 0)) { + int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE; + window.getDecorView().setSystemUiVisibility(flags); + window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + SDLActivity.mFullscreenModeActive = true; + } else { + int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE; + window.getDecorView().setSystemUiVisibility(flags); + window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + SDLActivity.mFullscreenModeActive = false; + } + } + } else { + Log.e(TAG, "error handling message, getContext() returned no Activity"); + } + } + break; + case COMMAND_TEXTEDIT_HIDE: + if (mTextEdit != null) { + // Note: On some devices setting view to GONE creates a flicker in landscape. + // Setting the View's sizes to 0 is similar to GONE but without the flicker. + // The sizes will be set to useful values when the keyboard is shown again. + mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0)); + + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); + + mScreenKeyboardShown = false; + + mSurface.requestFocus(); + } + break; + case COMMAND_SET_KEEP_SCREEN_ON: + { + if (context instanceof Activity) { + Window window = ((Activity) context).getWindow(); + if (window != null) { + if ((msg.obj instanceof Integer) && ((Integer) msg.obj != 0)) { + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + } + } + break; + } + default: + if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { + Log.e(TAG, "error handling message, command is " + msg.arg1); + } + } + } + } + + // Handler for the messages + Handler commandHandler = new SDLCommandHandler(); + + // Send a message from the SDLMain thread + protected boolean sendCommand(int command, Object data) { + Message msg = commandHandler.obtainMessage(); + msg.arg1 = command; + msg.obj = data; + boolean result = commandHandler.sendMessage(msg); + + if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) { + if (command == COMMAND_CHANGE_WINDOW_STYLE) { + // Ensure we don't return until the resize has actually happened, + // or 500ms have passed. + + boolean bShouldWait = false; + + if (data instanceof Integer) { + // Let's figure out if we're already laid out fullscreen or not. + Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + DisplayMetrics realMetrics = new DisplayMetrics(); + display.getRealMetrics(realMetrics); + + boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) && + (realMetrics.heightPixels == mSurface.getHeight())); + + if ((Integer) data == 1) { + // If we aren't laid out fullscreen or actively in fullscreen mode already, we're going + // to change size and should wait for surfaceChanged() before we return, so the size + // is right back in native code. If we're already laid out fullscreen, though, we're + // not going to change size even if we change decor modes, so we shouldn't wait for + // surfaceChanged() -- which may not even happen -- and should return immediately. + bShouldWait = !bFullscreenLayout; + } else { + // If we're laid out fullscreen (even if the status bar and nav bar are present), + // or are actively in fullscreen, we're going to change size and should wait for + // surfaceChanged before we return, so the size is right back in native code. + bShouldWait = bFullscreenLayout; + } + } + + if (bShouldWait && (SDLActivity.getContext() != null)) { + // We'll wait for the surfaceChanged() method, which will notify us + // when called. That way, we know our current size is really the + // size we need, instead of grabbing a size that's still got + // the navigation and/or status bars before they're hidden. + // + // We'll wait for up to half a second, because some devices + // take a surprisingly long time for the surface resize, but + // then we'll just give up and return. + // + synchronized (SDLActivity.getContext()) { + try { + SDLActivity.getContext().wait(500); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + } + } + } + + return result; + } + + // C functions we call + public static native String nativeGetVersion(); + public static native int nativeSetupJNI(); + public static native int nativeRunMain(String library, String function, Object arguments); + public static native void nativeLowMemory(); + public static native void nativeSendQuit(); + public static native void nativeQuit(); + public static native void nativePause(); + public static native void nativeResume(); + public static native void nativeFocusChanged(boolean hasFocus); + public static native void onNativeDropFile(String filename); + public static native void nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, float density, float rate); + public static native void onNativeResize(); + public static native void onNativeKeyDown(int keycode); + public static native void onNativeKeyUp(int keycode); + public static native boolean onNativeSoftReturnKey(); + public static native void onNativeKeyboardFocusLost(); + public static native void onNativeMouse(int button, int action, float x, float y, boolean relative); + public static native void onNativeTouch(int touchDevId, int pointerFingerId, + int action, float x, + float y, float p); + public static native void onNativeAccel(float x, float y, float z); + public static native void onNativeClipboardChanged(); + public static native void onNativeSurfaceCreated(); + public static native void onNativeSurfaceChanged(); + public static native void onNativeSurfaceDestroyed(); + public static native String nativeGetHint(String name); + public static native boolean nativeGetHintBoolean(String name, boolean default_value); + public static native void nativeSetenv(String name, String value); + public static native void nativeSetNaturalOrientation(int orientation); + public static native void onNativeRotationChanged(int rotation); + public static native void nativeAddTouch(int touchId, String name); + public static native void nativePermissionResult(int requestCode, boolean result); + public static native void onNativeLocaleChanged(); + public static native void onNativeDarkModeChanged(boolean enabled); + public static native boolean nativeAllowRecreateActivity(); + public static native int nativeCheckSDLThreadCounter(); + + /** + * This method is called by SDL using JNI. + */ + public static boolean setActivityTitle(String title) { + // Called from SDLMain() thread and can't directly affect the view + return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); + } + + /** + * This method is called by SDL using JNI. + */ + public static void setWindowStyle(boolean fullscreen) { + // Called from SDLMain() thread and can't directly affect the view + mSingleton.sendCommand(COMMAND_CHANGE_WINDOW_STYLE, fullscreen ? 1 : 0); + } + + /** + * This method is called by SDL using JNI. + * This is a static method for JNI convenience, it calls a non-static method + * so that is can be overridden + */ + public static void setOrientation(int w, int h, boolean resizable, String hint) + { + if (mSingleton != null) { + mSingleton.setOrientationBis(w, h, resizable, hint); + } + } + + /** + * This can be overridden + */ + public void setOrientationBis(int w, int h, boolean resizable, String hint) + { + int orientation_landscape = -1; + int orientation_portrait = -1; + + /* If set, hint "explicitly controls which UI orientations are allowed". */ + if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) { + orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; + } else if (hint.contains("LandscapeLeft")) { + orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + } else if (hint.contains("LandscapeRight")) { + orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + } + + /* exact match to 'Portrait' to distinguish with PortraitUpsideDown */ + boolean contains_Portrait = hint.contains("Portrait ") || hint.endsWith("Portrait"); + + if (contains_Portrait && hint.contains("PortraitUpsideDown")) { + orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; + } else if (contains_Portrait) { + orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + } else if (hint.contains("PortraitUpsideDown")) { + orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + } + + boolean is_landscape_allowed = (orientation_landscape != -1); + boolean is_portrait_allowed = (orientation_portrait != -1); + int req; /* Requested orientation */ + + /* No valid hint, nothing is explicitly allowed */ + if (!is_portrait_allowed && !is_landscape_allowed) { + if (resizable) { + /* All orientations are allowed */ + req = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR; + } else { + /* Fixed window and nothing specified. Get orientation from w/h of created window */ + req = (w > h ? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); + } + } else { + /* At least one orientation is allowed */ + if (resizable) { + if (is_portrait_allowed && is_landscape_allowed) { + /* hint allows both landscape and portrait, promote to full sensor */ + req = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR; + } else { + /* Use the only one allowed "orientation" */ + req = (is_landscape_allowed ? orientation_landscape : orientation_portrait); + } + } else { + /* Fixed window and both orientations are allowed. Choose one. */ + if (is_portrait_allowed && is_landscape_allowed) { + req = (w > h ? orientation_landscape : orientation_portrait); + } else { + /* Use the only one allowed "orientation" */ + req = (is_landscape_allowed ? orientation_landscape : orientation_portrait); + } + } + } + + Log.v(TAG, "setOrientation() requestedOrientation=" + req + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint); + mSingleton.setRequestedOrientation(req); + } + + /** + * This method is called by SDL using JNI. + */ + public static void minimizeWindow() { + + if (mSingleton == null) { + return; + } + + Intent startMain = new Intent(Intent.ACTION_MAIN); + startMain.addCategory(Intent.CATEGORY_HOME); + startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mSingleton.startActivity(startMain); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean shouldMinimizeOnFocusLoss() { + return false; + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean isScreenKeyboardShown() + { + if (mTextEdit == null) { + return false; + } + + if (!mScreenKeyboardShown) { + return false; + } + + InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + return imm.isAcceptingText(); + + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean supportsRelativeMouse() + { + // DeX mode in Samsung Experience 9.0 and earlier doesn't support relative mice properly under + // Android 7 APIs, and simply returns no data under Android 8 APIs. + // + // This is fixed in Samsung Experience 9.5, which corresponds to Android 8.1.0, and + // thus SDK version 27. If we are in DeX mode and not API 27 or higher, as a result, + // we should stick to relative mode. + // + if (Build.VERSION.SDK_INT < 27 /* Android 8.1 (O_MR1) */ && isDeXMode()) { + return false; + } + + return SDLActivity.getMotionListener().supportsRelativeMouse(); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean setRelativeMouseEnabled(boolean enabled) + { + if (enabled && !supportsRelativeMouse()) { + return false; + } + + return SDLActivity.getMotionListener().setRelativeMouseEnabled(enabled); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean sendMessage(int command, int param) { + if (mSingleton == null) { + return false; + } + return mSingleton.sendCommand(command, param); + } + + /** + * This method is called by SDL using JNI. + */ + public static Context getContext() { + return SDL.getContext(); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean isAndroidTV() { + UiModeManager uiModeManager = (UiModeManager) getContext().getSystemService(UI_MODE_SERVICE); + if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { + return true; + } + if (Build.MANUFACTURER.equals("MINIX") && Build.MODEL.equals("NEO-U1")) { + return true; + } + if (Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.equals("X96-W")) { + return true; + } + return Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.startsWith("TV"); + } + + public static double getDiagonal() + { + DisplayMetrics metrics = new DisplayMetrics(); + Activity activity = (Activity)getContext(); + if (activity == null) { + return 0.0; + } + activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); + + double dWidthInches = metrics.widthPixels / (double)metrics.xdpi; + double dHeightInches = metrics.heightPixels / (double)metrics.ydpi; + + return Math.sqrt((dWidthInches * dWidthInches) + (dHeightInches * dHeightInches)); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean isTablet() { + // If our diagonal size is seven inches or greater, we consider ourselves a tablet. + return (getDiagonal() >= 7.0); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean isChromebook() { + if (getContext() == null) { + return false; + } + return getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean isDeXMode() { + if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) { + return false; + } + try { + final Configuration config = getContext().getResources().getConfiguration(); + final Class configClass = config.getClass(); + return configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass) + == configClass.getField("semDesktopModeEnabled").getInt(config); + } catch(Exception ignored) { + return false; + } + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean getManifestEnvironmentVariables() { + try { + if (getContext() == null) { + return false; + } + + ApplicationInfo applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA); + Bundle bundle = applicationInfo.metaData; + if (bundle == null) { + return false; + } + String prefix = "SDL_ENV."; + final int trimLength = prefix.length(); + for (String key : bundle.keySet()) { + if (key.startsWith(prefix)) { + String name = key.substring(trimLength); + String value = bundle.get(key).toString(); + nativeSetenv(name, value); + } + } + /* environment variables set! */ + return true; + } catch (Exception e) { + Log.v(TAG, "exception " + e.toString()); + } + return false; + } + + // This method is called by SDLControllerManager's API 26 Generic Motion Handler. + public static View getContentView() { + return mLayout; + } + + static class ShowTextInputTask implements Runnable { + /* + * This is used to regulate the pan&scan method to have some offset from + * the bottom edge of the input region and the top edge of an input + * method (soft keyboard) + */ + static final int HEIGHT_PADDING = 15; + + public int x, y, w, h; + + public ShowTextInputTask(int x, int y, int w, int h) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + + /* Minimum size of 1 pixel, so it takes focus. */ + if (this.w <= 0) { + this.w = 1; + } + if (this.h + HEIGHT_PADDING <= 0) { + this.h = 1 - HEIGHT_PADDING; + } + } + + @Override + public void run() { + RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING); + params.leftMargin = x; + params.topMargin = y; + + if (mTextEdit == null) { + mTextEdit = new SDLDummyEdit(SDL.getContext()); + + mLayout.addView(mTextEdit, params); + } else { + mTextEdit.setLayoutParams(params); + } + + mTextEdit.setVisibility(View.VISIBLE); + mTextEdit.requestFocus(); + + InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mTextEdit, 0); + + mScreenKeyboardShown = true; + } + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean showTextInput(int x, int y, int w, int h) { + // Transfer the task to the main thread as a Runnable + return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h)); + } + + public static boolean isTextInputEvent(KeyEvent event) { + + // Key pressed with Ctrl should be sent as SDL_KEYDOWN/SDL_KEYUP and not SDL_TEXTINPUT + if (event.isCtrlPressed()) { + return false; + } + + return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE; + } + + public static boolean handleKeyEvent(View v, int keyCode, KeyEvent event, InputConnection ic) { + int deviceId = event.getDeviceId(); + int source = event.getSource(); + + if (source == InputDevice.SOURCE_UNKNOWN) { + InputDevice device = InputDevice.getDevice(deviceId); + if (device != null) { + source = device.getSources(); + } + } + +// if (event.getAction() == KeyEvent.ACTION_DOWN) { +// Log.v("SDL", "key down: " + keyCode + ", deviceId = " + deviceId + ", source = " + source); +// } else if (event.getAction() == KeyEvent.ACTION_UP) { +// Log.v("SDL", "key up: " + keyCode + ", deviceId = " + deviceId + ", source = " + source); +// } + + // Dispatch the different events depending on where they come from + // Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD + // So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD + // + // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and + // SOURCE_JOYSTICK, while its key events arrive from the keyboard source + // So, retrieve the device itself and check all of its sources + if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) { + // Note that we process events with specific key codes here + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) { + return true; + } + } else if (event.getAction() == KeyEvent.ACTION_UP) { + if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) { + return true; + } + } + } + + if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) { + // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses + // they are ignored here because sending them as mouse input to SDL is messy + if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) { + switch (event.getAction()) { + case KeyEvent.ACTION_DOWN: + case KeyEvent.ACTION_UP: + // mark the event as handled or it will be handled by system + // handling KEYCODE_BACK by system will call onBackPressed() + return true; + } + } + } + + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (isTextInputEvent(event)) { + if (ic != null) { + ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); + } else { + SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1); + } + } + onNativeKeyDown(keyCode); + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + onNativeKeyUp(keyCode); + return true; + } + + return false; + } + + /** + * This method is called by SDL using JNI. + */ + public static Surface getNativeSurface() { + if (SDLActivity.mSurface == null) { + return null; + } + return SDLActivity.mSurface.getNativeSurface(); + } + + // Input + + /** + * This method is called by SDL using JNI. + */ + public static void initTouch() { + int[] ids = InputDevice.getDeviceIds(); + + for (int id : ids) { + InputDevice device = InputDevice.getDevice(id); + /* Allow SOURCE_TOUCHSCREEN and also Virtual InputDevices because they can send TOUCHSCREEN events */ + if (device != null && ((device.getSources() & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN + || device.isVirtual())) { + + nativeAddTouch(device.getId(), device.getName()); + } + } + } + + // Messagebox + + /** Result of current messagebox. Also used for blocking the calling thread. */ + protected final int[] messageboxSelection = new int[1]; + + /** + * This method is called by SDL using JNI. + * Shows the messagebox from UI thread and block calling thread. + * buttonFlags, buttonIds and buttonTexts must have same length. + * @param buttonFlags array containing flags for every button. + * @param buttonIds array containing id for every button. + * @param buttonTexts array containing text for every button. + * @param colors null for default or array of length 5 containing colors. + * @return button id or -1. + */ + public int messageboxShowMessageBox( + final int flags, + final String title, + final String message, + final int[] buttonFlags, + final int[] buttonIds, + final String[] buttonTexts, + final int[] colors) { + + messageboxSelection[0] = -1; + + // sanity checks + + if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) { + return -1; // implementation broken + } + + // collect arguments for Dialog + + final Bundle args = new Bundle(); + args.putInt("flags", flags); + args.putString("title", title); + args.putString("message", message); + args.putIntArray("buttonFlags", buttonFlags); + args.putIntArray("buttonIds", buttonIds); + args.putStringArray("buttonTexts", buttonTexts); + args.putIntArray("colors", colors); + + // trigger Dialog creation on UI thread + + runOnUiThread(new Runnable() { + @Override + public void run() { + messageboxCreateAndShow(args); + } + }); + + // block the calling thread + + synchronized (messageboxSelection) { + try { + messageboxSelection.wait(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + return -1; + } + } + + // return selected value + + return messageboxSelection[0]; + } + + protected void messageboxCreateAndShow(Bundle args) { + + // TODO set values from "flags" to messagebox dialog + + // get colors + + int[] colors = args.getIntArray("colors"); + int backgroundColor; + int textColor; + int buttonBorderColor; + int buttonBackgroundColor; + int buttonSelectedColor; + if (colors != null) { + int i = -1; + backgroundColor = colors[++i]; + textColor = colors[++i]; + buttonBorderColor = colors[++i]; + buttonBackgroundColor = colors[++i]; + buttonSelectedColor = colors[++i]; + } else { + backgroundColor = Color.TRANSPARENT; + textColor = Color.TRANSPARENT; + buttonBorderColor = Color.TRANSPARENT; + buttonBackgroundColor = Color.TRANSPARENT; + buttonSelectedColor = Color.TRANSPARENT; + } + + // create dialog with title and a listener to wake up calling thread + + final AlertDialog dialog = new AlertDialog.Builder(this).create(); + dialog.setTitle(args.getString("title")); + dialog.setCancelable(false); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface unused) { + synchronized (messageboxSelection) { + messageboxSelection.notify(); + } + } + }); + + // create text + + TextView message = new TextView(this); + message.setGravity(Gravity.CENTER); + message.setText(args.getString("message")); + if (textColor != Color.TRANSPARENT) { + message.setTextColor(textColor); + } + + // create buttons + + int[] buttonFlags = args.getIntArray("buttonFlags"); + int[] buttonIds = args.getIntArray("buttonIds"); + String[] buttonTexts = args.getStringArray("buttonTexts"); + + final SparseArray