From ae0063f941b3542e5df502f29f1f18f0d17243db Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 3 Nov 2025 17:39:44 +0100 Subject: [PATCH 001/199] create BaseSystemBridge --- .../keymapper/sysbridge/service/SystemBridge.kt | 16 ++++++++++++++++ sysbridge/src/main/cpp/libevdev_jni.cpp | 12 ++++++------ .../{SystemBridge.kt => BaseSystemBridge.kt} | 10 +--------- 3 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt rename sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/{SystemBridge.kt => BaseSystemBridge.kt} (98%) diff --git a/app/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt b/app/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt new file mode 100644 index 0000000000..da2cc1854c --- /dev/null +++ b/app/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt @@ -0,0 +1,16 @@ +package io.github.sds100.keymapper.sysbridge.service + +import android.os.Looper + +class SystemBridge : BaseSystemBridge() { + + companion object { + @JvmStatic + fun main(args: Array) { + @Suppress("DEPRECATION") + Looper.prepareMainLooper() + SystemBridge() + Looper.loop() + } + } +} diff --git a/sysbridge/src/main/cpp/libevdev_jni.cpp b/sysbridge/src/main/cpp/libevdev_jni.cpp index 829d5df81a..276a3d10fd 100644 --- a/sysbridge/src/main/cpp/libevdev_jni.cpp +++ b/sysbridge/src/main/cpp/libevdev_jni.cpp @@ -58,7 +58,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) { extern "C" JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_grabEvdevDeviceNative(JNIEnv *env, +Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_grabEvdevDeviceNative(JNIEnv *env, jobject thiz, jstring jDevicePath) { @@ -284,7 +284,7 @@ static int MAX_EPOLL_EVENTS = 100; extern "C" JNIEXPORT void JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_startEvdevEventLoop(JNIEnv *env, +Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_startEvdevEventLoop(JNIEnv *env, jobject thiz, jobject jCallbackBinder) { std::unique_lock epollLock(epollMutex); @@ -469,7 +469,7 @@ Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_ungrabEvdevDevice extern "C" JNIEXPORT void JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_stopEvdevEventLoop(JNIEnv *env, +Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_stopEvdevEventLoop(JNIEnv *env, jobject thiz) { if (commandEventFd == -1) { return; @@ -490,7 +490,7 @@ Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_stopEvdevEventLoo extern "C" JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_writeEvdevEventNative(JNIEnv *env, +Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_writeEvdevEventNative(JNIEnv *env, jobject thiz, jstring jDevicePath, jint type, @@ -518,7 +518,7 @@ Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_writeEvdevEventNa } extern "C" JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_ungrabAllEvdevDevicesNative( +Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_ungrabAllEvdevDevicesNative( JNIEnv *env, jobject thiz) { @@ -600,7 +600,7 @@ createEvdevDeviceHandle(JNIEnv *env, const char *path, const char *name, int bus extern "C" JNIEXPORT jobjectArray JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_getEvdevDevicesNative(JNIEnv *env, +Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_getEvdevDevicesNative(JNIEnv *env, jobject thiz) { DIR *dir = opendir("/dev/input"); diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt similarity index 98% rename from sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt rename to sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt index fdb9d4cc66..1245e10a9c 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt @@ -52,7 +52,7 @@ import rikka.hidden.compat.UserManagerApis import rikka.hidden.compat.adapter.ProcessObserverAdapter @SuppressLint("LogNotTimber") -internal class SystemBridge : ISystemBridge.Stub() { +abstract class BaseSystemBridge : ISystemBridge.Stub() { external fun grabEvdevDeviceNative(devicePath: String): Boolean @@ -83,14 +83,6 @@ internal class SystemBridge : ISystemBridge.Stub() { private const val KEYMAPPER_CHECK_INTERVAL_MS = 60 * 1000L // 1 minute private const val DATA_ENABLED_REASON_USER: Int = 0 - @JvmStatic - fun main(args: Array) { - @Suppress("DEPRECATION") - Looper.prepareMainLooper() - SystemBridge() - Looper.loop() - } - private fun waitSystemService(name: String?) { var count = 0 From 9e7589d3ea781e218c3b152c1bada7c3fae5b5ff Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 3 Nov 2025 18:26:49 +0100 Subject: [PATCH 002/199] create separate evdev module --- app/build.gradle.kts | 1 + app/proguard-rules.pro | 2 +- app/src/main/AndroidManifest.xml | 2 +- .../keymapper/base/input/InputEventHub.kt | 2 +- evdev/.gitignore | 4 + evdev/build.gradle.kts | 175 ++++++++++++++++++ evdev/consumer-rules.pro | 0 evdev/proguard-rules.pro | 21 +++ .../keymapper/evdev}/IEvdevCallback.aidl | 2 +- evdev/src/main/cpp/CMakeLists.txt | 91 +++++++++ .../src/main/cpp/android/ftl/algorithm.h | 0 .../src/main/cpp/android/ftl/cast.h | 0 .../src/main/cpp/android/ftl/concat.h | 0 .../cpp/android/ftl/details/array_traits.h | 0 .../src/main/cpp/android/ftl/details/cast.h | 0 .../src/main/cpp/android/ftl/details/concat.h | 0 .../main/cpp/android/ftl/details/function.h | 0 .../src/main/cpp/android/ftl/details/future.h | 0 .../src/main/cpp/android/ftl/details/hash.h | 0 .../src/main/cpp/android/ftl/details/match.h | 0 .../src/main/cpp/android/ftl/details/mixins.h | 0 .../main/cpp/android/ftl/details/optional.h | 0 .../cpp/android/ftl/details/type_traits.h | 0 .../src/main/cpp/android/ftl/enum.h | 0 .../src/main/cpp/android/ftl/expected.h | 0 .../src/main/cpp/android/ftl/fake_guard.h | 0 .../src/main/cpp/android/ftl/finalizer.h | 0 .../src/main/cpp/android/ftl/flags.h | 0 .../src/main/cpp/android/ftl/function.h | 0 .../src/main/cpp/android/ftl/future.h | 0 .../src/main/cpp/android/ftl/hash.h | 0 .../src/main/cpp/android/ftl/ignore.h | 0 .../main/cpp/android/ftl/initializer_list.h | 0 .../src/main/cpp/android/ftl/match.h | 0 .../src/main/cpp/android/ftl/mixins.h | 0 .../src/main/cpp/android/ftl/non_null.h | 0 .../src/main/cpp/android/ftl/optional.h | 0 .../src/main/cpp/android/ftl/shared_mutex.h | 0 .../src/main/cpp/android/ftl/small_map.h | 0 .../src/main/cpp/android/ftl/small_vector.h | 0 .../src/main/cpp/android/ftl/static_vector.h | 0 .../src/main/cpp/android/ftl/string.h | 0 .../src/main/cpp/android/ftl/unit.h | 0 .../src/main/cpp/android/input/Input.cpp | 0 .../src/main/cpp/android/input/Input.h | 0 .../main/cpp/android/input/InputDevice.cpp | 0 .../src/main/cpp/android/input/InputDevice.h | 0 .../cpp/android/input/InputEventLabels.cpp | 0 .../main/cpp/android/input/InputEventLabels.h | 0 .../main/cpp/android/input/KeyLayoutMap.cpp | 0 .../src/main/cpp/android/input/KeyLayoutMap.h | 0 .../src/main/cpp/android/libbase/errors.h | 0 .../src/main/cpp/android/libbase/expected.h | 0 .../src/main/cpp/android/libbase/result.cpp | 0 .../src/main/cpp/android/libbase/result.h | 0 .../main/cpp/android/libbase/stringprintf.cpp | 0 .../main/cpp/android/libbase/stringprintf.h | 0 .../src/main/cpp/android/liblog/log_main.h | 0 .../main/cpp/android/ui/LogicalDisplayId.h | 0 .../src/main/cpp/android/utils/Errors.h | 0 .../src/main/cpp/android/utils/FileMap.cpp | 0 .../src/main/cpp/android/utils/FileMap.h | 0 .../main/cpp/android/utils/SharedBuffer.cpp | 0 .../src/main/cpp/android/utils/SharedBuffer.h | 0 .../src/main/cpp/android/utils/String16.cpp | 0 .../src/main/cpp/android/utils/String16.h | 0 .../src/main/cpp/android/utils/String8.cpp | 0 .../src/main/cpp/android/utils/String8.h | 0 .../src/main/cpp/android/utils/Tokenizer.cpp | 0 .../src/main/cpp/android/utils/Tokenizer.h | 0 .../src/main/cpp/android/utils/TypeHelpers.h | 0 .../src/main/cpp/android/utils/Unicode.cpp | 0 .../src/main/cpp/android/utils/Unicode.h | 0 .../src/main/cpp/libevdev/Makefile.am | 0 .../src/main/cpp/libevdev/libevdev-int.h | 0 .../src/main/cpp/libevdev/libevdev-names.c | 0 .../main/cpp/libevdev/libevdev-uinput-int.h | 0 .../src/main/cpp/libevdev/libevdev-uinput.c | 0 .../src/main/cpp/libevdev/libevdev-uinput.h | 0 .../src/main/cpp/libevdev/libevdev-util.h | 0 .../src/main/cpp/libevdev/libevdev.c | 0 .../src/main/cpp/libevdev/libevdev.h | 0 .../src/main/cpp/libevdev/libevdev.sym | 0 .../src/main/cpp/libevdev/make-event-names.py | 0 .../src/main/cpp/libevdev_jni.cpp | 6 +- evdev/src/main/cpp/logging.h | 30 +++ settings.gradle.kts | 1 + sysbridge/NDK_VERSION | 1 - sysbridge/build.gradle.kts | 115 +----------- .../keymapper/evdev/IEvdevCallback.aidl | 7 + .../keymapper/sysbridge/ISystemBridge.aidl | 2 +- sysbridge/src/main/cpp/CMakeLists.txt | 66 +------ .../sysbridge/service/BaseSystemBridge.kt | 2 +- 93 files changed, 341 insertions(+), 189 deletions(-) create mode 100644 evdev/.gitignore create mode 100644 evdev/build.gradle.kts create mode 100644 evdev/consumer-rules.pro create mode 100644 evdev/proguard-rules.pro rename {sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge => evdev/src/main/aidl/io/github/sds100/keymapper/evdev}/IEvdevCallback.aidl (83%) create mode 100644 evdev/src/main/cpp/CMakeLists.txt rename {sysbridge => evdev}/src/main/cpp/android/ftl/algorithm.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/cast.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/concat.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/details/array_traits.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/details/cast.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/details/concat.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/details/function.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/details/future.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/details/hash.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/details/match.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/details/mixins.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/details/optional.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/details/type_traits.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/enum.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/expected.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/fake_guard.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/finalizer.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/flags.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/function.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/future.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/hash.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/ignore.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/initializer_list.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/match.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/mixins.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/non_null.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/optional.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/shared_mutex.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/small_map.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/small_vector.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/static_vector.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/string.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ftl/unit.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/input/Input.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/input/Input.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/input/InputDevice.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/input/InputDevice.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/input/InputEventLabels.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/input/InputEventLabels.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/input/KeyLayoutMap.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/input/KeyLayoutMap.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/libbase/errors.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/libbase/expected.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/libbase/result.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/libbase/result.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/libbase/stringprintf.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/libbase/stringprintf.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/liblog/log_main.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/ui/LogicalDisplayId.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/Errors.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/FileMap.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/FileMap.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/SharedBuffer.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/SharedBuffer.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/String16.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/String16.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/String8.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/String8.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/Tokenizer.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/Tokenizer.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/TypeHelpers.h (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/Unicode.cpp (100%) rename {sysbridge => evdev}/src/main/cpp/android/utils/Unicode.h (100%) rename {sysbridge => evdev}/src/main/cpp/libevdev/Makefile.am (100%) rename {sysbridge => evdev}/src/main/cpp/libevdev/libevdev-int.h (100%) rename {sysbridge => evdev}/src/main/cpp/libevdev/libevdev-names.c (100%) rename {sysbridge => evdev}/src/main/cpp/libevdev/libevdev-uinput-int.h (100%) rename {sysbridge => evdev}/src/main/cpp/libevdev/libevdev-uinput.c (100%) rename {sysbridge => evdev}/src/main/cpp/libevdev/libevdev-uinput.h (100%) rename {sysbridge => evdev}/src/main/cpp/libevdev/libevdev-util.h (100%) rename {sysbridge => evdev}/src/main/cpp/libevdev/libevdev.c (100%) rename {sysbridge => evdev}/src/main/cpp/libevdev/libevdev.h (100%) rename {sysbridge => evdev}/src/main/cpp/libevdev/libevdev.sym (100%) rename {sysbridge => evdev}/src/main/cpp/libevdev/make-event-names.py (100%) mode change 100755 => 100644 rename {sysbridge => evdev}/src/main/cpp/libevdev_jni.cpp (99%) create mode 100644 evdev/src/main/cpp/logging.h delete mode 100644 sysbridge/NDK_VERSION create mode 100644 sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2b0504c7a9..7d234f10e3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -152,6 +152,7 @@ dependencies { implementation(project(":data")) implementation(project(":sysbridge")) implementation(project(":system")) + implementation(project(":evdev")) compileOnly(project(":systemstubs")) coreLibraryDesugaring(libs.desugar.jdk.libs) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 032e987d4b..c9ac9e7d76 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -109,7 +109,7 @@ # Keep all AIDL interface classes and their methods -keep class io.github.sds100.keymapper.sysbridge.ISystemBridge** { *; } --keep class io.github.sds100.keymapper.sysbridge.IEvdevCallback** { *; } +-keep class io.github.sds100.keymapper.evdev.IEvdevCallback** { *; } -keep class io.github.sds100.keymapper.sysbridge.IShizukuStarterService** { *; } -keepclassmembers class io.github.sds100.keymapper.sysbridge.shizuku.ShizukuStarterService { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c4e87ba265..d74d55e0eb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,7 +13,7 @@ - + file.isDirectory } + ?: throw GradleException("No prebuilt toolchain directories found in $prebuiltDir") + + if (hostDirs.size != 1) { + throw GradleException( + "Expected exactly one prebuilt toolchain directory in $prebuiltDir, found ${hostDirs.size}", + ) + } + val toolchainDir = hostDirs[0].absolutePath + + val inputHeader = "$toolchainDir/sysroot/usr/include/linux/input.h" + val inputEventCodesHeader = "$toolchainDir/sysroot/usr/include/linux/input-event-codes.h" + val outputHeader = "$projectDir/src/main/cpp/libevdev/event-names.h" + val pythonScript = "$projectDir/src/main/cpp/libevdev/make-event-names.py" + + commandLine("python3", pythonScript, inputHeader, inputEventCodesHeader) + + standardOutput = File(outputHeader).outputStream() + + inputs.file(pythonScript) + inputs.file(inputHeader) + inputs.file(inputEventCodesHeader) + outputs.file(outputHeader) +} + +// Task to compile AIDL files for NDK. +// Taken from https://github.com/lakinduboteju/AndroidNdkBinderExamples +val compileAidlNdk by tasks.registering(Exec::class) { + group = "build" + description = "Compiles AIDL files in src/main/aidl to NDK C++ headers and sources." + + val aidlSrcDir = project.file("src/main/aidl") + // Find all .aidl files. Using fileTree ensures it's dynamic. + val aidlFiles = project.fileTree(aidlSrcDir) { + include("**/IEvdevCallback.aidl") + } + + inputs.files(aidlFiles) + .withPathSensitivity(PathSensitivity.RELATIVE) + .withPropertyName("aidlInputFiles") + + val cppOutDir = project.file("src/main/cpp/aidl") + val cppHeaderOutDir = project.file("src/main/cpp") + + outputs.dir(cppOutDir).withPropertyName("cppOutputDir") + outputs.dir(cppHeaderOutDir).withPropertyName("cppHeaderOutputDir") + + // Path to the aidl executable in the Android SDK + val aidlToolPath = + android.sdkDirectory.toPath() + .resolve("build-tools") + .resolve(android.buildToolsVersion) + .resolve("aidl") + .absolutePathString() + val importSearchPath = aidlSrcDir.absolutePath + + // Ensure output directories exist before trying to write to them + cppOutDir.mkdirs() + cppHeaderOutDir.mkdirs() + + if (aidlFiles.isEmpty) { + logger.info("No AIDL files found in $aidlSrcDir. Skipping compileAidlNdk task.") + return@registering // Exit doLast if no files to process + } + + for (aidlFile in aidlFiles) { + logger.lifecycle("Compiling AIDL file (NDK): ${aidlFile.path}") + + commandLine( + aidlToolPath, + "--lang=ndk", + "-o", cppOutDir.absolutePath, + "-h", cppHeaderOutDir.absolutePath, + "-I", importSearchPath, + aidlFile.absolutePath, + ) + } + + logger.lifecycle( + "AIDL NDK compilation finished. Check outputs in $cppOutDir and $cppHeaderOutDir", + ) +} diff --git a/evdev/consumer-rules.pro b/evdev/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/evdev/proguard-rules.pro b/evdev/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/evdev/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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 *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/IEvdevCallback.aidl b/evdev/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl similarity index 83% rename from sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/IEvdevCallback.aidl rename to evdev/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl index f6b4039a6d..a304c78dab 100644 --- a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/IEvdevCallback.aidl +++ b/evdev/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.sysbridge; +package io.github.sds100.keymapper.evdev; interface IEvdevCallback { oneway void onEvdevEventLoopStarted(); diff --git a/evdev/src/main/cpp/CMakeLists.txt b/evdev/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000..6fbc7bbdbb --- /dev/null +++ b/evdev/src/main/cpp/CMakeLists.txt @@ -0,0 +1,91 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html. +# For more examples on how to use CMake, see https://github.com/android/ndk-samples. + +# Sets the minimum CMake version required for this project. +cmake_minimum_required(VERSION 3.22.1) + +# Declares the project name. The project name can be accessed via ${ PROJECT_NAME}, +# Since this is the top level CMakeLists.txt, the project name is also accessible +# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level +# build script scope). +project("evdev") + +set(CMAKE_CXX_STANDARD 20) + +set(C_FLAGS "-Werror=format -fdata-sections -ffunction-sections -fno-exceptions -fno-rtti -fno-threadsafe-statics") +set(LINKER_FLAGS "-Wl,--hash-style=both") + +if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message("Building Release...") + + set(C_FLAGS "${C_FLAGS} -O2 -fvisibility=hidden -fvisibility-inlines-hidden") + set(LINKER_FLAGS "${LINKER_FLAGS} -Wl,-exclude-libs,ALL -Wl,--gc-sections") +else () + message("Building Debug...") + + add_definitions(-DDEBUG) +endif () + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_FLAGS}") + +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}") +set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}") + +# Creates and names a library, sets it as either STATIC +# or SHARED, and provides the relative paths to its source code. +# You can define multiple libraries, and CMake builds them for you. +# Gradle automatically packages shared libraries with your APK. +# +# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define +# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME} +# is preferred for the same purpose. +# +# In order to load a library into your app from Java/Kotlin, you must call +# System.loadLibrary() and pass the name of the library defined here; +# for GameActivity/NativeActivity derived applications, the same library name must be +# used in the AndroidManifest.xml file. +add_library(evdev SHARED + # List C/C++ source files with relative paths to this CMakeLists.txt. + libevdev_jni.cpp + libevdev/libevdev.c + libevdev/libevdev-names.c + libevdev/libevdev-uinput.c + android/input/KeyLayoutMap.cpp + android/input/InputEventLabels.cpp + android/libbase/result.cpp + android/utils/Tokenizer.cpp + android/utils/String16.cpp + android/utils/String8.cpp + android/utils/SharedBuffer.cpp + android/utils/FileMap.cpp + android/utils/Unicode.cpp + android/input/InputDevice.cpp + android/input/Input.cpp + android/libbase/stringprintf.cpp + ${aidl_src_dir}/io/github/sds100/keymapper/evdev/IEvdevCallback.cpp) + +find_library( + binder_ndk-lib + binder_ndk +) + +# Specifies libraries CMake should link to your target library. You +# can link libraries from various origins, such as libraries defined in this +# build script, prebuilt third-party libraries, or Android system libraries. +target_link_libraries(evdev + # List libraries link to the target library + android + log + ${binder_ndk-lib}) + +# Add include directories for header files +target_include_directories(evdev PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/android + ${CMAKE_CURRENT_SOURCE_DIR}/android/input + ${CMAKE_CURRENT_SOURCE_DIR}/android/libbase + ${CMAKE_CURRENT_SOURCE_DIR}/android/utils + ${CMAKE_CURRENT_SOURCE_DIR}/libevdev) + diff --git a/sysbridge/src/main/cpp/android/ftl/algorithm.h b/evdev/src/main/cpp/android/ftl/algorithm.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/algorithm.h rename to evdev/src/main/cpp/android/ftl/algorithm.h diff --git a/sysbridge/src/main/cpp/android/ftl/cast.h b/evdev/src/main/cpp/android/ftl/cast.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/cast.h rename to evdev/src/main/cpp/android/ftl/cast.h diff --git a/sysbridge/src/main/cpp/android/ftl/concat.h b/evdev/src/main/cpp/android/ftl/concat.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/concat.h rename to evdev/src/main/cpp/android/ftl/concat.h diff --git a/sysbridge/src/main/cpp/android/ftl/details/array_traits.h b/evdev/src/main/cpp/android/ftl/details/array_traits.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/details/array_traits.h rename to evdev/src/main/cpp/android/ftl/details/array_traits.h diff --git a/sysbridge/src/main/cpp/android/ftl/details/cast.h b/evdev/src/main/cpp/android/ftl/details/cast.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/details/cast.h rename to evdev/src/main/cpp/android/ftl/details/cast.h diff --git a/sysbridge/src/main/cpp/android/ftl/details/concat.h b/evdev/src/main/cpp/android/ftl/details/concat.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/details/concat.h rename to evdev/src/main/cpp/android/ftl/details/concat.h diff --git a/sysbridge/src/main/cpp/android/ftl/details/function.h b/evdev/src/main/cpp/android/ftl/details/function.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/details/function.h rename to evdev/src/main/cpp/android/ftl/details/function.h diff --git a/sysbridge/src/main/cpp/android/ftl/details/future.h b/evdev/src/main/cpp/android/ftl/details/future.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/details/future.h rename to evdev/src/main/cpp/android/ftl/details/future.h diff --git a/sysbridge/src/main/cpp/android/ftl/details/hash.h b/evdev/src/main/cpp/android/ftl/details/hash.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/details/hash.h rename to evdev/src/main/cpp/android/ftl/details/hash.h diff --git a/sysbridge/src/main/cpp/android/ftl/details/match.h b/evdev/src/main/cpp/android/ftl/details/match.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/details/match.h rename to evdev/src/main/cpp/android/ftl/details/match.h diff --git a/sysbridge/src/main/cpp/android/ftl/details/mixins.h b/evdev/src/main/cpp/android/ftl/details/mixins.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/details/mixins.h rename to evdev/src/main/cpp/android/ftl/details/mixins.h diff --git a/sysbridge/src/main/cpp/android/ftl/details/optional.h b/evdev/src/main/cpp/android/ftl/details/optional.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/details/optional.h rename to evdev/src/main/cpp/android/ftl/details/optional.h diff --git a/sysbridge/src/main/cpp/android/ftl/details/type_traits.h b/evdev/src/main/cpp/android/ftl/details/type_traits.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/details/type_traits.h rename to evdev/src/main/cpp/android/ftl/details/type_traits.h diff --git a/sysbridge/src/main/cpp/android/ftl/enum.h b/evdev/src/main/cpp/android/ftl/enum.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/enum.h rename to evdev/src/main/cpp/android/ftl/enum.h diff --git a/sysbridge/src/main/cpp/android/ftl/expected.h b/evdev/src/main/cpp/android/ftl/expected.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/expected.h rename to evdev/src/main/cpp/android/ftl/expected.h diff --git a/sysbridge/src/main/cpp/android/ftl/fake_guard.h b/evdev/src/main/cpp/android/ftl/fake_guard.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/fake_guard.h rename to evdev/src/main/cpp/android/ftl/fake_guard.h diff --git a/sysbridge/src/main/cpp/android/ftl/finalizer.h b/evdev/src/main/cpp/android/ftl/finalizer.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/finalizer.h rename to evdev/src/main/cpp/android/ftl/finalizer.h diff --git a/sysbridge/src/main/cpp/android/ftl/flags.h b/evdev/src/main/cpp/android/ftl/flags.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/flags.h rename to evdev/src/main/cpp/android/ftl/flags.h diff --git a/sysbridge/src/main/cpp/android/ftl/function.h b/evdev/src/main/cpp/android/ftl/function.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/function.h rename to evdev/src/main/cpp/android/ftl/function.h diff --git a/sysbridge/src/main/cpp/android/ftl/future.h b/evdev/src/main/cpp/android/ftl/future.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/future.h rename to evdev/src/main/cpp/android/ftl/future.h diff --git a/sysbridge/src/main/cpp/android/ftl/hash.h b/evdev/src/main/cpp/android/ftl/hash.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/hash.h rename to evdev/src/main/cpp/android/ftl/hash.h diff --git a/sysbridge/src/main/cpp/android/ftl/ignore.h b/evdev/src/main/cpp/android/ftl/ignore.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/ignore.h rename to evdev/src/main/cpp/android/ftl/ignore.h diff --git a/sysbridge/src/main/cpp/android/ftl/initializer_list.h b/evdev/src/main/cpp/android/ftl/initializer_list.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/initializer_list.h rename to evdev/src/main/cpp/android/ftl/initializer_list.h diff --git a/sysbridge/src/main/cpp/android/ftl/match.h b/evdev/src/main/cpp/android/ftl/match.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/match.h rename to evdev/src/main/cpp/android/ftl/match.h diff --git a/sysbridge/src/main/cpp/android/ftl/mixins.h b/evdev/src/main/cpp/android/ftl/mixins.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/mixins.h rename to evdev/src/main/cpp/android/ftl/mixins.h diff --git a/sysbridge/src/main/cpp/android/ftl/non_null.h b/evdev/src/main/cpp/android/ftl/non_null.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/non_null.h rename to evdev/src/main/cpp/android/ftl/non_null.h diff --git a/sysbridge/src/main/cpp/android/ftl/optional.h b/evdev/src/main/cpp/android/ftl/optional.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/optional.h rename to evdev/src/main/cpp/android/ftl/optional.h diff --git a/sysbridge/src/main/cpp/android/ftl/shared_mutex.h b/evdev/src/main/cpp/android/ftl/shared_mutex.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/shared_mutex.h rename to evdev/src/main/cpp/android/ftl/shared_mutex.h diff --git a/sysbridge/src/main/cpp/android/ftl/small_map.h b/evdev/src/main/cpp/android/ftl/small_map.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/small_map.h rename to evdev/src/main/cpp/android/ftl/small_map.h diff --git a/sysbridge/src/main/cpp/android/ftl/small_vector.h b/evdev/src/main/cpp/android/ftl/small_vector.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/small_vector.h rename to evdev/src/main/cpp/android/ftl/small_vector.h diff --git a/sysbridge/src/main/cpp/android/ftl/static_vector.h b/evdev/src/main/cpp/android/ftl/static_vector.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/static_vector.h rename to evdev/src/main/cpp/android/ftl/static_vector.h diff --git a/sysbridge/src/main/cpp/android/ftl/string.h b/evdev/src/main/cpp/android/ftl/string.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/string.h rename to evdev/src/main/cpp/android/ftl/string.h diff --git a/sysbridge/src/main/cpp/android/ftl/unit.h b/evdev/src/main/cpp/android/ftl/unit.h similarity index 100% rename from sysbridge/src/main/cpp/android/ftl/unit.h rename to evdev/src/main/cpp/android/ftl/unit.h diff --git a/sysbridge/src/main/cpp/android/input/Input.cpp b/evdev/src/main/cpp/android/input/Input.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/input/Input.cpp rename to evdev/src/main/cpp/android/input/Input.cpp diff --git a/sysbridge/src/main/cpp/android/input/Input.h b/evdev/src/main/cpp/android/input/Input.h similarity index 100% rename from sysbridge/src/main/cpp/android/input/Input.h rename to evdev/src/main/cpp/android/input/Input.h diff --git a/sysbridge/src/main/cpp/android/input/InputDevice.cpp b/evdev/src/main/cpp/android/input/InputDevice.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/input/InputDevice.cpp rename to evdev/src/main/cpp/android/input/InputDevice.cpp diff --git a/sysbridge/src/main/cpp/android/input/InputDevice.h b/evdev/src/main/cpp/android/input/InputDevice.h similarity index 100% rename from sysbridge/src/main/cpp/android/input/InputDevice.h rename to evdev/src/main/cpp/android/input/InputDevice.h diff --git a/sysbridge/src/main/cpp/android/input/InputEventLabels.cpp b/evdev/src/main/cpp/android/input/InputEventLabels.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/input/InputEventLabels.cpp rename to evdev/src/main/cpp/android/input/InputEventLabels.cpp diff --git a/sysbridge/src/main/cpp/android/input/InputEventLabels.h b/evdev/src/main/cpp/android/input/InputEventLabels.h similarity index 100% rename from sysbridge/src/main/cpp/android/input/InputEventLabels.h rename to evdev/src/main/cpp/android/input/InputEventLabels.h diff --git a/sysbridge/src/main/cpp/android/input/KeyLayoutMap.cpp b/evdev/src/main/cpp/android/input/KeyLayoutMap.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/input/KeyLayoutMap.cpp rename to evdev/src/main/cpp/android/input/KeyLayoutMap.cpp diff --git a/sysbridge/src/main/cpp/android/input/KeyLayoutMap.h b/evdev/src/main/cpp/android/input/KeyLayoutMap.h similarity index 100% rename from sysbridge/src/main/cpp/android/input/KeyLayoutMap.h rename to evdev/src/main/cpp/android/input/KeyLayoutMap.h diff --git a/sysbridge/src/main/cpp/android/libbase/errors.h b/evdev/src/main/cpp/android/libbase/errors.h similarity index 100% rename from sysbridge/src/main/cpp/android/libbase/errors.h rename to evdev/src/main/cpp/android/libbase/errors.h diff --git a/sysbridge/src/main/cpp/android/libbase/expected.h b/evdev/src/main/cpp/android/libbase/expected.h similarity index 100% rename from sysbridge/src/main/cpp/android/libbase/expected.h rename to evdev/src/main/cpp/android/libbase/expected.h diff --git a/sysbridge/src/main/cpp/android/libbase/result.cpp b/evdev/src/main/cpp/android/libbase/result.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/libbase/result.cpp rename to evdev/src/main/cpp/android/libbase/result.cpp diff --git a/sysbridge/src/main/cpp/android/libbase/result.h b/evdev/src/main/cpp/android/libbase/result.h similarity index 100% rename from sysbridge/src/main/cpp/android/libbase/result.h rename to evdev/src/main/cpp/android/libbase/result.h diff --git a/sysbridge/src/main/cpp/android/libbase/stringprintf.cpp b/evdev/src/main/cpp/android/libbase/stringprintf.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/libbase/stringprintf.cpp rename to evdev/src/main/cpp/android/libbase/stringprintf.cpp diff --git a/sysbridge/src/main/cpp/android/libbase/stringprintf.h b/evdev/src/main/cpp/android/libbase/stringprintf.h similarity index 100% rename from sysbridge/src/main/cpp/android/libbase/stringprintf.h rename to evdev/src/main/cpp/android/libbase/stringprintf.h diff --git a/sysbridge/src/main/cpp/android/liblog/log_main.h b/evdev/src/main/cpp/android/liblog/log_main.h similarity index 100% rename from sysbridge/src/main/cpp/android/liblog/log_main.h rename to evdev/src/main/cpp/android/liblog/log_main.h diff --git a/sysbridge/src/main/cpp/android/ui/LogicalDisplayId.h b/evdev/src/main/cpp/android/ui/LogicalDisplayId.h similarity index 100% rename from sysbridge/src/main/cpp/android/ui/LogicalDisplayId.h rename to evdev/src/main/cpp/android/ui/LogicalDisplayId.h diff --git a/sysbridge/src/main/cpp/android/utils/Errors.h b/evdev/src/main/cpp/android/utils/Errors.h similarity index 100% rename from sysbridge/src/main/cpp/android/utils/Errors.h rename to evdev/src/main/cpp/android/utils/Errors.h diff --git a/sysbridge/src/main/cpp/android/utils/FileMap.cpp b/evdev/src/main/cpp/android/utils/FileMap.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/utils/FileMap.cpp rename to evdev/src/main/cpp/android/utils/FileMap.cpp diff --git a/sysbridge/src/main/cpp/android/utils/FileMap.h b/evdev/src/main/cpp/android/utils/FileMap.h similarity index 100% rename from sysbridge/src/main/cpp/android/utils/FileMap.h rename to evdev/src/main/cpp/android/utils/FileMap.h diff --git a/sysbridge/src/main/cpp/android/utils/SharedBuffer.cpp b/evdev/src/main/cpp/android/utils/SharedBuffer.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/utils/SharedBuffer.cpp rename to evdev/src/main/cpp/android/utils/SharedBuffer.cpp diff --git a/sysbridge/src/main/cpp/android/utils/SharedBuffer.h b/evdev/src/main/cpp/android/utils/SharedBuffer.h similarity index 100% rename from sysbridge/src/main/cpp/android/utils/SharedBuffer.h rename to evdev/src/main/cpp/android/utils/SharedBuffer.h diff --git a/sysbridge/src/main/cpp/android/utils/String16.cpp b/evdev/src/main/cpp/android/utils/String16.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/utils/String16.cpp rename to evdev/src/main/cpp/android/utils/String16.cpp diff --git a/sysbridge/src/main/cpp/android/utils/String16.h b/evdev/src/main/cpp/android/utils/String16.h similarity index 100% rename from sysbridge/src/main/cpp/android/utils/String16.h rename to evdev/src/main/cpp/android/utils/String16.h diff --git a/sysbridge/src/main/cpp/android/utils/String8.cpp b/evdev/src/main/cpp/android/utils/String8.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/utils/String8.cpp rename to evdev/src/main/cpp/android/utils/String8.cpp diff --git a/sysbridge/src/main/cpp/android/utils/String8.h b/evdev/src/main/cpp/android/utils/String8.h similarity index 100% rename from sysbridge/src/main/cpp/android/utils/String8.h rename to evdev/src/main/cpp/android/utils/String8.h diff --git a/sysbridge/src/main/cpp/android/utils/Tokenizer.cpp b/evdev/src/main/cpp/android/utils/Tokenizer.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/utils/Tokenizer.cpp rename to evdev/src/main/cpp/android/utils/Tokenizer.cpp diff --git a/sysbridge/src/main/cpp/android/utils/Tokenizer.h b/evdev/src/main/cpp/android/utils/Tokenizer.h similarity index 100% rename from sysbridge/src/main/cpp/android/utils/Tokenizer.h rename to evdev/src/main/cpp/android/utils/Tokenizer.h diff --git a/sysbridge/src/main/cpp/android/utils/TypeHelpers.h b/evdev/src/main/cpp/android/utils/TypeHelpers.h similarity index 100% rename from sysbridge/src/main/cpp/android/utils/TypeHelpers.h rename to evdev/src/main/cpp/android/utils/TypeHelpers.h diff --git a/sysbridge/src/main/cpp/android/utils/Unicode.cpp b/evdev/src/main/cpp/android/utils/Unicode.cpp similarity index 100% rename from sysbridge/src/main/cpp/android/utils/Unicode.cpp rename to evdev/src/main/cpp/android/utils/Unicode.cpp diff --git a/sysbridge/src/main/cpp/android/utils/Unicode.h b/evdev/src/main/cpp/android/utils/Unicode.h similarity index 100% rename from sysbridge/src/main/cpp/android/utils/Unicode.h rename to evdev/src/main/cpp/android/utils/Unicode.h diff --git a/sysbridge/src/main/cpp/libevdev/Makefile.am b/evdev/src/main/cpp/libevdev/Makefile.am similarity index 100% rename from sysbridge/src/main/cpp/libevdev/Makefile.am rename to evdev/src/main/cpp/libevdev/Makefile.am diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-int.h b/evdev/src/main/cpp/libevdev/libevdev-int.h similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-int.h rename to evdev/src/main/cpp/libevdev/libevdev-int.h diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-names.c b/evdev/src/main/cpp/libevdev/libevdev-names.c similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-names.c rename to evdev/src/main/cpp/libevdev/libevdev-names.c diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-uinput-int.h b/evdev/src/main/cpp/libevdev/libevdev-uinput-int.h similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-uinput-int.h rename to evdev/src/main/cpp/libevdev/libevdev-uinput-int.h diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-uinput.c b/evdev/src/main/cpp/libevdev/libevdev-uinput.c similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-uinput.c rename to evdev/src/main/cpp/libevdev/libevdev-uinput.c diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-uinput.h b/evdev/src/main/cpp/libevdev/libevdev-uinput.h similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-uinput.h rename to evdev/src/main/cpp/libevdev/libevdev-uinput.h diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-util.h b/evdev/src/main/cpp/libevdev/libevdev-util.h similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-util.h rename to evdev/src/main/cpp/libevdev/libevdev-util.h diff --git a/sysbridge/src/main/cpp/libevdev/libevdev.c b/evdev/src/main/cpp/libevdev/libevdev.c similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev.c rename to evdev/src/main/cpp/libevdev/libevdev.c diff --git a/sysbridge/src/main/cpp/libevdev/libevdev.h b/evdev/src/main/cpp/libevdev/libevdev.h similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev.h rename to evdev/src/main/cpp/libevdev/libevdev.h diff --git a/sysbridge/src/main/cpp/libevdev/libevdev.sym b/evdev/src/main/cpp/libevdev/libevdev.sym similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev.sym rename to evdev/src/main/cpp/libevdev/libevdev.sym diff --git a/sysbridge/src/main/cpp/libevdev/make-event-names.py b/evdev/src/main/cpp/libevdev/make-event-names.py old mode 100755 new mode 100644 similarity index 100% rename from sysbridge/src/main/cpp/libevdev/make-event-names.py rename to evdev/src/main/cpp/libevdev/make-event-names.py diff --git a/sysbridge/src/main/cpp/libevdev_jni.cpp b/evdev/src/main/cpp/libevdev_jni.cpp similarity index 99% rename from sysbridge/src/main/cpp/libevdev_jni.cpp rename to evdev/src/main/cpp/libevdev_jni.cpp index 276a3d10fd..7aa338dd31 100644 --- a/sysbridge/src/main/cpp/libevdev_jni.cpp +++ b/evdev/src/main/cpp/libevdev_jni.cpp @@ -10,7 +10,7 @@ #include "android/input/KeyLayoutMap.h" #include "android/libbase/result.h" #include "android/input/InputDevice.h" -#include "aidl/io/github/sds100/keymapper/sysbridge/IEvdevCallback.h" +#include "aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.h" #include #include #include @@ -19,7 +19,7 @@ #include #include -using aidl::io::github::sds100::keymapper::sysbridge::IEvdevCallback; +using aidl::io::github::sds100::keymapper::evdev::IEvdevCallback; enum CommandType { STOP @@ -416,7 +416,7 @@ Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_startEvdevEve extern "C" JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_ungrabEvdevDeviceNative(JNIEnv *env, +Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_ungrabEvdevDeviceNative(JNIEnv *env, jobject thiz, jstring jDevicePath) { const char *devicePath = env->GetStringUTFChars(jDevicePath, nullptr); diff --git a/evdev/src/main/cpp/logging.h b/evdev/src/main/cpp/logging.h new file mode 100644 index 0000000000..0bc5310dc8 --- /dev/null +++ b/evdev/src/main/cpp/logging.h @@ -0,0 +1,30 @@ +#ifndef _LOGGING_H +#define _LOGGING_H + +#include +#include "android/log.h" + +#ifndef LOG_TAG +#define LOG_TAG "KeyMapperSystemBridge" +#endif + +#ifndef NO_LOG +#ifndef NO_DEBUG_LOG +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#else +#define LOGD(...) +#endif +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) +#else +#define LOGD(...) +#define LOGV(...) +#define LOGI(...) +#define LOGW(...) +#define LOGE(...) +#define PLOGE(fmt, args...) +#endif +#endif // _LOGGING_H diff --git a/settings.gradle.kts b/settings.gradle.kts index f035ff415c..81636c377b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -31,3 +31,4 @@ include(":system") include(":common") include(":data") include(":sysbridge") +include(":evdev") diff --git a/sysbridge/NDK_VERSION b/sysbridge/NDK_VERSION deleted file mode 100644 index b48e72d6d9..0000000000 --- a/sysbridge/NDK_VERSION +++ /dev/null @@ -1 +0,0 @@ -27.2.12479018 \ No newline at end of file diff --git a/sysbridge/build.gradle.kts b/sysbridge/build.gradle.kts index c1bf2d10a2..911d9933c3 100644 --- a/sysbridge/build.gradle.kts +++ b/sysbridge/build.gradle.kts @@ -1,5 +1,3 @@ -import kotlin.io.path.absolutePathString - plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) @@ -13,15 +11,7 @@ android { namespace = "io.github.sds100.keymapper.sysbridge" compileSdk = libs.versions.compile.sdk.get().toInt() - // Read NDK version from NDK_VERSION file, with fallback to gradle.properties - // The NDK version is stored in a file so the same value can be used across multiple modules. - val ndkVersionFile = project.file("NDK_VERSION") - val ndkVersionFromFile = if (ndkVersionFile.exists()) { - ndkVersionFile.readText().trim() - } else { - null - } - ndkVersion = ndkVersionFromFile!! + ndkVersion = "27.2.12479018" defaultConfig { // Must be API 29 so that the binder-ndk library can be found. @@ -114,106 +104,3 @@ dependencies { implementation(libs.zhanghai.appiconloader) implementation(libs.rikka.rikkax.core) } - -tasks.named("preBuild") { - dependsOn(generateLibEvDevEventNames) - dependsOn(compileAidlNdk) -} - -// The list of event names needs to be parsed from the input.h file in the NDK. -// input.h can be found in the Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/linux/input.h -// folder on macOS. -val generateLibEvDevEventNames by tasks.registering(Exec::class) { - dependsOn(compileAidlNdk) - - group = "build" - description = "Generates event names header from input.h" - - val prebuiltDir = File(android.ndkDirectory, "toolchains/llvm/prebuilt") - - // The "darwin-x86_64" part of the path is different on each operating system but it seems like - // the SDK Manager only downloads the NDK specific to the local operating system. So, just - // go into the only directory that the "prebuilt" directory contains. - val hostDirs = prebuiltDir.listFiles { file -> file.isDirectory } - ?: throw GradleException("No prebuilt toolchain directories found in $prebuiltDir") - - if (hostDirs.size != 1) { - throw GradleException( - "Expected exactly one prebuilt toolchain directory in $prebuiltDir, found ${hostDirs.size}", - ) - } - val toolchainDir = hostDirs[0].absolutePath - - val inputHeader = "$toolchainDir/sysroot/usr/include/linux/input.h" - val inputEventCodesHeader = "$toolchainDir/sysroot/usr/include/linux/input-event-codes.h" - val outputHeader = "$projectDir/src/main/cpp/libevdev/event-names.h" - val pythonScript = "$projectDir/src/main/cpp/libevdev/make-event-names.py" - - commandLine("python3", pythonScript, inputHeader, inputEventCodesHeader) - - standardOutput = File(outputHeader).outputStream() - - inputs.file(pythonScript) - inputs.file(inputHeader) - inputs.file(inputEventCodesHeader) - outputs.file(outputHeader) -} - -// Task to compile AIDL files for NDK. -// Taken from https://github.com/lakinduboteju/AndroidNdkBinderExamples -val compileAidlNdk by tasks.registering(Exec::class) { - group = "build" - description = "Compiles AIDL files in src/main/aidl to NDK C++ headers and sources." - - val aidlSrcDir = project.file("src/main/aidl") - // Find all .aidl files. Using fileTree ensures it's dynamic. - val aidlFiles = project.fileTree(aidlSrcDir) { - include("**/IEvdevCallback.aidl") - include("**/InputDeviceIdentifier.aidl") - } - - inputs.files(aidlFiles) - .withPathSensitivity(PathSensitivity.RELATIVE) - .withPropertyName("aidlInputFiles") - - val cppOutDir = project.file("src/main/cpp/aidl") - val cppHeaderOutDir = project.file("src/main/cpp") - - outputs.dir(cppOutDir).withPropertyName("cppOutputDir") - outputs.dir(cppHeaderOutDir).withPropertyName("cppHeaderOutputDir") - - // Path to the aidl executable in the Android SDK - val aidlToolPath = - android.sdkDirectory.toPath() - .resolve("build-tools") - .resolve(android.buildToolsVersion) - .resolve("aidl") - .absolutePathString() - val importSearchPath = aidlSrcDir.absolutePath - - // Ensure output directories exist before trying to write to them - cppOutDir.mkdirs() - cppHeaderOutDir.mkdirs() - - if (aidlFiles.isEmpty) { - logger.info("No AIDL files found in $aidlSrcDir. Skipping compileAidlNdk task.") - return@registering // Exit doLast if no files to process - } - - for (aidlFile in aidlFiles) { - logger.lifecycle("Compiling AIDL file (NDK): ${aidlFile.path}") - - commandLine( - aidlToolPath, - "--lang=ndk", - "-o", cppOutDir.absolutePath, - "-h", cppHeaderOutDir.absolutePath, - "-I", importSearchPath, - aidlFile.absolutePath, - ) - } - - logger.lifecycle( - "AIDL NDK compilation finished. Check outputs in $cppOutDir and $cppHeaderOutDir", - ) -} diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl new file mode 100644 index 0000000000..a304c78dab --- /dev/null +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl @@ -0,0 +1,7 @@ +package io.github.sds100.keymapper.evdev; + +interface IEvdevCallback { + oneway void onEvdevEventLoopStarted(); + boolean onEvdevEvent(String devicePath, long timeSec, long timeUsec, int type, int code, int value, int androidCode); + void onEmergencyKillSystemBridge(); +} \ No newline at end of file diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl index 602310a084..345eebc998 100644 --- a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl @@ -1,6 +1,6 @@ package io.github.sds100.keymapper.sysbridge; -import io.github.sds100.keymapper.sysbridge.IEvdevCallback; +import io.github.sds100.keymapper.evdev.IEvdevCallback; import io.github.sds100.keymapper.common.models.EvdevDeviceHandle; import io.github.sds100.keymapper.common.models.ShellResult; import android.view.InputEvent; diff --git a/sysbridge/src/main/cpp/CMakeLists.txt b/sysbridge/src/main/cpp/CMakeLists.txt index 5781ee5879..b7eef6b73f 100644 --- a/sysbridge/src/main/cpp/CMakeLists.txt +++ b/sysbridge/src/main/cpp/CMakeLists.txt @@ -59,68 +59,4 @@ target_link_libraries(adb ${log-lib} boringssl::crypto_static) if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") add_custom_command(TARGET adb POST_BUILD COMMAND ${CMAKE_STRIP} --remove-section=.comment "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libadb.so") -endif () - -# Creates and names a library, sets it as either STATIC -# or SHARED, and provides the relative paths to its source code. -# You can define multiple libraries, and CMake builds them for you. -# Gradle automatically packages shared libraries with your APK. -# -# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define -# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME} -# is preferred for the same purpose. -# -# In order to load a library into your app from Java/Kotlin, you must call -# System.loadLibrary() and pass the name of the library defined here; -# for GameActivity/NativeActivity derived applications, the same library name must be -# used in the AndroidManifest.xml file. -add_library(evdev SHARED - # List C/C++ source files with relative paths to this CMakeLists.txt. - libevdev_jni.cpp - libevdev/libevdev.c - libevdev/libevdev-names.c - libevdev/libevdev-uinput.c - android/input/KeyLayoutMap.cpp - android/input/InputEventLabels.cpp - android/libbase/result.cpp - android/utils/Tokenizer.cpp - android/utils/String16.cpp - android/utils/String8.cpp - android/utils/SharedBuffer.cpp - android/utils/FileMap.cpp - android/utils/Unicode.cpp - android/input/InputDevice.cpp - android/input/Input.cpp - android/libbase/stringprintf.cpp - ${aidl_src_dir}/io/github/sds100/keymapper/sysbridge/IEvdevCallback.cpp) - -# Set a stable custom output directory for libevdev.so so it can be linked to in other modules -# Outputs to custom "libs" directory in the sysbridge module build directory -set_target_properties(evdev PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../build/libs/${ANDROID_ABI}" - LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../build/libs/${ANDROID_ABI}" - LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../build/libs/${ANDROID_ABI}") - -find_library( - binder_ndk-lib - binder_ndk -) - -# Specifies libraries CMake should link to your target library. You -# can link libraries from various origins, such as libraries defined in this -# build script, prebuilt third-party libraries, or Android system libraries. -target_link_libraries(evdev - # List libraries link to the target library - android - log - ${binder_ndk-lib}) - -# Add include directories for header files -target_include_directories(evdev PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/android - ${CMAKE_CURRENT_SOURCE_DIR}/android/input - ${CMAKE_CURRENT_SOURCE_DIR}/android/libbase - ${CMAKE_CURRENT_SOURCE_DIR}/android/utils - ${CMAKE_CURRENT_SOURCE_DIR}/libevdev) - +endif () \ No newline at end of file diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt index 1245e10a9c..7ac59046f4 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt @@ -32,7 +32,7 @@ import com.android.internal.telephony.ITelephony import io.github.sds100.keymapper.common.models.EvdevDeviceHandle import io.github.sds100.keymapper.common.models.ShellResult import io.github.sds100.keymapper.common.utils.UserHandleUtils -import io.github.sds100.keymapper.sysbridge.IEvdevCallback +import io.github.sds100.keymapper.evdev.IEvdevCallback import io.github.sds100.keymapper.sysbridge.ISystemBridge import io.github.sds100.keymapper.sysbridge.provider.BinderContainer import io.github.sds100.keymapper.sysbridge.provider.SystemBridgeBinderProvider From 34f5a2d76e411c0706074012d04f2f9921e398a2 Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 3 Nov 2025 23:49:13 +0100 Subject: [PATCH 003/199] setup Rust project for evdev_manager --- build.gradle.kts | 4 +- evdev/.gitignore | 3 +- evdev/.idea/.gitignore | 8 + evdev/.idea/gradle.xml | 11 + .../inspectionProfiles/Project_Default.xml | 6 + evdev/.idea/misc.xml | 14 + evdev/.idea/vcs.xml | 6 + evdev/build.gradle.kts | 40 ++- evdev/src/main/rust/evdev_manager/Cargo.lock | 272 ++++++++++++++++++ evdev/src/main/rust/evdev_manager/Cargo.toml | 14 + evdev/src/main/rust/evdev_manager/src/lib.rs | 57 ++++ gradle/libs.versions.toml | 3 + .../sysbridge/service/BaseSystemBridge.kt | 5 + 13 files changed, 435 insertions(+), 8 deletions(-) create mode 100644 evdev/.idea/.gitignore create mode 100644 evdev/.idea/gradle.xml create mode 100644 evdev/.idea/inspectionProfiles/Project_Default.xml create mode 100644 evdev/.idea/misc.xml create mode 100644 evdev/.idea/vcs.xml create mode 100644 evdev/src/main/rust/evdev_manager/Cargo.lock create mode 100644 evdev/src/main/rust/evdev_manager/Cargo.toml create mode 100644 evdev/src/main/rust/evdev_manager/src/lib.rs diff --git a/build.gradle.kts b/build.gradle.kts index 9e070cd106..78ed67584b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,9 @@ - // Top-level build file where you can add configuration options common to all sub-projects/modules. +// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false + // Must come before the others for the jnilibs folder to be automatically detected + alias(libs.plugins.mozilla.rust.android) apply false alias(libs.plugins.kotlin.compose) apply false alias(libs.plugins.kotlin.kapt) apply false alias(libs.plugins.kotlin.serialization) apply false diff --git a/evdev/.gitignore b/evdev/.gitignore index a20eae71c6..48b4c6e25e 100644 --- a/evdev/.gitignore +++ b/evdev/.gitignore @@ -1,4 +1,5 @@ /build .cxx /src/main/cpp/libevdev/event-names.h -/src/main/cpp/aidl/* \ No newline at end of file +/src/main/cpp/aidl/* +/src/main/rust/**/target/ diff --git a/evdev/.idea/.gitignore b/evdev/.idea/.gitignore new file mode 100644 index 0000000000..13566b81b0 --- /dev/null +++ b/evdev/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/evdev/.idea/gradle.xml b/evdev/.idea/gradle.xml new file mode 100644 index 0000000000..1b387c73e6 --- /dev/null +++ b/evdev/.idea/gradle.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/evdev/.idea/inspectionProfiles/Project_Default.xml b/evdev/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000..c5e1cad975 --- /dev/null +++ b/evdev/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/evdev/.idea/misc.xml b/evdev/.idea/misc.xml new file mode 100644 index 0000000000..e8e8dad566 --- /dev/null +++ b/evdev/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/evdev/.idea/vcs.xml b/evdev/.idea/vcs.xml new file mode 100644 index 0000000000..6c0b863585 --- /dev/null +++ b/evdev/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/evdev/build.gradle.kts b/evdev/build.gradle.kts index ad38f3774f..eec0227ac1 100644 --- a/evdev/build.gradle.kts +++ b/evdev/build.gradle.kts @@ -2,6 +2,7 @@ import kotlin.io.path.absolutePathString plugins { alias(libs.plugins.android.library) + alias(libs.plugins.mozilla.rust.android) } android { @@ -52,8 +53,6 @@ android { buildFeatures { // Disable because a Java implementation of IEvdevCallback is not required in this module aidl = false - prefab = true - buildConfig = true } packaging { @@ -69,12 +68,24 @@ android { } } -dependencies { +cargo { + module = "src/main/rust/evdev_manager" + libname = "evdev_manager" + targets = listOf("arm", "arm64", "x86", "x86_64") + + // Can not do this with buildType configurations. + // See https://github.com/mozilla/rust-android-gradle/issues/38 + profile = + if (gradle.startParameter.taskNames + .any { it.lowercase().contains("debug") } + ) { + "debug" + } else { + "release" + } } -tasks.named("preBuild") { - dependsOn(generateLibEvDevEventNames) - dependsOn(compileAidlNdk) +dependencies { } // The list of event names needs to be parsed from the input.h file in the NDK. @@ -173,3 +184,20 @@ val compileAidlNdk by tasks.registering(Exec::class) { "AIDL NDK compilation finished. Check outputs in $cppOutDir and $cppHeaderOutDir", ) } + +tasks.named("preBuild") { + dependsOn(generateLibEvDevEventNames) + dependsOn(compileAidlNdk) +} + +// Must come after all tasks above, otherwise gradle syncing fails. +// +// Run cargo build when the files change. +// See https://github.com/mozilla/rust-android-gradle/issues/166 +tasks.whenTaskAdded { + if (name == "mergeDebugJniLibFolders" || name == "mergeReleaseJniLibFolders") { + outputs.upToDateWhen { false } + + dependsOn("cargoBuild") + } +} diff --git a/evdev/src/main/rust/evdev_manager/Cargo.lock b/evdev/src/main/rust/evdev_manager/Cargo.lock new file mode 100644 index 0000000000..4cef43c5a2 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/Cargo.lock @@ -0,0 +1,272 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "android_liblog-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf82c031178ca72b38595a54d16df8a257df9deea7d97a8992870e5c6a738e7" +dependencies = [ + "libc", +] + +[[package]] +name = "android_log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc00e0d3a060cce3fa338f9644ce9a93901c79f5405330891aeca69c9957009a" +dependencies = [ + "android_liblog-sys", + "log 0.3.9", +] + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "evdev_manager" +version = "0.1.0" +dependencies = [ + "android_log", + "jni", + "log 0.4.28", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log 0.4.28", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +dependencies = [ + "log 0.4.28", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "syn" +version = "2.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" diff --git a/evdev/src/main/rust/evdev_manager/Cargo.toml b/evdev/src/main/rust/evdev_manager/Cargo.toml new file mode 100644 index 0000000000..c450603f19 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "evdev_manager" +version = "0.1.0" +edition = "2021" + +[lib] +name = "evdev_manager" +# "cdylib" is necessary to produce a C-style dynamic library that can be loaded by Android's JNI. +crate-type = ["cdylib"] + +[dependencies] +jni = "0.21.1" +log = "0.4.28" +android_log = "0.1.3" \ No newline at end of file diff --git a/evdev/src/main/rust/evdev_manager/src/lib.rs b/evdev/src/main/rust/evdev_manager/src/lib.rs new file mode 100644 index 0000000000..f3c3dc3679 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/src/lib.rs @@ -0,0 +1,57 @@ +#[macro_use] +extern crate log; +extern crate android_log; + +use jni::objects::{JClass, JString}; +use jni::sys::jstring; +use jni::JNIEnv; + +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_helloEvdevManager( + mut env: JNIEnv, + _class: JClass, + input: JString, +) -> jstring { + let input: String = env + .get_string(&input) + .expect("Couldn't get java string!") + .into(); + + let output = env + .new_string(format!("Hello from evdev Rust: {}", input)) + .expect("Couldn't create java string!"); + + // Initialize Android logger + android_log::init("KeyMapperSystemBridge").unwrap(); + + set_log_panic_hook(); + + info!("Hello from evdev Rust"); + + output.into_raw() +} + +fn set_log_panic_hook() { + std::panic::set_hook(Box::new(|panic_info| { + error!("PANIC in Rust code!"); + + if let Some(location) = panic_info.location() { + error!( + "Panic at {}:{}:{}", + location.file(), + location.line(), + location.column() + ); + } else { + error!("Panic at unknown location"); + } + + if let Some(payload) = panic_info.payload().downcast_ref::<&str>() { + error!("Panic message: {}", payload); + } else if let Some(payload) = panic_info.payload().downcast_ref::() { + error!("Panic message: {}", payload); + } else { + error!("Panic with unknown payload"); + } + })); +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 67a55b1535..c6670598b3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -79,6 +79,8 @@ ui-tooling = "1.8.1" # android.arch.persistence.room:testing libsu-core = "6.0.0" rikka-hidden = "4.4.0" +rust-android-gradle = "0.9.6" + [libraries] # Kotlin androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hilt-navigation-compose" } @@ -218,6 +220,7 @@ kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" } dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "dagger-hilt-android" } +mozilla-rust-android = { id = "org.mozilla.rust-android-gradle.rust-android", version.ref = "rust-android-gradle" } [bundles] splitties = [ diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt index 7ac59046f4..7d0be398d2 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt @@ -70,6 +70,8 @@ abstract class BaseSystemBridge : ISystemBridge.Stub() { external fun startEvdevEventLoop(callback: IBinder) external fun stopEvdevEventLoop() + external fun helloEvdevManager(input: String) + companion object { private const val TAG: String = "KeyMapperSystemBridge" private val systemBridgePackageName: String? by lazy { @@ -185,6 +187,9 @@ abstract class BaseSystemBridge : ISystemBridge.Stub() { val libraryPath = System.getProperty("keymapper_sysbridge.library.path") @SuppressLint("UnsafeDynamicallyLoadedCode") System.load("$libraryPath/libevdev.so") + System.load("$libraryPath/libevdev_manager.so") + + helloEvdevManager("test input") Log.i(TAG, "SystemBridge starting... Version code $versionCode") From d882b94c0ab8c546cf33a6fedf72afbd50f541e0 Mon Sep 17 00:00:00 2001 From: sds100 Date: Wed, 12 Nov 2025 12:09:14 +0100 Subject: [PATCH 004/199] #1897 create boilerplate code for Rust evdev manager --- evdev/build.gradle.kts | 30 +- evdev/src/main/cpp/CMakeLists.txt | 91 - evdev/src/main/rust/evdev_manager/Cargo.lock | 171 + evdev/src/main/rust/evdev_manager/Cargo.toml | 10 +- evdev/src/main/rust/evdev_manager/build.rs | 252 + .../src/main/rust/evdev_manager/rustfmt.toml | 4 + .../main/rust/evdev_manager/src/bindings.rs | 7629 +++++++++++++++++ .../src/main/rust/evdev_manager/src/enums.rs | 385 + .../src/main/rust/evdev_manager/src/evdev.rs | 416 + evdev/src/main/rust/evdev_manager/src/lib.rs | 4 + .../sysbridge/service/BaseSystemBridge.kt | 1 - 11 files changed, 8878 insertions(+), 115 deletions(-) delete mode 100644 evdev/src/main/cpp/CMakeLists.txt create mode 100644 evdev/src/main/rust/evdev_manager/build.rs create mode 100644 evdev/src/main/rust/evdev_manager/rustfmt.toml create mode 100644 evdev/src/main/rust/evdev_manager/src/bindings.rs create mode 100644 evdev/src/main/rust/evdev_manager/src/enums.rs create mode 100644 evdev/src/main/rust/evdev_manager/src/evdev.rs diff --git a/evdev/build.gradle.kts b/evdev/build.gradle.kts index eec0227ac1..11826a0fcf 100644 --- a/evdev/build.gradle.kts +++ b/evdev/build.gradle.kts @@ -17,21 +17,6 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") - - externalNativeBuild { - cmake { - val aidlSrcDir = project.file("src/main/cpp/aidl") - - // -DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON is required to get the app running on the Android 15. This is related to the new 16kB page size support. - // -DANDROID_WEAK_API_DEFS=ON is required so the libevdev_jni file can run code depending on the SDK. https://developer.android.com/ndk/guides/using-newer-apis - arguments( - "-DANDROID_STL=c++_static", - "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", - "-DANDROID_WEAK_API_DEFS=ON", - "-Daidl_src_dir=${aidlSrcDir.absolutePath}", - ) - } - } } buildTypes { @@ -43,13 +28,6 @@ android { } } - externalNativeBuild { - cmake { - path("src/main/cpp/CMakeLists.txt") - version = "3.22.1" - } - } - buildFeatures { // Disable because a Java implementation of IEvdevCallback is not required in this module aidl = false @@ -190,6 +168,14 @@ tasks.named("preBuild") { dependsOn(compileAidlNdk) } +// Ensure AIDL files are compiled before cargo build runs +afterEvaluate { + tasks.matching { it.name.contains("cargoBuild") }.configureEach { + dependsOn(compileAidlNdk) + dependsOn(generateLibEvDevEventNames) + } +} + // Must come after all tasks above, otherwise gradle syncing fails. // // Run cargo build when the files change. diff --git a/evdev/src/main/cpp/CMakeLists.txt b/evdev/src/main/cpp/CMakeLists.txt deleted file mode 100644 index 6fbc7bbdbb..0000000000 --- a/evdev/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,91 +0,0 @@ -# For more information about using CMake with Android Studio, read the -# documentation: https://d.android.com/studio/projects/add-native-code.html. -# For more examples on how to use CMake, see https://github.com/android/ndk-samples. - -# Sets the minimum CMake version required for this project. -cmake_minimum_required(VERSION 3.22.1) - -# Declares the project name. The project name can be accessed via ${ PROJECT_NAME}, -# Since this is the top level CMakeLists.txt, the project name is also accessible -# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level -# build script scope). -project("evdev") - -set(CMAKE_CXX_STANDARD 20) - -set(C_FLAGS "-Werror=format -fdata-sections -ffunction-sections -fno-exceptions -fno-rtti -fno-threadsafe-statics") -set(LINKER_FLAGS "-Wl,--hash-style=both") - -if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - message("Building Release...") - - set(C_FLAGS "${C_FLAGS} -O2 -fvisibility=hidden -fvisibility-inlines-hidden") - set(LINKER_FLAGS "${LINKER_FLAGS} -Wl,-exclude-libs,ALL -Wl,--gc-sections") -else () - message("Building Debug...") - - add_definitions(-DDEBUG) -endif () - -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_FLAGS}") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_FLAGS}") - -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}") -set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}") - -# Creates and names a library, sets it as either STATIC -# or SHARED, and provides the relative paths to its source code. -# You can define multiple libraries, and CMake builds them for you. -# Gradle automatically packages shared libraries with your APK. -# -# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define -# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME} -# is preferred for the same purpose. -# -# In order to load a library into your app from Java/Kotlin, you must call -# System.loadLibrary() and pass the name of the library defined here; -# for GameActivity/NativeActivity derived applications, the same library name must be -# used in the AndroidManifest.xml file. -add_library(evdev SHARED - # List C/C++ source files with relative paths to this CMakeLists.txt. - libevdev_jni.cpp - libevdev/libevdev.c - libevdev/libevdev-names.c - libevdev/libevdev-uinput.c - android/input/KeyLayoutMap.cpp - android/input/InputEventLabels.cpp - android/libbase/result.cpp - android/utils/Tokenizer.cpp - android/utils/String16.cpp - android/utils/String8.cpp - android/utils/SharedBuffer.cpp - android/utils/FileMap.cpp - android/utils/Unicode.cpp - android/input/InputDevice.cpp - android/input/Input.cpp - android/libbase/stringprintf.cpp - ${aidl_src_dir}/io/github/sds100/keymapper/evdev/IEvdevCallback.cpp) - -find_library( - binder_ndk-lib - binder_ndk -) - -# Specifies libraries CMake should link to your target library. You -# can link libraries from various origins, such as libraries defined in this -# build script, prebuilt third-party libraries, or Android system libraries. -target_link_libraries(evdev - # List libraries link to the target library - android - log - ${binder_ndk-lib}) - -# Add include directories for header files -target_include_directories(evdev PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/android - ${CMAKE_CURRENT_SOURCE_DIR}/android/input - ${CMAKE_CURRENT_SOURCE_DIR}/android/libbase - ${CMAKE_CURRENT_SOURCE_DIR}/android/utils - ${CMAKE_CURRENT_SOURCE_DIR}/libevdev) - diff --git a/evdev/src/main/rust/evdev_manager/Cargo.lock b/evdev/src/main/rust/evdev_manager/Cargo.lock index 4cef43c5a2..7596290a40 100644 --- a/evdev/src/main/rust/evdev_manager/Cargo.lock +++ b/evdev/src/main/rust/evdev_manager/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "android_liblog-sys" version = "0.1.4" @@ -21,24 +30,80 @@ dependencies = [ "log 0.3.9", ] +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log 0.4.28", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cc" +version = "1.2.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cesu8" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "combine" version = "4.6.7" @@ -49,15 +114,44 @@ dependencies = [ "memchr", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "evdev_manager" version = "0.1.0" dependencies = [ "android_log", + "bindgen", + "cc", "jni", "log 0.4.28", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "jni" version = "0.21.1" @@ -86,6 +180,16 @@ version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "log" version = "0.3.9" @@ -107,6 +211,32 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -125,6 +255,41 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "same-file" version = "1.0.6" @@ -134,6 +299,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "syn" version = "2.0.108" diff --git a/evdev/src/main/rust/evdev_manager/Cargo.toml b/evdev/src/main/rust/evdev_manager/Cargo.toml index c450603f19..dd94c0f1a2 100644 --- a/evdev/src/main/rust/evdev_manager/Cargo.toml +++ b/evdev/src/main/rust/evdev_manager/Cargo.toml @@ -8,7 +8,15 @@ name = "evdev_manager" # "cdylib" is necessary to produce a C-style dynamic library that can be loaded by Android's JNI. crate-type = ["cdylib"] +[profile.dev] +debug = true +strip = false # Don't strip symbols, keeps them in the .so file + [dependencies] jni = "0.21.1" log = "0.4.28" -android_log = "0.1.3" \ No newline at end of file +android_log = "0.1.3" + +[build-dependencies] +cc = "1.2.45" +bindgen = "0.72.1" \ No newline at end of file diff --git a/evdev/src/main/rust/evdev_manager/build.rs b/evdev/src/main/rust/evdev_manager/build.rs new file mode 100644 index 0000000000..2851b27740 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/build.rs @@ -0,0 +1,252 @@ +use std::env; +use std::path::{Path, PathBuf}; + +fn main() { + let manifest_dir: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + + // Map Rust target architecture to Android ABI directory + let target = env::var("TARGET").expect("TARGET environment variable not set"); + + // This crate only supports Android targets for actual builds. + // For cargo check on host systems, we'll skip C/C++ compilation but still + // generate bindings to allow type checking. + let is_android = target.contains("android"); + + if !is_android { + eprintln!("Warning: Building for non-Android target '{}'. Skipping C/C++ compilation.", target); + eprintln!("This crate is designed for Android. Use Gradle for actual builds."); + // Skip all compilation but succeed to allow cargo check to work + return; + } + + // Path to C/C++ source files + let cpp_dir = manifest_dir.join("../../cpp"); + + println!("cargo:rerun-if-changed={}", cpp_dir.to_str().unwrap()); + + // Find Android NDK sysroot for bindgen + let ndk_sysroot = find_ndk_sysroot(&manifest_dir); + let sysroot_include = ndk_sysroot.join("usr/include"); + + let libevdev_dir = cpp_dir.join("libevdev"); + let android_dir = cpp_dir.join("android"); + let aidl_dir = cpp_dir.join("aidl"); + + // Build C files from libevdev + let mut c_builder = cc::Build::new(); + c_builder + .file(libevdev_dir.join("libevdev.c")) + .file(libevdev_dir.join("libevdev-names.c")) + .file(libevdev_dir.join("libevdev-uinput.c")) + .include(&cpp_dir) + .include(&libevdev_dir) + .flag("-Werror=format") + .flag("-fdata-sections") + .flag("-ffunction-sections"); + + if env::var("PROFILE").unwrap() == "release" { + c_builder.flag("-O2").flag("-fvisibility=hidden"); + } + + c_builder.compile("evdev_c"); + + // Build C++ files + let mut cpp_builder = cc::Build::new(); + cpp_builder + .cpp(true) + .std("c++20") + // libevdev JNI wrapper + .file(cpp_dir.join("libevdev_jni.cpp")) + // Android input framework files + .file(android_dir.join("input/KeyLayoutMap.cpp")) + .file(android_dir.join("input/InputEventLabels.cpp")) + .file(android_dir.join("input/InputDevice.cpp")) + .file(android_dir.join("input/Input.cpp")) + // Android base library files + .file(android_dir.join("libbase/result.cpp")) + .file(android_dir.join("libbase/stringprintf.cpp")) + // Android utils files + .file(android_dir.join("utils/Tokenizer.cpp")) + .file(android_dir.join("utils/String16.cpp")) + .file(android_dir.join("utils/String8.cpp")) + .file(android_dir.join("utils/SharedBuffer.cpp")) + .file(android_dir.join("utils/FileMap.cpp")) + .file(android_dir.join("utils/Unicode.cpp")) + // AIDL generated file + .file(aidl_dir.join("io/github/sds100/keymapper/evdev/IEvdevCallback.cpp")) + // Include directories + .include(&cpp_dir) + .include(&android_dir) + .include(&android_dir.join("input")) + .include(&android_dir.join("libbase")) + .include(&android_dir.join("utils")) + .include(&libevdev_dir) + // Compiler flags matching CMakeLists.txt + .flag("-Werror=format") + .flag("-fdata-sections") + .flag("-ffunction-sections") + .flag("-fno-exceptions") + .flag("-fno-rtti") + .flag("-fno-threadsafe-statics"); + + if env::var("PROFILE").unwrap() == "release" { + cpp_builder + .flag("-O2") + .flag("-fvisibility=hidden") + .flag("-fvisibility-inlines-hidden"); + } + + cpp_builder.compile("evdev_cpp"); + + // Link against Android libraries + println!("cargo:rustc-link-lib=android"); + println!("cargo:rustc-link-lib=log"); + println!("cargo:rustc-link-lib=binder_ndk"); + + // Generate Rust bindings from libevdev headers + let evdev_headers_path = libevdev_dir.clone(); + + // Configure bindgen with Android NDK sysroot and include paths + let mut bindgen_builder = bindgen::Builder::default() + .formatter(bindgen::Formatter::Prettyplease) + // Disable all format/style warnings for generated code + .raw_line("#![allow(clippy::all)]") + .raw_line("#![allow(non_camel_case_types)]") + .raw_line("#![allow(non_snake_case)]") + .raw_line("#![allow(non_upper_case_globals)]") + .raw_line("#![allow(dead_code)]") + .raw_line("#![allow(rustdoc::broken_intra_doc_links)]") + .raw_line("#![allow(rustdoc::private_intra_doc_links)]") + .header(evdev_headers_path.join("libevdev.h").display().to_string()) + .header( + evdev_headers_path + .join("libevdev-int.h") + .display() + .to_string(), + ) + .header( + evdev_headers_path + .join("libevdev-uinput.h") + .display() + .to_string(), + ) + .header( + evdev_headers_path + .join("libevdev-uinput-int.h") + .display() + .to_string(), + ) + .clang_arg(format!("--sysroot={}", ndk_sysroot.display())) + .clang_arg(format!("-I{}", sysroot_include.display())); + + // Add architecture-specific includes if needed + let arch_include = get_arch_include_path(&target, &ndk_sysroot); + if arch_include.exists() { + bindgen_builder = bindgen_builder.clang_arg(format!("-I{}", arch_include.display())); + } + + let bindings = bindgen_builder + .generate() + .expect("Unable to generate bindings"); + + let out_path = manifest_dir.join("src"); + + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn find_ndk_sysroot(manifest_dir: &Path) -> PathBuf { + let sdk_dir = get_sdk_dir(manifest_dir).expect("SDK directory not available"); + let ndk_version = "27.2.12479018"; + + get_sysroot_for_version(&sdk_dir, &ndk_version) +} + +fn get_sdk_dir(manifest_dir: &Path) -> Option { + // 1. Read from local.properties file + // Navigate from evdev crate to project root + let local_properties = manifest_dir.join("../../../../../local.properties"); + + if let Ok(contents) = std::fs::read_to_string(&local_properties) { + for line in contents.lines() { + // Skip comments and empty lines + let line = line.trim(); + if line.is_empty() || line.starts_with('#') { + continue; + } + + // Look for sdk.dir=value or android.sdk.dir=value + if let Some(stripped) = line.strip_prefix("sdk.dir=") { + let sdk_path = stripped.trim(); + if !sdk_path.is_empty() { + return Some(sdk_path.to_string()); + } + } + if let Some(stripped) = line.strip_prefix("android.sdk.dir=") { + let sdk_path = stripped.trim(); + if !sdk_path.is_empty() { + return Some(sdk_path.to_string()); + } + } + } + } + + // 2. Check environment variables + if let Ok(sdk_dir) = env::var("ANDROID_SDK_ROOT") { + return Some(sdk_dir); + } + + if let Ok(sdk_dir) = env::var("ANDROID_HOME") { + return Some(sdk_dir); + } + + None +} + +fn get_sysroot_for_version(sdk_dir: &str, version: &str) -> PathBuf { + // Detect host platform + let host = if cfg!(target_os = "macos") { + "darwin-x86_64" + } else if cfg!(target_os = "linux") { + "linux-x86_64" + } else if cfg!(target_os = "windows") { + "windows-x86_64" + } else { + panic!("Unsupported host platform for NDK") + }; + + let sysroot = PathBuf::from(sdk_dir) + .join("ndk") + .join(version) + .join("toolchains") + .join("llvm") + .join("prebuilt") + .join(host) + .join("sysroot"); + + if !sysroot.exists() { + panic!( + "Could not find Android NDK sysroot for version {} at {}. Please ensure NDK {} is installed in {}/ndk/", + version, sysroot.display(), version, sdk_dir + ); + } + + sysroot +} + +fn get_arch_include_path(target: &str, sysroot: &Path) -> PathBuf { + let arch = if target.contains("aarch64") { + "aarch64-linux-android" + } else if target.contains("armv7") { + "arm-linux-androideabi" + } else if target.contains("i686") { + "i686-linux-android" + } else if target.contains("x86_64") { + "x86_64-linux-android" + } else { + return sysroot.join("usr").join("include").join("nonexistent"); + }; + + sysroot.join("usr").join("include").join(arch) +} diff --git a/evdev/src/main/rust/evdev_manager/rustfmt.toml b/evdev/src/main/rust/evdev_manager/rustfmt.toml new file mode 100644 index 0000000000..dc22519282 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/rustfmt.toml @@ -0,0 +1,4 @@ +# Workspace-wide rustfmt configuration +# Exclude generated bindings.rs from formatting +ignore = ["src/bindings.rs"] + diff --git a/evdev/src/main/rust/evdev_manager/src/bindings.rs b/evdev/src/main/rust/evdev_manager/src/bindings.rs new file mode 100644 index 0000000000..1acd99bfe1 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/src/bindings.rs @@ -0,0 +1,7629 @@ +/* automatically generated by rust-bindgen 0.72.1 */ + +#![allow(clippy::all)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(dead_code)] +#![allow(rustdoc::broken_intra_doc_links)] +#![allow(rustdoc::private_intra_doc_links)] + +/// If Bindgen could only determine the size and alignment of a +/// type, it is represented like this. +#[derive(PartialEq, Copy, Clone, Debug, Hash)] +#[repr(C)] +pub struct __BindgenOpaqueArray(pub [T; N]); +impl Default for __BindgenOpaqueArray { + fn default() -> Self { + Self([::default(); N]) + } +} +pub const __BIONIC__: u32 = 1; +pub const __WORDSIZE: u32 = 64; +pub const __bos_level: u32 = 0; +pub const __ANDROID_API_FUTURE__: u32 = 10000; +pub const __ANDROID_API__: u32 = 10000; +pub const __ANDROID_API_G__: u32 = 9; +pub const __ANDROID_API_I__: u32 = 14; +pub const __ANDROID_API_J__: u32 = 16; +pub const __ANDROID_API_J_MR1__: u32 = 17; +pub const __ANDROID_API_J_MR2__: u32 = 18; +pub const __ANDROID_API_K__: u32 = 19; +pub const __ANDROID_API_L__: u32 = 21; +pub const __ANDROID_API_L_MR1__: u32 = 22; +pub const __ANDROID_API_M__: u32 = 23; +pub const __ANDROID_API_N__: u32 = 24; +pub const __ANDROID_API_N_MR1__: u32 = 25; +pub const __ANDROID_API_O__: u32 = 26; +pub const __ANDROID_API_O_MR1__: u32 = 27; +pub const __ANDROID_API_P__: u32 = 28; +pub const __ANDROID_API_Q__: u32 = 29; +pub const __ANDROID_API_R__: u32 = 30; +pub const __ANDROID_API_S__: u32 = 31; +pub const __ANDROID_API_T__: u32 = 33; +pub const __ANDROID_API_U__: u32 = 34; +pub const __ANDROID_API_V__: u32 = 35; +pub const __ANDROID_NDK__: u32 = 1; +pub const __NDK_MAJOR__: u32 = 27; +pub const __NDK_MINOR__: u32 = 2; +pub const __NDK_BETA__: u32 = 0; +pub const __NDK_BUILD__: u32 = 12479018; +pub const __NDK_CANARY__: u32 = 0; +pub const WCHAR_MIN: u8 = 0u8; +pub const INT8_MIN: i32 = -128; +pub const INT8_MAX: u32 = 127; +pub const INT_LEAST8_MIN: i32 = -128; +pub const INT_LEAST8_MAX: u32 = 127; +pub const INT_FAST8_MIN: i32 = -128; +pub const INT_FAST8_MAX: u32 = 127; +pub const UINT8_MAX: u32 = 255; +pub const UINT_LEAST8_MAX: u32 = 255; +pub const UINT_FAST8_MAX: u32 = 255; +pub const INT16_MIN: i32 = -32768; +pub const INT16_MAX: u32 = 32767; +pub const INT_LEAST16_MIN: i32 = -32768; +pub const INT_LEAST16_MAX: u32 = 32767; +pub const UINT16_MAX: u32 = 65535; +pub const UINT_LEAST16_MAX: u32 = 65535; +pub const INT32_MIN: i32 = -2147483648; +pub const INT32_MAX: u32 = 2147483647; +pub const INT_LEAST32_MIN: i32 = -2147483648; +pub const INT_LEAST32_MAX: u32 = 2147483647; +pub const INT_FAST32_MIN: i32 = -2147483648; +pub const INT_FAST32_MAX: u32 = 2147483647; +pub const UINT32_MAX: u32 = 4294967295; +pub const UINT_LEAST32_MAX: u32 = 4294967295; +pub const UINT_FAST32_MAX: u32 = 4294967295; +pub const SIG_ATOMIC_MAX: u32 = 2147483647; +pub const SIG_ATOMIC_MIN: i32 = -2147483648; +pub const WINT_MAX: u32 = 4294967295; +pub const WINT_MIN: u32 = 0; +pub const __BITS_PER_LONG: u32 = 64; +pub const __FD_SETSIZE: u32 = 1024; +pub const ITIMER_REAL: u32 = 0; +pub const ITIMER_VIRTUAL: u32 = 1; +pub const ITIMER_PROF: u32 = 2; +pub const CLOCK_REALTIME: u32 = 0; +pub const CLOCK_MONOTONIC: u32 = 1; +pub const CLOCK_PROCESS_CPUTIME_ID: u32 = 2; +pub const CLOCK_THREAD_CPUTIME_ID: u32 = 3; +pub const CLOCK_MONOTONIC_RAW: u32 = 4; +pub const CLOCK_REALTIME_COARSE: u32 = 5; +pub const CLOCK_MONOTONIC_COARSE: u32 = 6; +pub const CLOCK_BOOTTIME: u32 = 7; +pub const CLOCK_REALTIME_ALARM: u32 = 8; +pub const CLOCK_BOOTTIME_ALARM: u32 = 9; +pub const CLOCK_SGI_CYCLE: u32 = 10; +pub const CLOCK_TAI: u32 = 11; +pub const MAX_CLOCKS: u32 = 16; +pub const CLOCKS_MASK: u32 = 1; +pub const CLOCKS_MONO: u32 = 1; +pub const TIMER_ABSTIME: u32 = 1; +pub const FPSIMD_MAGIC: u32 = 1179680769; +pub const ESR_MAGIC: u32 = 1163088385; +pub const EXTRA_MAGIC: u32 = 1163416577; +pub const SVE_MAGIC: u32 = 1398162689; +pub const SVE_SIG_FLAG_SM: u32 = 1; +pub const TPIDR2_MAGIC: u32 = 1414547714; +pub const ZA_MAGIC: u32 = 1412850501; +pub const ZT_MAGIC: u32 = 1515474433; +pub const __SVE_VQ_BYTES: u32 = 16; +pub const __SVE_VQ_MIN: u32 = 1; +pub const __SVE_VQ_MAX: u32 = 512; +pub const __SVE_VL_MIN: u32 = 16; +pub const __SVE_VL_MAX: u32 = 8192; +pub const __SVE_NUM_ZREGS: u32 = 32; +pub const __SVE_NUM_PREGS: u32 = 16; +pub const __SVE_ZREGS_OFFSET: u32 = 0; +pub const SVE_VQ_BYTES: u32 = 16; +pub const SVE_VQ_MIN: u32 = 1; +pub const SVE_VQ_MAX: u32 = 512; +pub const SVE_VL_MIN: u32 = 16; +pub const SVE_VL_MAX: u32 = 8192; +pub const SVE_NUM_ZREGS: u32 = 32; +pub const SVE_NUM_PREGS: u32 = 16; +pub const ZT_SIG_REG_SIZE: u32 = 512; +pub const ZT_SIG_REG_BYTES: u32 = 64; +pub const NR_OPEN: u32 = 1024; +pub const NGROUPS_MAX: u32 = 65536; +pub const ARG_MAX: u32 = 131072; +pub const LINK_MAX: u32 = 127; +pub const MAX_CANON: u32 = 255; +pub const MAX_INPUT: u32 = 255; +pub const NAME_MAX: u32 = 255; +pub const PATH_MAX: u32 = 4096; +pub const PIPE_BUF: u32 = 4096; +pub const XATTR_NAME_MAX: u32 = 255; +pub const XATTR_SIZE_MAX: u32 = 65536; +pub const XATTR_LIST_MAX: u32 = 65536; +pub const RTSIG_MAX: u32 = 32; +pub const PASS_MAX: u32 = 128; +pub const NL_ARGMAX: u32 = 9; +pub const NL_LANGMAX: u32 = 14; +pub const NL_MSGMAX: u32 = 32767; +pub const NL_NMAX: u32 = 1; +pub const NL_SETMAX: u32 = 255; +pub const NL_TEXTMAX: u32 = 255; +pub const TMP_MAX: u32 = 308915776; +pub const CHAR_BIT: u32 = 8; +pub const LONG_BIT: u32 = 64; +pub const WORD_BIT: u32 = 32; +pub const SCHAR_MAX: u32 = 127; +pub const SCHAR_MIN: i32 = -128; +pub const UCHAR_MAX: u32 = 255; +pub const CHAR_MIN: u32 = 0; +pub const CHAR_MAX: u32 = 255; +pub const USHRT_MAX: u32 = 65535; +pub const SHRT_MAX: u32 = 32767; +pub const SHRT_MIN: i32 = -32768; +pub const UINT_MAX: u32 = 4294967295; +pub const INT_MAX: u32 = 2147483647; +pub const INT_MIN: i32 = -2147483648; +pub const ULONG_MAX: i32 = -1; +pub const LONG_MAX: u64 = 9223372036854775807; +pub const LONG_MIN: i64 = -9223372036854775808; +pub const ULLONG_MAX: i32 = -1; +pub const LLONG_MAX: u64 = 9223372036854775807; +pub const LLONG_MIN: i64 = -9223372036854775808; +pub const LONG_LONG_MIN: i64 = -9223372036854775808; +pub const LONG_LONG_MAX: u64 = 9223372036854775807; +pub const ULONG_LONG_MAX: i32 = -1; +pub const UID_MAX: u32 = 4294967295; +pub const GID_MAX: u32 = 4294967295; +pub const SIZE_T_MAX: i32 = -1; +pub const SSIZE_MAX: u64 = 9223372036854775807; +pub const MB_LEN_MAX: u32 = 4; +pub const NZERO: u32 = 20; +pub const IOV_MAX: u32 = 1024; +pub const SEM_VALUE_MAX: u32 = 1073741823; +pub const _POSIX_VERSION: u32 = 200809; +pub const _POSIX2_VERSION: u32 = 200809; +pub const _XOPEN_VERSION: u32 = 700; +pub const __BIONIC_POSIX_FEATURE_MISSING: i32 = -1; +pub const _POSIX_ASYNCHRONOUS_IO: i32 = -1; +pub const _POSIX_CHOWN_RESTRICTED: u32 = 1; +pub const _POSIX_CPUTIME: u32 = 200809; +pub const _POSIX_FSYNC: u32 = 200809; +pub const _POSIX_IPV6: u32 = 200809; +pub const _POSIX_MAPPED_FILES: u32 = 200809; +pub const _POSIX_MEMLOCK_RANGE: u32 = 200809; +pub const _POSIX_MEMORY_PROTECTION: u32 = 200809; +pub const _POSIX_MESSAGE_PASSING: i32 = -1; +pub const _POSIX_MONOTONIC_CLOCK: u32 = 200809; +pub const _POSIX_NO_TRUNC: u32 = 1; +pub const _POSIX_PRIORITIZED_IO: i32 = -1; +pub const _POSIX_PRIORITY_SCHEDULING: u32 = 200809; +pub const _POSIX_RAW_SOCKETS: u32 = 200809; +pub const _POSIX_READER_WRITER_LOCKS: u32 = 200809; +pub const _POSIX_REGEXP: u32 = 1; +pub const _POSIX_SAVED_IDS: u32 = 1; +pub const _POSIX_SEMAPHORES: u32 = 200809; +pub const _POSIX_SHARED_MEMORY_OBJECTS: i32 = -1; +pub const _POSIX_SHELL: u32 = 1; +pub const _POSIX_SPORADIC_SERVER: i32 = -1; +pub const _POSIX_SYNCHRONIZED_IO: u32 = 200809; +pub const _POSIX_THREAD_ATTR_STACKADDR: u32 = 200809; +pub const _POSIX_THREAD_ATTR_STACKSIZE: u32 = 200809; +pub const _POSIX_THREAD_CPUTIME: u32 = 200809; +pub const _POSIX_THREAD_PRIO_INHERIT: i32 = -1; +pub const _POSIX_THREAD_PRIO_PROTECT: i32 = -1; +pub const _POSIX_THREAD_PRIORITY_SCHEDULING: u32 = 200809; +pub const _POSIX_THREAD_PROCESS_SHARED: u32 = 200809; +pub const _POSIX_THREAD_ROBUST_PRIO_INHERIT: i32 = -1; +pub const _POSIX_THREAD_ROBUST_PRIO_PROTECT: i32 = -1; +pub const _POSIX_THREAD_SAFE_FUNCTIONS: u32 = 200809; +pub const _POSIX_THREAD_SPORADIC_SERVER: i32 = -1; +pub const _POSIX_THREADS: u32 = 200809; +pub const _POSIX_TIMERS: u32 = 200809; +pub const _POSIX_TRACE: i32 = -1; +pub const _POSIX_TRACE_EVENT_FILTER: i32 = -1; +pub const _POSIX_TRACE_INHERIT: i32 = -1; +pub const _POSIX_TRACE_LOG: i32 = -1; +pub const _POSIX_TYPED_MEMORY_OBJECTS: i32 = -1; +pub const _POSIX_VDISABLE: u8 = 0u8; +pub const _POSIX2_C_BIND: u32 = 200809; +pub const _POSIX2_C_DEV: i32 = -1; +pub const _POSIX2_CHAR_TERM: u32 = 200809; +pub const _POSIX2_FORT_DEV: i32 = -1; +pub const _POSIX2_FORT_RUN: i32 = -1; +pub const _POSIX2_LOCALEDEF: i32 = -1; +pub const _POSIX2_SW_DEV: i32 = -1; +pub const _POSIX2_UPE: i32 = -1; +pub const _POSIX_V7_ILP32_OFF32: i32 = -1; +pub const _POSIX_V7_ILP32_OFFBIG: i32 = -1; +pub const _POSIX_V7_LP64_OFF64: u32 = 1; +pub const _POSIX_V7_LPBIG_OFFBIG: u32 = 1; +pub const _XOPEN_CRYPT: i32 = -1; +pub const _XOPEN_ENH_I18N: u32 = 1; +pub const _XOPEN_LEGACY: i32 = -1; +pub const _XOPEN_REALTIME: u32 = 1; +pub const _XOPEN_REALTIME_THREADS: u32 = 1; +pub const _XOPEN_SHM: u32 = 1; +pub const _XOPEN_STREAMS: i32 = -1; +pub const _XOPEN_UNIX: u32 = 1; +pub const _POSIX_AIO_LISTIO_MAX: u32 = 2; +pub const _POSIX_AIO_MAX: u32 = 1; +pub const _POSIX_ARG_MAX: u32 = 4096; +pub const _POSIX_CHILD_MAX: u32 = 25; +pub const _POSIX_CLOCKRES_MIN: u32 = 20000000; +pub const _POSIX_DELAYTIMER_MAX: u32 = 32; +pub const _POSIX_HOST_NAME_MAX: u32 = 255; +pub const _POSIX_LINK_MAX: u32 = 8; +pub const _POSIX_LOGIN_NAME_MAX: u32 = 9; +pub const _POSIX_MAX_CANON: u32 = 255; +pub const _POSIX_MAX_INPUT: u32 = 255; +pub const _POSIX_MQ_OPEN_MAX: u32 = 8; +pub const _POSIX_MQ_PRIO_MAX: u32 = 32; +pub const _POSIX_NAME_MAX: u32 = 14; +pub const _POSIX_NGROUPS_MAX: u32 = 8; +pub const _POSIX_OPEN_MAX: u32 = 20; +pub const _POSIX_PATH_MAX: u32 = 256; +pub const _POSIX_PIPE_BUF: u32 = 512; +pub const _POSIX_RE_DUP_MAX: u32 = 255; +pub const _POSIX_RTSIG_MAX: u32 = 8; +pub const _POSIX_SEM_NSEMS_MAX: u32 = 256; +pub const _POSIX_SEM_VALUE_MAX: u32 = 32767; +pub const _POSIX_SIGQUEUE_MAX: u32 = 32; +pub const _POSIX_SSIZE_MAX: u32 = 32767; +pub const _POSIX_STREAM_MAX: u32 = 8; +pub const _POSIX_SS_REPL_MAX: u32 = 4; +pub const _POSIX_SYMLINK_MAX: u32 = 255; +pub const _POSIX_SYMLOOP_MAX: u32 = 8; +pub const _POSIX_THREAD_DESTRUCTOR_ITERATIONS: u32 = 4; +pub const _POSIX_THREAD_KEYS_MAX: u32 = 128; +pub const _POSIX_THREAD_THREADS_MAX: u32 = 64; +pub const _POSIX_TIMER_MAX: u32 = 32; +pub const _POSIX_TRACE_EVENT_NAME_MAX: u32 = 30; +pub const _POSIX_TRACE_NAME_MAX: u32 = 8; +pub const _POSIX_TRACE_SYS_MAX: u32 = 8; +pub const _POSIX_TRACE_USER_EVENT_MAX: u32 = 32; +pub const _POSIX_TTY_NAME_MAX: u32 = 9; +pub const _POSIX_TZNAME_MAX: u32 = 6; +pub const _POSIX2_BC_BASE_MAX: u32 = 99; +pub const _POSIX2_BC_DIM_MAX: u32 = 2048; +pub const _POSIX2_BC_SCALE_MAX: u32 = 99; +pub const _POSIX2_BC_STRING_MAX: u32 = 1000; +pub const _POSIX2_CHARCLASS_NAME_MAX: u32 = 14; +pub const _POSIX2_COLL_WEIGHTS_MAX: u32 = 2; +pub const _POSIX2_EXPR_NEST_MAX: u32 = 32; +pub const _POSIX2_LINE_MAX: u32 = 2048; +pub const _POSIX2_RE_DUP_MAX: u32 = 255; +pub const _XOPEN_IOV_MAX: u32 = 16; +pub const _XOPEN_NAME_MAX: u32 = 255; +pub const _XOPEN_PATH_MAX: u32 = 1024; +pub const HOST_NAME_MAX: u32 = 255; +pub const LOGIN_NAME_MAX: u32 = 256; +pub const TTY_NAME_MAX: u32 = 32; +pub const PTHREAD_DESTRUCTOR_ITERATIONS: u32 = 4; +pub const PTHREAD_KEYS_MAX: u32 = 128; +pub const SA_RESTORER: u32 = 67108864; +pub const MINSIGSTKSZ: u32 = 5120; +pub const SIGSTKSZ: u32 = 16384; +pub const _KERNEL__NSIG: u32 = 64; +pub const _NSIG_BPW: u32 = 64; +pub const _NSIG_WORDS: u32 = 1; +pub const SIGHUP: u32 = 1; +pub const SIGINT: u32 = 2; +pub const SIGQUIT: u32 = 3; +pub const SIGILL: u32 = 4; +pub const SIGTRAP: u32 = 5; +pub const SIGABRT: u32 = 6; +pub const SIGIOT: u32 = 6; +pub const SIGBUS: u32 = 7; +pub const SIGFPE: u32 = 8; +pub const SIGKILL: u32 = 9; +pub const SIGUSR1: u32 = 10; +pub const SIGSEGV: u32 = 11; +pub const SIGUSR2: u32 = 12; +pub const SIGPIPE: u32 = 13; +pub const SIGALRM: u32 = 14; +pub const SIGTERM: u32 = 15; +pub const SIGSTKFLT: u32 = 16; +pub const SIGCHLD: u32 = 17; +pub const SIGCONT: u32 = 18; +pub const SIGSTOP: u32 = 19; +pub const SIGTSTP: u32 = 20; +pub const SIGTTIN: u32 = 21; +pub const SIGTTOU: u32 = 22; +pub const SIGURG: u32 = 23; +pub const SIGXCPU: u32 = 24; +pub const SIGXFSZ: u32 = 25; +pub const SIGVTALRM: u32 = 26; +pub const SIGPROF: u32 = 27; +pub const SIGWINCH: u32 = 28; +pub const SIGIO: u32 = 29; +pub const SIGPOLL: u32 = 29; +pub const SIGPWR: u32 = 30; +pub const SIGSYS: u32 = 31; +pub const SIGUNUSED: u32 = 31; +pub const __SIGRTMIN: u32 = 32; +pub const __SIGRTMAX: u32 = 64; +pub const SA_NOCLDSTOP: u32 = 1; +pub const SA_NOCLDWAIT: u32 = 2; +pub const SA_SIGINFO: u32 = 4; +pub const SA_UNSUPPORTED: u32 = 1024; +pub const SA_EXPOSE_TAGBITS: u32 = 2048; +pub const SA_ONSTACK: u32 = 134217728; +pub const SA_RESTART: u32 = 268435456; +pub const SA_NODEFER: u32 = 1073741824; +pub const SA_RESETHAND: u32 = 2147483648; +pub const SA_NOMASK: u32 = 1073741824; +pub const SA_ONESHOT: u32 = 2147483648; +pub const SIG_BLOCK: u32 = 0; +pub const SIG_UNBLOCK: u32 = 1; +pub const SIG_SETMASK: u32 = 2; +pub const SI_MAX_SIZE: u32 = 128; +pub const SI_USER: u32 = 0; +pub const SI_KERNEL: u32 = 128; +pub const SI_QUEUE: i32 = -1; +pub const SI_TIMER: i32 = -2; +pub const SI_MESGQ: i32 = -3; +pub const SI_ASYNCIO: i32 = -4; +pub const SI_SIGIO: i32 = -5; +pub const SI_TKILL: i32 = -6; +pub const SI_DETHREAD: i32 = -7; +pub const SI_ASYNCNL: i32 = -60; +pub const ILL_ILLOPC: u32 = 1; +pub const ILL_ILLOPN: u32 = 2; +pub const ILL_ILLADR: u32 = 3; +pub const ILL_ILLTRP: u32 = 4; +pub const ILL_PRVOPC: u32 = 5; +pub const ILL_PRVREG: u32 = 6; +pub const ILL_COPROC: u32 = 7; +pub const ILL_BADSTK: u32 = 8; +pub const ILL_BADIADDR: u32 = 9; +pub const __ILL_BREAK: u32 = 10; +pub const __ILL_BNDMOD: u32 = 11; +pub const NSIGILL: u32 = 11; +pub const FPE_INTDIV: u32 = 1; +pub const FPE_INTOVF: u32 = 2; +pub const FPE_FLTDIV: u32 = 3; +pub const FPE_FLTOVF: u32 = 4; +pub const FPE_FLTUND: u32 = 5; +pub const FPE_FLTRES: u32 = 6; +pub const FPE_FLTINV: u32 = 7; +pub const FPE_FLTSUB: u32 = 8; +pub const __FPE_DECOVF: u32 = 9; +pub const __FPE_DECDIV: u32 = 10; +pub const __FPE_DECERR: u32 = 11; +pub const __FPE_INVASC: u32 = 12; +pub const __FPE_INVDEC: u32 = 13; +pub const FPE_FLTUNK: u32 = 14; +pub const FPE_CONDTRAP: u32 = 15; +pub const NSIGFPE: u32 = 15; +pub const SEGV_MAPERR: u32 = 1; +pub const SEGV_ACCERR: u32 = 2; +pub const SEGV_BNDERR: u32 = 3; +pub const SEGV_PKUERR: u32 = 4; +pub const SEGV_ACCADI: u32 = 5; +pub const SEGV_ADIDERR: u32 = 6; +pub const SEGV_ADIPERR: u32 = 7; +pub const SEGV_MTEAERR: u32 = 8; +pub const SEGV_MTESERR: u32 = 9; +pub const SEGV_CPERR: u32 = 10; +pub const NSIGSEGV: u32 = 10; +pub const BUS_ADRALN: u32 = 1; +pub const BUS_ADRERR: u32 = 2; +pub const BUS_OBJERR: u32 = 3; +pub const BUS_MCEERR_AR: u32 = 4; +pub const BUS_MCEERR_AO: u32 = 5; +pub const NSIGBUS: u32 = 5; +pub const TRAP_BRKPT: u32 = 1; +pub const TRAP_TRACE: u32 = 2; +pub const TRAP_BRANCH: u32 = 3; +pub const TRAP_HWBKPT: u32 = 4; +pub const TRAP_UNK: u32 = 5; +pub const TRAP_PERF: u32 = 6; +pub const NSIGTRAP: u32 = 6; +pub const TRAP_PERF_FLAG_ASYNC: u32 = 1; +pub const CLD_EXITED: u32 = 1; +pub const CLD_KILLED: u32 = 2; +pub const CLD_DUMPED: u32 = 3; +pub const CLD_TRAPPED: u32 = 4; +pub const CLD_STOPPED: u32 = 5; +pub const CLD_CONTINUED: u32 = 6; +pub const NSIGCHLD: u32 = 6; +pub const POLL_IN: u32 = 1; +pub const POLL_OUT: u32 = 2; +pub const POLL_MSG: u32 = 3; +pub const POLL_ERR: u32 = 4; +pub const POLL_PRI: u32 = 5; +pub const POLL_HUP: u32 = 6; +pub const NSIGPOLL: u32 = 6; +pub const SYS_SECCOMP: u32 = 1; +pub const SYS_USER_DISPATCH: u32 = 2; +pub const NSIGSYS: u32 = 2; +pub const EMT_TAGOVF: u32 = 1; +pub const NSIGEMT: u32 = 1; +pub const SIGEV_SIGNAL: u32 = 0; +pub const SIGEV_NONE: u32 = 1; +pub const SIGEV_THREAD: u32 = 2; +pub const SIGEV_THREAD_ID: u32 = 4; +pub const SIGEV_MAX_SIZE: u32 = 64; +pub const SS_ONSTACK: u32 = 1; +pub const SS_DISABLE: u32 = 2; +pub const SS_AUTODISARM: u32 = 2147483648; +pub const SS_FLAG_BITS: u32 = 2147483648; +pub const _NSIG: u32 = 65; +pub const NSIG: u32 = 65; +pub const PAGE_SIZE: u32 = 4096; +pub const PAGE_MASK: i32 = -4096; +pub const NGREG: u32 = 34; +pub const FD_SETSIZE: u32 = 1024; +pub const _IOC_NRBITS: u32 = 8; +pub const _IOC_TYPEBITS: u32 = 8; +pub const _IOC_SIZEBITS: u32 = 14; +pub const _IOC_DIRBITS: u32 = 2; +pub const _IOC_NRMASK: u32 = 255; +pub const _IOC_TYPEMASK: u32 = 255; +pub const _IOC_SIZEMASK: u32 = 16383; +pub const _IOC_DIRMASK: u32 = 3; +pub const _IOC_NRSHIFT: u32 = 0; +pub const _IOC_TYPESHIFT: u32 = 8; +pub const _IOC_SIZESHIFT: u32 = 16; +pub const _IOC_DIRSHIFT: u32 = 30; +pub const _IOC_NONE: u32 = 0; +pub const _IOC_WRITE: u32 = 1; +pub const _IOC_READ: u32 = 2; +pub const IOC_IN: u32 = 1073741824; +pub const IOC_OUT: u32 = 2147483648; +pub const IOC_INOUT: u32 = 3221225472; +pub const IOCSIZE_MASK: u32 = 1073676288; +pub const IOCSIZE_SHIFT: u32 = 16; +pub const IGNBRK: u32 = 1; +pub const BRKINT: u32 = 2; +pub const IGNPAR: u32 = 4; +pub const PARMRK: u32 = 8; +pub const INPCK: u32 = 16; +pub const ISTRIP: u32 = 32; +pub const INLCR: u32 = 64; +pub const IGNCR: u32 = 128; +pub const ICRNL: u32 = 256; +pub const IXANY: u32 = 2048; +pub const OPOST: u32 = 1; +pub const OCRNL: u32 = 8; +pub const ONOCR: u32 = 16; +pub const ONLRET: u32 = 32; +pub const OFILL: u32 = 64; +pub const OFDEL: u32 = 128; +pub const B0: u32 = 0; +pub const B50: u32 = 1; +pub const B75: u32 = 2; +pub const B110: u32 = 3; +pub const B134: u32 = 4; +pub const B150: u32 = 5; +pub const B200: u32 = 6; +pub const B300: u32 = 7; +pub const B600: u32 = 8; +pub const B1200: u32 = 9; +pub const B1800: u32 = 10; +pub const B2400: u32 = 11; +pub const B4800: u32 = 12; +pub const B9600: u32 = 13; +pub const B19200: u32 = 14; +pub const B38400: u32 = 15; +pub const EXTA: u32 = 14; +pub const EXTB: u32 = 15; +pub const ADDRB: u32 = 536870912; +pub const CMSPAR: u32 = 1073741824; +pub const CRTSCTS: u32 = 2147483648; +pub const IBSHIFT: u32 = 16; +pub const TCOOFF: u32 = 0; +pub const TCOON: u32 = 1; +pub const TCIOFF: u32 = 2; +pub const TCION: u32 = 3; +pub const TCIFLUSH: u32 = 0; +pub const TCOFLUSH: u32 = 1; +pub const TCIOFLUSH: u32 = 2; +pub const NCCS: u32 = 19; +pub const VINTR: u32 = 0; +pub const VQUIT: u32 = 1; +pub const VERASE: u32 = 2; +pub const VKILL: u32 = 3; +pub const VEOF: u32 = 4; +pub const VTIME: u32 = 5; +pub const VMIN: u32 = 6; +pub const VSWTC: u32 = 7; +pub const VSTART: u32 = 8; +pub const VSTOP: u32 = 9; +pub const VSUSP: u32 = 10; +pub const VEOL: u32 = 11; +pub const VREPRINT: u32 = 12; +pub const VDISCARD: u32 = 13; +pub const VWERASE: u32 = 14; +pub const VLNEXT: u32 = 15; +pub const VEOL2: u32 = 16; +pub const IUCLC: u32 = 512; +pub const IXON: u32 = 1024; +pub const IXOFF: u32 = 4096; +pub const IMAXBEL: u32 = 8192; +pub const IUTF8: u32 = 16384; +pub const OLCUC: u32 = 2; +pub const ONLCR: u32 = 4; +pub const NLDLY: u32 = 256; +pub const NL0: u32 = 0; +pub const NL1: u32 = 256; +pub const CRDLY: u32 = 1536; +pub const CR0: u32 = 0; +pub const CR1: u32 = 512; +pub const CR2: u32 = 1024; +pub const CR3: u32 = 1536; +pub const TABDLY: u32 = 6144; +pub const TAB0: u32 = 0; +pub const TAB1: u32 = 2048; +pub const TAB2: u32 = 4096; +pub const TAB3: u32 = 6144; +pub const XTABS: u32 = 6144; +pub const BSDLY: u32 = 8192; +pub const BS0: u32 = 0; +pub const BS1: u32 = 8192; +pub const VTDLY: u32 = 16384; +pub const VT0: u32 = 0; +pub const VT1: u32 = 16384; +pub const FFDLY: u32 = 32768; +pub const FF0: u32 = 0; +pub const FF1: u32 = 32768; +pub const CBAUD: u32 = 4111; +pub const CSIZE: u32 = 48; +pub const CS5: u32 = 0; +pub const CS6: u32 = 16; +pub const CS7: u32 = 32; +pub const CS8: u32 = 48; +pub const CSTOPB: u32 = 64; +pub const CREAD: u32 = 128; +pub const PARENB: u32 = 256; +pub const PARODD: u32 = 512; +pub const HUPCL: u32 = 1024; +pub const CLOCAL: u32 = 2048; +pub const CBAUDEX: u32 = 4096; +pub const BOTHER: u32 = 4096; +pub const B57600: u32 = 4097; +pub const B115200: u32 = 4098; +pub const B230400: u32 = 4099; +pub const B460800: u32 = 4100; +pub const B500000: u32 = 4101; +pub const B576000: u32 = 4102; +pub const B921600: u32 = 4103; +pub const B1000000: u32 = 4104; +pub const B1152000: u32 = 4105; +pub const B1500000: u32 = 4106; +pub const B2000000: u32 = 4107; +pub const B2500000: u32 = 4108; +pub const B3000000: u32 = 4109; +pub const B3500000: u32 = 4110; +pub const B4000000: u32 = 4111; +pub const CIBAUD: u32 = 269418496; +pub const ISIG: u32 = 1; +pub const ICANON: u32 = 2; +pub const XCASE: u32 = 4; +pub const ECHO: u32 = 8; +pub const ECHOE: u32 = 16; +pub const ECHOK: u32 = 32; +pub const ECHONL: u32 = 64; +pub const NOFLSH: u32 = 128; +pub const TOSTOP: u32 = 256; +pub const ECHOCTL: u32 = 512; +pub const ECHOPRT: u32 = 1024; +pub const ECHOKE: u32 = 2048; +pub const FLUSHO: u32 = 4096; +pub const PENDIN: u32 = 16384; +pub const IEXTEN: u32 = 32768; +pub const EXTPROC: u32 = 65536; +pub const TCSANOW: u32 = 0; +pub const TCSADRAIN: u32 = 1; +pub const TCSAFLUSH: u32 = 2; +pub const TCGETS: u32 = 21505; +pub const TCSETS: u32 = 21506; +pub const TCSETSW: u32 = 21507; +pub const TCSETSF: u32 = 21508; +pub const TCGETA: u32 = 21509; +pub const TCSETA: u32 = 21510; +pub const TCSETAW: u32 = 21511; +pub const TCSETAF: u32 = 21512; +pub const TCSBRK: u32 = 21513; +pub const TCXONC: u32 = 21514; +pub const TCFLSH: u32 = 21515; +pub const TIOCEXCL: u32 = 21516; +pub const TIOCNXCL: u32 = 21517; +pub const TIOCSCTTY: u32 = 21518; +pub const TIOCGPGRP: u32 = 21519; +pub const TIOCSPGRP: u32 = 21520; +pub const TIOCOUTQ: u32 = 21521; +pub const TIOCSTI: u32 = 21522; +pub const TIOCGWINSZ: u32 = 21523; +pub const TIOCSWINSZ: u32 = 21524; +pub const TIOCMGET: u32 = 21525; +pub const TIOCMBIS: u32 = 21526; +pub const TIOCMBIC: u32 = 21527; +pub const TIOCMSET: u32 = 21528; +pub const TIOCGSOFTCAR: u32 = 21529; +pub const TIOCSSOFTCAR: u32 = 21530; +pub const FIONREAD: u32 = 21531; +pub const TIOCINQ: u32 = 21531; +pub const TIOCLINUX: u32 = 21532; +pub const TIOCCONS: u32 = 21533; +pub const TIOCGSERIAL: u32 = 21534; +pub const TIOCSSERIAL: u32 = 21535; +pub const TIOCPKT: u32 = 21536; +pub const FIONBIO: u32 = 21537; +pub const TIOCNOTTY: u32 = 21538; +pub const TIOCSETD: u32 = 21539; +pub const TIOCGETD: u32 = 21540; +pub const TCSBRKP: u32 = 21541; +pub const TIOCSBRK: u32 = 21543; +pub const TIOCCBRK: u32 = 21544; +pub const TIOCGSID: u32 = 21545; +pub const TIOCGRS485: u32 = 21550; +pub const TIOCSRS485: u32 = 21551; +pub const TCGETX: u32 = 21554; +pub const TCSETX: u32 = 21555; +pub const TCSETXF: u32 = 21556; +pub const TCSETXW: u32 = 21557; +pub const TIOCVHANGUP: u32 = 21559; +pub const FIONCLEX: u32 = 21584; +pub const FIOCLEX: u32 = 21585; +pub const FIOASYNC: u32 = 21586; +pub const TIOCSERCONFIG: u32 = 21587; +pub const TIOCSERGWILD: u32 = 21588; +pub const TIOCSERSWILD: u32 = 21589; +pub const TIOCGLCKTRMIOS: u32 = 21590; +pub const TIOCSLCKTRMIOS: u32 = 21591; +pub const TIOCSERGSTRUCT: u32 = 21592; +pub const TIOCSERGETLSR: u32 = 21593; +pub const TIOCSERGETMULTI: u32 = 21594; +pub const TIOCSERSETMULTI: u32 = 21595; +pub const TIOCMIWAIT: u32 = 21596; +pub const TIOCGICOUNT: u32 = 21597; +pub const FIOQSIZE: u32 = 21600; +pub const TIOCPKT_DATA: u32 = 0; +pub const TIOCPKT_FLUSHREAD: u32 = 1; +pub const TIOCPKT_FLUSHWRITE: u32 = 2; +pub const TIOCPKT_STOP: u32 = 4; +pub const TIOCPKT_START: u32 = 8; +pub const TIOCPKT_NOSTOP: u32 = 16; +pub const TIOCPKT_DOSTOP: u32 = 32; +pub const TIOCPKT_IOCTL: u32 = 64; +pub const TIOCSER_TEMT: u32 = 1; +pub const NCC: u32 = 8; +pub const TIOCM_LE: u32 = 1; +pub const TIOCM_DTR: u32 = 2; +pub const TIOCM_RTS: u32 = 4; +pub const TIOCM_ST: u32 = 8; +pub const TIOCM_SR: u32 = 16; +pub const TIOCM_CTS: u32 = 32; +pub const TIOCM_CAR: u32 = 64; +pub const TIOCM_RNG: u32 = 128; +pub const TIOCM_DSR: u32 = 256; +pub const TIOCM_CD: u32 = 64; +pub const TIOCM_RI: u32 = 128; +pub const TIOCM_OUT1: u32 = 8192; +pub const TIOCM_OUT2: u32 = 16384; +pub const TIOCM_LOOP: u32 = 32768; +pub const N_TTY: u32 = 0; +pub const N_SLIP: u32 = 1; +pub const N_MOUSE: u32 = 2; +pub const N_PPP: u32 = 3; +pub const N_STRIP: u32 = 4; +pub const N_AX25: u32 = 5; +pub const N_X25: u32 = 6; +pub const N_6PACK: u32 = 7; +pub const N_MASC: u32 = 8; +pub const N_R3964: u32 = 9; +pub const N_PROFIBUS_FDL: u32 = 10; +pub const N_IRDA: u32 = 11; +pub const N_SMSBLOCK: u32 = 12; +pub const N_HDLC: u32 = 13; +pub const N_SYNC_PPP: u32 = 14; +pub const N_HCI: u32 = 15; +pub const N_GIGASET_M101: u32 = 16; +pub const N_SLCAN: u32 = 17; +pub const N_PPS: u32 = 18; +pub const N_V253: u32 = 19; +pub const N_CAIF: u32 = 20; +pub const N_GSM0710: u32 = 21; +pub const N_TI_WL: u32 = 22; +pub const N_TRACESINK: u32 = 23; +pub const N_TRACEROUTER: u32 = 24; +pub const N_NCI: u32 = 25; +pub const N_SPEAKUP: u32 = 26; +pub const N_NULL: u32 = 27; +pub const N_MCTP: u32 = 28; +pub const N_DEVELOPMENT: u32 = 29; +pub const N_CAN327: u32 = 30; +pub const NR_LDISCS: u32 = 31; +pub const INPUT_PROP_POINTER: u32 = 0; +pub const INPUT_PROP_DIRECT: u32 = 1; +pub const INPUT_PROP_BUTTONPAD: u32 = 2; +pub const INPUT_PROP_SEMI_MT: u32 = 3; +pub const INPUT_PROP_TOPBUTTONPAD: u32 = 4; +pub const INPUT_PROP_POINTING_STICK: u32 = 5; +pub const INPUT_PROP_ACCELEROMETER: u32 = 6; +pub const INPUT_PROP_MAX: u32 = 31; +pub const INPUT_PROP_CNT: u32 = 32; +pub const EV_SYN: u32 = 0; +pub const EV_KEY: u32 = 1; +pub const EV_REL: u32 = 2; +pub const EV_ABS: u32 = 3; +pub const EV_MSC: u32 = 4; +pub const EV_SW: u32 = 5; +pub const EV_LED: u32 = 17; +pub const EV_SND: u32 = 18; +pub const EV_REP: u32 = 20; +pub const EV_FF: u32 = 21; +pub const EV_PWR: u32 = 22; +pub const EV_FF_STATUS: u32 = 23; +pub const EV_MAX: u32 = 31; +pub const EV_CNT: u32 = 32; +pub const SYN_REPORT: u32 = 0; +pub const SYN_CONFIG: u32 = 1; +pub const SYN_MT_REPORT: u32 = 2; +pub const SYN_DROPPED: u32 = 3; +pub const SYN_MAX: u32 = 15; +pub const SYN_CNT: u32 = 16; +pub const KEY_RESERVED: u32 = 0; +pub const KEY_ESC: u32 = 1; +pub const KEY_1: u32 = 2; +pub const KEY_2: u32 = 3; +pub const KEY_3: u32 = 4; +pub const KEY_4: u32 = 5; +pub const KEY_5: u32 = 6; +pub const KEY_6: u32 = 7; +pub const KEY_7: u32 = 8; +pub const KEY_8: u32 = 9; +pub const KEY_9: u32 = 10; +pub const KEY_0: u32 = 11; +pub const KEY_MINUS: u32 = 12; +pub const KEY_EQUAL: u32 = 13; +pub const KEY_BACKSPACE: u32 = 14; +pub const KEY_TAB: u32 = 15; +pub const KEY_Q: u32 = 16; +pub const KEY_W: u32 = 17; +pub const KEY_E: u32 = 18; +pub const KEY_R: u32 = 19; +pub const KEY_T: u32 = 20; +pub const KEY_Y: u32 = 21; +pub const KEY_U: u32 = 22; +pub const KEY_I: u32 = 23; +pub const KEY_O: u32 = 24; +pub const KEY_P: u32 = 25; +pub const KEY_LEFTBRACE: u32 = 26; +pub const KEY_RIGHTBRACE: u32 = 27; +pub const KEY_ENTER: u32 = 28; +pub const KEY_LEFTCTRL: u32 = 29; +pub const KEY_A: u32 = 30; +pub const KEY_S: u32 = 31; +pub const KEY_D: u32 = 32; +pub const KEY_F: u32 = 33; +pub const KEY_G: u32 = 34; +pub const KEY_H: u32 = 35; +pub const KEY_J: u32 = 36; +pub const KEY_K: u32 = 37; +pub const KEY_L: u32 = 38; +pub const KEY_SEMICOLON: u32 = 39; +pub const KEY_APOSTROPHE: u32 = 40; +pub const KEY_GRAVE: u32 = 41; +pub const KEY_LEFTSHIFT: u32 = 42; +pub const KEY_BACKSLASH: u32 = 43; +pub const KEY_Z: u32 = 44; +pub const KEY_X: u32 = 45; +pub const KEY_C: u32 = 46; +pub const KEY_V: u32 = 47; +pub const KEY_B: u32 = 48; +pub const KEY_N: u32 = 49; +pub const KEY_M: u32 = 50; +pub const KEY_COMMA: u32 = 51; +pub const KEY_DOT: u32 = 52; +pub const KEY_SLASH: u32 = 53; +pub const KEY_RIGHTSHIFT: u32 = 54; +pub const KEY_KPASTERISK: u32 = 55; +pub const KEY_LEFTALT: u32 = 56; +pub const KEY_SPACE: u32 = 57; +pub const KEY_CAPSLOCK: u32 = 58; +pub const KEY_F1: u32 = 59; +pub const KEY_F2: u32 = 60; +pub const KEY_F3: u32 = 61; +pub const KEY_F4: u32 = 62; +pub const KEY_F5: u32 = 63; +pub const KEY_F6: u32 = 64; +pub const KEY_F7: u32 = 65; +pub const KEY_F8: u32 = 66; +pub const KEY_F9: u32 = 67; +pub const KEY_F10: u32 = 68; +pub const KEY_NUMLOCK: u32 = 69; +pub const KEY_SCROLLLOCK: u32 = 70; +pub const KEY_KP7: u32 = 71; +pub const KEY_KP8: u32 = 72; +pub const KEY_KP9: u32 = 73; +pub const KEY_KPMINUS: u32 = 74; +pub const KEY_KP4: u32 = 75; +pub const KEY_KP5: u32 = 76; +pub const KEY_KP6: u32 = 77; +pub const KEY_KPPLUS: u32 = 78; +pub const KEY_KP1: u32 = 79; +pub const KEY_KP2: u32 = 80; +pub const KEY_KP3: u32 = 81; +pub const KEY_KP0: u32 = 82; +pub const KEY_KPDOT: u32 = 83; +pub const KEY_ZENKAKUHANKAKU: u32 = 85; +pub const KEY_102ND: u32 = 86; +pub const KEY_F11: u32 = 87; +pub const KEY_F12: u32 = 88; +pub const KEY_RO: u32 = 89; +pub const KEY_KATAKANA: u32 = 90; +pub const KEY_HIRAGANA: u32 = 91; +pub const KEY_HENKAN: u32 = 92; +pub const KEY_KATAKANAHIRAGANA: u32 = 93; +pub const KEY_MUHENKAN: u32 = 94; +pub const KEY_KPJPCOMMA: u32 = 95; +pub const KEY_KPENTER: u32 = 96; +pub const KEY_RIGHTCTRL: u32 = 97; +pub const KEY_KPSLASH: u32 = 98; +pub const KEY_SYSRQ: u32 = 99; +pub const KEY_RIGHTALT: u32 = 100; +pub const KEY_LINEFEED: u32 = 101; +pub const KEY_HOME: u32 = 102; +pub const KEY_UP: u32 = 103; +pub const KEY_PAGEUP: u32 = 104; +pub const KEY_LEFT: u32 = 105; +pub const KEY_RIGHT: u32 = 106; +pub const KEY_END: u32 = 107; +pub const KEY_DOWN: u32 = 108; +pub const KEY_PAGEDOWN: u32 = 109; +pub const KEY_INSERT: u32 = 110; +pub const KEY_DELETE: u32 = 111; +pub const KEY_MACRO: u32 = 112; +pub const KEY_MUTE: u32 = 113; +pub const KEY_VOLUMEDOWN: u32 = 114; +pub const KEY_VOLUMEUP: u32 = 115; +pub const KEY_POWER: u32 = 116; +pub const KEY_KPEQUAL: u32 = 117; +pub const KEY_KPPLUSMINUS: u32 = 118; +pub const KEY_PAUSE: u32 = 119; +pub const KEY_SCALE: u32 = 120; +pub const KEY_KPCOMMA: u32 = 121; +pub const KEY_HANGEUL: u32 = 122; +pub const KEY_HANGUEL: u32 = 122; +pub const KEY_HANJA: u32 = 123; +pub const KEY_YEN: u32 = 124; +pub const KEY_LEFTMETA: u32 = 125; +pub const KEY_RIGHTMETA: u32 = 126; +pub const KEY_COMPOSE: u32 = 127; +pub const KEY_STOP: u32 = 128; +pub const KEY_AGAIN: u32 = 129; +pub const KEY_PROPS: u32 = 130; +pub const KEY_UNDO: u32 = 131; +pub const KEY_FRONT: u32 = 132; +pub const KEY_COPY: u32 = 133; +pub const KEY_OPEN: u32 = 134; +pub const KEY_PASTE: u32 = 135; +pub const KEY_FIND: u32 = 136; +pub const KEY_CUT: u32 = 137; +pub const KEY_HELP: u32 = 138; +pub const KEY_MENU: u32 = 139; +pub const KEY_CALC: u32 = 140; +pub const KEY_SETUP: u32 = 141; +pub const KEY_SLEEP: u32 = 142; +pub const KEY_WAKEUP: u32 = 143; +pub const KEY_FILE: u32 = 144; +pub const KEY_SENDFILE: u32 = 145; +pub const KEY_DELETEFILE: u32 = 146; +pub const KEY_XFER: u32 = 147; +pub const KEY_PROG1: u32 = 148; +pub const KEY_PROG2: u32 = 149; +pub const KEY_WWW: u32 = 150; +pub const KEY_MSDOS: u32 = 151; +pub const KEY_COFFEE: u32 = 152; +pub const KEY_SCREENLOCK: u32 = 152; +pub const KEY_ROTATE_DISPLAY: u32 = 153; +pub const KEY_DIRECTION: u32 = 153; +pub const KEY_CYCLEWINDOWS: u32 = 154; +pub const KEY_MAIL: u32 = 155; +pub const KEY_BOOKMARKS: u32 = 156; +pub const KEY_COMPUTER: u32 = 157; +pub const KEY_BACK: u32 = 158; +pub const KEY_FORWARD: u32 = 159; +pub const KEY_CLOSECD: u32 = 160; +pub const KEY_EJECTCD: u32 = 161; +pub const KEY_EJECTCLOSECD: u32 = 162; +pub const KEY_NEXTSONG: u32 = 163; +pub const KEY_PLAYPAUSE: u32 = 164; +pub const KEY_PREVIOUSSONG: u32 = 165; +pub const KEY_STOPCD: u32 = 166; +pub const KEY_RECORD: u32 = 167; +pub const KEY_REWIND: u32 = 168; +pub const KEY_PHONE: u32 = 169; +pub const KEY_ISO: u32 = 170; +pub const KEY_CONFIG: u32 = 171; +pub const KEY_HOMEPAGE: u32 = 172; +pub const KEY_REFRESH: u32 = 173; +pub const KEY_EXIT: u32 = 174; +pub const KEY_MOVE: u32 = 175; +pub const KEY_EDIT: u32 = 176; +pub const KEY_SCROLLUP: u32 = 177; +pub const KEY_SCROLLDOWN: u32 = 178; +pub const KEY_KPLEFTPAREN: u32 = 179; +pub const KEY_KPRIGHTPAREN: u32 = 180; +pub const KEY_NEW: u32 = 181; +pub const KEY_REDO: u32 = 182; +pub const KEY_F13: u32 = 183; +pub const KEY_F14: u32 = 184; +pub const KEY_F15: u32 = 185; +pub const KEY_F16: u32 = 186; +pub const KEY_F17: u32 = 187; +pub const KEY_F18: u32 = 188; +pub const KEY_F19: u32 = 189; +pub const KEY_F20: u32 = 190; +pub const KEY_F21: u32 = 191; +pub const KEY_F22: u32 = 192; +pub const KEY_F23: u32 = 193; +pub const KEY_F24: u32 = 194; +pub const KEY_PLAYCD: u32 = 200; +pub const KEY_PAUSECD: u32 = 201; +pub const KEY_PROG3: u32 = 202; +pub const KEY_PROG4: u32 = 203; +pub const KEY_ALL_APPLICATIONS: u32 = 204; +pub const KEY_DASHBOARD: u32 = 204; +pub const KEY_SUSPEND: u32 = 205; +pub const KEY_CLOSE: u32 = 206; +pub const KEY_PLAY: u32 = 207; +pub const KEY_FASTFORWARD: u32 = 208; +pub const KEY_BASSBOOST: u32 = 209; +pub const KEY_PRINT: u32 = 210; +pub const KEY_HP: u32 = 211; +pub const KEY_CAMERA: u32 = 212; +pub const KEY_SOUND: u32 = 213; +pub const KEY_QUESTION: u32 = 214; +pub const KEY_EMAIL: u32 = 215; +pub const KEY_CHAT: u32 = 216; +pub const KEY_SEARCH: u32 = 217; +pub const KEY_CONNECT: u32 = 218; +pub const KEY_FINANCE: u32 = 219; +pub const KEY_SPORT: u32 = 220; +pub const KEY_SHOP: u32 = 221; +pub const KEY_ALTERASE: u32 = 222; +pub const KEY_CANCEL: u32 = 223; +pub const KEY_BRIGHTNESSDOWN: u32 = 224; +pub const KEY_BRIGHTNESSUP: u32 = 225; +pub const KEY_MEDIA: u32 = 226; +pub const KEY_SWITCHVIDEOMODE: u32 = 227; +pub const KEY_KBDILLUMTOGGLE: u32 = 228; +pub const KEY_KBDILLUMDOWN: u32 = 229; +pub const KEY_KBDILLUMUP: u32 = 230; +pub const KEY_SEND: u32 = 231; +pub const KEY_REPLY: u32 = 232; +pub const KEY_FORWARDMAIL: u32 = 233; +pub const KEY_SAVE: u32 = 234; +pub const KEY_DOCUMENTS: u32 = 235; +pub const KEY_BATTERY: u32 = 236; +pub const KEY_BLUETOOTH: u32 = 237; +pub const KEY_WLAN: u32 = 238; +pub const KEY_UWB: u32 = 239; +pub const KEY_UNKNOWN: u32 = 240; +pub const KEY_VIDEO_NEXT: u32 = 241; +pub const KEY_VIDEO_PREV: u32 = 242; +pub const KEY_BRIGHTNESS_CYCLE: u32 = 243; +pub const KEY_BRIGHTNESS_AUTO: u32 = 244; +pub const KEY_BRIGHTNESS_ZERO: u32 = 244; +pub const KEY_DISPLAY_OFF: u32 = 245; +pub const KEY_WWAN: u32 = 246; +pub const KEY_WIMAX: u32 = 246; +pub const KEY_RFKILL: u32 = 247; +pub const KEY_MICMUTE: u32 = 248; +pub const BTN_MISC: u32 = 256; +pub const BTN_0: u32 = 256; +pub const BTN_1: u32 = 257; +pub const BTN_2: u32 = 258; +pub const BTN_3: u32 = 259; +pub const BTN_4: u32 = 260; +pub const BTN_5: u32 = 261; +pub const BTN_6: u32 = 262; +pub const BTN_7: u32 = 263; +pub const BTN_8: u32 = 264; +pub const BTN_9: u32 = 265; +pub const BTN_MOUSE: u32 = 272; +pub const BTN_LEFT: u32 = 272; +pub const BTN_RIGHT: u32 = 273; +pub const BTN_MIDDLE: u32 = 274; +pub const BTN_SIDE: u32 = 275; +pub const BTN_EXTRA: u32 = 276; +pub const BTN_FORWARD: u32 = 277; +pub const BTN_BACK: u32 = 278; +pub const BTN_TASK: u32 = 279; +pub const BTN_JOYSTICK: u32 = 288; +pub const BTN_TRIGGER: u32 = 288; +pub const BTN_THUMB: u32 = 289; +pub const BTN_THUMB2: u32 = 290; +pub const BTN_TOP: u32 = 291; +pub const BTN_TOP2: u32 = 292; +pub const BTN_PINKIE: u32 = 293; +pub const BTN_BASE: u32 = 294; +pub const BTN_BASE2: u32 = 295; +pub const BTN_BASE3: u32 = 296; +pub const BTN_BASE4: u32 = 297; +pub const BTN_BASE5: u32 = 298; +pub const BTN_BASE6: u32 = 299; +pub const BTN_DEAD: u32 = 303; +pub const BTN_GAMEPAD: u32 = 304; +pub const BTN_SOUTH: u32 = 304; +pub const BTN_A: u32 = 304; +pub const BTN_EAST: u32 = 305; +pub const BTN_B: u32 = 305; +pub const BTN_C: u32 = 306; +pub const BTN_NORTH: u32 = 307; +pub const BTN_X: u32 = 307; +pub const BTN_WEST: u32 = 308; +pub const BTN_Y: u32 = 308; +pub const BTN_Z: u32 = 309; +pub const BTN_TL: u32 = 310; +pub const BTN_TR: u32 = 311; +pub const BTN_TL2: u32 = 312; +pub const BTN_TR2: u32 = 313; +pub const BTN_SELECT: u32 = 314; +pub const BTN_START: u32 = 315; +pub const BTN_MODE: u32 = 316; +pub const BTN_THUMBL: u32 = 317; +pub const BTN_THUMBR: u32 = 318; +pub const BTN_DIGI: u32 = 320; +pub const BTN_TOOL_PEN: u32 = 320; +pub const BTN_TOOL_RUBBER: u32 = 321; +pub const BTN_TOOL_BRUSH: u32 = 322; +pub const BTN_TOOL_PENCIL: u32 = 323; +pub const BTN_TOOL_AIRBRUSH: u32 = 324; +pub const BTN_TOOL_FINGER: u32 = 325; +pub const BTN_TOOL_MOUSE: u32 = 326; +pub const BTN_TOOL_LENS: u32 = 327; +pub const BTN_TOOL_QUINTTAP: u32 = 328; +pub const BTN_STYLUS3: u32 = 329; +pub const BTN_TOUCH: u32 = 330; +pub const BTN_STYLUS: u32 = 331; +pub const BTN_STYLUS2: u32 = 332; +pub const BTN_TOOL_DOUBLETAP: u32 = 333; +pub const BTN_TOOL_TRIPLETAP: u32 = 334; +pub const BTN_TOOL_QUADTAP: u32 = 335; +pub const BTN_WHEEL: u32 = 336; +pub const BTN_GEAR_DOWN: u32 = 336; +pub const BTN_GEAR_UP: u32 = 337; +pub const KEY_OK: u32 = 352; +pub const KEY_SELECT: u32 = 353; +pub const KEY_GOTO: u32 = 354; +pub const KEY_CLEAR: u32 = 355; +pub const KEY_POWER2: u32 = 356; +pub const KEY_OPTION: u32 = 357; +pub const KEY_INFO: u32 = 358; +pub const KEY_TIME: u32 = 359; +pub const KEY_VENDOR: u32 = 360; +pub const KEY_ARCHIVE: u32 = 361; +pub const KEY_PROGRAM: u32 = 362; +pub const KEY_CHANNEL: u32 = 363; +pub const KEY_FAVORITES: u32 = 364; +pub const KEY_EPG: u32 = 365; +pub const KEY_PVR: u32 = 366; +pub const KEY_MHP: u32 = 367; +pub const KEY_LANGUAGE: u32 = 368; +pub const KEY_TITLE: u32 = 369; +pub const KEY_SUBTITLE: u32 = 370; +pub const KEY_ANGLE: u32 = 371; +pub const KEY_FULL_SCREEN: u32 = 372; +pub const KEY_ZOOM: u32 = 372; +pub const KEY_MODE: u32 = 373; +pub const KEY_KEYBOARD: u32 = 374; +pub const KEY_ASPECT_RATIO: u32 = 375; +pub const KEY_SCREEN: u32 = 375; +pub const KEY_PC: u32 = 376; +pub const KEY_TV: u32 = 377; +pub const KEY_TV2: u32 = 378; +pub const KEY_VCR: u32 = 379; +pub const KEY_VCR2: u32 = 380; +pub const KEY_SAT: u32 = 381; +pub const KEY_SAT2: u32 = 382; +pub const KEY_CD: u32 = 383; +pub const KEY_TAPE: u32 = 384; +pub const KEY_RADIO: u32 = 385; +pub const KEY_TUNER: u32 = 386; +pub const KEY_PLAYER: u32 = 387; +pub const KEY_TEXT: u32 = 388; +pub const KEY_DVD: u32 = 389; +pub const KEY_AUX: u32 = 390; +pub const KEY_MP3: u32 = 391; +pub const KEY_AUDIO: u32 = 392; +pub const KEY_VIDEO: u32 = 393; +pub const KEY_DIRECTORY: u32 = 394; +pub const KEY_LIST: u32 = 395; +pub const KEY_MEMO: u32 = 396; +pub const KEY_CALENDAR: u32 = 397; +pub const KEY_RED: u32 = 398; +pub const KEY_GREEN: u32 = 399; +pub const KEY_YELLOW: u32 = 400; +pub const KEY_BLUE: u32 = 401; +pub const KEY_CHANNELUP: u32 = 402; +pub const KEY_CHANNELDOWN: u32 = 403; +pub const KEY_FIRST: u32 = 404; +pub const KEY_LAST: u32 = 405; +pub const KEY_AB: u32 = 406; +pub const KEY_NEXT: u32 = 407; +pub const KEY_RESTART: u32 = 408; +pub const KEY_SLOW: u32 = 409; +pub const KEY_SHUFFLE: u32 = 410; +pub const KEY_BREAK: u32 = 411; +pub const KEY_PREVIOUS: u32 = 412; +pub const KEY_DIGITS: u32 = 413; +pub const KEY_TEEN: u32 = 414; +pub const KEY_TWEN: u32 = 415; +pub const KEY_VIDEOPHONE: u32 = 416; +pub const KEY_GAMES: u32 = 417; +pub const KEY_ZOOMIN: u32 = 418; +pub const KEY_ZOOMOUT: u32 = 419; +pub const KEY_ZOOMRESET: u32 = 420; +pub const KEY_WORDPROCESSOR: u32 = 421; +pub const KEY_EDITOR: u32 = 422; +pub const KEY_SPREADSHEET: u32 = 423; +pub const KEY_GRAPHICSEDITOR: u32 = 424; +pub const KEY_PRESENTATION: u32 = 425; +pub const KEY_DATABASE: u32 = 426; +pub const KEY_NEWS: u32 = 427; +pub const KEY_VOICEMAIL: u32 = 428; +pub const KEY_ADDRESSBOOK: u32 = 429; +pub const KEY_MESSENGER: u32 = 430; +pub const KEY_DISPLAYTOGGLE: u32 = 431; +pub const KEY_BRIGHTNESS_TOGGLE: u32 = 431; +pub const KEY_SPELLCHECK: u32 = 432; +pub const KEY_LOGOFF: u32 = 433; +pub const KEY_DOLLAR: u32 = 434; +pub const KEY_EURO: u32 = 435; +pub const KEY_FRAMEBACK: u32 = 436; +pub const KEY_FRAMEFORWARD: u32 = 437; +pub const KEY_CONTEXT_MENU: u32 = 438; +pub const KEY_MEDIA_REPEAT: u32 = 439; +pub const KEY_10CHANNELSUP: u32 = 440; +pub const KEY_10CHANNELSDOWN: u32 = 441; +pub const KEY_IMAGES: u32 = 442; +pub const KEY_NOTIFICATION_CENTER: u32 = 444; +pub const KEY_PICKUP_PHONE: u32 = 445; +pub const KEY_HANGUP_PHONE: u32 = 446; +pub const KEY_DEL_EOL: u32 = 448; +pub const KEY_DEL_EOS: u32 = 449; +pub const KEY_INS_LINE: u32 = 450; +pub const KEY_DEL_LINE: u32 = 451; +pub const KEY_FN: u32 = 464; +pub const KEY_FN_ESC: u32 = 465; +pub const KEY_FN_F1: u32 = 466; +pub const KEY_FN_F2: u32 = 467; +pub const KEY_FN_F3: u32 = 468; +pub const KEY_FN_F4: u32 = 469; +pub const KEY_FN_F5: u32 = 470; +pub const KEY_FN_F6: u32 = 471; +pub const KEY_FN_F7: u32 = 472; +pub const KEY_FN_F8: u32 = 473; +pub const KEY_FN_F9: u32 = 474; +pub const KEY_FN_F10: u32 = 475; +pub const KEY_FN_F11: u32 = 476; +pub const KEY_FN_F12: u32 = 477; +pub const KEY_FN_1: u32 = 478; +pub const KEY_FN_2: u32 = 479; +pub const KEY_FN_D: u32 = 480; +pub const KEY_FN_E: u32 = 481; +pub const KEY_FN_F: u32 = 482; +pub const KEY_FN_S: u32 = 483; +pub const KEY_FN_B: u32 = 484; +pub const KEY_FN_RIGHT_SHIFT: u32 = 485; +pub const KEY_BRL_DOT1: u32 = 497; +pub const KEY_BRL_DOT2: u32 = 498; +pub const KEY_BRL_DOT3: u32 = 499; +pub const KEY_BRL_DOT4: u32 = 500; +pub const KEY_BRL_DOT5: u32 = 501; +pub const KEY_BRL_DOT6: u32 = 502; +pub const KEY_BRL_DOT7: u32 = 503; +pub const KEY_BRL_DOT8: u32 = 504; +pub const KEY_BRL_DOT9: u32 = 505; +pub const KEY_BRL_DOT10: u32 = 506; +pub const KEY_NUMERIC_0: u32 = 512; +pub const KEY_NUMERIC_1: u32 = 513; +pub const KEY_NUMERIC_2: u32 = 514; +pub const KEY_NUMERIC_3: u32 = 515; +pub const KEY_NUMERIC_4: u32 = 516; +pub const KEY_NUMERIC_5: u32 = 517; +pub const KEY_NUMERIC_6: u32 = 518; +pub const KEY_NUMERIC_7: u32 = 519; +pub const KEY_NUMERIC_8: u32 = 520; +pub const KEY_NUMERIC_9: u32 = 521; +pub const KEY_NUMERIC_STAR: u32 = 522; +pub const KEY_NUMERIC_POUND: u32 = 523; +pub const KEY_NUMERIC_A: u32 = 524; +pub const KEY_NUMERIC_B: u32 = 525; +pub const KEY_NUMERIC_C: u32 = 526; +pub const KEY_NUMERIC_D: u32 = 527; +pub const KEY_CAMERA_FOCUS: u32 = 528; +pub const KEY_WPS_BUTTON: u32 = 529; +pub const KEY_TOUCHPAD_TOGGLE: u32 = 530; +pub const KEY_TOUCHPAD_ON: u32 = 531; +pub const KEY_TOUCHPAD_OFF: u32 = 532; +pub const KEY_CAMERA_ZOOMIN: u32 = 533; +pub const KEY_CAMERA_ZOOMOUT: u32 = 534; +pub const KEY_CAMERA_UP: u32 = 535; +pub const KEY_CAMERA_DOWN: u32 = 536; +pub const KEY_CAMERA_LEFT: u32 = 537; +pub const KEY_CAMERA_RIGHT: u32 = 538; +pub const KEY_ATTENDANT_ON: u32 = 539; +pub const KEY_ATTENDANT_OFF: u32 = 540; +pub const KEY_ATTENDANT_TOGGLE: u32 = 541; +pub const KEY_LIGHTS_TOGGLE: u32 = 542; +pub const BTN_DPAD_UP: u32 = 544; +pub const BTN_DPAD_DOWN: u32 = 545; +pub const BTN_DPAD_LEFT: u32 = 546; +pub const BTN_DPAD_RIGHT: u32 = 547; +pub const KEY_ALS_TOGGLE: u32 = 560; +pub const KEY_ROTATE_LOCK_TOGGLE: u32 = 561; +pub const KEY_BUTTONCONFIG: u32 = 576; +pub const KEY_TASKMANAGER: u32 = 577; +pub const KEY_JOURNAL: u32 = 578; +pub const KEY_CONTROLPANEL: u32 = 579; +pub const KEY_APPSELECT: u32 = 580; +pub const KEY_SCREENSAVER: u32 = 581; +pub const KEY_VOICECOMMAND: u32 = 582; +pub const KEY_ASSISTANT: u32 = 583; +pub const KEY_KBD_LAYOUT_NEXT: u32 = 584; +pub const KEY_EMOJI_PICKER: u32 = 585; +pub const KEY_DICTATE: u32 = 586; +pub const KEY_CAMERA_ACCESS_ENABLE: u32 = 587; +pub const KEY_CAMERA_ACCESS_DISABLE: u32 = 588; +pub const KEY_CAMERA_ACCESS_TOGGLE: u32 = 589; +pub const KEY_BRIGHTNESS_MIN: u32 = 592; +pub const KEY_BRIGHTNESS_MAX: u32 = 593; +pub const KEY_KBDINPUTASSIST_PREV: u32 = 608; +pub const KEY_KBDINPUTASSIST_NEXT: u32 = 609; +pub const KEY_KBDINPUTASSIST_PREVGROUP: u32 = 610; +pub const KEY_KBDINPUTASSIST_NEXTGROUP: u32 = 611; +pub const KEY_KBDINPUTASSIST_ACCEPT: u32 = 612; +pub const KEY_KBDINPUTASSIST_CANCEL: u32 = 613; +pub const KEY_RIGHT_UP: u32 = 614; +pub const KEY_RIGHT_DOWN: u32 = 615; +pub const KEY_LEFT_UP: u32 = 616; +pub const KEY_LEFT_DOWN: u32 = 617; +pub const KEY_ROOT_MENU: u32 = 618; +pub const KEY_MEDIA_TOP_MENU: u32 = 619; +pub const KEY_NUMERIC_11: u32 = 620; +pub const KEY_NUMERIC_12: u32 = 621; +pub const KEY_AUDIO_DESC: u32 = 622; +pub const KEY_3D_MODE: u32 = 623; +pub const KEY_NEXT_FAVORITE: u32 = 624; +pub const KEY_STOP_RECORD: u32 = 625; +pub const KEY_PAUSE_RECORD: u32 = 626; +pub const KEY_VOD: u32 = 627; +pub const KEY_UNMUTE: u32 = 628; +pub const KEY_FASTREVERSE: u32 = 629; +pub const KEY_SLOWREVERSE: u32 = 630; +pub const KEY_DATA: u32 = 631; +pub const KEY_ONSCREEN_KEYBOARD: u32 = 632; +pub const KEY_PRIVACY_SCREEN_TOGGLE: u32 = 633; +pub const KEY_SELECTIVE_SCREENSHOT: u32 = 634; +pub const KEY_NEXT_ELEMENT: u32 = 635; +pub const KEY_PREVIOUS_ELEMENT: u32 = 636; +pub const KEY_AUTOPILOT_ENGAGE_TOGGLE: u32 = 637; +pub const KEY_MARK_WAYPOINT: u32 = 638; +pub const KEY_SOS: u32 = 639; +pub const KEY_NAV_CHART: u32 = 640; +pub const KEY_FISHING_CHART: u32 = 641; +pub const KEY_SINGLE_RANGE_RADAR: u32 = 642; +pub const KEY_DUAL_RANGE_RADAR: u32 = 643; +pub const KEY_RADAR_OVERLAY: u32 = 644; +pub const KEY_TRADITIONAL_SONAR: u32 = 645; +pub const KEY_CLEARVU_SONAR: u32 = 646; +pub const KEY_SIDEVU_SONAR: u32 = 647; +pub const KEY_NAV_INFO: u32 = 648; +pub const KEY_BRIGHTNESS_MENU: u32 = 649; +pub const KEY_MACRO1: u32 = 656; +pub const KEY_MACRO2: u32 = 657; +pub const KEY_MACRO3: u32 = 658; +pub const KEY_MACRO4: u32 = 659; +pub const KEY_MACRO5: u32 = 660; +pub const KEY_MACRO6: u32 = 661; +pub const KEY_MACRO7: u32 = 662; +pub const KEY_MACRO8: u32 = 663; +pub const KEY_MACRO9: u32 = 664; +pub const KEY_MACRO10: u32 = 665; +pub const KEY_MACRO11: u32 = 666; +pub const KEY_MACRO12: u32 = 667; +pub const KEY_MACRO13: u32 = 668; +pub const KEY_MACRO14: u32 = 669; +pub const KEY_MACRO15: u32 = 670; +pub const KEY_MACRO16: u32 = 671; +pub const KEY_MACRO17: u32 = 672; +pub const KEY_MACRO18: u32 = 673; +pub const KEY_MACRO19: u32 = 674; +pub const KEY_MACRO20: u32 = 675; +pub const KEY_MACRO21: u32 = 676; +pub const KEY_MACRO22: u32 = 677; +pub const KEY_MACRO23: u32 = 678; +pub const KEY_MACRO24: u32 = 679; +pub const KEY_MACRO25: u32 = 680; +pub const KEY_MACRO26: u32 = 681; +pub const KEY_MACRO27: u32 = 682; +pub const KEY_MACRO28: u32 = 683; +pub const KEY_MACRO29: u32 = 684; +pub const KEY_MACRO30: u32 = 685; +pub const KEY_MACRO_RECORD_START: u32 = 688; +pub const KEY_MACRO_RECORD_STOP: u32 = 689; +pub const KEY_MACRO_PRESET_CYCLE: u32 = 690; +pub const KEY_MACRO_PRESET1: u32 = 691; +pub const KEY_MACRO_PRESET2: u32 = 692; +pub const KEY_MACRO_PRESET3: u32 = 693; +pub const KEY_KBD_LCD_MENU1: u32 = 696; +pub const KEY_KBD_LCD_MENU2: u32 = 697; +pub const KEY_KBD_LCD_MENU3: u32 = 698; +pub const KEY_KBD_LCD_MENU4: u32 = 699; +pub const KEY_KBD_LCD_MENU5: u32 = 700; +pub const BTN_TRIGGER_HAPPY: u32 = 704; +pub const BTN_TRIGGER_HAPPY1: u32 = 704; +pub const BTN_TRIGGER_HAPPY2: u32 = 705; +pub const BTN_TRIGGER_HAPPY3: u32 = 706; +pub const BTN_TRIGGER_HAPPY4: u32 = 707; +pub const BTN_TRIGGER_HAPPY5: u32 = 708; +pub const BTN_TRIGGER_HAPPY6: u32 = 709; +pub const BTN_TRIGGER_HAPPY7: u32 = 710; +pub const BTN_TRIGGER_HAPPY8: u32 = 711; +pub const BTN_TRIGGER_HAPPY9: u32 = 712; +pub const BTN_TRIGGER_HAPPY10: u32 = 713; +pub const BTN_TRIGGER_HAPPY11: u32 = 714; +pub const BTN_TRIGGER_HAPPY12: u32 = 715; +pub const BTN_TRIGGER_HAPPY13: u32 = 716; +pub const BTN_TRIGGER_HAPPY14: u32 = 717; +pub const BTN_TRIGGER_HAPPY15: u32 = 718; +pub const BTN_TRIGGER_HAPPY16: u32 = 719; +pub const BTN_TRIGGER_HAPPY17: u32 = 720; +pub const BTN_TRIGGER_HAPPY18: u32 = 721; +pub const BTN_TRIGGER_HAPPY19: u32 = 722; +pub const BTN_TRIGGER_HAPPY20: u32 = 723; +pub const BTN_TRIGGER_HAPPY21: u32 = 724; +pub const BTN_TRIGGER_HAPPY22: u32 = 725; +pub const BTN_TRIGGER_HAPPY23: u32 = 726; +pub const BTN_TRIGGER_HAPPY24: u32 = 727; +pub const BTN_TRIGGER_HAPPY25: u32 = 728; +pub const BTN_TRIGGER_HAPPY26: u32 = 729; +pub const BTN_TRIGGER_HAPPY27: u32 = 730; +pub const BTN_TRIGGER_HAPPY28: u32 = 731; +pub const BTN_TRIGGER_HAPPY29: u32 = 732; +pub const BTN_TRIGGER_HAPPY30: u32 = 733; +pub const BTN_TRIGGER_HAPPY31: u32 = 734; +pub const BTN_TRIGGER_HAPPY32: u32 = 735; +pub const BTN_TRIGGER_HAPPY33: u32 = 736; +pub const BTN_TRIGGER_HAPPY34: u32 = 737; +pub const BTN_TRIGGER_HAPPY35: u32 = 738; +pub const BTN_TRIGGER_HAPPY36: u32 = 739; +pub const BTN_TRIGGER_HAPPY37: u32 = 740; +pub const BTN_TRIGGER_HAPPY38: u32 = 741; +pub const BTN_TRIGGER_HAPPY39: u32 = 742; +pub const BTN_TRIGGER_HAPPY40: u32 = 743; +pub const KEY_MIN_INTERESTING: u32 = 113; +pub const KEY_MAX: u32 = 767; +pub const KEY_CNT: u32 = 768; +pub const REL_X: u32 = 0; +pub const REL_Y: u32 = 1; +pub const REL_Z: u32 = 2; +pub const REL_RX: u32 = 3; +pub const REL_RY: u32 = 4; +pub const REL_RZ: u32 = 5; +pub const REL_HWHEEL: u32 = 6; +pub const REL_DIAL: u32 = 7; +pub const REL_WHEEL: u32 = 8; +pub const REL_MISC: u32 = 9; +pub const REL_RESERVED: u32 = 10; +pub const REL_WHEEL_HI_RES: u32 = 11; +pub const REL_HWHEEL_HI_RES: u32 = 12; +pub const REL_MAX: u32 = 15; +pub const REL_CNT: u32 = 16; +pub const ABS_X: u32 = 0; +pub const ABS_Y: u32 = 1; +pub const ABS_Z: u32 = 2; +pub const ABS_RX: u32 = 3; +pub const ABS_RY: u32 = 4; +pub const ABS_RZ: u32 = 5; +pub const ABS_THROTTLE: u32 = 6; +pub const ABS_RUDDER: u32 = 7; +pub const ABS_WHEEL: u32 = 8; +pub const ABS_GAS: u32 = 9; +pub const ABS_BRAKE: u32 = 10; +pub const ABS_HAT0X: u32 = 16; +pub const ABS_HAT0Y: u32 = 17; +pub const ABS_HAT1X: u32 = 18; +pub const ABS_HAT1Y: u32 = 19; +pub const ABS_HAT2X: u32 = 20; +pub const ABS_HAT2Y: u32 = 21; +pub const ABS_HAT3X: u32 = 22; +pub const ABS_HAT3Y: u32 = 23; +pub const ABS_PRESSURE: u32 = 24; +pub const ABS_DISTANCE: u32 = 25; +pub const ABS_TILT_X: u32 = 26; +pub const ABS_TILT_Y: u32 = 27; +pub const ABS_TOOL_WIDTH: u32 = 28; +pub const ABS_VOLUME: u32 = 32; +pub const ABS_PROFILE: u32 = 33; +pub const ABS_MISC: u32 = 40; +pub const ABS_RESERVED: u32 = 46; +pub const ABS_MT_SLOT: u32 = 47; +pub const ABS_MT_TOUCH_MAJOR: u32 = 48; +pub const ABS_MT_TOUCH_MINOR: u32 = 49; +pub const ABS_MT_WIDTH_MAJOR: u32 = 50; +pub const ABS_MT_WIDTH_MINOR: u32 = 51; +pub const ABS_MT_ORIENTATION: u32 = 52; +pub const ABS_MT_POSITION_X: u32 = 53; +pub const ABS_MT_POSITION_Y: u32 = 54; +pub const ABS_MT_TOOL_TYPE: u32 = 55; +pub const ABS_MT_BLOB_ID: u32 = 56; +pub const ABS_MT_TRACKING_ID: u32 = 57; +pub const ABS_MT_PRESSURE: u32 = 58; +pub const ABS_MT_DISTANCE: u32 = 59; +pub const ABS_MT_TOOL_X: u32 = 60; +pub const ABS_MT_TOOL_Y: u32 = 61; +pub const ABS_MAX: u32 = 63; +pub const ABS_CNT: u32 = 64; +pub const SW_LID: u32 = 0; +pub const SW_TABLET_MODE: u32 = 1; +pub const SW_HEADPHONE_INSERT: u32 = 2; +pub const SW_RFKILL_ALL: u32 = 3; +pub const SW_RADIO: u32 = 3; +pub const SW_MICROPHONE_INSERT: u32 = 4; +pub const SW_DOCK: u32 = 5; +pub const SW_LINEOUT_INSERT: u32 = 6; +pub const SW_JACK_PHYSICAL_INSERT: u32 = 7; +pub const SW_VIDEOOUT_INSERT: u32 = 8; +pub const SW_CAMERA_LENS_COVER: u32 = 9; +pub const SW_KEYPAD_SLIDE: u32 = 10; +pub const SW_FRONT_PROXIMITY: u32 = 11; +pub const SW_ROTATE_LOCK: u32 = 12; +pub const SW_LINEIN_INSERT: u32 = 13; +pub const SW_MUTE_DEVICE: u32 = 14; +pub const SW_PEN_INSERTED: u32 = 15; +pub const SW_MACHINE_COVER: u32 = 16; +pub const SW_MAX: u32 = 16; +pub const SW_CNT: u32 = 17; +pub const MSC_SERIAL: u32 = 0; +pub const MSC_PULSELED: u32 = 1; +pub const MSC_GESTURE: u32 = 2; +pub const MSC_RAW: u32 = 3; +pub const MSC_SCAN: u32 = 4; +pub const MSC_TIMESTAMP: u32 = 5; +pub const MSC_MAX: u32 = 7; +pub const MSC_CNT: u32 = 8; +pub const LED_NUML: u32 = 0; +pub const LED_CAPSL: u32 = 1; +pub const LED_SCROLLL: u32 = 2; +pub const LED_COMPOSE: u32 = 3; +pub const LED_KANA: u32 = 4; +pub const LED_SLEEP: u32 = 5; +pub const LED_SUSPEND: u32 = 6; +pub const LED_MUTE: u32 = 7; +pub const LED_MISC: u32 = 8; +pub const LED_MAIL: u32 = 9; +pub const LED_CHARGING: u32 = 10; +pub const LED_MAX: u32 = 15; +pub const LED_CNT: u32 = 16; +pub const REP_DELAY: u32 = 0; +pub const REP_PERIOD: u32 = 1; +pub const REP_MAX: u32 = 1; +pub const REP_CNT: u32 = 2; +pub const SND_CLICK: u32 = 0; +pub const SND_BELL: u32 = 1; +pub const SND_TONE: u32 = 2; +pub const SND_MAX: u32 = 7; +pub const SND_CNT: u32 = 8; +pub const EV_VERSION: u32 = 65537; +pub const INPUT_KEYMAP_BY_INDEX: u32 = 1; +pub const ID_BUS: u32 = 0; +pub const ID_VENDOR: u32 = 1; +pub const ID_PRODUCT: u32 = 2; +pub const ID_VERSION: u32 = 3; +pub const BUS_PCI: u32 = 1; +pub const BUS_ISAPNP: u32 = 2; +pub const BUS_USB: u32 = 3; +pub const BUS_HIL: u32 = 4; +pub const BUS_BLUETOOTH: u32 = 5; +pub const BUS_VIRTUAL: u32 = 6; +pub const BUS_ISA: u32 = 16; +pub const BUS_I8042: u32 = 17; +pub const BUS_XTKBD: u32 = 18; +pub const BUS_RS232: u32 = 19; +pub const BUS_GAMEPORT: u32 = 20; +pub const BUS_PARPORT: u32 = 21; +pub const BUS_AMIGA: u32 = 22; +pub const BUS_ADB: u32 = 23; +pub const BUS_I2C: u32 = 24; +pub const BUS_HOST: u32 = 25; +pub const BUS_GSC: u32 = 26; +pub const BUS_ATARI: u32 = 27; +pub const BUS_SPI: u32 = 28; +pub const BUS_RMI: u32 = 29; +pub const BUS_CEC: u32 = 30; +pub const BUS_INTEL_ISHTP: u32 = 31; +pub const BUS_AMD_SFH: u32 = 32; +pub const MT_TOOL_FINGER: u32 = 0; +pub const MT_TOOL_PEN: u32 = 1; +pub const MT_TOOL_PALM: u32 = 2; +pub const MT_TOOL_DIAL: u32 = 10; +pub const MT_TOOL_MAX: u32 = 15; +pub const FF_STATUS_STOPPED: u32 = 0; +pub const FF_STATUS_PLAYING: u32 = 1; +pub const FF_STATUS_MAX: u32 = 1; +pub const FF_RUMBLE: u32 = 80; +pub const FF_PERIODIC: u32 = 81; +pub const FF_CONSTANT: u32 = 82; +pub const FF_SPRING: u32 = 83; +pub const FF_FRICTION: u32 = 84; +pub const FF_DAMPER: u32 = 85; +pub const FF_INERTIA: u32 = 86; +pub const FF_RAMP: u32 = 87; +pub const FF_EFFECT_MIN: u32 = 80; +pub const FF_EFFECT_MAX: u32 = 87; +pub const FF_SQUARE: u32 = 88; +pub const FF_TRIANGLE: u32 = 89; +pub const FF_SINE: u32 = 90; +pub const FF_SAW_UP: u32 = 91; +pub const FF_SAW_DOWN: u32 = 92; +pub const FF_CUSTOM: u32 = 93; +pub const FF_WAVEFORM_MIN: u32 = 88; +pub const FF_WAVEFORM_MAX: u32 = 93; +pub const FF_GAIN: u32 = 96; +pub const FF_AUTOCENTER: u32 = 97; +pub const FF_MAX_EFFECTS: u32 = 96; +pub const FF_MAX: u32 = 127; +pub const FF_CNT: u32 = 128; +pub const SEEK_SET: u32 = 0; +pub const SEEK_CUR: u32 = 1; +pub const SEEK_END: u32 = 2; +pub const _IOFBF: u32 = 0; +pub const _IOLBF: u32 = 1; +pub const _IONBF: u32 = 2; +pub const BUFSIZ: u32 = 1024; +pub const EOF: i32 = -1; +pub const FOPEN_MAX: u32 = 20; +pub const FILENAME_MAX: u32 = 4096; +pub const L_tmpnam: u32 = 4096; +pub const P_tmpdir: &[u8; 6] = b"/tmp/\0"; +pub const L_ctermid: u32 = 1024; +pub const WNOHANG: u32 = 1; +pub const WUNTRACED: u32 = 2; +pub const WSTOPPED: u32 = 2; +pub const WEXITED: u32 = 4; +pub const WCONTINUED: u32 = 8; +pub const WNOWAIT: u32 = 16777216; +pub const __WNOTHREAD: u32 = 536870912; +pub const __WALL: u32 = 1073741824; +pub const __WCLONE: u32 = 2147483648; +pub const P_ALL: u32 = 0; +pub const P_PID: u32 = 1; +pub const P_PGID: u32 = 2; +pub const P_PIDFD: u32 = 3; +pub const STRUCT_MALLINFO_DECLARED: u32 = 1; +pub const M_DECAY_TIME: i32 = -100; +pub const M_PURGE: i32 = -101; +pub const M_PURGE_ALL: i32 = -104; +pub const M_MEMTAG_TUNING: i32 = -102; +pub const M_MEMTAG_TUNING_BUFFER_OVERFLOW: u32 = 0; +pub const M_MEMTAG_TUNING_UAF: u32 = 1; +pub const M_THREAD_DISABLE_MEM_INIT: i32 = -103; +pub const M_CACHE_COUNT_MAX: i32 = -200; +pub const M_CACHE_SIZE_MAX: i32 = -201; +pub const M_TSDS_COUNT_MAX: i32 = -202; +pub const M_BIONIC_ZERO_INIT: i32 = -203; +pub const M_BIONIC_SET_HEAP_TAGGING_LEVEL: i32 = -204; +pub const M_LOG_STATS: i32 = -205; +pub const EXIT_FAILURE: u32 = 1; +pub const EXIT_SUCCESS: u32 = 0; +pub const RAND_MAX: u32 = 2147483647; +pub const __bool_true_false_are_defined: u32 = 1; +pub const true_: u32 = 1; +pub const false_: u32 = 0; +pub const EPERM: u32 = 1; +pub const ENOENT: u32 = 2; +pub const ESRCH: u32 = 3; +pub const EINTR: u32 = 4; +pub const EIO: u32 = 5; +pub const ENXIO: u32 = 6; +pub const E2BIG: u32 = 7; +pub const ENOEXEC: u32 = 8; +pub const EBADF: u32 = 9; +pub const ECHILD: u32 = 10; +pub const EAGAIN: u32 = 11; +pub const ENOMEM: u32 = 12; +pub const EACCES: u32 = 13; +pub const EFAULT: u32 = 14; +pub const ENOTBLK: u32 = 15; +pub const EBUSY: u32 = 16; +pub const EEXIST: u32 = 17; +pub const EXDEV: u32 = 18; +pub const ENODEV: u32 = 19; +pub const ENOTDIR: u32 = 20; +pub const EISDIR: u32 = 21; +pub const EINVAL: u32 = 22; +pub const ENFILE: u32 = 23; +pub const EMFILE: u32 = 24; +pub const ENOTTY: u32 = 25; +pub const ETXTBSY: u32 = 26; +pub const EFBIG: u32 = 27; +pub const ENOSPC: u32 = 28; +pub const ESPIPE: u32 = 29; +pub const EROFS: u32 = 30; +pub const EMLINK: u32 = 31; +pub const EPIPE: u32 = 32; +pub const EDOM: u32 = 33; +pub const ERANGE: u32 = 34; +pub const EDEADLK: u32 = 35; +pub const ENAMETOOLONG: u32 = 36; +pub const ENOLCK: u32 = 37; +pub const ENOSYS: u32 = 38; +pub const ENOTEMPTY: u32 = 39; +pub const ELOOP: u32 = 40; +pub const EWOULDBLOCK: u32 = 11; +pub const ENOMSG: u32 = 42; +pub const EIDRM: u32 = 43; +pub const ECHRNG: u32 = 44; +pub const EL2NSYNC: u32 = 45; +pub const EL3HLT: u32 = 46; +pub const EL3RST: u32 = 47; +pub const ELNRNG: u32 = 48; +pub const EUNATCH: u32 = 49; +pub const ENOCSI: u32 = 50; +pub const EL2HLT: u32 = 51; +pub const EBADE: u32 = 52; +pub const EBADR: u32 = 53; +pub const EXFULL: u32 = 54; +pub const ENOANO: u32 = 55; +pub const EBADRQC: u32 = 56; +pub const EBADSLT: u32 = 57; +pub const EDEADLOCK: u32 = 35; +pub const EBFONT: u32 = 59; +pub const ENOSTR: u32 = 60; +pub const ENODATA: u32 = 61; +pub const ETIME: u32 = 62; +pub const ENOSR: u32 = 63; +pub const ENONET: u32 = 64; +pub const ENOPKG: u32 = 65; +pub const EREMOTE: u32 = 66; +pub const ENOLINK: u32 = 67; +pub const EADV: u32 = 68; +pub const ESRMNT: u32 = 69; +pub const ECOMM: u32 = 70; +pub const EPROTO: u32 = 71; +pub const EMULTIHOP: u32 = 72; +pub const EDOTDOT: u32 = 73; +pub const EBADMSG: u32 = 74; +pub const EOVERFLOW: u32 = 75; +pub const ENOTUNIQ: u32 = 76; +pub const EBADFD: u32 = 77; +pub const EREMCHG: u32 = 78; +pub const ELIBACC: u32 = 79; +pub const ELIBBAD: u32 = 80; +pub const ELIBSCN: u32 = 81; +pub const ELIBMAX: u32 = 82; +pub const ELIBEXEC: u32 = 83; +pub const EILSEQ: u32 = 84; +pub const ERESTART: u32 = 85; +pub const ESTRPIPE: u32 = 86; +pub const EUSERS: u32 = 87; +pub const ENOTSOCK: u32 = 88; +pub const EDESTADDRREQ: u32 = 89; +pub const EMSGSIZE: u32 = 90; +pub const EPROTOTYPE: u32 = 91; +pub const ENOPROTOOPT: u32 = 92; +pub const EPROTONOSUPPORT: u32 = 93; +pub const ESOCKTNOSUPPORT: u32 = 94; +pub const EOPNOTSUPP: u32 = 95; +pub const EPFNOSUPPORT: u32 = 96; +pub const EAFNOSUPPORT: u32 = 97; +pub const EADDRINUSE: u32 = 98; +pub const EADDRNOTAVAIL: u32 = 99; +pub const ENETDOWN: u32 = 100; +pub const ENETUNREACH: u32 = 101; +pub const ENETRESET: u32 = 102; +pub const ECONNABORTED: u32 = 103; +pub const ECONNRESET: u32 = 104; +pub const ENOBUFS: u32 = 105; +pub const EISCONN: u32 = 106; +pub const ENOTCONN: u32 = 107; +pub const ESHUTDOWN: u32 = 108; +pub const ETOOMANYREFS: u32 = 109; +pub const ETIMEDOUT: u32 = 110; +pub const ECONNREFUSED: u32 = 111; +pub const EHOSTDOWN: u32 = 112; +pub const EHOSTUNREACH: u32 = 113; +pub const EALREADY: u32 = 114; +pub const EINPROGRESS: u32 = 115; +pub const ESTALE: u32 = 116; +pub const EUCLEAN: u32 = 117; +pub const ENOTNAM: u32 = 118; +pub const ENAVAIL: u32 = 119; +pub const EISNAM: u32 = 120; +pub const EREMOTEIO: u32 = 121; +pub const EDQUOT: u32 = 122; +pub const ENOMEDIUM: u32 = 123; +pub const EMEDIUMTYPE: u32 = 124; +pub const ECANCELED: u32 = 125; +pub const ENOKEY: u32 = 126; +pub const EKEYEXPIRED: u32 = 127; +pub const EKEYREVOKED: u32 = 128; +pub const EKEYREJECTED: u32 = 129; +pub const EOWNERDEAD: u32 = 130; +pub const ENOTRECOVERABLE: u32 = 131; +pub const ERFKILL: u32 = 132; +pub const EHWPOISON: u32 = 133; +pub const ENOTSUP: u32 = 95; +pub const MAX_NAME: u32 = 256; +pub const ABS_MT_MIN: u32 = 47; +pub const ABS_MT_MAX: u32 = 61; +pub const ABS_MT_CNT: u32 = 15; +unsafe extern "C" { + pub fn android_get_application_target_sdk_version() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn android_get_device_api_level() -> ::std::os::raw::c_int; +} +pub type wchar_t = ::std::os::raw::c_uint; +#[repr(C)] +#[repr(align(16))] +#[derive(Debug, Copy, Clone)] +pub struct max_align_t { + pub __clang_max_align_nonce1: ::std::os::raw::c_longlong, + pub __bindgen_padding_0: u64, + pub __clang_max_align_nonce2: u128, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of max_align_t"][::std::mem::size_of::() - 32usize]; + ["Alignment of max_align_t"][::std::mem::align_of::() - 16usize]; + [ + "Offset of field: max_align_t::__clang_max_align_nonce1", + ][::std::mem::offset_of!(max_align_t, __clang_max_align_nonce1) - 0usize]; + [ + "Offset of field: max_align_t::__clang_max_align_nonce2", + ][::std::mem::offset_of!(max_align_t, __clang_max_align_nonce2) - 16usize]; +}; +pub type __int8_t = ::std::os::raw::c_schar; +pub type __uint8_t = ::std::os::raw::c_uchar; +pub type __int16_t = ::std::os::raw::c_short; +pub type __uint16_t = ::std::os::raw::c_ushort; +pub type __int32_t = ::std::os::raw::c_int; +pub type __uint32_t = ::std::os::raw::c_uint; +pub type __int64_t = ::std::os::raw::c_long; +pub type __uint64_t = ::std::os::raw::c_ulong; +pub type __intptr_t = ::std::os::raw::c_long; +pub type __uintptr_t = ::std::os::raw::c_ulong; +pub type int_least8_t = i8; +pub type uint_least8_t = u8; +pub type int_least16_t = i16; +pub type uint_least16_t = u16; +pub type int_least32_t = i32; +pub type uint_least32_t = u32; +pub type int_least64_t = i64; +pub type uint_least64_t = u64; +pub type int_fast8_t = i8; +pub type uint_fast8_t = u8; +pub type int_fast64_t = i64; +pub type uint_fast64_t = u64; +pub type int_fast16_t = i64; +pub type uint_fast16_t = u64; +pub type int_fast32_t = i64; +pub type uint_fast32_t = u64; +pub type uintmax_t = u64; +pub type intmax_t = i64; +pub type __s8 = ::std::os::raw::c_schar; +pub type __u8 = ::std::os::raw::c_uchar; +pub type __s16 = ::std::os::raw::c_short; +pub type __u16 = ::std::os::raw::c_ushort; +pub type __s32 = ::std::os::raw::c_int; +pub type __u32 = ::std::os::raw::c_uint; +pub type __s64 = ::std::os::raw::c_longlong; +pub type __u64 = ::std::os::raw::c_ulonglong; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __kernel_fd_set { + pub fds_bits: [::std::os::raw::c_ulong; 16usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of __kernel_fd_set"][::std::mem::size_of::<__kernel_fd_set>() - 128usize]; + ["Alignment of __kernel_fd_set"][::std::mem::align_of::<__kernel_fd_set>() - 8usize]; + [ + "Offset of field: __kernel_fd_set::fds_bits", + ][::std::mem::offset_of!(__kernel_fd_set, fds_bits) - 0usize]; +}; +pub type __kernel_sighandler_t = ::std::option::Option< + unsafe extern "C" fn(arg1: ::std::os::raw::c_int), +>; +pub type __kernel_key_t = ::std::os::raw::c_int; +pub type __kernel_mqd_t = ::std::os::raw::c_int; +pub type __kernel_old_uid_t = ::std::os::raw::c_ushort; +pub type __kernel_old_gid_t = ::std::os::raw::c_ushort; +pub type __kernel_long_t = ::std::os::raw::c_long; +pub type __kernel_ulong_t = ::std::os::raw::c_ulong; +pub type __kernel_ino_t = __kernel_ulong_t; +pub type __kernel_mode_t = ::std::os::raw::c_uint; +pub type __kernel_pid_t = ::std::os::raw::c_int; +pub type __kernel_ipc_pid_t = ::std::os::raw::c_int; +pub type __kernel_uid_t = ::std::os::raw::c_uint; +pub type __kernel_gid_t = ::std::os::raw::c_uint; +pub type __kernel_suseconds_t = __kernel_long_t; +pub type __kernel_daddr_t = ::std::os::raw::c_int; +pub type __kernel_uid32_t = ::std::os::raw::c_uint; +pub type __kernel_gid32_t = ::std::os::raw::c_uint; +pub type __kernel_old_dev_t = ::std::os::raw::c_uint; +pub type __kernel_size_t = __kernel_ulong_t; +pub type __kernel_ssize_t = __kernel_long_t; +pub type __kernel_ptrdiff_t = __kernel_long_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __kernel_fsid_t { + pub val: [::std::os::raw::c_int; 2usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of __kernel_fsid_t"][::std::mem::size_of::<__kernel_fsid_t>() - 8usize]; + ["Alignment of __kernel_fsid_t"][::std::mem::align_of::<__kernel_fsid_t>() - 4usize]; + [ + "Offset of field: __kernel_fsid_t::val", + ][::std::mem::offset_of!(__kernel_fsid_t, val) - 0usize]; +}; +pub type __kernel_off_t = __kernel_long_t; +pub type __kernel_loff_t = ::std::os::raw::c_longlong; +pub type __kernel_old_time_t = __kernel_long_t; +pub type __kernel_time_t = __kernel_long_t; +pub type __kernel_time64_t = ::std::os::raw::c_longlong; +pub type __kernel_clock_t = __kernel_long_t; +pub type __kernel_timer_t = ::std::os::raw::c_int; +pub type __kernel_clockid_t = ::std::os::raw::c_int; +pub type __kernel_caddr_t = *mut ::std::os::raw::c_char; +pub type __kernel_uid16_t = ::std::os::raw::c_ushort; +pub type __kernel_gid16_t = ::std::os::raw::c_ushort; +pub type __s128 = i128; +pub type __u128 = u128; +pub type __le16 = __u16; +pub type __be16 = __u16; +pub type __le32 = __u32; +pub type __be32 = __u32; +pub type __le64 = __u64; +pub type __be64 = __u64; +pub type __sum16 = __u16; +pub type __wsum = __u32; +pub type __poll_t = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct pthread_attr_t { + pub flags: u32, + pub stack_base: *mut ::std::os::raw::c_void, + pub stack_size: usize, + pub guard_size: usize, + pub sched_policy: i32, + pub sched_priority: i32, + pub __reserved: [::std::os::raw::c_char; 16usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of pthread_attr_t"][::std::mem::size_of::() - 56usize]; + ["Alignment of pthread_attr_t"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: pthread_attr_t::flags", + ][::std::mem::offset_of!(pthread_attr_t, flags) - 0usize]; + [ + "Offset of field: pthread_attr_t::stack_base", + ][::std::mem::offset_of!(pthread_attr_t, stack_base) - 8usize]; + [ + "Offset of field: pthread_attr_t::stack_size", + ][::std::mem::offset_of!(pthread_attr_t, stack_size) - 16usize]; + [ + "Offset of field: pthread_attr_t::guard_size", + ][::std::mem::offset_of!(pthread_attr_t, guard_size) - 24usize]; + [ + "Offset of field: pthread_attr_t::sched_policy", + ][::std::mem::offset_of!(pthread_attr_t, sched_policy) - 32usize]; + [ + "Offset of field: pthread_attr_t::sched_priority", + ][::std::mem::offset_of!(pthread_attr_t, sched_priority) - 36usize]; + [ + "Offset of field: pthread_attr_t::__reserved", + ][::std::mem::offset_of!(pthread_attr_t, __reserved) - 40usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct pthread_barrier_t { + pub __private: [i64; 4usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of pthread_barrier_t"][::std::mem::size_of::() - 32usize]; + [ + "Alignment of pthread_barrier_t", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: pthread_barrier_t::__private", + ][::std::mem::offset_of!(pthread_barrier_t, __private) - 0usize]; +}; +pub type pthread_barrierattr_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct pthread_cond_t { + pub __private: [i32; 12usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of pthread_cond_t"][::std::mem::size_of::() - 48usize]; + ["Alignment of pthread_cond_t"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: pthread_cond_t::__private", + ][::std::mem::offset_of!(pthread_cond_t, __private) - 0usize]; +}; +pub type pthread_condattr_t = ::std::os::raw::c_long; +pub type pthread_key_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct pthread_mutex_t { + pub __private: [i32; 10usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of pthread_mutex_t"][::std::mem::size_of::() - 40usize]; + ["Alignment of pthread_mutex_t"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: pthread_mutex_t::__private", + ][::std::mem::offset_of!(pthread_mutex_t, __private) - 0usize]; +}; +pub type pthread_mutexattr_t = ::std::os::raw::c_long; +pub type pthread_once_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct pthread_rwlock_t { + pub __private: [i32; 14usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of pthread_rwlock_t"][::std::mem::size_of::() - 56usize]; + [ + "Alignment of pthread_rwlock_t", + ][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: pthread_rwlock_t::__private", + ][::std::mem::offset_of!(pthread_rwlock_t, __private) - 0usize]; +}; +pub type pthread_rwlockattr_t = ::std::os::raw::c_long; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct pthread_spinlock_t { + pub __private: i64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of pthread_spinlock_t"][::std::mem::size_of::() - 8usize]; + [ + "Alignment of pthread_spinlock_t", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: pthread_spinlock_t::__private", + ][::std::mem::offset_of!(pthread_spinlock_t, __private) - 0usize]; +}; +pub type pthread_t = ::std::os::raw::c_long; +pub type __gid_t = __kernel_gid32_t; +pub type gid_t = __gid_t; +pub type __uid_t = __kernel_uid32_t; +pub type uid_t = __uid_t; +pub type __pid_t = __kernel_pid_t; +pub type pid_t = __pid_t; +pub type __id_t = u32; +pub type id_t = __id_t; +pub type blkcnt_t = ::std::os::raw::c_ulong; +pub type blksize_t = ::std::os::raw::c_ulong; +pub type caddr_t = __kernel_caddr_t; +pub type clock_t = __kernel_clock_t; +pub type __clockid_t = __kernel_clockid_t; +pub type clockid_t = __clockid_t; +pub type daddr_t = __kernel_daddr_t; +pub type fsblkcnt_t = ::std::os::raw::c_ulong; +pub type fsfilcnt_t = ::std::os::raw::c_ulong; +pub type __mode_t = __kernel_mode_t; +pub type mode_t = __mode_t; +pub type __key_t = __kernel_key_t; +pub type key_t = __key_t; +pub type __ino_t = __kernel_ino_t; +pub type ino_t = __ino_t; +pub type ino64_t = u64; +pub type __nlink_t = u32; +pub type nlink_t = __nlink_t; +pub type __timer_t = *mut ::std::os::raw::c_void; +pub type timer_t = __timer_t; +pub type __suseconds_t = __kernel_suseconds_t; +pub type suseconds_t = __suseconds_t; +pub type __useconds_t = u32; +pub type useconds_t = __useconds_t; +pub type dev_t = u64; +pub type __time_t = __kernel_time_t; +pub type time_t = __time_t; +pub type off_t = i64; +pub type loff_t = off_t; +pub type off64_t = loff_t; +pub type __socklen_t = u32; +pub type socklen_t = __socklen_t; +pub type __va_list = __BindgenOpaqueArray; +pub type uint_t = ::std::os::raw::c_uint; +pub type uint = ::std::os::raw::c_uint; +pub type u_char = ::std::os::raw::c_uchar; +pub type u_short = ::std::os::raw::c_ushort; +pub type u_int = ::std::os::raw::c_uint; +pub type u_long = ::std::os::raw::c_ulong; +pub type u_int32_t = u32; +pub type u_int16_t = u16; +pub type u_int8_t = u8; +pub type u_int64_t = u64; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct timespec { + pub tv_sec: time_t, + pub tv_nsec: ::std::os::raw::c_long, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of timespec"][::std::mem::size_of::() - 16usize]; + ["Alignment of timespec"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: timespec::tv_sec", + ][::std::mem::offset_of!(timespec, tv_sec) - 0usize]; + [ + "Offset of field: timespec::tv_nsec", + ][::std::mem::offset_of!(timespec, tv_nsec) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __kernel_timespec { + pub tv_sec: __kernel_time64_t, + pub tv_nsec: ::std::os::raw::c_longlong, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of __kernel_timespec"][::std::mem::size_of::<__kernel_timespec>() - 16usize]; + [ + "Alignment of __kernel_timespec", + ][::std::mem::align_of::<__kernel_timespec>() - 8usize]; + [ + "Offset of field: __kernel_timespec::tv_sec", + ][::std::mem::offset_of!(__kernel_timespec, tv_sec) - 0usize]; + [ + "Offset of field: __kernel_timespec::tv_nsec", + ][::std::mem::offset_of!(__kernel_timespec, tv_nsec) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __kernel_itimerspec { + pub it_interval: __kernel_timespec, + pub it_value: __kernel_timespec, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __kernel_itimerspec", + ][::std::mem::size_of::<__kernel_itimerspec>() - 32usize]; + [ + "Alignment of __kernel_itimerspec", + ][::std::mem::align_of::<__kernel_itimerspec>() - 8usize]; + [ + "Offset of field: __kernel_itimerspec::it_interval", + ][::std::mem::offset_of!(__kernel_itimerspec, it_interval) - 0usize]; + [ + "Offset of field: __kernel_itimerspec::it_value", + ][::std::mem::offset_of!(__kernel_itimerspec, it_value) - 16usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __kernel_old_timespec { + pub tv_sec: __kernel_old_time_t, + pub tv_nsec: ::std::os::raw::c_long, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __kernel_old_timespec", + ][::std::mem::size_of::<__kernel_old_timespec>() - 16usize]; + [ + "Alignment of __kernel_old_timespec", + ][::std::mem::align_of::<__kernel_old_timespec>() - 8usize]; + [ + "Offset of field: __kernel_old_timespec::tv_sec", + ][::std::mem::offset_of!(__kernel_old_timespec, tv_sec) - 0usize]; + [ + "Offset of field: __kernel_old_timespec::tv_nsec", + ][::std::mem::offset_of!(__kernel_old_timespec, tv_nsec) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __kernel_sock_timeval { + pub tv_sec: __s64, + pub tv_usec: __s64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __kernel_sock_timeval", + ][::std::mem::size_of::<__kernel_sock_timeval>() - 16usize]; + [ + "Alignment of __kernel_sock_timeval", + ][::std::mem::align_of::<__kernel_sock_timeval>() - 8usize]; + [ + "Offset of field: __kernel_sock_timeval::tv_sec", + ][::std::mem::offset_of!(__kernel_sock_timeval, tv_sec) - 0usize]; + [ + "Offset of field: __kernel_sock_timeval::tv_usec", + ][::std::mem::offset_of!(__kernel_sock_timeval, tv_usec) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct timeval { + pub tv_sec: __kernel_old_time_t, + pub tv_usec: __kernel_suseconds_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of timeval"][::std::mem::size_of::() - 16usize]; + ["Alignment of timeval"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: timeval::tv_sec", + ][::std::mem::offset_of!(timeval, tv_sec) - 0usize]; + [ + "Offset of field: timeval::tv_usec", + ][::std::mem::offset_of!(timeval, tv_usec) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct itimerspec { + pub it_interval: timespec, + pub it_value: timespec, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of itimerspec"][::std::mem::size_of::() - 32usize]; + ["Alignment of itimerspec"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: itimerspec::it_interval", + ][::std::mem::offset_of!(itimerspec, it_interval) - 0usize]; + [ + "Offset of field: itimerspec::it_value", + ][::std::mem::offset_of!(itimerspec, it_value) - 16usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct itimerval { + pub it_interval: timeval, + pub it_value: timeval, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of itimerval"][::std::mem::size_of::() - 32usize]; + ["Alignment of itimerval"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: itimerval::it_interval", + ][::std::mem::offset_of!(itimerval, it_interval) - 0usize]; + [ + "Offset of field: itimerval::it_value", + ][::std::mem::offset_of!(itimerval, it_value) - 16usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct timezone { + pub tz_minuteswest: ::std::os::raw::c_int, + pub tz_dsttime: ::std::os::raw::c_int, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of timezone"][::std::mem::size_of::() - 8usize]; + ["Alignment of timezone"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: timezone::tz_minuteswest", + ][::std::mem::offset_of!(timezone, tz_minuteswest) - 0usize]; + [ + "Offset of field: timezone::tz_dsttime", + ][::std::mem::offset_of!(timezone, tz_dsttime) - 4usize]; +}; +#[repr(C)] +#[repr(align(16))] +#[derive(Debug, Copy, Clone)] +pub struct sigcontext { + pub fault_address: __u64, + pub regs: [__u64; 31usize], + pub sp: __u64, + pub pc: __u64, + pub pstate: __u64, + pub __bindgen_padding_0: [u8; 8usize], + pub __reserved: [__u8; 4096usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sigcontext"][::std::mem::size_of::() - 4384usize]; + ["Alignment of sigcontext"][::std::mem::align_of::() - 16usize]; + [ + "Offset of field: sigcontext::fault_address", + ][::std::mem::offset_of!(sigcontext, fault_address) - 0usize]; + [ + "Offset of field: sigcontext::regs", + ][::std::mem::offset_of!(sigcontext, regs) - 8usize]; + [ + "Offset of field: sigcontext::sp", + ][::std::mem::offset_of!(sigcontext, sp) - 256usize]; + [ + "Offset of field: sigcontext::pc", + ][::std::mem::offset_of!(sigcontext, pc) - 264usize]; + [ + "Offset of field: sigcontext::pstate", + ][::std::mem::offset_of!(sigcontext, pstate) - 272usize]; + [ + "Offset of field: sigcontext::__reserved", + ][::std::mem::offset_of!(sigcontext, __reserved) - 288usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _aarch64_ctx { + pub magic: __u32, + pub size: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of _aarch64_ctx"][::std::mem::size_of::<_aarch64_ctx>() - 8usize]; + ["Alignment of _aarch64_ctx"][::std::mem::align_of::<_aarch64_ctx>() - 4usize]; + [ + "Offset of field: _aarch64_ctx::magic", + ][::std::mem::offset_of!(_aarch64_ctx, magic) - 0usize]; + [ + "Offset of field: _aarch64_ctx::size", + ][::std::mem::offset_of!(_aarch64_ctx, size) - 4usize]; +}; +#[repr(C)] +#[repr(align(16))] +#[derive(Debug, Copy, Clone)] +pub struct fpsimd_context { + pub head: _aarch64_ctx, + pub fpsr: __u32, + pub fpcr: __u32, + pub vregs: [__uint128_t; 32usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of fpsimd_context"][::std::mem::size_of::() - 528usize]; + ["Alignment of fpsimd_context"][::std::mem::align_of::() - 16usize]; + [ + "Offset of field: fpsimd_context::head", + ][::std::mem::offset_of!(fpsimd_context, head) - 0usize]; + [ + "Offset of field: fpsimd_context::fpsr", + ][::std::mem::offset_of!(fpsimd_context, fpsr) - 8usize]; + [ + "Offset of field: fpsimd_context::fpcr", + ][::std::mem::offset_of!(fpsimd_context, fpcr) - 12usize]; + [ + "Offset of field: fpsimd_context::vregs", + ][::std::mem::offset_of!(fpsimd_context, vregs) - 16usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct esr_context { + pub head: _aarch64_ctx, + pub esr: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of esr_context"][::std::mem::size_of::() - 16usize]; + ["Alignment of esr_context"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: esr_context::head", + ][::std::mem::offset_of!(esr_context, head) - 0usize]; + [ + "Offset of field: esr_context::esr", + ][::std::mem::offset_of!(esr_context, esr) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct extra_context { + pub head: _aarch64_ctx, + pub datap: __u64, + pub size: __u32, + pub __reserved: [__u32; 3usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of extra_context"][::std::mem::size_of::() - 32usize]; + ["Alignment of extra_context"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: extra_context::head", + ][::std::mem::offset_of!(extra_context, head) - 0usize]; + [ + "Offset of field: extra_context::datap", + ][::std::mem::offset_of!(extra_context, datap) - 8usize]; + [ + "Offset of field: extra_context::size", + ][::std::mem::offset_of!(extra_context, size) - 16usize]; + [ + "Offset of field: extra_context::__reserved", + ][::std::mem::offset_of!(extra_context, __reserved) - 20usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sve_context { + pub head: _aarch64_ctx, + pub vl: __u16, + pub flags: __u16, + pub __reserved: [__u16; 2usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sve_context"][::std::mem::size_of::() - 16usize]; + ["Alignment of sve_context"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: sve_context::head", + ][::std::mem::offset_of!(sve_context, head) - 0usize]; + [ + "Offset of field: sve_context::vl", + ][::std::mem::offset_of!(sve_context, vl) - 8usize]; + [ + "Offset of field: sve_context::flags", + ][::std::mem::offset_of!(sve_context, flags) - 10usize]; + [ + "Offset of field: sve_context::__reserved", + ][::std::mem::offset_of!(sve_context, __reserved) - 12usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct tpidr2_context { + pub head: _aarch64_ctx, + pub tpidr2: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of tpidr2_context"][::std::mem::size_of::() - 16usize]; + ["Alignment of tpidr2_context"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: tpidr2_context::head", + ][::std::mem::offset_of!(tpidr2_context, head) - 0usize]; + [ + "Offset of field: tpidr2_context::tpidr2", + ][::std::mem::offset_of!(tpidr2_context, tpidr2) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct za_context { + pub head: _aarch64_ctx, + pub vl: __u16, + pub __reserved: [__u16; 3usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of za_context"][::std::mem::size_of::() - 16usize]; + ["Alignment of za_context"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: za_context::head", + ][::std::mem::offset_of!(za_context, head) - 0usize]; + ["Offset of field: za_context::vl"][::std::mem::offset_of!(za_context, vl) - 8usize]; + [ + "Offset of field: za_context::__reserved", + ][::std::mem::offset_of!(za_context, __reserved) - 10usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct zt_context { + pub head: _aarch64_ctx, + pub nregs: __u16, + pub __reserved: [__u16; 3usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of zt_context"][::std::mem::size_of::() - 16usize]; + ["Alignment of zt_context"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: zt_context::head", + ][::std::mem::offset_of!(zt_context, head) - 0usize]; + [ + "Offset of field: zt_context::nregs", + ][::std::mem::offset_of!(zt_context, nregs) - 8usize]; + [ + "Offset of field: zt_context::__reserved", + ][::std::mem::offset_of!(zt_context, __reserved) - 10usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sigset_t { + pub sig: [::std::os::raw::c_ulong; 1usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sigset_t"][::std::mem::size_of::() - 8usize]; + ["Alignment of sigset_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: sigset_t::sig"][::std::mem::offset_of!(sigset_t, sig) - 0usize]; +}; +pub type old_sigset_t = ::std::os::raw::c_ulong; +pub type __signalfn_t = ::std::option::Option< + unsafe extern "C" fn(arg1: ::std::os::raw::c_int), +>; +pub type __sighandler_t = __signalfn_t; +pub type __restorefn_t = ::std::option::Option; +pub type __sigrestore_t = __restorefn_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __kernel_sigaction { + pub sa_handler: __sighandler_t, + pub sa_flags: ::std::os::raw::c_ulong, + pub sa_restorer: __sigrestore_t, + pub sa_mask: sigset_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __kernel_sigaction", + ][::std::mem::size_of::<__kernel_sigaction>() - 32usize]; + [ + "Alignment of __kernel_sigaction", + ][::std::mem::align_of::<__kernel_sigaction>() - 8usize]; + [ + "Offset of field: __kernel_sigaction::sa_handler", + ][::std::mem::offset_of!(__kernel_sigaction, sa_handler) - 0usize]; + [ + "Offset of field: __kernel_sigaction::sa_flags", + ][::std::mem::offset_of!(__kernel_sigaction, sa_flags) - 8usize]; + [ + "Offset of field: __kernel_sigaction::sa_restorer", + ][::std::mem::offset_of!(__kernel_sigaction, sa_restorer) - 16usize]; + [ + "Offset of field: __kernel_sigaction::sa_mask", + ][::std::mem::offset_of!(__kernel_sigaction, sa_mask) - 24usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sigaltstack { + pub ss_sp: *mut ::std::os::raw::c_void, + pub ss_flags: ::std::os::raw::c_int, + pub ss_size: __kernel_size_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sigaltstack"][::std::mem::size_of::() - 24usize]; + ["Alignment of sigaltstack"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: sigaltstack::ss_sp", + ][::std::mem::offset_of!(sigaltstack, ss_sp) - 0usize]; + [ + "Offset of field: sigaltstack::ss_flags", + ][::std::mem::offset_of!(sigaltstack, ss_flags) - 8usize]; + [ + "Offset of field: sigaltstack::ss_size", + ][::std::mem::offset_of!(sigaltstack, ss_size) - 16usize]; +}; +pub type stack_t = sigaltstack; +#[repr(C)] +#[derive(Copy, Clone)] +pub union sigval { + pub sival_int: ::std::os::raw::c_int, + pub sival_ptr: *mut ::std::os::raw::c_void, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sigval"][::std::mem::size_of::() - 8usize]; + ["Alignment of sigval"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: sigval::sival_int", + ][::std::mem::offset_of!(sigval, sival_int) - 0usize]; + [ + "Offset of field: sigval::sival_ptr", + ][::std::mem::offset_of!(sigval, sival_ptr) - 0usize]; +}; +pub type sigval_t = sigval; +#[repr(C)] +#[derive(Copy, Clone)] +pub union __sifields { + pub _kill: __sifields__bindgen_ty_1, + pub _timer: __sifields__bindgen_ty_2, + pub _rt: __sifields__bindgen_ty_3, + pub _sigchld: __sifields__bindgen_ty_4, + pub _sigfault: __sifields__bindgen_ty_5, + pub _sigpoll: __sifields__bindgen_ty_6, + pub _sigsys: __sifields__bindgen_ty_7, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sifields__bindgen_ty_1 { + pub _pid: __kernel_pid_t, + pub _uid: __kernel_uid32_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __sifields__bindgen_ty_1", + ][::std::mem::size_of::<__sifields__bindgen_ty_1>() - 8usize]; + [ + "Alignment of __sifields__bindgen_ty_1", + ][::std::mem::align_of::<__sifields__bindgen_ty_1>() - 4usize]; + [ + "Offset of field: __sifields__bindgen_ty_1::_pid", + ][::std::mem::offset_of!(__sifields__bindgen_ty_1, _pid) - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_1::_uid", + ][::std::mem::offset_of!(__sifields__bindgen_ty_1, _uid) - 4usize]; +}; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct __sifields__bindgen_ty_2 { + pub _tid: __kernel_timer_t, + pub _overrun: ::std::os::raw::c_int, + pub _sigval: sigval_t, + pub _sys_private: ::std::os::raw::c_int, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __sifields__bindgen_ty_2", + ][::std::mem::size_of::<__sifields__bindgen_ty_2>() - 24usize]; + [ + "Alignment of __sifields__bindgen_ty_2", + ][::std::mem::align_of::<__sifields__bindgen_ty_2>() - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_2::_tid", + ][::std::mem::offset_of!(__sifields__bindgen_ty_2, _tid) - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_2::_overrun", + ][::std::mem::offset_of!(__sifields__bindgen_ty_2, _overrun) - 4usize]; + [ + "Offset of field: __sifields__bindgen_ty_2::_sigval", + ][::std::mem::offset_of!(__sifields__bindgen_ty_2, _sigval) - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_2::_sys_private", + ][::std::mem::offset_of!(__sifields__bindgen_ty_2, _sys_private) - 16usize]; +}; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct __sifields__bindgen_ty_3 { + pub _pid: __kernel_pid_t, + pub _uid: __kernel_uid32_t, + pub _sigval: sigval_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __sifields__bindgen_ty_3", + ][::std::mem::size_of::<__sifields__bindgen_ty_3>() - 16usize]; + [ + "Alignment of __sifields__bindgen_ty_3", + ][::std::mem::align_of::<__sifields__bindgen_ty_3>() - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_3::_pid", + ][::std::mem::offset_of!(__sifields__bindgen_ty_3, _pid) - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_3::_uid", + ][::std::mem::offset_of!(__sifields__bindgen_ty_3, _uid) - 4usize]; + [ + "Offset of field: __sifields__bindgen_ty_3::_sigval", + ][::std::mem::offset_of!(__sifields__bindgen_ty_3, _sigval) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sifields__bindgen_ty_4 { + pub _pid: __kernel_pid_t, + pub _uid: __kernel_uid32_t, + pub _status: ::std::os::raw::c_int, + pub _utime: __kernel_clock_t, + pub _stime: __kernel_clock_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __sifields__bindgen_ty_4", + ][::std::mem::size_of::<__sifields__bindgen_ty_4>() - 32usize]; + [ + "Alignment of __sifields__bindgen_ty_4", + ][::std::mem::align_of::<__sifields__bindgen_ty_4>() - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_4::_pid", + ][::std::mem::offset_of!(__sifields__bindgen_ty_4, _pid) - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_4::_uid", + ][::std::mem::offset_of!(__sifields__bindgen_ty_4, _uid) - 4usize]; + [ + "Offset of field: __sifields__bindgen_ty_4::_status", + ][::std::mem::offset_of!(__sifields__bindgen_ty_4, _status) - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_4::_utime", + ][::std::mem::offset_of!(__sifields__bindgen_ty_4, _utime) - 16usize]; + [ + "Offset of field: __sifields__bindgen_ty_4::_stime", + ][::std::mem::offset_of!(__sifields__bindgen_ty_4, _stime) - 24usize]; +}; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct __sifields__bindgen_ty_5 { + pub _addr: *mut ::std::os::raw::c_void, + pub __bindgen_anon_1: __sifields__bindgen_ty_5__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union __sifields__bindgen_ty_5__bindgen_ty_1 { + pub _trapno: ::std::os::raw::c_int, + pub _addr_lsb: ::std::os::raw::c_short, + pub _addr_bnd: __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1, + pub _addr_pkey: __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_2, + pub _perf: __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1 { + pub _dummy_bnd: [::std::os::raw::c_char; 8usize], + pub _lower: *mut ::std::os::raw::c_void, + pub _upper: *mut ::std::os::raw::c_void, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1", + ][::std::mem::size_of::<__sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1>() + - 24usize]; + [ + "Alignment of __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1", + ][::std::mem::align_of::<__sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1>() + - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1::_dummy_bnd", + ][::std::mem::offset_of!( + __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1, _dummy_bnd + ) - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1::_lower", + ][::std::mem::offset_of!( + __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1, _lower + ) - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1::_upper", + ][::std::mem::offset_of!( + __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_1, _upper + ) - 16usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_2 { + pub _dummy_pkey: [::std::os::raw::c_char; 8usize], + pub _pkey: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_2", + ][::std::mem::size_of::<__sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_2>() + - 12usize]; + [ + "Alignment of __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_2", + ][::std::mem::align_of::<__sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_2>() + - 4usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_2::_dummy_pkey", + ][::std::mem::offset_of!( + __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_2, _dummy_pkey + ) - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_2::_pkey", + ][::std::mem::offset_of!(__sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_2, _pkey) + - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3 { + pub _data: ::std::os::raw::c_ulong, + pub _type: __u32, + pub _flags: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3", + ][::std::mem::size_of::<__sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3>() + - 16usize]; + [ + "Alignment of __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3", + ][::std::mem::align_of::<__sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3>() + - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3::_data", + ][::std::mem::offset_of!(__sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3, _data) + - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3::_type", + ][::std::mem::offset_of!(__sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3, _type) + - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3::_flags", + ][::std::mem::offset_of!( + __sifields__bindgen_ty_5__bindgen_ty_1__bindgen_ty_3, _flags + ) - 12usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __sifields__bindgen_ty_5__bindgen_ty_1", + ][::std::mem::size_of::<__sifields__bindgen_ty_5__bindgen_ty_1>() - 24usize]; + [ + "Alignment of __sifields__bindgen_ty_5__bindgen_ty_1", + ][::std::mem::align_of::<__sifields__bindgen_ty_5__bindgen_ty_1>() - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1::_trapno", + ][::std::mem::offset_of!(__sifields__bindgen_ty_5__bindgen_ty_1, _trapno) - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1::_addr_lsb", + ][::std::mem::offset_of!(__sifields__bindgen_ty_5__bindgen_ty_1, _addr_lsb) + - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1::_addr_bnd", + ][::std::mem::offset_of!(__sifields__bindgen_ty_5__bindgen_ty_1, _addr_bnd) + - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1::_addr_pkey", + ][::std::mem::offset_of!(__sifields__bindgen_ty_5__bindgen_ty_1, _addr_pkey) + - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_5__bindgen_ty_1::_perf", + ][::std::mem::offset_of!(__sifields__bindgen_ty_5__bindgen_ty_1, _perf) - 0usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __sifields__bindgen_ty_5", + ][::std::mem::size_of::<__sifields__bindgen_ty_5>() - 32usize]; + [ + "Alignment of __sifields__bindgen_ty_5", + ][::std::mem::align_of::<__sifields__bindgen_ty_5>() - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_5::_addr", + ][::std::mem::offset_of!(__sifields__bindgen_ty_5, _addr) - 0usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sifields__bindgen_ty_6 { + pub _band: ::std::os::raw::c_long, + pub _fd: ::std::os::raw::c_int, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __sifields__bindgen_ty_6", + ][::std::mem::size_of::<__sifields__bindgen_ty_6>() - 16usize]; + [ + "Alignment of __sifields__bindgen_ty_6", + ][::std::mem::align_of::<__sifields__bindgen_ty_6>() - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_6::_band", + ][::std::mem::offset_of!(__sifields__bindgen_ty_6, _band) - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_6::_fd", + ][::std::mem::offset_of!(__sifields__bindgen_ty_6, _fd) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sifields__bindgen_ty_7 { + pub _call_addr: *mut ::std::os::raw::c_void, + pub _syscall: ::std::os::raw::c_int, + pub _arch: ::std::os::raw::c_uint, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of __sifields__bindgen_ty_7", + ][::std::mem::size_of::<__sifields__bindgen_ty_7>() - 16usize]; + [ + "Alignment of __sifields__bindgen_ty_7", + ][::std::mem::align_of::<__sifields__bindgen_ty_7>() - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_7::_call_addr", + ][::std::mem::offset_of!(__sifields__bindgen_ty_7, _call_addr) - 0usize]; + [ + "Offset of field: __sifields__bindgen_ty_7::_syscall", + ][::std::mem::offset_of!(__sifields__bindgen_ty_7, _syscall) - 8usize]; + [ + "Offset of field: __sifields__bindgen_ty_7::_arch", + ][::std::mem::offset_of!(__sifields__bindgen_ty_7, _arch) - 12usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of __sifields"][::std::mem::size_of::<__sifields>() - 32usize]; + ["Alignment of __sifields"][::std::mem::align_of::<__sifields>() - 8usize]; + [ + "Offset of field: __sifields::_kill", + ][::std::mem::offset_of!(__sifields, _kill) - 0usize]; + [ + "Offset of field: __sifields::_timer", + ][::std::mem::offset_of!(__sifields, _timer) - 0usize]; + [ + "Offset of field: __sifields::_rt", + ][::std::mem::offset_of!(__sifields, _rt) - 0usize]; + [ + "Offset of field: __sifields::_sigchld", + ][::std::mem::offset_of!(__sifields, _sigchld) - 0usize]; + [ + "Offset of field: __sifields::_sigfault", + ][::std::mem::offset_of!(__sifields, _sigfault) - 0usize]; + [ + "Offset of field: __sifields::_sigpoll", + ][::std::mem::offset_of!(__sifields, _sigpoll) - 0usize]; + [ + "Offset of field: __sifields::_sigsys", + ][::std::mem::offset_of!(__sifields, _sigsys) - 0usize]; +}; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct siginfo { + pub __bindgen_anon_1: siginfo__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union siginfo__bindgen_ty_1 { + pub __bindgen_anon_1: siginfo__bindgen_ty_1__bindgen_ty_1, + pub _si_pad: [::std::os::raw::c_int; 32usize], +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct siginfo__bindgen_ty_1__bindgen_ty_1 { + pub si_signo: ::std::os::raw::c_int, + pub si_errno: ::std::os::raw::c_int, + pub si_code: ::std::os::raw::c_int, + pub _sifields: __sifields, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of siginfo__bindgen_ty_1__bindgen_ty_1", + ][::std::mem::size_of::() - 48usize]; + [ + "Alignment of siginfo__bindgen_ty_1__bindgen_ty_1", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: siginfo__bindgen_ty_1__bindgen_ty_1::si_signo", + ][::std::mem::offset_of!(siginfo__bindgen_ty_1__bindgen_ty_1, si_signo) - 0usize]; + [ + "Offset of field: siginfo__bindgen_ty_1__bindgen_ty_1::si_errno", + ][::std::mem::offset_of!(siginfo__bindgen_ty_1__bindgen_ty_1, si_errno) - 4usize]; + [ + "Offset of field: siginfo__bindgen_ty_1__bindgen_ty_1::si_code", + ][::std::mem::offset_of!(siginfo__bindgen_ty_1__bindgen_ty_1, si_code) - 8usize]; + [ + "Offset of field: siginfo__bindgen_ty_1__bindgen_ty_1::_sifields", + ][::std::mem::offset_of!(siginfo__bindgen_ty_1__bindgen_ty_1, _sifields) - 16usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of siginfo__bindgen_ty_1", + ][::std::mem::size_of::() - 128usize]; + [ + "Alignment of siginfo__bindgen_ty_1", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: siginfo__bindgen_ty_1::_si_pad", + ][::std::mem::offset_of!(siginfo__bindgen_ty_1, _si_pad) - 0usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of siginfo"][::std::mem::size_of::() - 128usize]; + ["Alignment of siginfo"][::std::mem::align_of::() - 8usize]; +}; +pub type siginfo_t = siginfo; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct sigevent { + pub sigev_value: sigval_t, + pub sigev_signo: ::std::os::raw::c_int, + pub sigev_notify: ::std::os::raw::c_int, + pub _sigev_un: sigevent__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union sigevent__bindgen_ty_1 { + pub _pad: [::std::os::raw::c_int; 12usize], + pub _tid: ::std::os::raw::c_int, + pub _sigev_thread: sigevent__bindgen_ty_1__bindgen_ty_1, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sigevent__bindgen_ty_1__bindgen_ty_1 { + pub _function: ::std::option::Option, + pub _attribute: *mut ::std::os::raw::c_void, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of sigevent__bindgen_ty_1__bindgen_ty_1", + ][::std::mem::size_of::() - 16usize]; + [ + "Alignment of sigevent__bindgen_ty_1__bindgen_ty_1", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: sigevent__bindgen_ty_1__bindgen_ty_1::_function", + ][::std::mem::offset_of!(sigevent__bindgen_ty_1__bindgen_ty_1, _function) - 0usize]; + [ + "Offset of field: sigevent__bindgen_ty_1__bindgen_ty_1::_attribute", + ][::std::mem::offset_of!(sigevent__bindgen_ty_1__bindgen_ty_1, _attribute) - 8usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of sigevent__bindgen_ty_1", + ][::std::mem::size_of::() - 48usize]; + [ + "Alignment of sigevent__bindgen_ty_1", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: sigevent__bindgen_ty_1::_pad", + ][::std::mem::offset_of!(sigevent__bindgen_ty_1, _pad) - 0usize]; + [ + "Offset of field: sigevent__bindgen_ty_1::_tid", + ][::std::mem::offset_of!(sigevent__bindgen_ty_1, _tid) - 0usize]; + [ + "Offset of field: sigevent__bindgen_ty_1::_sigev_thread", + ][::std::mem::offset_of!(sigevent__bindgen_ty_1, _sigev_thread) - 0usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sigevent"][::std::mem::size_of::() - 64usize]; + ["Alignment of sigevent"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: sigevent::sigev_value", + ][::std::mem::offset_of!(sigevent, sigev_value) - 0usize]; + [ + "Offset of field: sigevent::sigev_signo", + ][::std::mem::offset_of!(sigevent, sigev_signo) - 8usize]; + [ + "Offset of field: sigevent::sigev_notify", + ][::std::mem::offset_of!(sigevent, sigev_notify) - 12usize]; + [ + "Offset of field: sigevent::_sigev_un", + ][::std::mem::offset_of!(sigevent, _sigev_un) - 16usize]; +}; +pub type sigevent_t = sigevent; +pub type sig_atomic_t = ::std::os::raw::c_int; +pub type sig_t = __sighandler_t; +pub type sighandler_t = __sighandler_t; +pub type sigset64_t = sigset_t; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct sigaction { + pub sa_flags: ::std::os::raw::c_int, + pub __bindgen_anon_1: sigaction__bindgen_ty_1, + pub sa_mask: sigset_t, + pub sa_restorer: ::std::option::Option, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union sigaction__bindgen_ty_1 { + pub sa_handler: sighandler_t, + pub sa_sigaction: ::std::option::Option< + unsafe extern "C" fn( + arg1: ::std::os::raw::c_int, + arg2: *mut siginfo, + arg3: *mut ::std::os::raw::c_void, + ), + >, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of sigaction__bindgen_ty_1", + ][::std::mem::size_of::() - 8usize]; + [ + "Alignment of sigaction__bindgen_ty_1", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: sigaction__bindgen_ty_1::sa_handler", + ][::std::mem::offset_of!(sigaction__bindgen_ty_1, sa_handler) - 0usize]; + [ + "Offset of field: sigaction__bindgen_ty_1::sa_sigaction", + ][::std::mem::offset_of!(sigaction__bindgen_ty_1, sa_sigaction) - 0usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sigaction"][::std::mem::size_of::() - 32usize]; + ["Alignment of sigaction"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: sigaction::sa_flags", + ][::std::mem::offset_of!(sigaction, sa_flags) - 0usize]; + [ + "Offset of field: sigaction::sa_mask", + ][::std::mem::offset_of!(sigaction, sa_mask) - 16usize]; + [ + "Offset of field: sigaction::sa_restorer", + ][::std::mem::offset_of!(sigaction, sa_restorer) - 24usize]; +}; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct sigaction64 { + pub sa_flags: ::std::os::raw::c_int, + pub __bindgen_anon_1: sigaction64__bindgen_ty_1, + pub sa_mask: sigset_t, + pub sa_restorer: ::std::option::Option, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union sigaction64__bindgen_ty_1 { + pub sa_handler: sighandler_t, + pub sa_sigaction: ::std::option::Option< + unsafe extern "C" fn( + arg1: ::std::os::raw::c_int, + arg2: *mut siginfo, + arg3: *mut ::std::os::raw::c_void, + ), + >, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of sigaction64__bindgen_ty_1", + ][::std::mem::size_of::() - 8usize]; + [ + "Alignment of sigaction64__bindgen_ty_1", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: sigaction64__bindgen_ty_1::sa_handler", + ][::std::mem::offset_of!(sigaction64__bindgen_ty_1, sa_handler) - 0usize]; + [ + "Offset of field: sigaction64__bindgen_ty_1::sa_sigaction", + ][::std::mem::offset_of!(sigaction64__bindgen_ty_1, sa_sigaction) - 0usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sigaction64"][::std::mem::size_of::() - 32usize]; + ["Alignment of sigaction64"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: sigaction64::sa_flags", + ][::std::mem::offset_of!(sigaction64, sa_flags) - 0usize]; + [ + "Offset of field: sigaction64::sa_mask", + ][::std::mem::offset_of!(sigaction64, sa_mask) - 16usize]; + [ + "Offset of field: sigaction64::sa_restorer", + ][::std::mem::offset_of!(sigaction64, sa_restorer) - 24usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct user_regs_struct { + pub regs: [u64; 31usize], + pub sp: u64, + pub pc: u64, + pub pstate: u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of user_regs_struct"][::std::mem::size_of::() - 272usize]; + [ + "Alignment of user_regs_struct", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: user_regs_struct::regs", + ][::std::mem::offset_of!(user_regs_struct, regs) - 0usize]; + [ + "Offset of field: user_regs_struct::sp", + ][::std::mem::offset_of!(user_regs_struct, sp) - 248usize]; + [ + "Offset of field: user_regs_struct::pc", + ][::std::mem::offset_of!(user_regs_struct, pc) - 256usize]; + [ + "Offset of field: user_regs_struct::pstate", + ][::std::mem::offset_of!(user_regs_struct, pstate) - 264usize]; +}; +#[repr(C)] +#[repr(align(16))] +#[derive(Debug, Copy, Clone)] +pub struct user_fpsimd_struct { + pub vregs: [__uint128_t; 32usize], + pub fpsr: u32, + pub fpcr: u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of user_fpsimd_struct", + ][::std::mem::size_of::() - 528usize]; + [ + "Alignment of user_fpsimd_struct", + ][::std::mem::align_of::() - 16usize]; + [ + "Offset of field: user_fpsimd_struct::vregs", + ][::std::mem::offset_of!(user_fpsimd_struct, vregs) - 0usize]; + [ + "Offset of field: user_fpsimd_struct::fpsr", + ][::std::mem::offset_of!(user_fpsimd_struct, fpsr) - 512usize]; + [ + "Offset of field: user_fpsimd_struct::fpcr", + ][::std::mem::offset_of!(user_fpsimd_struct, fpcr) - 516usize]; +}; +pub type greg_t = ::std::os::raw::c_ulong; +pub type gregset_t = [greg_t; 34usize]; +pub type fpregset_t = user_fpsimd_struct; +pub type mcontext_t = sigcontext; +#[repr(C)] +#[repr(align(16))] +#[derive(Copy, Clone)] +pub struct ucontext { + pub uc_flags: ::std::os::raw::c_ulong, + pub uc_link: *mut ucontext, + pub uc_stack: stack_t, + pub __bindgen_anon_1: ucontext__bindgen_ty_1, + pub __padding: [::std::os::raw::c_char; 120usize], + pub __bindgen_padding_0: u64, + pub uc_mcontext: mcontext_t, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union ucontext__bindgen_ty_1 { + pub uc_sigmask: sigset_t, + pub uc_sigmask64: sigset64_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of ucontext__bindgen_ty_1", + ][::std::mem::size_of::() - 8usize]; + [ + "Alignment of ucontext__bindgen_ty_1", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: ucontext__bindgen_ty_1::uc_sigmask", + ][::std::mem::offset_of!(ucontext__bindgen_ty_1, uc_sigmask) - 0usize]; + [ + "Offset of field: ucontext__bindgen_ty_1::uc_sigmask64", + ][::std::mem::offset_of!(ucontext__bindgen_ty_1, uc_sigmask64) - 0usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ucontext"][::std::mem::size_of::() - 4560usize]; + ["Alignment of ucontext"][::std::mem::align_of::() - 16usize]; + [ + "Offset of field: ucontext::uc_flags", + ][::std::mem::offset_of!(ucontext, uc_flags) - 0usize]; + [ + "Offset of field: ucontext::uc_link", + ][::std::mem::offset_of!(ucontext, uc_link) - 8usize]; + [ + "Offset of field: ucontext::uc_stack", + ][::std::mem::offset_of!(ucontext, uc_stack) - 16usize]; + [ + "Offset of field: ucontext::__padding", + ][::std::mem::offset_of!(ucontext, __padding) - 48usize]; + [ + "Offset of field: ucontext::uc_mcontext", + ][::std::mem::offset_of!(ucontext, uc_mcontext) - 176usize]; +}; +pub type ucontext_t = ucontext; +unsafe extern "C" { + pub fn __libc_current_sigrtmin() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn __libc_current_sigrtmax() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub static sys_siglist: [*const ::std::os::raw::c_char; 65usize]; +} +unsafe extern "C" { + pub static sys_signame: [*const ::std::os::raw::c_char; 65usize]; +} +unsafe extern "C" { + pub fn sigaction( + __signal: ::std::os::raw::c_int, + __new_action: *const sigaction, + __old_action: *mut sigaction, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigaction64( + __signal: ::std::os::raw::c_int, + __new_action: *const sigaction64, + __old_action: *mut sigaction64, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn siginterrupt( + __signal: ::std::os::raw::c_int, + __flag: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn signal( + __signal: ::std::os::raw::c_int, + __handler: sighandler_t, + ) -> sighandler_t; +} +unsafe extern "C" { + pub fn sigaddset( + __set: *mut sigset_t, + __signal: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigaddset64( + __set: *mut sigset64_t, + __signal: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigdelset( + __set: *mut sigset_t, + __signal: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigdelset64( + __set: *mut sigset64_t, + __signal: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigemptyset(__set: *mut sigset_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigemptyset64(__set: *mut sigset64_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigfillset(__set: *mut sigset_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigfillset64(__set: *mut sigset64_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigismember( + __set: *const sigset_t, + __signal: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigismember64( + __set: *const sigset64_t, + __signal: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigpending(__set: *mut sigset_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigpending64(__set: *mut sigset64_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigprocmask( + __how: ::std::os::raw::c_int, + __new_set: *const sigset_t, + __old_set: *mut sigset_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigprocmask64( + __how: ::std::os::raw::c_int, + __new_set: *const sigset64_t, + __old_set: *mut sigset64_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigsuspend(__mask: *const sigset_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigsuspend64(__mask: *const sigset64_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigwait( + __set: *const sigset_t, + __signal: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigwait64( + __set: *const sigset64_t, + __signal: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sighold(__signal: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigignore(__signal: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigpause(__signal: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigrelse(__signal: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigset( + __signal: ::std::os::raw::c_int, + __handler: sighandler_t, + ) -> sighandler_t; +} +unsafe extern "C" { + pub fn raise(__signal: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn kill(__pid: pid_t, __signal: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn killpg( + __pgrp: ::std::os::raw::c_int, + __signal: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn tgkill( + __tgid: ::std::os::raw::c_int, + __tid: ::std::os::raw::c_int, + __signal: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigaltstack( + __new_signal_stack: *const stack_t, + __old_signal_stack: *mut stack_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn psiginfo(__info: *const siginfo_t, __msg: *const ::std::os::raw::c_char); +} +unsafe extern "C" { + pub fn psignal( + __signal: ::std::os::raw::c_int, + __msg: *const ::std::os::raw::c_char, + ); +} +unsafe extern "C" { + pub fn pthread_kill( + __pthread: pthread_t, + __signal: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn pthread_sigmask( + __how: ::std::os::raw::c_int, + __new_set: *const sigset_t, + __old_set: *mut sigset_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn pthread_sigmask64( + __how: ::std::os::raw::c_int, + __new_set: *const sigset64_t, + __old_set: *mut sigset64_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigqueue( + __pid: pid_t, + __signal: ::std::os::raw::c_int, + __value: sigval, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigtimedwait( + __set: *const sigset_t, + __info: *mut siginfo_t, + __timeout: *const timespec, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigtimedwait64( + __set: *const sigset64_t, + __info: *mut siginfo_t, + __timeout: *const timespec, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigwaitinfo( + __set: *const sigset_t, + __info: *mut siginfo_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sigwaitinfo64( + __set: *const sigset64_t, + __info: *mut siginfo_t, + ) -> ::std::os::raw::c_int; +} +pub type fd_mask = ::std::os::raw::c_ulong; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct fd_set { + pub fds_bits: [fd_mask; 16usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of fd_set"][::std::mem::size_of::() - 128usize]; + ["Alignment of fd_set"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: fd_set::fds_bits", + ][::std::mem::offset_of!(fd_set, fds_bits) - 0usize]; +}; +unsafe extern "C" { + pub fn __FD_CLR_chk(arg1: ::std::os::raw::c_int, arg2: *mut fd_set, arg3: usize); +} +unsafe extern "C" { + pub fn __FD_SET_chk(arg1: ::std::os::raw::c_int, arg2: *mut fd_set, arg3: usize); +} +unsafe extern "C" { + pub fn __FD_ISSET_chk( + arg1: ::std::os::raw::c_int, + arg2: *const fd_set, + arg3: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn select( + __max_fd_plus_one: ::std::os::raw::c_int, + __read_fds: *mut fd_set, + __write_fds: *mut fd_set, + __exception_fds: *mut fd_set, + __timeout: *mut timeval, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn pselect( + __max_fd_plus_one: ::std::os::raw::c_int, + __read_fds: *mut fd_set, + __write_fds: *mut fd_set, + __exception_fds: *mut fd_set, + __timeout: *const timespec, + __mask: *const sigset_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn pselect64( + __max_fd_plus_one: ::std::os::raw::c_int, + __read_fds: *mut fd_set, + __write_fds: *mut fd_set, + __exception_fds: *mut fd_set, + __timeout: *const timespec, + __mask: *const sigset64_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn gettimeofday( + __tv: *mut timeval, + __tz: *mut timezone, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn settimeofday( + __tv: *const timeval, + __tz: *const timezone, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn getitimer( + __which: ::std::os::raw::c_int, + __current_value: *mut itimerval, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn setitimer( + __which: ::std::os::raw::c_int, + __new_value: *const itimerval, + __old_value: *mut itimerval, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn utimes( + __path: *const ::std::os::raw::c_char, + __times: *const timeval, + ) -> ::std::os::raw::c_int; +} +pub type cc_t = ::std::os::raw::c_uchar; +pub type speed_t = ::std::os::raw::c_uint; +pub type tcflag_t = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct termios { + pub c_iflag: tcflag_t, + pub c_oflag: tcflag_t, + pub c_cflag: tcflag_t, + pub c_lflag: tcflag_t, + pub c_line: cc_t, + pub c_cc: [cc_t; 19usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of termios"][::std::mem::size_of::() - 36usize]; + ["Alignment of termios"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: termios::c_iflag", + ][::std::mem::offset_of!(termios, c_iflag) - 0usize]; + [ + "Offset of field: termios::c_oflag", + ][::std::mem::offset_of!(termios, c_oflag) - 4usize]; + [ + "Offset of field: termios::c_cflag", + ][::std::mem::offset_of!(termios, c_cflag) - 8usize]; + [ + "Offset of field: termios::c_lflag", + ][::std::mem::offset_of!(termios, c_lflag) - 12usize]; + [ + "Offset of field: termios::c_line", + ][::std::mem::offset_of!(termios, c_line) - 16usize]; + ["Offset of field: termios::c_cc"][::std::mem::offset_of!(termios, c_cc) - 17usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct termios2 { + pub c_iflag: tcflag_t, + pub c_oflag: tcflag_t, + pub c_cflag: tcflag_t, + pub c_lflag: tcflag_t, + pub c_line: cc_t, + pub c_cc: [cc_t; 19usize], + pub c_ispeed: speed_t, + pub c_ospeed: speed_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of termios2"][::std::mem::size_of::() - 44usize]; + ["Alignment of termios2"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: termios2::c_iflag", + ][::std::mem::offset_of!(termios2, c_iflag) - 0usize]; + [ + "Offset of field: termios2::c_oflag", + ][::std::mem::offset_of!(termios2, c_oflag) - 4usize]; + [ + "Offset of field: termios2::c_cflag", + ][::std::mem::offset_of!(termios2, c_cflag) - 8usize]; + [ + "Offset of field: termios2::c_lflag", + ][::std::mem::offset_of!(termios2, c_lflag) - 12usize]; + [ + "Offset of field: termios2::c_line", + ][::std::mem::offset_of!(termios2, c_line) - 16usize]; + [ + "Offset of field: termios2::c_cc", + ][::std::mem::offset_of!(termios2, c_cc) - 17usize]; + [ + "Offset of field: termios2::c_ispeed", + ][::std::mem::offset_of!(termios2, c_ispeed) - 36usize]; + [ + "Offset of field: termios2::c_ospeed", + ][::std::mem::offset_of!(termios2, c_ospeed) - 40usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ktermios { + pub c_iflag: tcflag_t, + pub c_oflag: tcflag_t, + pub c_cflag: tcflag_t, + pub c_lflag: tcflag_t, + pub c_line: cc_t, + pub c_cc: [cc_t; 19usize], + pub c_ispeed: speed_t, + pub c_ospeed: speed_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ktermios"][::std::mem::size_of::() - 44usize]; + ["Alignment of ktermios"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: ktermios::c_iflag", + ][::std::mem::offset_of!(ktermios, c_iflag) - 0usize]; + [ + "Offset of field: ktermios::c_oflag", + ][::std::mem::offset_of!(ktermios, c_oflag) - 4usize]; + [ + "Offset of field: ktermios::c_cflag", + ][::std::mem::offset_of!(ktermios, c_cflag) - 8usize]; + [ + "Offset of field: ktermios::c_lflag", + ][::std::mem::offset_of!(ktermios, c_lflag) - 12usize]; + [ + "Offset of field: ktermios::c_line", + ][::std::mem::offset_of!(ktermios, c_line) - 16usize]; + [ + "Offset of field: ktermios::c_cc", + ][::std::mem::offset_of!(ktermios, c_cc) - 17usize]; + [ + "Offset of field: ktermios::c_ispeed", + ][::std::mem::offset_of!(ktermios, c_ispeed) - 36usize]; + [ + "Offset of field: ktermios::c_ospeed", + ][::std::mem::offset_of!(ktermios, c_ospeed) - 40usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct winsize { + pub ws_row: ::std::os::raw::c_ushort, + pub ws_col: ::std::os::raw::c_ushort, + pub ws_xpixel: ::std::os::raw::c_ushort, + pub ws_ypixel: ::std::os::raw::c_ushort, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of winsize"][::std::mem::size_of::() - 8usize]; + ["Alignment of winsize"][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: winsize::ws_row", + ][::std::mem::offset_of!(winsize, ws_row) - 0usize]; + [ + "Offset of field: winsize::ws_col", + ][::std::mem::offset_of!(winsize, ws_col) - 2usize]; + [ + "Offset of field: winsize::ws_xpixel", + ][::std::mem::offset_of!(winsize, ws_xpixel) - 4usize]; + [ + "Offset of field: winsize::ws_ypixel", + ][::std::mem::offset_of!(winsize, ws_ypixel) - 6usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct termio { + pub c_iflag: ::std::os::raw::c_ushort, + pub c_oflag: ::std::os::raw::c_ushort, + pub c_cflag: ::std::os::raw::c_ushort, + pub c_lflag: ::std::os::raw::c_ushort, + pub c_line: ::std::os::raw::c_uchar, + pub c_cc: [::std::os::raw::c_uchar; 8usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of termio"][::std::mem::size_of::() - 18usize]; + ["Alignment of termio"][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: termio::c_iflag", + ][::std::mem::offset_of!(termio, c_iflag) - 0usize]; + [ + "Offset of field: termio::c_oflag", + ][::std::mem::offset_of!(termio, c_oflag) - 2usize]; + [ + "Offset of field: termio::c_cflag", + ][::std::mem::offset_of!(termio, c_cflag) - 4usize]; + [ + "Offset of field: termio::c_lflag", + ][::std::mem::offset_of!(termio, c_lflag) - 6usize]; + ["Offset of field: termio::c_line"][::std::mem::offset_of!(termio, c_line) - 8usize]; + ["Offset of field: termio::c_cc"][::std::mem::offset_of!(termio, c_cc) - 9usize]; +}; +unsafe extern "C" { + pub fn ioctl( + __fd: ::std::os::raw::c_int, + __op: ::std::os::raw::c_int, + ... + ) -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct input_event { + pub time: timeval, + pub type_: __u16, + pub code: __u16, + pub value: __s32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of input_event"][::std::mem::size_of::() - 24usize]; + ["Alignment of input_event"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: input_event::time", + ][::std::mem::offset_of!(input_event, time) - 0usize]; + [ + "Offset of field: input_event::type_", + ][::std::mem::offset_of!(input_event, type_) - 16usize]; + [ + "Offset of field: input_event::code", + ][::std::mem::offset_of!(input_event, code) - 18usize]; + [ + "Offset of field: input_event::value", + ][::std::mem::offset_of!(input_event, value) - 20usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct input_id { + pub bustype: __u16, + pub vendor: __u16, + pub product: __u16, + pub version: __u16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of input_id"][::std::mem::size_of::() - 8usize]; + ["Alignment of input_id"][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: input_id::bustype", + ][::std::mem::offset_of!(input_id, bustype) - 0usize]; + [ + "Offset of field: input_id::vendor", + ][::std::mem::offset_of!(input_id, vendor) - 2usize]; + [ + "Offset of field: input_id::product", + ][::std::mem::offset_of!(input_id, product) - 4usize]; + [ + "Offset of field: input_id::version", + ][::std::mem::offset_of!(input_id, version) - 6usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct input_absinfo { + pub value: __s32, + pub minimum: __s32, + pub maximum: __s32, + pub fuzz: __s32, + pub flat: __s32, + pub resolution: __s32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of input_absinfo"][::std::mem::size_of::() - 24usize]; + ["Alignment of input_absinfo"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: input_absinfo::value", + ][::std::mem::offset_of!(input_absinfo, value) - 0usize]; + [ + "Offset of field: input_absinfo::minimum", + ][::std::mem::offset_of!(input_absinfo, minimum) - 4usize]; + [ + "Offset of field: input_absinfo::maximum", + ][::std::mem::offset_of!(input_absinfo, maximum) - 8usize]; + [ + "Offset of field: input_absinfo::fuzz", + ][::std::mem::offset_of!(input_absinfo, fuzz) - 12usize]; + [ + "Offset of field: input_absinfo::flat", + ][::std::mem::offset_of!(input_absinfo, flat) - 16usize]; + [ + "Offset of field: input_absinfo::resolution", + ][::std::mem::offset_of!(input_absinfo, resolution) - 20usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct input_keymap_entry { + pub flags: __u8, + pub len: __u8, + pub index: __u16, + pub keycode: __u32, + pub scancode: [__u8; 32usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of input_keymap_entry", + ][::std::mem::size_of::() - 40usize]; + [ + "Alignment of input_keymap_entry", + ][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: input_keymap_entry::flags", + ][::std::mem::offset_of!(input_keymap_entry, flags) - 0usize]; + [ + "Offset of field: input_keymap_entry::len", + ][::std::mem::offset_of!(input_keymap_entry, len) - 1usize]; + [ + "Offset of field: input_keymap_entry::index", + ][::std::mem::offset_of!(input_keymap_entry, index) - 2usize]; + [ + "Offset of field: input_keymap_entry::keycode", + ][::std::mem::offset_of!(input_keymap_entry, keycode) - 4usize]; + [ + "Offset of field: input_keymap_entry::scancode", + ][::std::mem::offset_of!(input_keymap_entry, scancode) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct input_mask { + pub type_: __u32, + pub codes_size: __u32, + pub codes_ptr: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of input_mask"][::std::mem::size_of::() - 16usize]; + ["Alignment of input_mask"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: input_mask::type_", + ][::std::mem::offset_of!(input_mask, type_) - 0usize]; + [ + "Offset of field: input_mask::codes_size", + ][::std::mem::offset_of!(input_mask, codes_size) - 4usize]; + [ + "Offset of field: input_mask::codes_ptr", + ][::std::mem::offset_of!(input_mask, codes_ptr) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ff_replay { + pub length: __u16, + pub delay: __u16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ff_replay"][::std::mem::size_of::() - 4usize]; + ["Alignment of ff_replay"][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: ff_replay::length", + ][::std::mem::offset_of!(ff_replay, length) - 0usize]; + [ + "Offset of field: ff_replay::delay", + ][::std::mem::offset_of!(ff_replay, delay) - 2usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ff_trigger { + pub button: __u16, + pub interval: __u16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ff_trigger"][::std::mem::size_of::() - 4usize]; + ["Alignment of ff_trigger"][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: ff_trigger::button", + ][::std::mem::offset_of!(ff_trigger, button) - 0usize]; + [ + "Offset of field: ff_trigger::interval", + ][::std::mem::offset_of!(ff_trigger, interval) - 2usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ff_envelope { + pub attack_length: __u16, + pub attack_level: __u16, + pub fade_length: __u16, + pub fade_level: __u16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ff_envelope"][::std::mem::size_of::() - 8usize]; + ["Alignment of ff_envelope"][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: ff_envelope::attack_length", + ][::std::mem::offset_of!(ff_envelope, attack_length) - 0usize]; + [ + "Offset of field: ff_envelope::attack_level", + ][::std::mem::offset_of!(ff_envelope, attack_level) - 2usize]; + [ + "Offset of field: ff_envelope::fade_length", + ][::std::mem::offset_of!(ff_envelope, fade_length) - 4usize]; + [ + "Offset of field: ff_envelope::fade_level", + ][::std::mem::offset_of!(ff_envelope, fade_level) - 6usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ff_constant_effect { + pub level: __s16, + pub envelope: ff_envelope, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of ff_constant_effect", + ][::std::mem::size_of::() - 10usize]; + [ + "Alignment of ff_constant_effect", + ][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: ff_constant_effect::level", + ][::std::mem::offset_of!(ff_constant_effect, level) - 0usize]; + [ + "Offset of field: ff_constant_effect::envelope", + ][::std::mem::offset_of!(ff_constant_effect, envelope) - 2usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ff_ramp_effect { + pub start_level: __s16, + pub end_level: __s16, + pub envelope: ff_envelope, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ff_ramp_effect"][::std::mem::size_of::() - 12usize]; + ["Alignment of ff_ramp_effect"][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: ff_ramp_effect::start_level", + ][::std::mem::offset_of!(ff_ramp_effect, start_level) - 0usize]; + [ + "Offset of field: ff_ramp_effect::end_level", + ][::std::mem::offset_of!(ff_ramp_effect, end_level) - 2usize]; + [ + "Offset of field: ff_ramp_effect::envelope", + ][::std::mem::offset_of!(ff_ramp_effect, envelope) - 4usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ff_condition_effect { + pub right_saturation: __u16, + pub left_saturation: __u16, + pub right_coeff: __s16, + pub left_coeff: __s16, + pub deadband: __u16, + pub center: __s16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of ff_condition_effect", + ][::std::mem::size_of::() - 12usize]; + [ + "Alignment of ff_condition_effect", + ][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: ff_condition_effect::right_saturation", + ][::std::mem::offset_of!(ff_condition_effect, right_saturation) - 0usize]; + [ + "Offset of field: ff_condition_effect::left_saturation", + ][::std::mem::offset_of!(ff_condition_effect, left_saturation) - 2usize]; + [ + "Offset of field: ff_condition_effect::right_coeff", + ][::std::mem::offset_of!(ff_condition_effect, right_coeff) - 4usize]; + [ + "Offset of field: ff_condition_effect::left_coeff", + ][::std::mem::offset_of!(ff_condition_effect, left_coeff) - 6usize]; + [ + "Offset of field: ff_condition_effect::deadband", + ][::std::mem::offset_of!(ff_condition_effect, deadband) - 8usize]; + [ + "Offset of field: ff_condition_effect::center", + ][::std::mem::offset_of!(ff_condition_effect, center) - 10usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ff_periodic_effect { + pub waveform: __u16, + pub period: __u16, + pub magnitude: __s16, + pub offset: __s16, + pub phase: __u16, + pub envelope: ff_envelope, + pub custom_len: __u32, + pub custom_data: *mut __s16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of ff_periodic_effect", + ][::std::mem::size_of::() - 32usize]; + [ + "Alignment of ff_periodic_effect", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: ff_periodic_effect::waveform", + ][::std::mem::offset_of!(ff_periodic_effect, waveform) - 0usize]; + [ + "Offset of field: ff_periodic_effect::period", + ][::std::mem::offset_of!(ff_periodic_effect, period) - 2usize]; + [ + "Offset of field: ff_periodic_effect::magnitude", + ][::std::mem::offset_of!(ff_periodic_effect, magnitude) - 4usize]; + [ + "Offset of field: ff_periodic_effect::offset", + ][::std::mem::offset_of!(ff_periodic_effect, offset) - 6usize]; + [ + "Offset of field: ff_periodic_effect::phase", + ][::std::mem::offset_of!(ff_periodic_effect, phase) - 8usize]; + [ + "Offset of field: ff_periodic_effect::envelope", + ][::std::mem::offset_of!(ff_periodic_effect, envelope) - 10usize]; + [ + "Offset of field: ff_periodic_effect::custom_len", + ][::std::mem::offset_of!(ff_periodic_effect, custom_len) - 20usize]; + [ + "Offset of field: ff_periodic_effect::custom_data", + ][::std::mem::offset_of!(ff_periodic_effect, custom_data) - 24usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ff_rumble_effect { + pub strong_magnitude: __u16, + pub weak_magnitude: __u16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ff_rumble_effect"][::std::mem::size_of::() - 4usize]; + [ + "Alignment of ff_rumble_effect", + ][::std::mem::align_of::() - 2usize]; + [ + "Offset of field: ff_rumble_effect::strong_magnitude", + ][::std::mem::offset_of!(ff_rumble_effect, strong_magnitude) - 0usize]; + [ + "Offset of field: ff_rumble_effect::weak_magnitude", + ][::std::mem::offset_of!(ff_rumble_effect, weak_magnitude) - 2usize]; +}; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct ff_effect { + pub type_: __u16, + pub id: __s16, + pub direction: __u16, + pub trigger: ff_trigger, + pub replay: ff_replay, + pub u: ff_effect__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union ff_effect__bindgen_ty_1 { + pub constant: ff_constant_effect, + pub ramp: ff_ramp_effect, + pub periodic: ff_periodic_effect, + pub condition: [ff_condition_effect; 2usize], + pub rumble: ff_rumble_effect, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of ff_effect__bindgen_ty_1", + ][::std::mem::size_of::() - 32usize]; + [ + "Alignment of ff_effect__bindgen_ty_1", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: ff_effect__bindgen_ty_1::constant", + ][::std::mem::offset_of!(ff_effect__bindgen_ty_1, constant) - 0usize]; + [ + "Offset of field: ff_effect__bindgen_ty_1::ramp", + ][::std::mem::offset_of!(ff_effect__bindgen_ty_1, ramp) - 0usize]; + [ + "Offset of field: ff_effect__bindgen_ty_1::periodic", + ][::std::mem::offset_of!(ff_effect__bindgen_ty_1, periodic) - 0usize]; + [ + "Offset of field: ff_effect__bindgen_ty_1::condition", + ][::std::mem::offset_of!(ff_effect__bindgen_ty_1, condition) - 0usize]; + [ + "Offset of field: ff_effect__bindgen_ty_1::rumble", + ][::std::mem::offset_of!(ff_effect__bindgen_ty_1, rumble) - 0usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ff_effect"][::std::mem::size_of::() - 48usize]; + ["Alignment of ff_effect"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: ff_effect::type_", + ][::std::mem::offset_of!(ff_effect, type_) - 0usize]; + ["Offset of field: ff_effect::id"][::std::mem::offset_of!(ff_effect, id) - 2usize]; + [ + "Offset of field: ff_effect::direction", + ][::std::mem::offset_of!(ff_effect, direction) - 4usize]; + [ + "Offset of field: ff_effect::trigger", + ][::std::mem::offset_of!(ff_effect, trigger) - 6usize]; + [ + "Offset of field: ff_effect::replay", + ][::std::mem::offset_of!(ff_effect, replay) - 10usize]; + ["Offset of field: ff_effect::u"][::std::mem::offset_of!(ff_effect, u) - 16usize]; +}; +pub type __gnuc_va_list = __BindgenOpaqueArray; +pub type va_list = __BindgenOpaqueArray; +///< Process data in sync mode +pub const libevdev_read_flag_LIBEVDEV_READ_FLAG_SYNC: libevdev_read_flag = 1; +///< Process data in normal mode +pub const libevdev_read_flag_LIBEVDEV_READ_FLAG_NORMAL: libevdev_read_flag = 2; +/**< Pretend the next event is a SYN_DROPPED and +require the caller to sync*/ +pub const libevdev_read_flag_LIBEVDEV_READ_FLAG_FORCE_SYNC: libevdev_read_flag = 4; +///< The fd is not in O_NONBLOCK and a read may block +pub const libevdev_read_flag_LIBEVDEV_READ_FLAG_BLOCKING: libevdev_read_flag = 8; +/// @ingroup events +pub type libevdev_read_flag = ::std::os::raw::c_uint; +unsafe extern "C" { + /** @ingroup init + + Initialize a new libevdev device. This function only allocates the + required memory and initializes the struct to sane default values. + To actually hook up the device to a kernel device, use + libevdev_set_fd(). + + Memory allocated through libevdev_new() must be released by the + caller with libevdev_free(). + + @see libevdev_set_fd + @see libevdev_free*/ + pub fn libevdev_new() -> *mut libevdev; +} +unsafe extern "C" { + /** @ingroup init + + Initialize a new libevdev device from the given fd. + + This is a shortcut for + + @code + int err; + struct libevdev *dev = libevdev_new(); + err = libevdev_set_fd(dev, fd); + @endcode + + @param fd A file descriptor to the device in O_RDWR or O_RDONLY mode. + @param[out] dev The newly initialized evdev device. + + @return On success, 0 is returned and dev is set to the newly + allocated struct. On failure, a negative errno is returned and the value + of dev is undefined. + + @see libevdev_free*/ + pub fn libevdev_new_from_fd( + fd: ::std::os::raw::c_int, + dev: *mut *mut libevdev, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup init + + Clean up and free the libevdev struct. After completion, the struct + libevdev is invalid and must not be used. + + Note that calling libevdev_free() does not close the file descriptor + currently associated with this instance. + + @param dev The evdev device + + @note This function may be called before libevdev_set_fd().*/ + pub fn libevdev_free(dev: *mut libevdev); +} +///< critical errors and application bugs +pub const libevdev_log_priority_LIBEVDEV_LOG_ERROR: libevdev_log_priority = 10; +///< informational messages +pub const libevdev_log_priority_LIBEVDEV_LOG_INFO: libevdev_log_priority = 20; +///< debug information +pub const libevdev_log_priority_LIBEVDEV_LOG_DEBUG: libevdev_log_priority = 30; +/// @ingroup logging +pub type libevdev_log_priority = ::std::os::raw::c_uint; +/** @ingroup logging + + Logging function called by library-internal logging. + This function is expected to treat its input like printf would. + + @param priority Log priority of this message + @param data User-supplied data pointer (see libevdev_set_log_function()) + @param file libevdev source code file generating this message + @param line libevdev source code line generating this message + @param func libevdev source code function generating this message + @param format printf-style format string + @param args List of arguments + + @see libevdev_set_log_function*/ +pub type libevdev_log_func_t = ::std::option::Option< + unsafe extern "C" fn( + priority: libevdev_log_priority, + data: *mut ::std::os::raw::c_void, + file: *const ::std::os::raw::c_char, + line: ::std::os::raw::c_int, + func: *const ::std::os::raw::c_char, + format: *const ::std::os::raw::c_char, + args: va_list, + ), +>; +unsafe extern "C" { + /** @ingroup logging + + Set a printf-style logging handler for library-internal logging. The default + logging function is to stdout. + + @note The global log handler is only called if no context-specific log + handler has been set with libevdev_set_device_log_function(). + + @param logfunc The logging function for this device. If NULL, the current + logging function is unset and no logging is performed. + @param data User-specific data passed to the log handler. + + @note This function may be called before libevdev_set_fd(). + + @deprecated Use per-context logging instead, see + libevdev_set_device_log_function().*/ + pub fn libevdev_set_log_function( + logfunc: libevdev_log_func_t, + data: *mut ::std::os::raw::c_void, + ); +} +unsafe extern "C" { + /** @ingroup logging + + Define the minimum level to be printed to the log handler. + Messages higher than this level are printed, others are discarded. This + is a global setting and applies to any future logging messages. + + @param priority Minimum priority to be printed to the log. + + @deprecated Use per-context logging instead, see + libevdev_set_device_log_function().*/ + pub fn libevdev_set_log_priority(priority: libevdev_log_priority); +} +unsafe extern "C" { + /** @ingroup logging + + Return the current log priority level. Messages higher than this level + are printed, others are discarded. This is a global setting. + + @return the current log level + + @deprecated Use per-context logging instead, see + libevdev_set_device_log_function().*/ + pub fn libevdev_get_log_priority() -> libevdev_log_priority; +} +/** @ingroup logging + + Logging function called by library-internal logging for a specific + libevdev context. This function is expected to treat its input like + printf would. + + @param dev The evdev device + @param priority Log priority of this message + @param data User-supplied data pointer (see libevdev_set_log_function()) + @param file libevdev source code file generating this message + @param line libevdev source code line generating this message + @param func libevdev source code function generating this message + @param format printf-style format string + @param args List of arguments + + @see libevdev_set_log_function + @since 1.3*/ +pub type libevdev_device_log_func_t = ::std::option::Option< + unsafe extern "C" fn( + dev: *const libevdev, + priority: libevdev_log_priority, + data: *mut ::std::os::raw::c_void, + file: *const ::std::os::raw::c_char, + line: ::std::os::raw::c_int, + func: *const ::std::os::raw::c_char, + format: *const ::std::os::raw::c_char, + args: va_list, + ), +>; +unsafe extern "C" { + /** @ingroup logging + + Set a printf-style logging handler for library-internal logging for this + device context. The default logging function is NULL, i.e. the global log + handler is invoked. If a context-specific log handler is set, the global + log handler is not invoked for this device. + + @note This log function applies for this device context only, even if + another context exists for the same fd. + + @param dev The evdev device + @param logfunc The logging function for this device. If NULL, the current + logging function is unset and logging falls back to the global log + handler, if any. + @param priority Minimum priority to be printed to the log. + @param data User-specific data passed to the log handler. + + @note This function may be called before libevdev_set_fd(). + @since 1.3*/ + pub fn libevdev_set_device_log_function( + dev: *mut libevdev, + logfunc: libevdev_device_log_func_t, + priority: libevdev_log_priority, + data: *mut ::std::os::raw::c_void, + ); +} +///< Grab the device if not currently grabbed +pub const libevdev_grab_mode_LIBEVDEV_GRAB: libevdev_grab_mode = 3; +///< Ungrab the device if currently grabbed +pub const libevdev_grab_mode_LIBEVDEV_UNGRAB: libevdev_grab_mode = 4; +/// @ingroup init +pub type libevdev_grab_mode = ::std::os::raw::c_uint; +unsafe extern "C" { + /** @ingroup init + + Grab or ungrab the device through a kernel EVIOCGRAB. This prevents other + clients (including kernel-internal ones such as rfkill) from receiving + events from this device. + + This is generally a bad idea. Don't do this. + + Grabbing an already grabbed device, or ungrabbing an ungrabbed device is + a noop and always succeeds. + + A grab is an operation tied to a file descriptor, not a device. If a + client changes the file descriptor with libevdev_change_fd(), it must + also re-issue a grab with libevdev_grab(). + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param grab If true, grab the device. Otherwise ungrab the device. + + @return 0 if the device was successfully grabbed or ungrabbed, or a + negative errno in case of failure.*/ + pub fn libevdev_grab( + dev: *mut libevdev, + grab: libevdev_grab_mode, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup init + + Set the fd for this struct and initialize internal data. + The fd must be in O_RDONLY or O_RDWR mode. + + This function may only be called once per device. If the device changed and + you need to re-read a device, use libevdev_free() and libevdev_new(). If + you need to change the fd after closing and re-opening the same device, use + libevdev_change_fd(). + + A caller should ensure that any events currently pending on the fd are + drained before the file descriptor is passed to libevdev for + initialization. Due to how the kernel's ioctl handling works, the initial + device state will reflect the current device state *after* applying all + events currently pending on the fd. Thus, if the fd is not drained, the + state visible to the caller will be inconsistent with the events + immediately available on the device. This does not affect state-less + events like EV_REL. + + Unless otherwise specified, libevdev function behavior is undefined until + a successful call to libevdev_set_fd(). + + @param dev The evdev device + @param fd The file descriptor for the device + + @return 0 on success, or a negative errno on failure + + @see libevdev_change_fd + @see libevdev_new + @see libevdev_free*/ + pub fn libevdev_set_fd( + dev: *mut libevdev, + fd: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup init + + Change the fd for this device, without re-reading the actual device. If the fd + changes after initializing the device, for example after a VT-switch in the + X.org X server, this function updates the internal fd to the newly opened. + No check is made that new fd points to the same device. If the device has + changed, libevdev's behavior is undefined. + + libevdev does not sync itself after changing the fd and keeps the current + device state. Use libevdev_next_event with the + @ref LIBEVDEV_READ_FLAG_FORCE_SYNC flag to force a re-sync. + + The example code below illustrates how to force a re-sync of the + library-internal state. Note that this code doesn't handle the events in + the caller, it merely forces an update of the internal library state. + @code + struct input_event ev; + libevdev_change_fd(dev, new_fd); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + while (libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev) == LIBEVDEV_READ_STATUS_SYNC) + ; // noop + @endcode + + The fd may be open in O_RDONLY or O_RDWR. + + After changing the fd, the device is assumed ungrabbed and a caller must + call libevdev_grab() again. + + It is an error to call this function before calling libevdev_set_fd(). + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param fd The new fd + + @return 0 on success, or -1 on failure. + + @see libevdev_set_fd*/ + pub fn libevdev_change_fd( + dev: *mut libevdev, + fd: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup init + + @param dev The evdev device + + @return The previously set fd, or -1 if none had been set previously. + @note This function may be called before libevdev_set_fd().*/ + pub fn libevdev_get_fd(dev: *const libevdev) -> ::std::os::raw::c_int; +} +/** libevdev_next_event() has finished without an error + and an event is available for processing. + + @see libevdev_next_event*/ +pub const libevdev_read_status_LIBEVDEV_READ_STATUS_SUCCESS: libevdev_read_status = 0; +/** Depending on the libevdev_next_event() read flag: + * libevdev received a SYN_DROPPED from the device, and the caller should + now resync the device, or, + * an event has been read in sync mode. + + @see libevdev_next_event*/ +pub const libevdev_read_status_LIBEVDEV_READ_STATUS_SYNC: libevdev_read_status = 1; +/// @ingroup events +pub type libevdev_read_status = ::std::os::raw::c_uint; +unsafe extern "C" { + /** @ingroup events + + Get the next event from the device. This function operates in two different + modes: normal mode or sync mode. + + In normal mode (when flags has @ref LIBEVDEV_READ_FLAG_NORMAL set), this + function returns @ref LIBEVDEV_READ_STATUS_SUCCESS and returns the event + in the argument @p ev. If no events are available at this + time, it returns -EAGAIN and ev is undefined. + + If the current event is an EV_SYN SYN_DROPPED event, this function returns + @ref LIBEVDEV_READ_STATUS_SYNC and ev is set to the EV_SYN event. + The caller should now call this function with the + @ref LIBEVDEV_READ_FLAG_SYNC flag set, to get the set of events that make up the + device state delta. This function returns @ref LIBEVDEV_READ_STATUS_SYNC for + each event part of that delta, until it returns -EAGAIN once all events + have been synced. For more details on what libevdev does to sync after a + SYN_DROPPED event, see @ref syn_dropped. + + If a device needs to be synced by the caller but the caller does not call + with the @ref LIBEVDEV_READ_FLAG_SYNC flag set, all events from the diff are + dropped after libevdev updates its internal state and event processing + continues as normal. Note that the current slot and the state of touch + points may have updated during the SYN_DROPPED event, it is strongly + recommended that a caller ignoring all sync events calls + libevdev_get_current_slot() and checks the ABS_MT_TRACKING_ID values for + all slots. + + If a device has changed state without events being enqueued in libevdev, + e.g. after changing the file descriptor, use the @ref + LIBEVDEV_READ_FLAG_FORCE_SYNC flag. This triggers an internal sync of the + device and libevdev_next_event() returns @ref LIBEVDEV_READ_STATUS_SYNC. + Any state changes are available as events as described above. If + @ref LIBEVDEV_READ_FLAG_FORCE_SYNC is set, the value of ev is undefined. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param flags Set of flags to determine behaviour. If @ref LIBEVDEV_READ_FLAG_NORMAL + is set, the next event is read in normal mode. If @ref LIBEVDEV_READ_FLAG_SYNC is + set, the next event is read in sync mode. + @param ev On success, set to the current event. + @return On failure, a negative errno is returned. + @retval LIBEVDEV_READ_STATUS_SUCCESS One or more events were read of the + device and ev points to the next event in the queue + @retval -EAGAIN No events are currently available on the device + @retval LIBEVDEV_READ_STATUS_SYNC A SYN_DROPPED event was received, or a + synced event was returned and ev points to the SYN_DROPPED event + + @note This function is signal-safe.*/ + pub fn libevdev_next_event( + dev: *mut libevdev, + flags: ::std::os::raw::c_uint, + ev: *mut input_event, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup events + + Check if there are events waiting for us. This function does not read an + event off the fd and may not access the fd at all. If there are events + queued internally this function will return non-zero. If the internal + queue is empty, this function will poll the file descriptor for data. + + This is a convenience function for simple processes, most complex programs + are expected to use select(2) or poll(2) on the file descriptor. The kernel + guarantees that if data is available, it is a multiple of sizeof(struct + input_event), and thus calling libevdev_next_event() when select(2) or + poll(2) return is safe. You do not need libevdev_has_event_pending() if + you're using select(2) or poll(2). + + @param dev The evdev device, already initialized with libevdev_set_fd() + @return On failure, a negative errno is returned. + @retval 0 No event is currently available + @retval 1 One or more events are available on the fd + + @note This function is signal-safe.*/ + pub fn libevdev_has_event_pending(dev: *mut libevdev) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + Retrieve the device's name, either as set by the caller or as read from + the kernel. The string returned is valid until libevdev_free() or until + libevdev_set_name(), whichever comes earlier. + + @param dev The evdev device, already initialized with libevdev_set_fd() + + @return The device name as read off the kernel device. The name is never + NULL but it may be the empty string. + + @note This function is signal-safe.*/ + pub fn libevdev_get_name(dev: *const libevdev) -> *const ::std::os::raw::c_char; +} +unsafe extern "C" { + /** @ingroup kernel + + Change the device's name as returned by libevdev_get_name(). This + function destroys the string previously returned by libevdev_get_name(), + a caller must take care that no references are kept. + + @param dev The evdev device + @param name The new, non-NULL, name to assign to this device. + + @note This function may be called before libevdev_set_fd(). A call to + libevdev_set_fd() will overwrite any previously set value.*/ + pub fn libevdev_set_name(dev: *mut libevdev, name: *const ::std::os::raw::c_char); +} +unsafe extern "C" { + /** @ingroup bits + + Retrieve the device's physical location, either as set by the caller or + as read from the kernel. The string returned is valid until + libevdev_free() or until libevdev_set_phys(), whichever comes earlier. + + Virtual devices such as uinput devices have no phys location. + + @param dev The evdev device, already initialized with libevdev_set_fd() + + @return The physical location of this device, or NULL if there is none + + @note This function is signal safe.*/ + pub fn libevdev_get_phys(dev: *const libevdev) -> *const ::std::os::raw::c_char; +} +unsafe extern "C" { + /** @ingroup kernel + + Change the device's physical location as returned by libevdev_get_phys(). + This function destroys the string previously returned by + libevdev_get_phys(), a caller must take care that no references are kept. + + @param dev The evdev device + @param phys The new phys to assign to this device. + + @note This function may be called before libevdev_set_fd(). A call to + libevdev_set_fd() will overwrite any previously set value.*/ + pub fn libevdev_set_phys(dev: *mut libevdev, phys: *const ::std::os::raw::c_char); +} +unsafe extern "C" { + /** @ingroup bits + + Retrieve the device's unique identifier, either as set by the caller or + as read from the kernel. The string returned is valid until + libevdev_free() or until libevdev_set_uniq(), whichever comes earlier. + + @param dev The evdev device, already initialized with libevdev_set_fd() + + @return The unique identifier for this device, or NULL if there is none + + @note This function is signal safe.*/ + pub fn libevdev_get_uniq(dev: *const libevdev) -> *const ::std::os::raw::c_char; +} +unsafe extern "C" { + /** @ingroup kernel + + Change the device's unique identifier as returned by libevdev_get_uniq(). + This function destroys the string previously returned by + libevdev_get_uniq(), a caller must take care that no references are kept. + + @param dev The evdev device + @param uniq The new uniq to assign to this device. + + @note This function may be called before libevdev_set_fd(). A call to + libevdev_set_fd() will overwrite any previously set value.*/ + pub fn libevdev_set_uniq(dev: *mut libevdev, uniq: *const ::std::os::raw::c_char); +} +unsafe extern "C" { + /** @ingroup bits + + @param dev The evdev device, already initialized with libevdev_set_fd() + + @return The device's product ID + + @note This function is signal-safe.*/ + pub fn libevdev_get_id_product(dev: *const libevdev) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + @param dev The evdev device + @param product_id The product ID to assign to this device + + @note This function may be called before libevdev_set_fd(). A call to + libevdev_set_fd() will overwrite any previously set value. Even though + the function accepts an int for product_id the value is truncated at 16 + bits.*/ + pub fn libevdev_set_id_product( + dev: *mut libevdev, + product_id: ::std::os::raw::c_int, + ); +} +unsafe extern "C" { + /** @ingroup bits + + @param dev The evdev device, already initialized with libevdev_set_fd() + + @return The device's vendor ID + + @note This function is signal-safe.*/ + pub fn libevdev_get_id_vendor(dev: *const libevdev) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + @param dev The evdev device + @param vendor_id The vendor ID to assign to this device + + @note This function may be called before libevdev_set_fd(). A call to + libevdev_set_fd() will overwrite any previously set value. Even though + the function accepts an int for vendor_id the value is truncated at 16 + bits.*/ + pub fn libevdev_set_id_vendor(dev: *mut libevdev, vendor_id: ::std::os::raw::c_int); +} +unsafe extern "C" { + /** @ingroup bits + + @param dev The evdev device, already initialized with libevdev_set_fd() + + @return The device's bus type + + @note This function is signal-safe.*/ + pub fn libevdev_get_id_bustype(dev: *const libevdev) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + @param dev The evdev device + @param bustype The bustype to assign to this device + + @note This function may be called before libevdev_set_fd(). A call to + libevdev_set_fd() will overwrite any previously set value. Even though + the function accepts an int for bustype the value is truncated at 16 + bits.*/ + pub fn libevdev_set_id_bustype(dev: *mut libevdev, bustype: ::std::os::raw::c_int); +} +unsafe extern "C" { + /** @ingroup bits + + @param dev The evdev device, already initialized with libevdev_set_fd() + + @return The device's firmware version + + @note This function is signal-safe.*/ + pub fn libevdev_get_id_version(dev: *const libevdev) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + @param dev The evdev device + @param version The version to assign to this device + + @note This function may be called before libevdev_set_fd(). A call to + libevdev_set_fd() will overwrite any previously set value. Even though + the function accepts an int for version the value is truncated at 16 + bits.*/ + pub fn libevdev_set_id_version(dev: *mut libevdev, version: ::std::os::raw::c_int); +} +unsafe extern "C" { + /** @ingroup bits + + @param dev The evdev device, already initialized with libevdev_set_fd() + + @return The driver version for this device + + @note This function is signal-safe.*/ + pub fn libevdev_get_driver_version(dev: *const libevdev) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param prop The input property to query for, one of INPUT_PROP_... + + @return 1 if the device provides this input property, or 0 otherwise. + + @note This function is signal-safe*/ + pub fn libevdev_has_property( + dev: *const libevdev, + prop: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + @param dev The evdev device + @param prop The input property to enable, one of INPUT_PROP_... + + @return 0 on success or -1 on failure + + @note This function may be called before libevdev_set_fd(). A call to + libevdev_set_fd() will overwrite any previously set value.*/ + pub fn libevdev_enable_property( + dev: *mut libevdev, + prop: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + @param dev The evdev device + @param prop The input property to disable, one of INPUT_PROP_... + + @return 0 on success or -1 on failure*/ + pub fn libevdev_disable_property( + dev: *mut libevdev, + prop: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param type The event type to query for, one of EV_SYN, EV_REL, etc. + + @return 1 if the device supports this event type, or 0 otherwise. + + @note This function is signal-safe.*/ + pub fn libevdev_has_event_type( + dev: *const libevdev, + type_: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + @param code The event code to query for, one of ABS_X, REL_X, etc. + + @return 1 if the device supports this event type and code, or 0 otherwise. + + @note This function is signal-safe.*/ + pub fn libevdev_has_event_code( + dev: *const libevdev, + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + Get the minimum axis value for the given axis, as advertised by the kernel. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + + @return axis minimum for the given axis or 0 if the axis is invalid + + @note This function is signal-safe.*/ + pub fn libevdev_get_abs_minimum( + dev: *const libevdev, + code: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + Get the maximum axis value for the given axis, as advertised by the kernel. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + + @return axis maximum for the given axis or 0 if the axis is invalid + + @note This function is signal-safe.*/ + pub fn libevdev_get_abs_maximum( + dev: *const libevdev, + code: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + Get the axis fuzz for the given axis, as advertised by the kernel. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + + @return axis fuzz for the given axis or 0 if the axis is invalid + + @note This function is signal-safe.*/ + pub fn libevdev_get_abs_fuzz( + dev: *const libevdev, + code: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + Get the axis flat for the given axis, as advertised by the kernel. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + + @return axis flat for the given axis or 0 if the axis is invalid + + @note This function is signal-safe.*/ + pub fn libevdev_get_abs_flat( + dev: *const libevdev, + code: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + Get the axis resolution for the given axis, as advertised by the kernel. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + + @return axis resolution for the given axis or 0 if the axis is invalid + + @note This function is signal-safe.*/ + pub fn libevdev_get_abs_resolution( + dev: *const libevdev, + code: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + Get the axis info for the given axis, as advertised by the kernel. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + + @return The input_absinfo for the given code, or NULL if the device does + not support this event code. + + @note This function is signal-safe.*/ + pub fn libevdev_get_abs_info( + dev: *const libevdev, + code: ::std::os::raw::c_uint, + ) -> *const input_absinfo; +} +unsafe extern "C" { + /** @ingroup bits + + Behaviour of this function is undefined if the device does not provide + the event. + + If the device supports ABS_MT_SLOT, the value returned for any ABS_MT_* + event code is undefined. Use libevdev_get_slot_value() instead. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + @param code The event code to query for, one of ABS_X, REL_X, etc. + + @return The current value of the event. + + @note This function is signal-safe. + @note The value for ABS_MT_ events is undefined, use + libevdev_get_slot_value() instead + + @see libevdev_get_slot_value*/ + pub fn libevdev_get_event_value( + dev: *const libevdev, + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + Set the value for a given event type and code. This only makes sense for + some event types, e.g. setting the value for EV_REL is pointless. + + This is a local modification only affecting only this representation of + this device. A future call to libevdev_get_event_value() will return this + value, unless the value was overwritten by an event. + + If the device supports ABS_MT_SLOT, the value set for any ABS_MT_* + event code is the value of the currently active slot. You should use + libevdev_set_slot_value() instead. + + If the device supports ABS_MT_SLOT and the type is EV_ABS and the code is + ABS_MT_SLOT, the value must be a positive number less then the number of + slots on the device. Otherwise, libevdev_set_event_value() returns -1. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + @param code The event code to set the value for, one of ABS_X, LED_NUML, etc. + @param value The new value to set + + @return 0 on success, or -1 on failure. + @retval -1 + - the device does not have the event type or + - code enabled, or the code is outside the, or + - the code is outside the allowed limits for the given type, or + - the type cannot be set, or + - the value is not permitted for the given code. + + @see libevdev_set_slot_value + @see libevdev_get_event_value*/ + pub fn libevdev_set_event_value( + dev: *mut libevdev, + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + value: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + Fetch the current value of the event type. This is a shortcut for + + @code + if (libevdev_has_event_type(dev, t) && libevdev_has_event_code(dev, t, c)) + val = libevdev_get_event_value(dev, t, c); + @endcode + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + @param code The event code to query for, one of ABS_X, REL_X, etc. + @param[out] value The current value of this axis returned. + + @return If the device supports this event type and code, the return value is + non-zero and value is set to the current value of this axis. Otherwise, + 0 is returned and value is unmodified. + + @note This function is signal-safe. + @note The value for ABS_MT_ events is undefined, use + libevdev_fetch_slot_value() instead + + @see libevdev_fetch_slot_value*/ + pub fn libevdev_fetch_event_value( + dev: *const libevdev, + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + value: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup mt + + Return the current value of the code for the given slot. + + The return value is undefined for a slot exceeding the available slots on + the device, for a code that is not in the permitted ABS_MT range or for a + device that does not have slots. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param slot The numerical slot number, must be smaller than the total number + of slots on this device + @param code The event code to query for, one of ABS_MT_POSITION_X, etc. + + @note This function is signal-safe. + @note The value for events other than ABS_MT_ is undefined, use + libevdev_fetch_value() instead + + @see libevdev_get_event_value*/ + pub fn libevdev_get_slot_value( + dev: *const libevdev, + slot: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + Set the value for a given code for the given slot. + + This is a local modification only affecting only this representation of + this device. A future call to libevdev_get_slot_value() will return this + value, unless the value was overwritten by an event. + + This function does not set event values for axes outside the ABS_MT range, + use libevdev_set_event_value() instead. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param slot The numerical slot number, must be smaller than the total number + of slots on this device + @param code The event code to set the value for, one of ABS_MT_POSITION_X, etc. + @param value The new value to set + + @return 0 on success, or -1 on failure. + @retval -1 + - the device does not have the event code enabled, or + - the code is outside the allowed limits for multitouch events, or + - the slot number is outside the limits for this device, or + - the device does not support multitouch events. + + @see libevdev_set_event_value + @see libevdev_get_slot_value*/ + pub fn libevdev_set_slot_value( + dev: *mut libevdev, + slot: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + value: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup mt + + Fetch the current value of the code for the given slot. This is a shortcut for + + @code + if (libevdev_has_event_type(dev, EV_ABS) && + libevdev_has_event_code(dev, EV_ABS, c) && + slot < device->number_of_slots) + val = libevdev_get_slot_value(dev, slot, c); + @endcode + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param slot The numerical slot number, must be smaller than the total number + of slots on this * device + @param[out] value The current value of this axis returned. + + @param code The event code to query for, one of ABS_MT_POSITION_X, etc. + @return If the device supports this event code, the return value is + non-zero and value is set to the current value of this axis. Otherwise, or + if the event code is not an ABS_MT_* event code, 0 is returned and value + is unmodified. + + @note This function is signal-safe.*/ + pub fn libevdev_fetch_slot_value( + dev: *const libevdev, + slot: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + value: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup mt + + Get the number of slots supported by this device. + + @param dev The evdev device, already initialized with libevdev_set_fd() + + @return The number of slots supported, or -1 if the device does not provide + any slots + + @note A device may provide ABS_MT_SLOT but a total number of 0 slots. Hence + the return value of -1 for "device does not provide slots at all"*/ + pub fn libevdev_get_num_slots(dev: *const libevdev) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup mt + + Get the currently active slot. This may differ from the value + an ioctl may return at this time as events may have been read off the fd + since changing the slot value but those events are still in the buffer + waiting to be processed. The returned value is the value a caller would + see if it were to process events manually one-by-one. + + @param dev The evdev device, already initialized with libevdev_set_fd() + + @return the currently active slot (logically) + + @note This function is signal-safe.*/ + pub fn libevdev_get_current_slot(dev: *const libevdev) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + Change the minimum for the given EV_ABS event code, if the code exists. + This function has no effect if libevdev_has_event_code() returns false for + this code. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code One of ABS_X, ABS_Y, ... + @param val The new minimum for this axis*/ + pub fn libevdev_set_abs_minimum( + dev: *mut libevdev, + code: ::std::os::raw::c_uint, + val: ::std::os::raw::c_int, + ); +} +unsafe extern "C" { + /** @ingroup kernel + + Change the maximum for the given EV_ABS event code, if the code exists. + This function has no effect if libevdev_has_event_code() returns false for + this code. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code One of ABS_X, ABS_Y, ... + @param val The new maxium for this axis*/ + pub fn libevdev_set_abs_maximum( + dev: *mut libevdev, + code: ::std::os::raw::c_uint, + val: ::std::os::raw::c_int, + ); +} +unsafe extern "C" { + /** @ingroup kernel + + Change the fuzz for the given EV_ABS event code, if the code exists. + This function has no effect if libevdev_has_event_code() returns false for + this code. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code One of ABS_X, ABS_Y, ... + @param val The new fuzz for this axis*/ + pub fn libevdev_set_abs_fuzz( + dev: *mut libevdev, + code: ::std::os::raw::c_uint, + val: ::std::os::raw::c_int, + ); +} +unsafe extern "C" { + /** @ingroup kernel + + Change the flat for the given EV_ABS event code, if the code exists. + This function has no effect if libevdev_has_event_code() returns false for + this code. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code One of ABS_X, ABS_Y, ... + @param val The new flat for this axis*/ + pub fn libevdev_set_abs_flat( + dev: *mut libevdev, + code: ::std::os::raw::c_uint, + val: ::std::os::raw::c_int, + ); +} +unsafe extern "C" { + /** @ingroup kernel + + Change the resolution for the given EV_ABS event code, if the code exists. + This function has no effect if libevdev_has_event_code() returns false for + this code. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code One of ABS_X, ABS_Y, ... + @param val The new axis resolution*/ + pub fn libevdev_set_abs_resolution( + dev: *mut libevdev, + code: ::std::os::raw::c_uint, + val: ::std::os::raw::c_int, + ); +} +unsafe extern "C" { + /** @ingroup kernel + + Change the abs info for the given EV_ABS event code, if the code exists. + This function has no effect if libevdev_has_event_code() returns false for + this code. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code One of ABS_X, ABS_Y, ... + @param abs The new absolute axis data (min, max, fuzz, flat, resolution)*/ + pub fn libevdev_set_abs_info( + dev: *mut libevdev, + code: ::std::os::raw::c_uint, + abs: *const input_absinfo, + ); +} +unsafe extern "C" { + /** @ingroup kernel + + Forcibly enable an event type on this device, even if the underlying + device does not support it. While this cannot make the device actually + report such events, it will now return true for libevdev_has_event_type(). + + This is a local modification only affecting only this representation of + this device. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param type The event type to enable (EV_ABS, EV_KEY, ...) + + @return 0 on success or -1 otherwise + + @see libevdev_has_event_type*/ + pub fn libevdev_enable_event_type( + dev: *mut libevdev, + type_: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + Forcibly disable an event type on this device, even if the underlying + device provides it. This effectively mutes the respective set of + events. libevdev will filter any events matching this type and none will + reach the caller. libevdev_has_event_type() will return false for this + type. + + In most cases, a caller likely only wants to disable a single code, not + the whole type. Use libevdev_disable_event_code() for that. + + Disabling EV_SYN will not work. Don't shoot yourself in the foot. + It hurts. + + This is a local modification only affecting only this representation of + this device. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param type The event type to disable (EV_ABS, EV_KEY, ...) + + @return 0 on success or -1 otherwise + + @see libevdev_has_event_type + @see libevdev_disable_event_type*/ + pub fn libevdev_disable_event_type( + dev: *mut libevdev, + type_: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + Forcibly enable an event code on this device, even if the underlying + device does not support it. While this cannot make the device actually + report such events, it will now return true for libevdev_has_event_code(). + + The last argument depends on the type and code: + - If type is EV_ABS, data must be a pointer to a struct input_absinfo + containing the data for this axis. + - If type is EV_REP, data must be a pointer to a int containing the data + for this axis + - For all other types, the argument must be NULL. + + This function calls libevdev_enable_event_type() if necessary. + + This is a local modification only affecting only this representation of + this device. + + If this function is called with a type of EV_ABS and EV_REP on a device + that already has the given event code enabled, the values in data + overwrite the previous values. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param type The event type to enable (EV_ABS, EV_KEY, ...) + @param code The event code to enable (ABS_X, REL_X, etc.) + @param data If type is EV_ABS, data points to a struct input_absinfo. If type is EV_REP, data + points to an integer. Otherwise, data must be NULL. + + @return 0 on success or -1 otherwise + + @see libevdev_enable_event_type*/ + pub fn libevdev_enable_event_code( + dev: *mut libevdev, + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + data: *const ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + Forcibly disable an event code on this device, even if the underlying + device provides it. This effectively mutes the respective set of + events. libevdev will filter any events matching this type and code and + none will reach the caller. libevdev_has_event_code() will return false for + this code. + + Disabling all event codes for a given type will not disable the event + type. Use libevdev_disable_event_type() for that. + + This is a local modification only affecting only this representation of + this device. + + Disabling codes of type EV_SYN will not work. Don't shoot yourself in the + foot. It hurts. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param type The event type to disable (EV_ABS, EV_KEY, ...) + @param code The event code to disable (ABS_X, REL_X, etc.) + + @return 0 on success or -1 otherwise + + @see libevdev_has_event_code + @see libevdev_disable_event_type*/ + pub fn libevdev_disable_event_code( + dev: *mut libevdev, + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + Set the device's EV_ABS axis to the value defined in the abs + parameter. This will be written to the kernel. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code The EV_ABS event code to modify, one of ABS_X, ABS_Y, etc. + @param abs Axis info to set the kernel axis to + + @return 0 on success, or a negative errno on failure + + @see libevdev_enable_event_code*/ + pub fn libevdev_kernel_set_abs_info( + dev: *mut libevdev, + code: ::std::os::raw::c_uint, + abs: *const input_absinfo, + ) -> ::std::os::raw::c_int; +} +///< Turn the LED on +pub const libevdev_led_value_LIBEVDEV_LED_ON: libevdev_led_value = 3; +///< Turn the LED off +pub const libevdev_led_value_LIBEVDEV_LED_OFF: libevdev_led_value = 4; +/// @ingroup kernel +pub type libevdev_led_value = ::std::os::raw::c_uint; +unsafe extern "C" { + /** @ingroup kernel + + Turn an LED on or off. Convenience function, if you need to modify multiple + LEDs simultaneously, use libevdev_kernel_set_led_values() instead. + + @note enabling an LED requires write permissions on the device's file descriptor. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param code The EV_LED event code to modify, one of LED_NUML, LED_CAPSL, ... + @param value Specifies whether to turn the LED on or off + @return 0 on success, or a negative errno on failure*/ + pub fn libevdev_kernel_set_led_value( + dev: *mut libevdev, + code: ::std::os::raw::c_uint, + value: libevdev_led_value, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + Turn multiple LEDs on or off simultaneously. This function expects a pair + of LED codes and values to set them to, terminated by a -1. For example, to + switch the NumLock LED on but the CapsLock LED off, use: + + @code + libevdev_kernel_set_led_values(dev, LED_NUML, LIBEVDEV_LED_ON, + LED_CAPSL, LIBEVDEV_LED_OFF, + -1); + @endcode + + If any LED code or value is invalid, this function returns -EINVAL and no + LEDs are modified. + + @note enabling an LED requires write permissions on the device's file descriptor. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param ... A pair of LED_* event codes and libevdev_led_value_t, followed by + -1 to terminate the list. + @return 0 on success, or a negative errno on failure*/ + pub fn libevdev_kernel_set_led_values( + dev: *mut libevdev, + ... + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup kernel + + Set the clock ID to be used for timestamps. Further events from this device + will report an event time based on the given clock. + + This is a modification only affecting this representation of + this device. + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param clockid The clock to use for future events. Permitted values + are CLOCK_MONOTONIC and CLOCK_REALTIME (the default). + @return 0 on success, or a negative errno on failure*/ + pub fn libevdev_set_clock_id( + dev: *mut libevdev, + clockid: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Helper function to check if an event is of a specific type. This is + virtually the same as: + + ev->type == type + + with the exception that some sanity checks are performed to ensure type + is valid. + + @note The ranges for types are compiled into libevdev. If the kernel + changes the max value, libevdev will not automatically pick these up. + + @param ev The input event to check + @param type Input event type to compare the event against (EV_REL, EV_ABS, + etc.) + + @return 1 if the event type matches the given type, 0 otherwise (or if + type is invalid)*/ + pub fn libevdev_event_is_type( + ev: *const input_event, + type_: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Helper function to check if an event is of a specific type and code. This + is virtually the same as: + + ev->type == type && ev->code == code + + with the exception that some sanity checks are performed to ensure type and + code are valid. + + @note The ranges for types and codes are compiled into libevdev. If the kernel + changes the max value, libevdev will not automatically pick these up. + + @param ev The input event to check + @param type Input event type to compare the event against (EV_REL, EV_ABS, + etc.) + @param code Input event code to compare the event against (ABS_X, REL_X, + etc.) + + @return 1 if the event type matches the given type and code, 0 otherwise + (or if type/code are invalid)*/ + pub fn libevdev_event_is_code( + ev: *const input_event, + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + @param type The event type to return the name for. + + @return The name of the given event type (e.g. EV_ABS) or NULL for an + invalid type + + @note The list of names is compiled into libevdev. If the kernel adds new + defines for new event types, libevdev will not automatically pick these up.*/ + pub fn libevdev_event_type_get_name( + type_: ::std::os::raw::c_uint, + ) -> *const ::std::os::raw::c_char; +} +unsafe extern "C" { + /** @ingroup misc + + @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + @param code The event code to return the name for (e.g. ABS_X) + + @return The name of the given event code (e.g. ABS_X) or NULL for an + invalid type or code + + @note The list of names is compiled into libevdev. If the kernel adds new + defines for new event codes, libevdev will not automatically pick these up.*/ + pub fn libevdev_event_code_get_name( + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + ) -> *const ::std::os::raw::c_char; +} +unsafe extern "C" { + /** @ingroup misc + + This function resolves the event value for a code. + + For almost all event codes this will return NULL as the value is just a + numerical value. As of kernel 4.17, the only event code that will return + a non-NULL value is EV_ABS/ABS_MT_TOOL_TYPE. + + @param type The event type for the value to query (EV_ABS, etc.) + @param code The event code for the value to query (e.g. ABS_MT_TOOL_TYPE) + @param value The event value to return the name for (e.g. MT_TOOL_PALM) + + @return The name of the given event value (e.g. MT_TOOL_PALM) or NULL for + an invalid type or code or NULL for an axis that has numerical values + only. + + @note The list of names is compiled into libevdev. If the kernel adds new + defines for new event values, libevdev will not automatically pick these up.*/ + pub fn libevdev_event_value_get_name( + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + value: ::std::os::raw::c_int, + ) -> *const ::std::os::raw::c_char; +} +unsafe extern "C" { + /** @ingroup misc + + @param prop The input prop to return the name for (e.g. INPUT_PROP_BUTTONPAD) + + @return The name of the given input prop (e.g. INPUT_PROP_BUTTONPAD) or NULL for an + invalid property + + @note The list of names is compiled into libevdev. If the kernel adds new + defines for new properties libevdev will not automatically pick these up. + @note On older kernels input properties may not be defined and + libevdev_property_get_name() will always return NULL*/ + pub fn libevdev_property_get_name( + prop: ::std::os::raw::c_uint, + ) -> *const ::std::os::raw::c_char; +} +unsafe extern "C" { + /** @ingroup misc + + @param type The event type to return the maximum for (EV_ABS, EV_REL, etc.). No max is defined for + EV_SYN. + + @return The max value defined for the given event type, e.g. ABS_MAX for a type of EV_ABS, or -1 + for an invalid type. + + @note The max value is compiled into libevdev. If the kernel changes the + max value, libevdev will not automatically pick these up.*/ + pub fn libevdev_event_type_get_max( + type_: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an event-type by its name. Event-types start with "EV_" followed by + the name (eg., "EV_ABS"). The "EV_" prefix must be included in the name. It + returns the constant assigned to the event-type or -1 if not found. + + @param name A non-NULL string describing an input-event type ("EV_KEY", + "EV_ABS", ...), zero-terminated. + + @return The given type constant for the passed name or -1 if not found. + + @note EV_MAX is also recognized.*/ + pub fn libevdev_event_type_from_name( + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an event-type by its name. Event-types start with "EV_" followed by + the name (eg., "EV_ABS"). The "EV_" prefix must be included in the name. It + returns the constant assigned to the event-type or -1 if not found. + + @param name A non-NULL string describing an input-event type ("EV_KEY", + "EV_ABS", ...). + @param len The length of the passed string excluding any terminating 0 + character. + + @return The given type constant for the passed name or -1 if not found. + + @note EV_MAX is also recognized.*/ + pub fn libevdev_event_type_from_name_n( + name: *const ::std::os::raw::c_char, + len: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an event code by its type and name. Event codes start with a fixed + prefix followed by their name (eg., "ABS_X"). The prefix must be included in + the name. It returns the constant assigned to the event code or -1 if not + found. + + You have to pass the event type where to look for the name. For instance, to + resolve "ABS_X" you need to pass EV_ABS as type and "ABS_X" as string. + Supported event codes are codes starting with SYN_, KEY_, BTN_, REL_, ABS_, + MSC_, SND_, SW_, LED_, REP_, FF_. + + @param type The event type (EV_* constant) where to look for the name. + @param name A non-NULL string describing an input-event code ("KEY_A", + "ABS_X", "BTN_Y", ...), zero-terminated. + + @return The given code constant for the passed name or -1 if not found.*/ + pub fn libevdev_event_code_from_name( + type_: ::std::os::raw::c_uint, + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an event code by its type and name. Event codes start with a fixed + prefix followed by their name (eg., "ABS_X"). The prefix must be included in + the name. It returns the constant assigned to the event code or -1 if not + found. + + You have to pass the event type where to look for the name. For instance, to + resolve "ABS_X" you need to pass EV_ABS as type and "ABS_X" as string. + Supported event codes are codes starting with SYN_, KEY_, BTN_, REL_, ABS_, + MSC_, SND_, SW_, LED_, REP_, FF_. + + @param type The event type (EV_* constant) where to look for the name. + @param name A non-NULL string describing an input-event code ("KEY_A", + "ABS_X", "BTN_Y", ...). + @param len The length of the string in @p name excluding any terminating 0 + character. + + @return The given code constant for the name or -1 if not found.*/ + pub fn libevdev_event_code_from_name_n( + type_: ::std::os::raw::c_uint, + name: *const ::std::os::raw::c_char, + len: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an event value by its type, code and name. Event values start + with a fixed prefix followed by their name (eg., "MT_TOOL_PALM"). The + prefix must be included in the name. It returns the constant assigned + to the event code or -1 if not found. + + You have to pass the event type and code where to look for the name. For + instance, to resolve "MT_TOOL_PALM" you need to pass EV_ABS as type, + ABS_MT_TOOL_TYPE as code and "MT_TOOL_PALM" as string. + + As of kernel 4.17, only EV_ABS/ABS_MT_TOOL_TYPE support name resolution. + + @param type The event type (EV_* constant) where to look for the name. + @param code The event code (ABS_* constant) where to look for the name. + @param name A non-NULL string describing an input-event value + ("MT_TOOL_TYPE", ...) + + @return The given value constant for the name or -1 if not found.*/ + pub fn libevdev_event_value_from_name( + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an event type for a event code name. For example, the name + "ABS_Y" returns EV_ABS. For the lookup to succeed, the name must be + unique, which is the case for all defines as of kernel 5.0 and likely to + be the case in the future. + + This is equivalent to libevdev_event_type_from_name() but takes the code + name instead of the type name. + + @param name A non-NULL string describing an input-event value + ("ABS_X", "REL_Y", "KEY_A", ...) + + @return The given event code for the name or -1 if not found.*/ + pub fn libevdev_event_type_from_code_name( + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an event type for a event code name. For example, the name + "ABS_Y" returns EV_ABS. For the lookup to succeed, the name must be + unique, which is the case for all defines as of kernel 5.0 and likely to + be the case in the future. + + This is equivalent to libevdev_event_type_from_name_n() but takes the code + name instead of the type name. + + @param name A non-NULL string describing an input-event value + ("ABS_X", "REL_Y", "KEY_A", ...) + @param len The length of the passed string excluding any terminating 0 + character. + + @return The given event code for the name or -1 if not found.*/ + pub fn libevdev_event_type_from_code_name_n( + name: *const ::std::os::raw::c_char, + len: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an event code by its name. For example, the name "ABS_Y" + returns 1. For the lookup to succeed, the name must be unique, which is + the case for all defines as of kernel 5.0 and likely to be the case in + the future. + + This is equivalent to libevdev_event_code_from_name() without the need + for knowing the event type. + + @param name A non-NULL string describing an input-event value + ("ABS_X", "REL_Y", "KEY_A", ...) + + @return The given event code for the name or -1 if not found.*/ + pub fn libevdev_event_code_from_code_name( + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an event code by its name. For example, the name "ABS_Y" + returns 1. For the lookup to succeed, the name must be unique, which is + the case for all defines as of kernel 5.0 and likely to be the case in + the future. + + This is equivalent to libevdev_event_code_from_name_n() without the need + for knowing the event type. + + @param name A non-NULL string describing an input-event value + ("ABS_X", "REL_Y", "KEY_A", ...) + @param len The length of the passed string excluding any terminating 0 + character. + + @return The given event code for the name or -1 if not found.*/ + pub fn libevdev_event_code_from_code_name_n( + name: *const ::std::os::raw::c_char, + len: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an event value by its type, code and name. Event values start + with a fixed prefix followed by their name (eg., "MT_TOOL_PALM"). The + prefix must be included in the name. It returns the constant assigned + to the event code or -1 if not found. + + You have to pass the event type and code where to look for the name. For + instance, to resolve "MT_TOOL_PALM" you need to pass EV_ABS as type, + ABS_MT_TOOL_TYPE as code and "MT_TOOL_PALM" as string. + + As of kernel 4.17, only EV_ABS/ABS_MT_TOOL_TYPE support name resolution. + + @param type The event type (EV_* constant) where to look for the name. + @param code The event code (ABS_* constant) where to look for the name. + @param name A non-NULL string describing an input-event value + ("MT_TOOL_TYPE", ...) + @param len The length of the string in @p name excluding any terminating 0 + character. + + @return The given value constant for the name or -1 if not found.*/ + pub fn libevdev_event_value_from_name_n( + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + name: *const ::std::os::raw::c_char, + len: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an input property by its name. Properties start with the fixed + prefix "INPUT_PROP_" followed by their name (eg., "INPUT_PROP_POINTER"). + The prefix must be included in the name. It returns the constant assigned + to the property or -1 if not found. + + @param name A non-NULL string describing an input property + + @return The given code constant for the name or -1 if not found.*/ + pub fn libevdev_property_from_name( + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup misc + + Look up an input property by its name. Properties start with the fixed + prefix "INPUT_PROP_" followed by their name (eg., "INPUT_PROP_POINTER"). + The prefix must be included in the name. It returns the constant assigned + to the property or -1 if not found. + + @param name A non-NULL string describing an input property + @param len The length of the string in @p name excluding any terminating 0 + character. + + @return The given code constant for the name or -1 if not found.*/ + pub fn libevdev_property_from_name_n( + name: *const ::std::os::raw::c_char, + len: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup bits + + Get the repeat delay and repeat period values for this device. This + function is a convenience function only, EV_REP is supported by + libevdev_get_event_value(). + + @param dev The evdev device, already initialized with libevdev_set_fd() + @param delay If not null, set to the repeat delay value + @param period If not null, set to the repeat period value + + @return 0 on success, -1 if this device does not have repeat settings. + + @note This function is signal-safe + + @see libevdev_get_event_value*/ + pub fn libevdev_get_repeat( + dev: *const libevdev, + delay: *mut ::std::os::raw::c_int, + period: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +pub type fpos_t = off_t; +pub type fpos64_t = off64_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sFILE { + _unused: [u8; 0], +} +pub type FILE = __sFILE; +unsafe extern "C" { + pub static mut stdin: *mut FILE; +} +unsafe extern "C" { + pub static mut stdout: *mut FILE; +} +unsafe extern "C" { + pub static mut stderr: *mut FILE; +} +unsafe extern "C" { + pub fn clearerr(__fp: *mut FILE); +} +unsafe extern "C" { + pub fn fclose(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn feof(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn ferror(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fflush(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fgetc(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fgets( + __buf: *mut ::std::os::raw::c_char, + __size: ::std::os::raw::c_int, + __fp: *mut FILE, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn fprintf( + __fp: *mut FILE, + __fmt: *const ::std::os::raw::c_char, + ... + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fputc(__ch: ::std::os::raw::c_int, __fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fputs( + __s: *const ::std::os::raw::c_char, + __fp: *mut FILE, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fread( + __buf: *mut ::std::os::raw::c_void, + __size: ::std::os::raw::c_ulong, + __count: ::std::os::raw::c_ulong, + __fp: *mut FILE, + ) -> ::std::os::raw::c_ulong; +} +unsafe extern "C" { + pub fn fscanf( + __fp: *mut FILE, + __fmt: *const ::std::os::raw::c_char, + ... + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fwrite( + __buf: *const ::std::os::raw::c_void, + __size: ::std::os::raw::c_ulong, + __count: ::std::os::raw::c_ulong, + __fp: *mut FILE, + ) -> ::std::os::raw::c_ulong; +} +unsafe extern "C" { + pub fn getc(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn getchar() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn getdelim( + __line_ptr: *mut *mut ::std::os::raw::c_char, + __line_length_ptr: *mut usize, + __delimiter: ::std::os::raw::c_int, + __fp: *mut FILE, + ) -> isize; +} +unsafe extern "C" { + pub fn getline( + __line_ptr: *mut *mut ::std::os::raw::c_char, + __line_length_ptr: *mut usize, + __fp: *mut FILE, + ) -> isize; +} +unsafe extern "C" { + pub fn perror(__msg: *const ::std::os::raw::c_char); +} +unsafe extern "C" { + pub fn printf(__fmt: *const ::std::os::raw::c_char, ...) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn putc(__ch: ::std::os::raw::c_int, __fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn putchar(__ch: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn puts(__s: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn remove(__path: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn rewind(__fp: *mut FILE); +} +unsafe extern "C" { + pub fn scanf(__fmt: *const ::std::os::raw::c_char, ...) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn setbuf(__fp: *mut FILE, __buf: *mut ::std::os::raw::c_char); +} +unsafe extern "C" { + pub fn setvbuf( + __fp: *mut FILE, + __buf: *mut ::std::os::raw::c_char, + __mode: ::std::os::raw::c_int, + __size: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sscanf( + __s: *const ::std::os::raw::c_char, + __fmt: *const ::std::os::raw::c_char, + ... + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn ungetc(__ch: ::std::os::raw::c_int, __fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn vfprintf( + __fp: *mut FILE, + __fmt: *const ::std::os::raw::c_char, + __args: __BindgenOpaqueArray, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn vprintf( + __fp: *const ::std::os::raw::c_char, + __args: __BindgenOpaqueArray, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn dprintf( + __fd: ::std::os::raw::c_int, + __fmt: *const ::std::os::raw::c_char, + ... + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn vdprintf( + __fd: ::std::os::raw::c_int, + __fmt: *const ::std::os::raw::c_char, + __args: va_list, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn sprintf( + __s: *mut ::std::os::raw::c_char, + __fmt: *const ::std::os::raw::c_char, + ... + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn vsprintf( + __s: *mut ::std::os::raw::c_char, + __fmt: *const ::std::os::raw::c_char, + __args: __BindgenOpaqueArray, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn tmpnam(__s: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn tempnam( + __dir: *const ::std::os::raw::c_char, + __prefix: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn rename( + __old_path: *const ::std::os::raw::c_char, + __new_path: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn renameat( + __old_dir_fd: ::std::os::raw::c_int, + __old_path: *const ::std::os::raw::c_char, + __new_dir_fd: ::std::os::raw::c_int, + __new_path: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fseek( + __fp: *mut FILE, + __offset: ::std::os::raw::c_long, + __whence: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn ftell(__fp: *mut FILE) -> ::std::os::raw::c_long; +} +unsafe extern "C" { + pub fn fgetpos(__fp: *mut FILE, __pos: *mut fpos_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fsetpos(__fp: *mut FILE, __pos: *const fpos_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fseeko( + __fp: *mut FILE, + __offset: off_t, + __whence: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn ftello(__fp: *mut FILE) -> off_t; +} +unsafe extern "C" { + pub fn fgetpos64(__fp: *mut FILE, __pos: *mut fpos64_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fsetpos64(__fp: *mut FILE, __pos: *const fpos64_t) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fseeko64( + __fp: *mut FILE, + __offset: off64_t, + __whence: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn ftello64(__fp: *mut FILE) -> off64_t; +} +unsafe extern "C" { + pub fn fopen( + __path: *const ::std::os::raw::c_char, + __mode: *const ::std::os::raw::c_char, + ) -> *mut FILE; +} +unsafe extern "C" { + pub fn fopen64( + __path: *const ::std::os::raw::c_char, + __mode: *const ::std::os::raw::c_char, + ) -> *mut FILE; +} +unsafe extern "C" { + pub fn freopen( + __path: *const ::std::os::raw::c_char, + __mode: *const ::std::os::raw::c_char, + __fp: *mut FILE, + ) -> *mut FILE; +} +unsafe extern "C" { + pub fn freopen64( + __path: *const ::std::os::raw::c_char, + __mode: *const ::std::os::raw::c_char, + __fp: *mut FILE, + ) -> *mut FILE; +} +unsafe extern "C" { + pub fn tmpfile() -> *mut FILE; +} +unsafe extern "C" { + pub fn tmpfile64() -> *mut FILE; +} +unsafe extern "C" { + pub fn snprintf( + __buf: *mut ::std::os::raw::c_char, + __size: ::std::os::raw::c_ulong, + __fmt: *const ::std::os::raw::c_char, + ... + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn vfscanf( + __fp: *mut FILE, + __fmt: *const ::std::os::raw::c_char, + __args: __BindgenOpaqueArray, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn vscanf( + __fmt: *const ::std::os::raw::c_char, + __args: __BindgenOpaqueArray, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn vsnprintf( + __buf: *mut ::std::os::raw::c_char, + __size: ::std::os::raw::c_ulong, + __fmt: *const ::std::os::raw::c_char, + __args: __BindgenOpaqueArray, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn vsscanf( + __s: *const ::std::os::raw::c_char, + __fmt: *const ::std::os::raw::c_char, + __args: __BindgenOpaqueArray, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn ctermid(__buf: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn fdopen( + __fd: ::std::os::raw::c_int, + __mode: *const ::std::os::raw::c_char, + ) -> *mut FILE; +} +unsafe extern "C" { + pub fn fileno(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn pclose(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn popen( + __command: *const ::std::os::raw::c_char, + __mode: *const ::std::os::raw::c_char, + ) -> *mut FILE; +} +unsafe extern "C" { + pub fn flockfile(__fp: *mut FILE); +} +unsafe extern "C" { + pub fn ftrylockfile(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn funlockfile(__fp: *mut FILE); +} +unsafe extern "C" { + pub fn getc_unlocked(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn getchar_unlocked() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn putc_unlocked( + __ch: ::std::os::raw::c_int, + __fp: *mut FILE, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn putchar_unlocked(__ch: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fmemopen( + __buf: *mut ::std::os::raw::c_void, + __size: usize, + __mode: *const ::std::os::raw::c_char, + ) -> *mut FILE; +} +unsafe extern "C" { + pub fn open_memstream( + __ptr: *mut *mut ::std::os::raw::c_char, + __size_ptr: *mut usize, + ) -> *mut FILE; +} +unsafe extern "C" { + pub fn asprintf( + __s_ptr: *mut *mut ::std::os::raw::c_char, + __fmt: *const ::std::os::raw::c_char, + ... + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fgetln( + __fp: *mut FILE, + __length_ptr: *mut usize, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn fpurge(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn setbuffer( + __fp: *mut FILE, + __buf: *mut ::std::os::raw::c_char, + __size: ::std::os::raw::c_int, + ); +} +unsafe extern "C" { + pub fn setlinebuf(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn vasprintf( + __s_ptr: *mut *mut ::std::os::raw::c_char, + __fmt: *const ::std::os::raw::c_char, + __args: va_list, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn clearerr_unlocked(__fp: *mut FILE); +} +unsafe extern "C" { + pub fn feof_unlocked(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn ferror_unlocked(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn fileno_unlocked(__fp: *mut FILE) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn malloc(__byte_count: ::std::os::raw::c_ulong) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn calloc( + __item_count: ::std::os::raw::c_ulong, + __item_size: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn realloc( + __ptr: *mut ::std::os::raw::c_void, + __byte_count: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn reallocarray( + __ptr: *mut ::std::os::raw::c_void, + __item_count: usize, + __item_size: usize, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn free(__ptr: *mut ::std::os::raw::c_void); +} +unsafe extern "C" { + pub fn memalign( + __alignment: ::std::os::raw::c_ulong, + __byte_count: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn malloc_usable_size(__ptr: *const ::std::os::raw::c_void) -> usize; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct mallinfo { + pub arena: usize, + pub ordblks: usize, + pub smblks: usize, + pub hblks: usize, + pub hblkhd: usize, + pub usmblks: usize, + pub fsmblks: usize, + pub uordblks: usize, + pub fordblks: usize, + pub keepcost: usize, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of mallinfo"][::std::mem::size_of::() - 80usize]; + ["Alignment of mallinfo"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: mallinfo::arena", + ][::std::mem::offset_of!(mallinfo, arena) - 0usize]; + [ + "Offset of field: mallinfo::ordblks", + ][::std::mem::offset_of!(mallinfo, ordblks) - 8usize]; + [ + "Offset of field: mallinfo::smblks", + ][::std::mem::offset_of!(mallinfo, smblks) - 16usize]; + [ + "Offset of field: mallinfo::hblks", + ][::std::mem::offset_of!(mallinfo, hblks) - 24usize]; + [ + "Offset of field: mallinfo::hblkhd", + ][::std::mem::offset_of!(mallinfo, hblkhd) - 32usize]; + [ + "Offset of field: mallinfo::usmblks", + ][::std::mem::offset_of!(mallinfo, usmblks) - 40usize]; + [ + "Offset of field: mallinfo::fsmblks", + ][::std::mem::offset_of!(mallinfo, fsmblks) - 48usize]; + [ + "Offset of field: mallinfo::uordblks", + ][::std::mem::offset_of!(mallinfo, uordblks) - 56usize]; + [ + "Offset of field: mallinfo::fordblks", + ][::std::mem::offset_of!(mallinfo, fordblks) - 64usize]; + [ + "Offset of field: mallinfo::keepcost", + ][::std::mem::offset_of!(mallinfo, keepcost) - 72usize]; +}; +unsafe extern "C" { + pub fn mallinfo() -> mallinfo; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct mallinfo2 { + pub arena: usize, + pub ordblks: usize, + pub smblks: usize, + pub hblks: usize, + pub hblkhd: usize, + pub usmblks: usize, + pub fsmblks: usize, + pub uordblks: usize, + pub fordblks: usize, + pub keepcost: usize, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of mallinfo2"][::std::mem::size_of::() - 80usize]; + ["Alignment of mallinfo2"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: mallinfo2::arena", + ][::std::mem::offset_of!(mallinfo2, arena) - 0usize]; + [ + "Offset of field: mallinfo2::ordblks", + ][::std::mem::offset_of!(mallinfo2, ordblks) - 8usize]; + [ + "Offset of field: mallinfo2::smblks", + ][::std::mem::offset_of!(mallinfo2, smblks) - 16usize]; + [ + "Offset of field: mallinfo2::hblks", + ][::std::mem::offset_of!(mallinfo2, hblks) - 24usize]; + [ + "Offset of field: mallinfo2::hblkhd", + ][::std::mem::offset_of!(mallinfo2, hblkhd) - 32usize]; + [ + "Offset of field: mallinfo2::usmblks", + ][::std::mem::offset_of!(mallinfo2, usmblks) - 40usize]; + [ + "Offset of field: mallinfo2::fsmblks", + ][::std::mem::offset_of!(mallinfo2, fsmblks) - 48usize]; + [ + "Offset of field: mallinfo2::uordblks", + ][::std::mem::offset_of!(mallinfo2, uordblks) - 56usize]; + [ + "Offset of field: mallinfo2::fordblks", + ][::std::mem::offset_of!(mallinfo2, fordblks) - 64usize]; + [ + "Offset of field: mallinfo2::keepcost", + ][::std::mem::offset_of!(mallinfo2, keepcost) - 72usize]; +}; +unsafe extern "C" { + pub fn malloc_info( + __must_be_zero: ::std::os::raw::c_int, + __fp: *mut FILE, + ) -> ::std::os::raw::c_int; +} +pub const HeapTaggingLevel_M_HEAP_TAGGING_LEVEL_NONE: HeapTaggingLevel = 0; +pub const HeapTaggingLevel_M_HEAP_TAGGING_LEVEL_TBI: HeapTaggingLevel = 1; +pub const HeapTaggingLevel_M_HEAP_TAGGING_LEVEL_ASYNC: HeapTaggingLevel = 2; +pub const HeapTaggingLevel_M_HEAP_TAGGING_LEVEL_SYNC: HeapTaggingLevel = 3; +pub type HeapTaggingLevel = ::std::os::raw::c_uint; +unsafe extern "C" { + pub fn mallopt( + __option: ::std::os::raw::c_int, + __value: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub static mut __malloc_hook: ::std::option::Option< + unsafe extern "C" fn( + __byte_count: usize, + __caller: *const ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void, + >; +} +unsafe extern "C" { + pub static mut __realloc_hook: ::std::option::Option< + unsafe extern "C" fn( + __ptr: *mut ::std::os::raw::c_void, + __byte_count: usize, + __caller: *const ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void, + >; +} +unsafe extern "C" { + pub static mut __free_hook: ::std::option::Option< + unsafe extern "C" fn( + __ptr: *mut ::std::os::raw::c_void, + __caller: *const ::std::os::raw::c_void, + ), + >; +} +unsafe extern "C" { + pub static mut __memalign_hook: ::std::option::Option< + unsafe extern "C" fn( + __alignment: usize, + __byte_count: usize, + __caller: *const ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void, + >; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __locale_t { + _unused: [u8; 0], +} +pub type locale_t = *mut __locale_t; +unsafe extern "C" { + pub fn abort() -> !; +} +unsafe extern "C" { + pub fn exit(__status: ::std::os::raw::c_int) -> !; +} +unsafe extern "C" { + pub fn _Exit(__status: ::std::os::raw::c_int) -> !; +} +unsafe extern "C" { + pub fn atexit( + __fn: ::std::option::Option, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn at_quick_exit( + __fn: ::std::option::Option, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn quick_exit(__status: ::std::os::raw::c_int) -> !; +} +unsafe extern "C" { + pub fn getenv(__name: *const ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn putenv(__assignment: *mut ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn setenv( + __name: *const ::std::os::raw::c_char, + __value: *const ::std::os::raw::c_char, + __overwrite: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn unsetenv(__name: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn clearenv() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn mkdtemp( + __template: *mut ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn mktemp( + __template: *mut ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn mkostemp64( + __template: *mut ::std::os::raw::c_char, + __flags: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn mkostemp( + __template: *mut ::std::os::raw::c_char, + __flags: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn mkostemps64( + __template: *mut ::std::os::raw::c_char, + __suffix_length: ::std::os::raw::c_int, + __flags: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn mkostemps( + __template: *mut ::std::os::raw::c_char, + __suffix_length: ::std::os::raw::c_int, + __flags: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn mkstemp64(__template: *mut ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn mkstemp(__template: *mut ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn mkstemps64( + __template: *mut ::std::os::raw::c_char, + __flags: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn mkstemps( + __template: *mut ::std::os::raw::c_char, + __flags: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn strtol( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_long; +} +unsafe extern "C" { + pub fn strtoll( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_longlong; +} +unsafe extern "C" { + pub fn strtoul( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_ulong; +} +unsafe extern "C" { + pub fn strtoull( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_ulonglong; +} +unsafe extern "C" { + pub fn posix_memalign( + __memptr: *mut *mut ::std::os::raw::c_void, + __alignment: usize, + __size: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn aligned_alloc( + __alignment: ::std::os::raw::c_ulong, + __size: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn strtod( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + ) -> f64; +} +unsafe extern "C" { + pub fn strtold( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + ) -> u128; +} +unsafe extern "C" { + pub fn strtoul_l( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + __l: locale_t, + ) -> ::std::os::raw::c_ulong; +} +unsafe extern "C" { + pub fn atoi(__s: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn atol(__s: *const ::std::os::raw::c_char) -> ::std::os::raw::c_long; +} +unsafe extern "C" { + pub fn atoll(__s: *const ::std::os::raw::c_char) -> ::std::os::raw::c_longlong; +} +unsafe extern "C" { + pub fn realpath( + __path: *const ::std::os::raw::c_char, + __resolved: *mut ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn system(__command: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn bsearch( + __key: *const ::std::os::raw::c_void, + __base: *const ::std::os::raw::c_void, + __nmemb: usize, + __size: usize, + __comparator: ::std::option::Option< + unsafe extern "C" fn( + __lhs: *const ::std::os::raw::c_void, + __rhs: *const ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn qsort( + __base: *mut ::std::os::raw::c_void, + __nmemb: usize, + __size: usize, + __comparator: ::std::option::Option< + unsafe extern "C" fn( + __lhs: *const ::std::os::raw::c_void, + __rhs: *const ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, + ); +} +unsafe extern "C" { + pub fn arc4random() -> u32; +} +unsafe extern "C" { + pub fn arc4random_uniform(__upper_bound: u32) -> u32; +} +unsafe extern "C" { + pub fn arc4random_buf(__buf: *mut ::std::os::raw::c_void, __n: usize); +} +unsafe extern "C" { + pub fn rand_r(__seed_ptr: *mut ::std::os::raw::c_uint) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn drand48() -> f64; +} +unsafe extern "C" { + pub fn erand48(__xsubi: *mut ::std::os::raw::c_ushort) -> f64; +} +unsafe extern "C" { + pub fn jrand48(__xsubi: *mut ::std::os::raw::c_ushort) -> ::std::os::raw::c_long; +} +unsafe extern "C" { + pub fn lcong48(__param: *mut ::std::os::raw::c_ushort); +} +unsafe extern "C" { + pub fn lrand48() -> ::std::os::raw::c_long; +} +unsafe extern "C" { + pub fn mrand48() -> ::std::os::raw::c_long; +} +unsafe extern "C" { + pub fn nrand48(__xsubi: *mut ::std::os::raw::c_ushort) -> ::std::os::raw::c_long; +} +unsafe extern "C" { + pub fn seed48( + __seed16v: *mut ::std::os::raw::c_ushort, + ) -> *mut ::std::os::raw::c_ushort; +} +unsafe extern "C" { + pub fn srand48(__seed: ::std::os::raw::c_long); +} +unsafe extern "C" { + pub fn initstate( + __seed: ::std::os::raw::c_uint, + __state: *mut ::std::os::raw::c_char, + __n: usize, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn setstate(__state: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn getpt() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn posix_openpt(__flags: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn ptsname(__fd: ::std::os::raw::c_int) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn ptsname_r( + __fd: ::std::os::raw::c_int, + __buf: *mut ::std::os::raw::c_char, + __n: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn unlockpt(__fd: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn getsubopt( + __option: *mut *mut ::std::os::raw::c_char, + __tokens: *const *mut ::std::os::raw::c_char, + __value_ptr: *mut *mut ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct div_t { + pub quot: ::std::os::raw::c_int, + pub rem: ::std::os::raw::c_int, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of div_t"][::std::mem::size_of::() - 8usize]; + ["Alignment of div_t"][::std::mem::align_of::() - 4usize]; + ["Offset of field: div_t::quot"][::std::mem::offset_of!(div_t, quot) - 0usize]; + ["Offset of field: div_t::rem"][::std::mem::offset_of!(div_t, rem) - 4usize]; +}; +unsafe extern "C" { + pub fn div( + __numerator: ::std::os::raw::c_int, + __denominator: ::std::os::raw::c_int, + ) -> div_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ldiv_t { + pub quot: ::std::os::raw::c_long, + pub rem: ::std::os::raw::c_long, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ldiv_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of ldiv_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: ldiv_t::quot"][::std::mem::offset_of!(ldiv_t, quot) - 0usize]; + ["Offset of field: ldiv_t::rem"][::std::mem::offset_of!(ldiv_t, rem) - 8usize]; +}; +unsafe extern "C" { + pub fn ldiv( + __numerator: ::std::os::raw::c_long, + __denominator: ::std::os::raw::c_long, + ) -> ldiv_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct lldiv_t { + pub quot: ::std::os::raw::c_longlong, + pub rem: ::std::os::raw::c_longlong, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of lldiv_t"][::std::mem::size_of::() - 16usize]; + ["Alignment of lldiv_t"][::std::mem::align_of::() - 8usize]; + ["Offset of field: lldiv_t::quot"][::std::mem::offset_of!(lldiv_t, quot) - 0usize]; + ["Offset of field: lldiv_t::rem"][::std::mem::offset_of!(lldiv_t, rem) - 8usize]; +}; +unsafe extern "C" { + pub fn lldiv( + __numerator: ::std::os::raw::c_longlong, + __denominator: ::std::os::raw::c_longlong, + ) -> lldiv_t; +} +unsafe extern "C" { + pub fn getloadavg( + __averages: *mut f64, + __n: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn getprogname() -> *const ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn setprogname(__name: *const ::std::os::raw::c_char); +} +unsafe extern "C" { + pub fn mblen( + __s: *const ::std::os::raw::c_char, + __n: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn mbstowcs( + __dst: *mut wchar_t, + __src: *const ::std::os::raw::c_char, + __n: usize, + ) -> usize; +} +unsafe extern "C" { + pub fn mbtowc( + __wc_ptr: *mut wchar_t, + __s: *const ::std::os::raw::c_char, + __n: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn wctomb( + __dst: *mut ::std::os::raw::c_char, + __wc: wchar_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn wcstombs( + __dst: *mut ::std::os::raw::c_char, + __src: *const wchar_t, + __n: usize, + ) -> usize; +} +unsafe extern "C" { + pub fn __ctype_get_mb_cur_max() -> usize; +} +unsafe extern "C" { + pub fn abs(__x: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn labs(__x: ::std::os::raw::c_long) -> ::std::os::raw::c_long; +} +unsafe extern "C" { + pub fn llabs(__x: ::std::os::raw::c_longlong) -> ::std::os::raw::c_longlong; +} +unsafe extern "C" { + pub fn strtof( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + ) -> f32; +} +unsafe extern "C" { + pub fn atof(__s: *const ::std::os::raw::c_char) -> f64; +} +unsafe extern "C" { + pub fn rand() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn srand(__seed: ::std::os::raw::c_uint); +} +unsafe extern "C" { + pub fn random() -> ::std::os::raw::c_long; +} +unsafe extern "C" { + pub fn srandom(__seed: ::std::os::raw::c_uint); +} +unsafe extern "C" { + pub fn grantpt(__fd: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn strtoll_l( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + __l: locale_t, + ) -> ::std::os::raw::c_longlong; +} +unsafe extern "C" { + pub fn strtoull_l( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + __l: locale_t, + ) -> ::std::os::raw::c_ulonglong; +} +unsafe extern "C" { + pub fn strtold_l( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + __l: locale_t, + ) -> u128; +} +unsafe extern "C" { + pub fn strtod_l( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + __l: locale_t, + ) -> f64; +} +unsafe extern "C" { + pub fn strtof_l( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + __l: locale_t, + ) -> f32; +} +unsafe extern "C" { + pub fn strtol_l( + __s: *const ::std::os::raw::c_char, + __end_ptr: *mut *mut ::std::os::raw::c_char, + arg1: ::std::os::raw::c_int, + __l: locale_t, + ) -> ::std::os::raw::c_long; +} +unsafe extern "C" { + pub fn __errno() -> *mut ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn strcasecmp( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn strcasecmp_l( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + __l: locale_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn strncasecmp( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn strncasecmp_l( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + __n: usize, + __l: locale_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn memccpy( + __dst: *mut ::std::os::raw::c_void, + __src: *const ::std::os::raw::c_void, + __stop_char: ::std::os::raw::c_int, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn memchr( + __s: *const ::std::os::raw::c_void, + __ch: ::std::os::raw::c_int, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn memrchr( + __s: *const ::std::os::raw::c_void, + __ch: ::std::os::raw::c_int, + __n: usize, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn memcmp( + __lhs: *const ::std::os::raw::c_void, + __rhs: *const ::std::os::raw::c_void, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn memcpy( + arg1: *mut ::std::os::raw::c_void, + arg2: *const ::std::os::raw::c_void, + arg3: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn memmove( + __dst: *mut ::std::os::raw::c_void, + __src: *const ::std::os::raw::c_void, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn memset( + __dst: *mut ::std::os::raw::c_void, + __ch: ::std::os::raw::c_int, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn memset_explicit( + __dst: *mut ::std::os::raw::c_void, + __ch: ::std::os::raw::c_int, + __n: usize, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn memmem( + __haystack: *const ::std::os::raw::c_void, + __haystack_size: usize, + __needle: *const ::std::os::raw::c_void, + __needle_size: usize, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn strchr( + __s: *const ::std::os::raw::c_char, + __ch: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn __strchr_chk( + __s: *const ::std::os::raw::c_char, + __ch: ::std::os::raw::c_int, + __n: usize, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strrchr( + __s: *const ::std::os::raw::c_char, + __ch: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn __strrchr_chk( + __s: *const ::std::os::raw::c_char, + __ch: ::std::os::raw::c_int, + __n: usize, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strlen(__s: *const ::std::os::raw::c_char) -> ::std::os::raw::c_ulong; +} +unsafe extern "C" { + pub fn __strlen_chk(__s: *const ::std::os::raw::c_char, __n: usize) -> usize; +} +unsafe extern "C" { + pub fn strcmp( + __lhs: *const ::std::os::raw::c_char, + __rhs: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn stpcpy( + __dst: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strcpy( + __dst: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strcat( + __dst: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strdup(__s: *const ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strstr( + __haystack: *const ::std::os::raw::c_char, + __needle: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strcasestr( + __haystack: *const ::std::os::raw::c_char, + __needle: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strtok( + __s: *mut ::std::os::raw::c_char, + __delimiter: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strtok_r( + __s: *mut ::std::os::raw::c_char, + __delimiter: *const ::std::os::raw::c_char, + __pos_ptr: *mut *mut ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strerror(__errno_value: ::std::os::raw::c_int) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strerror_l( + __errno_value: ::std::os::raw::c_int, + __l: locale_t, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strerror_r( + __errno_value: ::std::os::raw::c_int, + __buf: *mut ::std::os::raw::c_char, + __n: usize, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn strnlen(__s: *const ::std::os::raw::c_char, __n: usize) -> usize; +} +unsafe extern "C" { + pub fn strncat( + __dst: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strndup( + __s: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strncmp( + __lhs: *const ::std::os::raw::c_char, + __rhs: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn stpncpy( + __dst: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strncpy( + __dst: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strlcat( + __dst: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_ulong; +} +unsafe extern "C" { + pub fn strlcpy( + __dst: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_ulong; +} +unsafe extern "C" { + pub fn strcspn( + __s: *const ::std::os::raw::c_char, + __reject: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_ulong; +} +unsafe extern "C" { + pub fn strpbrk( + __s: *const ::std::os::raw::c_char, + __accept: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strsep( + __s_ptr: *mut *mut ::std::os::raw::c_char, + __delimiter: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strspn( + __s: *const ::std::os::raw::c_char, + __accept: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_ulong; +} +unsafe extern "C" { + pub fn strsignal(__signal: ::std::os::raw::c_int) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn strcoll( + __lhs: *const ::std::os::raw::c_char, + __rhs: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn strxfrm( + __dst: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_ulong; +} +unsafe extern "C" { + pub fn strcoll_l( + __lhs: *const ::std::os::raw::c_char, + __rhs: *const ::std::os::raw::c_char, + __l: locale_t, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn strxfrm_l( + __dst: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: usize, + __l: locale_t, + ) -> usize; +} +pub const SyncState_SYNC_NONE: SyncState = 0; +pub const SyncState_SYNC_NEEDED: SyncState = 1; +pub const SyncState_SYNC_IN_PROGRESS: SyncState = 2; +/** Sync state machine: + default state: SYNC_NONE + + SYNC_NONE → SYN_DROPPED or forced sync → SYNC_NEEDED + SYNC_NEEDED → libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC) → SYNC_IN_PROGRESS + SYNC_NEEDED → libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC_NONE) → SYNC_NONE + SYNC_IN_PROGRESS → libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC_NONE) → SYNC_NONE + SYNC_IN_PROGRESS → no sync events left → SYNC_NONE +*/ +pub type SyncState = ::std::os::raw::c_uint; +/** Internal only: log data used to send messages to the respective log + handler. We re-use the same struct for a global and inside + struct libevdev. + For the global, device_handler is NULL, for per-device instance + global_handler is NULL.*/ +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct logdata { + pub priority: libevdev_log_priority, + /// minimum logging priority + pub global_handler: libevdev_log_func_t, + /// global handler function + pub device_handler: libevdev_device_log_func_t, + /// per-device handler function + pub userdata: *mut ::std::os::raw::c_void, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of logdata"][::std::mem::size_of::() - 32usize]; + ["Alignment of logdata"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: logdata::priority", + ][::std::mem::offset_of!(logdata, priority) - 0usize]; + [ + "Offset of field: logdata::global_handler", + ][::std::mem::offset_of!(logdata, global_handler) - 8usize]; + [ + "Offset of field: logdata::device_handler", + ][::std::mem::offset_of!(logdata, device_handler) - 16usize]; + [ + "Offset of field: logdata::userdata", + ][::std::mem::offset_of!(logdata, userdata) - 24usize]; +}; +/** @ingroup init + + Opaque struct representing an evdev device.*/ +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct libevdev { + pub fd: ::std::os::raw::c_int, + pub initialized: bool, + pub name: *mut ::std::os::raw::c_char, + pub phys: *mut ::std::os::raw::c_char, + pub uniq: *mut ::std::os::raw::c_char, + pub ids: input_id, + pub driver_version: ::std::os::raw::c_int, + pub bits: [::std::os::raw::c_ulong; 1usize], + pub props: [::std::os::raw::c_ulong; 1usize], + pub key_bits: [::std::os::raw::c_ulong; 12usize], + pub rel_bits: [::std::os::raw::c_ulong; 1usize], + pub abs_bits: [::std::os::raw::c_ulong; 1usize], + pub led_bits: [::std::os::raw::c_ulong; 1usize], + pub msc_bits: [::std::os::raw::c_ulong; 1usize], + pub sw_bits: [::std::os::raw::c_ulong; 1usize], + pub rep_bits: [::std::os::raw::c_ulong; 1usize], + pub ff_bits: [::std::os::raw::c_ulong; 2usize], + pub snd_bits: [::std::os::raw::c_ulong; 1usize], + pub key_values: [::std::os::raw::c_ulong; 12usize], + pub led_values: [::std::os::raw::c_ulong; 1usize], + pub sw_values: [::std::os::raw::c_ulong; 1usize], + pub abs_info: [input_absinfo; 64usize], + pub mt_slot_vals: *mut ::std::os::raw::c_int, + ///< valid slots in mt_slot_vals + pub num_slots: ::std::os::raw::c_int, + pub current_slot: ::std::os::raw::c_int, + pub rep_values: [::std::os::raw::c_int; 2usize], + pub sync_state: SyncState, + pub grabbed: libevdev_grab_mode, + pub queue: *mut input_event, + ///< size of queue in elements + pub queue_size: usize, + ///< next event index + pub queue_next: usize, + ///< number of sync events + pub queue_nsync: usize, + pub last_event_time: timeval, + pub log: logdata, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of libevdev"][::std::mem::size_of::() - 1992usize]; + ["Alignment of libevdev"][::std::mem::align_of::() - 8usize]; + ["Offset of field: libevdev::fd"][::std::mem::offset_of!(libevdev, fd) - 0usize]; + [ + "Offset of field: libevdev::initialized", + ][::std::mem::offset_of!(libevdev, initialized) - 4usize]; + ["Offset of field: libevdev::name"][::std::mem::offset_of!(libevdev, name) - 8usize]; + [ + "Offset of field: libevdev::phys", + ][::std::mem::offset_of!(libevdev, phys) - 16usize]; + [ + "Offset of field: libevdev::uniq", + ][::std::mem::offset_of!(libevdev, uniq) - 24usize]; + ["Offset of field: libevdev::ids"][::std::mem::offset_of!(libevdev, ids) - 32usize]; + [ + "Offset of field: libevdev::driver_version", + ][::std::mem::offset_of!(libevdev, driver_version) - 40usize]; + [ + "Offset of field: libevdev::bits", + ][::std::mem::offset_of!(libevdev, bits) - 48usize]; + [ + "Offset of field: libevdev::props", + ][::std::mem::offset_of!(libevdev, props) - 56usize]; + [ + "Offset of field: libevdev::key_bits", + ][::std::mem::offset_of!(libevdev, key_bits) - 64usize]; + [ + "Offset of field: libevdev::rel_bits", + ][::std::mem::offset_of!(libevdev, rel_bits) - 160usize]; + [ + "Offset of field: libevdev::abs_bits", + ][::std::mem::offset_of!(libevdev, abs_bits) - 168usize]; + [ + "Offset of field: libevdev::led_bits", + ][::std::mem::offset_of!(libevdev, led_bits) - 176usize]; + [ + "Offset of field: libevdev::msc_bits", + ][::std::mem::offset_of!(libevdev, msc_bits) - 184usize]; + [ + "Offset of field: libevdev::sw_bits", + ][::std::mem::offset_of!(libevdev, sw_bits) - 192usize]; + [ + "Offset of field: libevdev::rep_bits", + ][::std::mem::offset_of!(libevdev, rep_bits) - 200usize]; + [ + "Offset of field: libevdev::ff_bits", + ][::std::mem::offset_of!(libevdev, ff_bits) - 208usize]; + [ + "Offset of field: libevdev::snd_bits", + ][::std::mem::offset_of!(libevdev, snd_bits) - 224usize]; + [ + "Offset of field: libevdev::key_values", + ][::std::mem::offset_of!(libevdev, key_values) - 232usize]; + [ + "Offset of field: libevdev::led_values", + ][::std::mem::offset_of!(libevdev, led_values) - 328usize]; + [ + "Offset of field: libevdev::sw_values", + ][::std::mem::offset_of!(libevdev, sw_values) - 336usize]; + [ + "Offset of field: libevdev::abs_info", + ][::std::mem::offset_of!(libevdev, abs_info) - 344usize]; + [ + "Offset of field: libevdev::mt_slot_vals", + ][::std::mem::offset_of!(libevdev, mt_slot_vals) - 1880usize]; + [ + "Offset of field: libevdev::num_slots", + ][::std::mem::offset_of!(libevdev, num_slots) - 1888usize]; + [ + "Offset of field: libevdev::current_slot", + ][::std::mem::offset_of!(libevdev, current_slot) - 1892usize]; + [ + "Offset of field: libevdev::rep_values", + ][::std::mem::offset_of!(libevdev, rep_values) - 1896usize]; + [ + "Offset of field: libevdev::sync_state", + ][::std::mem::offset_of!(libevdev, sync_state) - 1904usize]; + [ + "Offset of field: libevdev::grabbed", + ][::std::mem::offset_of!(libevdev, grabbed) - 1908usize]; + [ + "Offset of field: libevdev::queue", + ][::std::mem::offset_of!(libevdev, queue) - 1912usize]; + [ + "Offset of field: libevdev::queue_size", + ][::std::mem::offset_of!(libevdev, queue_size) - 1920usize]; + [ + "Offset of field: libevdev::queue_next", + ][::std::mem::offset_of!(libevdev, queue_next) - 1928usize]; + [ + "Offset of field: libevdev::queue_nsync", + ][::std::mem::offset_of!(libevdev, queue_nsync) - 1936usize]; + [ + "Offset of field: libevdev::last_event_time", + ][::std::mem::offset_of!(libevdev, last_event_time) - 1944usize]; + [ + "Offset of field: libevdev::log", + ][::std::mem::offset_of!(libevdev, log) - 1960usize]; +}; +unsafe extern "C" { + pub fn _libevdev_log_msg( + dev: *const libevdev, + priority: libevdev_log_priority, + file: *const ::std::os::raw::c_char, + line: ::std::os::raw::c_int, + func: *const ::std::os::raw::c_char, + format: *const ::std::os::raw::c_char, + ... + ); +} +unsafe extern "C" { + pub fn _libevdev_log_priority(dev: *const libevdev) -> libevdev_log_priority; +} +///< let libevdev open and close @c /dev/uinput +pub const libevdev_uinput_open_mode_LIBEVDEV_UINPUT_OPEN_MANAGED: libevdev_uinput_open_mode = -2; +/** @defgroup uinput uinput device creation + + Creation of uinput devices based on existing libevdev devices. These functions + help to create uinput devices that emulate libevdev devices. In the simplest + form it serves to duplicate an existing device: + + @code + int err; + int fd, uifd; + struct libevdev *dev; + struct libevdev_uinput *uidev; + + fd = open("/dev/input/event0", O_RDONLY); + if (fd < 0) + return -errno; + + err = libevdev_new_from_fd(fd, &dev); + if (err != 0) + return err; + + uifd = open("/dev/uinput", O_RDWR); + if (uifd < 0) + return -errno; + + err = libevdev_uinput_create_from_device(dev, uifd, &uidev); + if (err != 0) + return err; + + // post a REL_X event + err = libevdev_uinput_write_event(uidev, EV_REL, REL_X, -1); + if (err != 0) + return err; + err = libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0); + if (err != 0) + return err; + + libevdev_uinput_destroy(uidev); + libevdev_free(dev); + close(uifd); + close(fd); + + @endcode + + Alternatively, a device can be constructed from scratch: + + @code + int err; + struct libevdev *dev; + struct libevdev_uinput *uidev; + + dev = libevdev_new(); + libevdev_set_name(dev, "test device"); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + libevdev_enable_event_type(dev, EV_KEY); + libevdev_enable_event_code(dev, EV_KEY, BTN_LEFT, NULL); + libevdev_enable_event_code(dev, EV_KEY, BTN_MIDDLE, NULL); + libevdev_enable_event_code(dev, EV_KEY, BTN_RIGHT, NULL); + + err = libevdev_uinput_create_from_device(dev, + LIBEVDEV_UINPUT_OPEN_MANAGED, + &uidev); + if (err != 0) + return err; + + // ... do something ... + + libevdev_uinput_destroy(uidev); + + @endcode*/ +pub type libevdev_uinput_open_mode = ::std::os::raw::c_int; +unsafe extern "C" { + /** @ingroup uinput + + Create a uinput device based on the given libevdev device. The uinput device + will be an exact copy of the libevdev device, minus the bits that uinput doesn't + allow to be set. + + If uinput_fd is @ref LIBEVDEV_UINPUT_OPEN_MANAGED, libevdev_uinput_create_from_device() + will open @c /dev/uinput in read/write mode and manage the file descriptor. + Otherwise, uinput_fd must be opened by the caller and opened with the + appropriate permissions. + + The device's lifetime is tied to the uinput file descriptor, closing it will + destroy the uinput device. You should call libevdev_uinput_destroy() before + closing the file descriptor to free allocated resources. + A file descriptor can only create one uinput device at a time; the second device + will fail with -EINVAL. + + You don't need to keep the file descriptor variable around, + libevdev_uinput_get_fd() will return it when needed. + + @note Due to limitations in the uinput kernel module, REP_DELAY and + REP_PERIOD will default to the kernel defaults, not to the ones set in the + source device. + + @note On FreeBSD, if the UI_GET_SYSNAME ioctl() fails, there is no other way + to get a device, and the function call will fail. + + @param dev The device to duplicate + @param uinput_fd @ref LIBEVDEV_UINPUT_OPEN_MANAGED or a file descriptor to @c /dev/uinput, + @param[out] uinput_dev The newly created libevdev device. + + @return 0 on success or a negative errno on failure. On failure, the value of + uinput_dev is unmodified. + + @see libevdev_uinput_destroy*/ + pub fn libevdev_uinput_create_from_device( + dev: *const libevdev, + uinput_fd: ::std::os::raw::c_int, + uinput_dev: *mut *mut libevdev_uinput, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup uinput + + Destroy a previously created uinput device and free associated memory. + + If the device was opened with @ref LIBEVDEV_UINPUT_OPEN_MANAGED, + libevdev_uinput_destroy() also closes the file descriptor. Otherwise, the + fd is left as-is and must be closed by the caller. + + @param uinput_dev A previously created uinput device.*/ + pub fn libevdev_uinput_destroy(uinput_dev: *mut libevdev_uinput); +} +unsafe extern "C" { + /** @ingroup uinput + + Return the file descriptor used to create this uinput device. This is the + fd pointing to /dev/uinput. This file descriptor may be used to write + events that are emitted by the uinput device. + Closing this file descriptor will destroy the uinput device, you should + call libevdev_uinput_destroy() first to free allocated resources. + + @param uinput_dev A previously created uinput device. + + @return The file descriptor used to create this device*/ + pub fn libevdev_uinput_get_fd( + uinput_dev: *const libevdev_uinput, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + /** @ingroup uinput + + Return the syspath representing this uinput device. If the UI_GET_SYSNAME + ioctl is not available, libevdev makes an educated guess. + The UI_GET_SYSNAME ioctl is available since Linux 3.15. + + The syspath returned is the one of the input node itself + (e.g. /sys/devices/virtual/input/input123), not the syspath of the device + node returned with libevdev_uinput_get_devnode(). + + @note This function may return NULL if UI_GET_SYSNAME is not available. + In that case, libevdev uses ctime and the device name to guess devices. + To avoid false positives, wait at least 1.5s between creating devices that + have the same name. + + @note FreeBSD does not have sysfs, on FreeBSD this function always returns + NULL. + + @param uinput_dev A previously created uinput device. + @return The syspath for this device, including the preceding /sys + + @see libevdev_uinput_get_devnode*/ + pub fn libevdev_uinput_get_syspath( + uinput_dev: *mut libevdev_uinput, + ) -> *const ::std::os::raw::c_char; +} +unsafe extern "C" { + /** @ingroup uinput + + Return the device node representing this uinput device. + + This relies on libevdev_uinput_get_syspath() to provide a valid syspath. + See libevdev_uinput_get_syspath() for more details. + + @note This function may return NULL. libevdev may have to guess the + syspath and the device node. See libevdev_uinput_get_syspath() for details. + + @note On FreeBSD, this function can not return NULL. libudev uses the + UI_GET_SYSNAME ioctl to get the device node on this platform and if that + fails, the call to libevdev_uinput_create_from_device() fails. + + @param uinput_dev A previously created uinput device. + @return The device node for this device, in the form of /dev/input/eventN + + @see libevdev_uinput_get_syspath*/ + pub fn libevdev_uinput_get_devnode( + uinput_dev: *mut libevdev_uinput, + ) -> *const ::std::os::raw::c_char; +} +unsafe extern "C" { + /** @ingroup uinput + + Post an event through the uinput device. It is the caller's responsibility + that any event sequence is terminated with an EV_SYN/SYN_REPORT/0 event. + Otherwise, listeners on the device node will not see the events until the + next EV_SYN event is posted. + + @param uinput_dev A previously created uinput device. + @param type Event type (EV_ABS, EV_REL, etc.) + @param code Event code (ABS_X, REL_Y, etc.) + @param value The event value + @return 0 on success or a negative errno on error*/ + pub fn libevdev_uinput_write_event( + uinput_dev: *const libevdev_uinput, + type_: ::std::os::raw::c_uint, + code: ::std::os::raw::c_uint, + value: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct libevdev_uinput { + ///< file descriptor to uinput + pub fd: ::std::os::raw::c_int, + ///< do we need to close it? + pub fd_is_managed: ::std::os::raw::c_int, + ///< device name + pub name: *mut ::std::os::raw::c_char, + ///< /sys path + pub syspath: *mut ::std::os::raw::c_char, + ///< device node + pub devnode: *mut ::std::os::raw::c_char, + ///< before/after UI_DEV_CREATE + pub ctime: [time_t; 2usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of libevdev_uinput"][::std::mem::size_of::() - 48usize]; + ["Alignment of libevdev_uinput"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: libevdev_uinput::fd", + ][::std::mem::offset_of!(libevdev_uinput, fd) - 0usize]; + [ + "Offset of field: libevdev_uinput::fd_is_managed", + ][::std::mem::offset_of!(libevdev_uinput, fd_is_managed) - 4usize]; + [ + "Offset of field: libevdev_uinput::name", + ][::std::mem::offset_of!(libevdev_uinput, name) - 8usize]; + [ + "Offset of field: libevdev_uinput::syspath", + ][::std::mem::offset_of!(libevdev_uinput, syspath) - 16usize]; + [ + "Offset of field: libevdev_uinput::devnode", + ][::std::mem::offset_of!(libevdev_uinput, devnode) - 24usize]; + [ + "Offset of field: libevdev_uinput::ctime", + ][::std::mem::offset_of!(libevdev_uinput, ctime) - 32usize]; +}; +pub type __uint128_t = u128; diff --git a/evdev/src/main/rust/evdev_manager/src/enums.rs b/evdev/src/main/rust/evdev_manager/src/enums.rs new file mode 100644 index 0000000000..c71cb67b60 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/src/enums.rs @@ -0,0 +1,385 @@ +use crate::bindings; +use std::fmt; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[repr(u32)] +pub enum EventType { + Syn = bindings::EV_SYN, + Key = bindings::EV_KEY, + Rel = bindings::EV_REL, + Abs = bindings::EV_ABS, + Msc = bindings::EV_MSC, + Sw = bindings::EV_SW, + Led = bindings::EV_LED, + Snd = bindings::EV_SND, + Rep = bindings::EV_REP, + Ff = bindings::EV_FF, + Pwr = bindings::EV_PWR, + FfStatus = bindings::EV_FF_STATUS, +} + +impl EventType { + /// Convert a u32 value to an EventType + pub fn from_raw(value: u32) -> Option { + match value { + bindings::EV_SYN => Some(Self::Syn), + bindings::EV_KEY => Some(Self::Key), + bindings::EV_REL => Some(Self::Rel), + bindings::EV_ABS => Some(Self::Abs), + bindings::EV_MSC => Some(Self::Msc), + bindings::EV_SW => Some(Self::Sw), + bindings::EV_LED => Some(Self::Led), + bindings::EV_SND => Some(Self::Snd), + bindings::EV_REP => Some(Self::Rep), + bindings::EV_FF => Some(Self::Ff), + bindings::EV_PWR => Some(Self::Pwr), + bindings::EV_FF_STATUS => Some(Self::FfStatus), + _ => None, + } + } + + /// Convert EventType to u32 + pub const fn as_raw(self) -> u32 { + self as u32 + } +} + +impl std::fmt::Display for EventType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for u32 { + fn from(event_type: EventType) -> Self { + event_type.as_raw() + } +} + +impl TryFrom for EventType { + type Error = (); + + fn try_from(value: u32) -> Result { + Self::from_raw(value).ok_or(()) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[repr(u32)] +pub enum EvRel { + X = bindings::REL_X, + Y = bindings::REL_Y, + Z = bindings::REL_Z, + Rx = bindings::REL_RX, + Ry = bindings::REL_RY, + Rz = bindings::REL_RZ, + Hwheel = bindings::REL_HWHEEL, + Dial = bindings::REL_DIAL, + Wheel = bindings::REL_WHEEL, + Misc = bindings::REL_MISC, + Reserved = bindings::REL_RESERVED, + WheelHiRes = bindings::REL_WHEEL_HI_RES, + HwheelHiRes = bindings::REL_HWHEEL_HI_RES, +} + +impl EvRel { + pub fn from_raw(value: u32) -> Option { + match value { + bindings::REL_X => Some(Self::X), + bindings::REL_Y => Some(Self::Y), + bindings::REL_Z => Some(Self::Z), + bindings::REL_RX => Some(Self::Rx), + bindings::REL_RY => Some(Self::Ry), + bindings::REL_RZ => Some(Self::Rz), + bindings::REL_HWHEEL => Some(Self::Hwheel), + bindings::REL_DIAL => Some(Self::Dial), + bindings::REL_WHEEL => Some(Self::Wheel), + bindings::REL_MISC => Some(Self::Misc), + bindings::REL_RESERVED => Some(Self::Reserved), + bindings::REL_WHEEL_HI_RES => Some(Self::WheelHiRes), + bindings::REL_HWHEEL_HI_RES => Some(Self::HwheelHiRes), + _ => None, + } + } + + pub const fn as_raw(self) -> u32 { + self as u32 + } +} + +impl std::fmt::Display for EvRel { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for u32 { + fn from(ev_rel: EvRel) -> Self { + ev_rel.as_raw() + } +} + +impl TryFrom for EvRel { + type Error = (); + + fn try_from(value: u32) -> Result { + Self::from_raw(value).ok_or(()) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[repr(u32)] +pub enum EvAbs { + X = bindings::ABS_X, + Y = bindings::ABS_Y, + Z = bindings::ABS_Z, + Rx = bindings::ABS_RX, + Ry = bindings::ABS_RY, + Rz = bindings::ABS_RZ, + Throttle = bindings::ABS_THROTTLE, + Rudder = bindings::ABS_RUDDER, + Wheel = bindings::ABS_WHEEL, + Gas = bindings::ABS_GAS, + Brake = bindings::ABS_BRAKE, + Hat0X = bindings::ABS_HAT0X, + Hat0Y = bindings::ABS_HAT0Y, + Hat1X = bindings::ABS_HAT1X, + Hat1Y = bindings::ABS_HAT1Y, + Hat2X = bindings::ABS_HAT2X, + Hat2Y = bindings::ABS_HAT2Y, + Hat3X = bindings::ABS_HAT3X, + Hat3Y = bindings::ABS_HAT3Y, + Pressure = bindings::ABS_PRESSURE, + Distance = bindings::ABS_DISTANCE, + TiltX = bindings::ABS_TILT_X, + TiltY = bindings::ABS_TILT_Y, + ToolWidth = bindings::ABS_TOOL_WIDTH, + Volume = bindings::ABS_VOLUME, + Profile = bindings::ABS_PROFILE, + Misc = bindings::ABS_MISC, + Reserved = bindings::ABS_RESERVED, + MtSlot = bindings::ABS_MT_SLOT, + MtTouchMajor = bindings::ABS_MT_TOUCH_MAJOR, + MtTouchMinor = bindings::ABS_MT_TOUCH_MINOR, + MtWidthMajor = bindings::ABS_MT_WIDTH_MAJOR, + MtWidthMinor = bindings::ABS_MT_WIDTH_MINOR, + MtOrientation = bindings::ABS_MT_ORIENTATION, + MtPositionX = bindings::ABS_MT_POSITION_X, + MtPositionY = bindings::ABS_MT_POSITION_Y, + MtToolType = bindings::ABS_MT_TOOL_TYPE, + MtBlobId = bindings::ABS_MT_BLOB_ID, + MtTrackingId = bindings::ABS_MT_TRACKING_ID, + MtPressure = bindings::ABS_MT_PRESSURE, + MtDistance = bindings::ABS_MT_DISTANCE, + MtToolX = bindings::ABS_MT_TOOL_X, + MtToolY = bindings::ABS_MT_TOOL_Y, +} + +impl EvAbs { + pub fn from_raw(value: u32) -> Option { + match value { + bindings::ABS_X => Some(Self::X), + bindings::ABS_Y => Some(Self::Y), + bindings::ABS_Z => Some(Self::Z), + bindings::ABS_RX => Some(Self::Rx), + bindings::ABS_RY => Some(Self::Ry), + bindings::ABS_RZ => Some(Self::Rz), + bindings::ABS_THROTTLE => Some(Self::Throttle), + bindings::ABS_RUDDER => Some(Self::Rudder), + bindings::ABS_WHEEL => Some(Self::Wheel), + bindings::ABS_GAS => Some(Self::Gas), + bindings::ABS_BRAKE => Some(Self::Brake), + bindings::ABS_HAT0X => Some(Self::Hat0X), + bindings::ABS_HAT0Y => Some(Self::Hat0Y), + bindings::ABS_HAT1X => Some(Self::Hat1X), + bindings::ABS_HAT1Y => Some(Self::Hat1Y), + bindings::ABS_HAT2X => Some(Self::Hat2X), + bindings::ABS_HAT2Y => Some(Self::Hat2Y), + bindings::ABS_HAT3X => Some(Self::Hat3X), + bindings::ABS_HAT3Y => Some(Self::Hat3Y), + bindings::ABS_PRESSURE => Some(Self::Pressure), + bindings::ABS_DISTANCE => Some(Self::Distance), + bindings::ABS_TILT_X => Some(Self::TiltX), + bindings::ABS_TILT_Y => Some(Self::TiltY), + bindings::ABS_TOOL_WIDTH => Some(Self::ToolWidth), + bindings::ABS_VOLUME => Some(Self::Volume), + bindings::ABS_PROFILE => Some(Self::Profile), + bindings::ABS_MISC => Some(Self::Misc), + bindings::ABS_RESERVED => Some(Self::Reserved), + bindings::ABS_MT_SLOT => Some(Self::MtSlot), + bindings::ABS_MT_TOUCH_MAJOR => Some(Self::MtTouchMajor), + bindings::ABS_MT_TOUCH_MINOR => Some(Self::MtTouchMinor), + bindings::ABS_MT_WIDTH_MAJOR => Some(Self::MtWidthMajor), + bindings::ABS_MT_WIDTH_MINOR => Some(Self::MtWidthMinor), + bindings::ABS_MT_ORIENTATION => Some(Self::MtOrientation), + bindings::ABS_MT_POSITION_X => Some(Self::MtPositionX), + bindings::ABS_MT_POSITION_Y => Some(Self::MtPositionY), + bindings::ABS_MT_TOOL_TYPE => Some(Self::MtToolType), + bindings::ABS_MT_BLOB_ID => Some(Self::MtBlobId), + bindings::ABS_MT_TRACKING_ID => Some(Self::MtTrackingId), + bindings::ABS_MT_PRESSURE => Some(Self::MtPressure), + bindings::ABS_MT_DISTANCE => Some(Self::MtDistance), + bindings::ABS_MT_TOOL_X => Some(Self::MtToolX), + bindings::ABS_MT_TOOL_Y => Some(Self::MtToolY), + _ => None, + } + } + + pub const fn as_raw(self) -> u32 { + self as u32 + } +} + +impl std::fmt::Display for EvAbs { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for u32 { + fn from(ev_abs: EvAbs) -> Self { + ev_abs.as_raw() + } +} + +impl TryFrom for EvAbs { + type Error = (); + + fn try_from(value: u32) -> Result { + Self::from_raw(value).ok_or(()) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[repr(u32)] +pub enum InputProp { + Pointer = bindings::INPUT_PROP_POINTER, + Direct = bindings::INPUT_PROP_DIRECT, + Buttonpad = bindings::INPUT_PROP_BUTTONPAD, + SemiMt = bindings::INPUT_PROP_SEMI_MT, + TopButtonpad = bindings::INPUT_PROP_TOPBUTTONPAD, + PointingStick = bindings::INPUT_PROP_POINTING_STICK, + Accelerometer = bindings::INPUT_PROP_ACCELEROMETER, +} + +impl InputProp { + pub fn from_raw(value: u32) -> Option { + match value { + bindings::INPUT_PROP_POINTER => Some(Self::Pointer), + bindings::INPUT_PROP_DIRECT => Some(Self::Direct), + bindings::INPUT_PROP_BUTTONPAD => Some(Self::Buttonpad), + bindings::INPUT_PROP_SEMI_MT => Some(Self::SemiMt), + bindings::INPUT_PROP_TOPBUTTONPAD => Some(Self::TopButtonpad), + bindings::INPUT_PROP_POINTING_STICK => Some(Self::PointingStick), + bindings::INPUT_PROP_ACCELEROMETER => Some(Self::Accelerometer), + _ => None, + } + } + + pub const fn as_raw(self) -> u32 { + self as u32 + } +} + +impl std::fmt::Display for InputProp { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for u32 { + fn from(input_prop: InputProp) -> Self { + input_prop.as_raw() + } +} + +impl TryFrom for InputProp { + type Error = (); + + fn try_from(value: u32) -> Result { + Self::from_raw(value).ok_or(()) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[repr(u32)] +pub enum BusType { + Pci = bindings::BUS_PCI, + Isapnp = bindings::BUS_ISAPNP, + Usb = bindings::BUS_USB, + Hil = bindings::BUS_HIL, + Bluetooth = bindings::BUS_BLUETOOTH, + Virtual = bindings::BUS_VIRTUAL, + Isa = bindings::BUS_ISA, + I8042 = bindings::BUS_I8042, + Xtkbd = bindings::BUS_XTKBD, + Rs232 = bindings::BUS_RS232, + Gameport = bindings::BUS_GAMEPORT, + Parport = bindings::BUS_PARPORT, + Amiga = bindings::BUS_AMIGA, + Adb = bindings::BUS_ADB, + I2c = bindings::BUS_I2C, + Host = bindings::BUS_HOST, + Gsc = bindings::BUS_GSC, + Atari = bindings::BUS_ATARI, + Spi = bindings::BUS_SPI, + Rmi = bindings::BUS_RMI, + Cec = bindings::BUS_CEC, + IntelIshtp = bindings::BUS_INTEL_ISHTP, + AmdSfh = bindings::BUS_AMD_SFH, +} + +impl BusType { + pub fn from_raw(value: u32) -> Option { + match value { + bindings::BUS_PCI => Some(Self::Pci), + bindings::BUS_ISAPNP => Some(Self::Isapnp), + bindings::BUS_USB => Some(Self::Usb), + bindings::BUS_HIL => Some(Self::Hil), + bindings::BUS_BLUETOOTH => Some(Self::Bluetooth), + bindings::BUS_VIRTUAL => Some(Self::Virtual), + bindings::BUS_ISA => Some(Self::Isa), + bindings::BUS_I8042 => Some(Self::I8042), + bindings::BUS_XTKBD => Some(Self::Xtkbd), + bindings::BUS_RS232 => Some(Self::Rs232), + bindings::BUS_GAMEPORT => Some(Self::Gameport), + bindings::BUS_PARPORT => Some(Self::Parport), + bindings::BUS_AMIGA => Some(Self::Amiga), + bindings::BUS_ADB => Some(Self::Adb), + bindings::BUS_I2C => Some(Self::I2c), + bindings::BUS_HOST => Some(Self::Host), + bindings::BUS_GSC => Some(Self::Gsc), + bindings::BUS_ATARI => Some(Self::Atari), + bindings::BUS_SPI => Some(Self::Spi), + bindings::BUS_RMI => Some(Self::Rmi), + bindings::BUS_CEC => Some(Self::Cec), + bindings::BUS_INTEL_ISHTP => Some(Self::IntelIshtp), + bindings::BUS_AMD_SFH => Some(Self::AmdSfh), + _ => None, + } + } + + pub const fn as_raw(self) -> u32 { + self as u32 + } +} + +impl std::fmt::Display for BusType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for u32 { + fn from(bus_type: BusType) -> Self { + bus_type.as_raw() + } +} + +impl TryFrom for BusType { + type Error = (); + + fn try_from(value: u32) -> Result { + Self::from_raw(value).ok_or(()) + } +} diff --git a/evdev/src/main/rust/evdev_manager/src/evdev.rs b/evdev/src/main/rust/evdev_manager/src/evdev.rs new file mode 100644 index 0000000000..21533f2ce9 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/src/evdev.rs @@ -0,0 +1,416 @@ +use crate::bindings; +use crate::enums::{EvAbs, EventType, InputProp}; +use std::error::Error; +use std::ffi::{c_void, CStr, CString}; +use std::io::Error as IOError; +use std::io::ErrorKind; +use std::mem::MaybeUninit; +use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd}; +use std::os::raw::c_int; +use std::ptr; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EvdevErrorCode { + NoSuchFileOrDirectory, + IoError, + NoSuchDevice, + BadFileDescriptor, + OutOfMemory, + WouldBlock, + PermissionDenied, + InvalidArgument, + Unknown(i32), +} + +impl EvdevErrorCode { + pub fn from_code(code: i32) -> Self { + // libevdev returns negative errno values + match -code as u32 { + bindings::ENOENT => Self::NoSuchFileOrDirectory, + bindings::EIO => Self::IoError, + bindings::EBADF => Self::BadFileDescriptor, + bindings::EAGAIN => Self::WouldBlock, + bindings::ENOMEM => Self::OutOfMemory, + bindings::EACCES => Self::PermissionDenied, + bindings::ENODEV => Self::NoSuchDevice, + bindings::EINVAL => Self::InvalidArgument, + _ => Self::Unknown(code), + } + } + + pub fn to_code(self) -> i32 { + -(match self { + Self::NoSuchFileOrDirectory => bindings::ENOENT as i32, + Self::IoError => bindings::EIO as i32, + Self::BadFileDescriptor => bindings::EBADF as i32, + Self::WouldBlock => bindings::EAGAIN as i32, + Self::OutOfMemory => bindings::ENOMEM as i32, + Self::PermissionDenied => bindings::EACCES as i32, + Self::NoSuchDevice => bindings::ENODEV as i32, + Self::InvalidArgument => bindings::EINVAL as i32, + Self::Unknown(code) => return code, + }) + } + + pub fn description(&self) -> &'static str { + match self { + Self::NoSuchFileOrDirectory => "No such file or directory (device not found)", + Self::IoError => "Input/output error", + Self::NoSuchDevice => "No such device", + Self::BadFileDescriptor => "Bad file descriptor", + Self::OutOfMemory => "Out of memory", + Self::WouldBlock => "Resource temporarily unavailable", + Self::PermissionDenied => "Permission denied", + Self::InvalidArgument => "Invalid argument", + Self::Unknown(_) => "Unknown error", + } + } +} + +#[derive(Debug)] +pub struct EvdevError { + kind: EvdevErrorCode, + code: i32, + message: String, +} + +impl EvdevError { + fn new(code: i32) -> Self { + let kind = EvdevErrorCode::from_code(code); + let message = if let EvdevErrorCode::Unknown(_) = kind { + format!("libevdev error: {}", code) + } else { + format!("libevdev error: {} ({})", kind.description(), -code) + }; + + Self { + kind, + code, + message, + } + } + + pub fn code(&self) -> i32 { + self.code + } + + pub fn kind(&self) -> EvdevErrorCode { + self.kind + } +} + +impl std::fmt::Display for EvdevError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.message) + } +} + +impl Error for EvdevError {} + +impl From for IOError { + fn from(err: EvdevError) -> Self { + IOError::from_raw_os_error(-err.code) + } +} + +pub struct EvdevDevice { + ptr: *mut bindings::libevdev, + fd: Option, +} + +impl EvdevDevice { + /// Create a new libevdev device from a file descriptor + /// Must use IntoRawFd so ownership is transferred, instead of borrowing and it + /// automatically being closed. + pub fn new_from_fd(file: F) -> Result { + let owned_fd = unsafe { OwnedFd::from_raw_fd(file.into_raw_fd()) }; + let mut dev: *mut bindings::libevdev = ptr::null_mut(); + + let result = unsafe { bindings::libevdev_new_from_fd(owned_fd.as_raw_fd(), &mut dev) }; + + if result < 0 { + return Err(EvdevError::new(result)); + } + + Ok(Self { + ptr: dev, + fd: Some(owned_fd), + }) + } + + /// Create a new libevdev device struct + pub fn new() -> Result { + let dev: *mut bindings::libevdev = unsafe { bindings::libevdev_new() }; + Ok(Self { ptr: dev, fd: None }) + } + + pub fn set_name(&mut self, name: &str) -> Result<(), Box> { + let name_cstr = CString::new(name)?; + unsafe { + bindings::libevdev_set_name(self.ptr, name_cstr.as_ptr()); + } + Ok(()) + } + + /// Get the device name + pub fn name(&self) -> String { + let name_ptr = unsafe { bindings::libevdev_get_name(self.ptr) }; + + if name_ptr.is_null() { + String::new() + } else { + let name_cstr = unsafe { CStr::from_ptr(name_ptr) }; + name_cstr.to_string_lossy().into_owned() + } + } + + pub fn enable_property(&mut self, prop: InputProp) -> Result<(), Box> { + let result = unsafe { bindings::libevdev_enable_property(self.ptr, prop.into()) }; + + if result < 0 { + return Err(Box::new(EvdevError::new(result))); + } + + Ok(()) + } + + /// Enable an event code. + pub fn enable_event_code( + &mut self, + event_type: EventType, + event_code: u32, + ) -> Result<(), Box> { + if event_type == EventType::Abs { + let error = IOError::new( + ErrorKind::InvalidInput, + "Enabling EV_ABS requires abs_info. Use enable_event_abs function.", + ); + + return Err(Box::new(error)); + } + + if event_type == EventType::Rep { + let error = IOError::new( + ErrorKind::InvalidInput, + "EV_REP requires an integer is not supported in this wrapper. A dedicated function must be created.", ); + + return Err(Box::new(error)); + } + + let result = unsafe { + bindings::libevdev_enable_event_code( + self.ptr, + event_type.into(), + event_code, + ptr::null(), + ) + }; + + if result < 0 { + return Err(Box::new(EvdevError::new(result))); + } + + Ok(()) + } + + /// libevdev will automatically enable the event type. + pub fn enable_event_abs( + &mut self, + abs_code: EvAbs, + abs_info: impl Into, + ) -> Result<(), EvdevError> { + let abs_info_raw = abs_info.into(); + + let result = unsafe { + bindings::libevdev_enable_event_code( + self.ptr, + EventType::Abs.into(), + abs_code.into(), + ptr::from_ref(&abs_info_raw) as *const c_void, + ) + }; + + if result < 0 { + return Err(EvdevError::new(result)); + } + + Ok(()) + } + + /// Grab the device through a kernel EVIOCGRAB + /// + /// This prevents other clients (including kernel-internal ones such as rfkill) + /// from receiving events from this device. + /// + /// **Warning:** This is generally a bad idea. Use with caution. + /// + /// Grabbing an already grabbed device is a no-op and always succeeds. + /// + /// A grab is an operation tied to a file descriptor, not a device. If you + /// change the file descriptor, you must also re-issue a grab. + /// + /// # Returns + /// * `Ok(())` - If the device was successfully grabbed + /// * `Err(EvdevError)` - If the grab failed + pub fn grab(&mut self) -> Result<(), EvdevError> { + self.set_grab_mode(bindings::libevdev_grab_mode_LIBEVDEV_GRAB) + } + + /// Ungrab the device if currently grabbed + /// + /// This allows other clients to receive events from this device again. + /// + /// Ungrabbing an ungrabbed device is a no-op and always succeeds. + /// + /// # Returns + /// * `Ok(())` - If the device was successfully ungrabbed + /// * `Err(EvdevError)` - If the ungrab failed + pub fn ungrab(&mut self) -> Result<(), EvdevError> { + self.set_grab_mode(bindings::libevdev_grab_mode_LIBEVDEV_UNGRAB) + } + + fn set_grab_mode(&mut self, mode: u32) -> Result<(), EvdevError> { + let result = unsafe { bindings::libevdev_grab(self.ptr, mode) }; + + if result < 0 { + return Err(EvdevError::new(result)); + } + + Ok(()) + } + + /// Read the next event from the device (non-blocking) + /// + /// Returns `Ok(Some(InputEvent))` if an event was read, + /// `Ok(None)` if no events are available (EAGAIN), + /// or `Err(EvdevError)` on error. + pub fn next_event(&mut self) -> Result, EvdevError> { + let mut ev = MaybeUninit::::uninit(); + + let result = unsafe { + bindings::libevdev_next_event( + self.ptr, + // TODO use same setup as sysbridge for reading from multiple devices at once in a blocking mannerz + bindings::libevdev_read_flag_LIBEVDEV_READ_FLAG_BLOCKING, + ev.as_mut_ptr(), + ) + }; + + match result { + 0.. => { + let ev = unsafe { ev.assume_init() }; + Ok(Some(InputEvent { + time_sec: ev.time.tv_sec, + time_usec: ev.time.tv_usec, + event_type: EventType::from_raw(ev.type_ as u32).expect("Unknown event type"), + code: ev.code as u32, + value: ev.value, + })) + } + -11 => Ok(None), + _ => Err(EvdevError::new(result)), + } + } + + /// Get the raw pointer (for internal use) + pub(crate) fn as_ptr(&self) -> *const bindings::libevdev { + self.ptr as *const bindings::libevdev + } +} + +/// Represents a single input event +#[derive(Debug, Clone, Copy)] +pub struct InputEvent { + pub time_sec: i64, + pub time_usec: i64, + pub event_type: EventType, + pub code: u32, + pub value: i32, +} + +impl Drop for EvdevDevice { + fn drop(&mut self) { + unsafe { + if !self.ptr.is_null() { + bindings::libevdev_free(self.ptr); + } + } + + // fd is dropped automatically + } +} + +pub struct UInputDevice { + ptr: *mut bindings::libevdev_uinput, +} + +impl UInputDevice { + /// Create a uinput device from an existing libevdev device + pub fn create_from_device(device: &EvdevDevice) -> Result { + let mut uinput_dev: *mut bindings::libevdev_uinput = ptr::null_mut(); + + let result = unsafe { + bindings::libevdev_uinput_create_from_device( + device.as_ptr(), + bindings::libevdev_uinput_open_mode_LIBEVDEV_UINPUT_OPEN_MANAGED, + &mut uinput_dev, + ) + }; + + if result < 0 { + return Err(EvdevError::new(result)); + } + + Ok(Self { ptr: uinput_dev }) + } + + /// Get the device node path (e.g., "/dev/input/eventN") + pub fn devnode(&self) -> Option { + let devnode_ptr = unsafe { bindings::libevdev_uinput_get_devnode(self.ptr) }; + + if !devnode_ptr.is_null() { + let devnode_cstr = unsafe { CStr::from_ptr(devnode_ptr) }; + Some(devnode_cstr.to_string_lossy().into_owned()) + } else { + None + } + } + + /// Get the file descriptor for this uinput device + pub fn fd(&self) -> c_int { + unsafe { bindings::libevdev_uinput_get_fd(self.ptr) } + } + + /// Write a generic event to the uinput device + /// + /// # Arguments + /// * `event_type` - Event type (EV_ABS, EV_KEY, EV_SYN, etc.) + /// * `event_code` - Event code (ABS_X, BTN_LEFT, SYN_REPORT, etc.) + /// * `value` - Event value + pub fn write_event( + &self, + event_type: EventType, + event_code: u32, + value: i32, + ) -> Result<(), EvdevError> { + let result = unsafe { + bindings::libevdev_uinput_write_event(self.ptr, event_type.into(), event_code, value) + }; + + if result < 0 { + return Err(EvdevError::new(result)); + } + + Ok(()) + } +} + +impl Drop for UInputDevice { + fn drop(&mut self) { + unsafe { + if !self.ptr.is_null() { + bindings::libevdev_uinput_destroy(self.ptr); + } + } + } +} diff --git a/evdev/src/main/rust/evdev_manager/src/lib.rs b/evdev/src/main/rust/evdev_manager/src/lib.rs index f3c3dc3679..598de37b42 100644 --- a/evdev/src/main/rust/evdev_manager/src/lib.rs +++ b/evdev/src/main/rust/evdev_manager/src/lib.rs @@ -1,3 +1,7 @@ +mod bindings; +mod enums; +mod evdev; + #[macro_use] extern crate log; extern crate android_log; diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt index 68a3c4f544..485321f3ab 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/BaseSystemBridge.kt @@ -197,7 +197,6 @@ abstract class BaseSystemBridge : ISystemBridge.Stub() { val libraryPath = System.getProperty("keymapper_sysbridge.library.path") @SuppressLint("UnsafeDynamicallyLoadedCode") - System.load("$libraryPath/libevdev.so") System.load("$libraryPath/libevdev_manager.so") helloEvdevManager("test input") From bce9b3d162dc3200db861ff6812f4809943be0aa Mon Sep 17 00:00:00 2001 From: sds100 Date: Wed, 12 Nov 2025 15:19:07 +0100 Subject: [PATCH 005/199] #1897 create C wrapper for KeyLayoutMap --- evdev/src/main/cpp/keylayoutmap_c.cpp | 100 ++++++++++++++++++ evdev/src/main/cpp/keylayoutmap_c.h | 57 ++++++++++ evdev/src/main/cpp/libevdev_jni.cpp | 1 + evdev/src/main/rust/evdev_manager/build.rs | 47 +++++--- .../main/rust/evdev_manager/src/bindings.rs | 59 +++++++++++ evdev/src/main/rust/evdev_manager/src/lib.rs | 2 +- 6 files changed, 251 insertions(+), 15 deletions(-) create mode 100644 evdev/src/main/cpp/keylayoutmap_c.cpp create mode 100644 evdev/src/main/cpp/keylayoutmap_c.h diff --git a/evdev/src/main/cpp/keylayoutmap_c.cpp b/evdev/src/main/cpp/keylayoutmap_c.cpp new file mode 100644 index 0000000000..e4342aa93e --- /dev/null +++ b/evdev/src/main/cpp/keylayoutmap_c.cpp @@ -0,0 +1,100 @@ +/* + * C interface implementation for KeyLayoutMap + */ + +#include "keylayoutmap_c.h" +#include "android/input/KeyLayoutMap.h" +#include "android/utils/Errors.h" + +extern "C" { + +int keylayoutmap_load(const char* filename, KeyLayoutMapHandle* out_handle) { + if (!filename || !out_handle) { + return -1; // Invalid argument + } + + auto result = android::KeyLayoutMap::load(filename, nullptr); + if (!result.ok()) { + *out_handle = nullptr; + return -2; // Load failed + } + + // Extract the shared_ptr and store it + // We need to keep the shared_ptr alive, so we'll new it + std::shared_ptr* map_ptr = new std::shared_ptr(*result); + *out_handle = reinterpret_cast(map_ptr); + return 0; // Success +} + +int keylayoutmap_load_contents(const char* filename, const char* contents, KeyLayoutMapHandle* out_handle) { + if (!filename || !contents || !out_handle) { + return -1; // Invalid argument + } + + auto result = android::KeyLayoutMap::loadContents(filename, contents); + if (!result.ok()) { + *out_handle = nullptr; + return -2; // Load failed + } + + std::shared_ptr* map_ptr = new std::shared_ptr(*result); + *out_handle = reinterpret_cast(map_ptr); + return 0; // Success +} + +int keylayoutmap_map_key(KeyLayoutMapHandle handle, int32_t scan_code, int32_t usage_code, + int32_t* out_key_code, uint32_t* out_flags) { + if (!handle || !out_key_code || !out_flags) { + return -1; // Invalid argument + } + + std::shared_ptr* map_ptr = + reinterpret_cast*>(handle); + + if (!map_ptr || !*map_ptr) { + return -1; // Invalid handle + } + + android::status_t status = (*map_ptr)->mapKey(scan_code, usage_code, out_key_code, out_flags); + return static_cast(status); +} + +int keylayoutmap_map_axis(KeyLayoutMapHandle handle, int32_t scan_code, AxisInfo* out_axis_info) { + if (!handle || !out_axis_info) { + return -1; // Invalid argument + } + + std::shared_ptr* map_ptr = + reinterpret_cast*>(handle); + + if (!map_ptr || !*map_ptr) { + return -1; // Invalid handle + } + + auto axis_opt = (*map_ptr)->mapAxis(scan_code); + if (!axis_opt.has_value()) { + return -2; // Axis not found + } + + const android::AxisInfo& axis = axis_opt.value(); + out_axis_info->mode = static_cast(axis.mode); + out_axis_info->axis = axis.axis; + out_axis_info->highAxis = axis.highAxis; + out_axis_info->splitValue = axis.splitValue; + out_axis_info->flatOverride = axis.flatOverride; + return 0; // Success +} + +void keylayoutmap_destroy(KeyLayoutMapHandle handle) { + if (!handle) { + return; + } + + std::shared_ptr* map_ptr = + reinterpret_cast*>(handle); + + delete map_ptr; +} + +} // extern "C" + diff --git a/evdev/src/main/cpp/keylayoutmap_c.h b/evdev/src/main/cpp/keylayoutmap_c.h new file mode 100644 index 0000000000..4787f6611c --- /dev/null +++ b/evdev/src/main/cpp/keylayoutmap_c.h @@ -0,0 +1,57 @@ +/* + * C interface wrapper for KeyLayoutMap + * This provides a simple C API that can be easily bound from Rust + */ + +#ifndef KEYLAYOUTMAP_C_H +#define KEYLAYOUTMAP_C_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Opaque handle to KeyLayoutMap +typedef void* KeyLayoutMapHandle; + +// AxisInfo structure (matches android::AxisInfo) +typedef struct { + int32_t mode; // 0 = MODE_NORMAL, 1 = MODE_INVERT, 2 = MODE_SPLIT + int32_t axis; + int32_t highAxis; + int32_t splitValue; + int32_t flatOverride; +} AxisInfo; + +// Load KeyLayoutMap from file +// Returns 0 on success, non-zero error code on failure +// On success, *out_handle will be set to a valid handle (must be freed with keylayoutmap_destroy) +// On failure, *out_handle will be NULL +int keylayoutmap_load(const char* filename, KeyLayoutMapHandle* out_handle); + +// Load KeyLayoutMap from file contents +// Returns 0 on success, non-zero error code on failure +int keylayoutmap_load_contents(const char* filename, const char* contents, KeyLayoutMapHandle* out_handle); + +// Map a key (scan code or usage code) to Android key code +// Returns 0 on success, non-zero error code on failure +// On success, *out_key_code and *out_flags will be set +int keylayoutmap_map_key(KeyLayoutMapHandle handle, int32_t scan_code, int32_t usage_code, + int32_t* out_key_code, uint32_t* out_flags); + +// Map an axis (scan code) to AxisInfo +// Returns 0 if axis was found, non-zero if not found +// On success, *out_axis_info will be set +int keylayoutmap_map_axis(KeyLayoutMapHandle handle, int32_t scan_code, AxisInfo* out_axis_info); + +// Destroy a KeyLayoutMap handle +// Safe to call with NULL handle +void keylayoutmap_destroy(KeyLayoutMapHandle handle); + +#ifdef __cplusplus +} +#endif + +#endif // KEYLAYOUTMAP_C_H + diff --git a/evdev/src/main/cpp/libevdev_jni.cpp b/evdev/src/main/cpp/libevdev_jni.cpp index 7aa338dd31..f3e21ef39b 100644 --- a/evdev/src/main/cpp/libevdev_jni.cpp +++ b/evdev/src/main/cpp/libevdev_jni.cpp @@ -124,6 +124,7 @@ Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_grabEvdevDevi identifier.vendor = libevdev_get_id_vendor(dev); identifier.product = libevdev_get_id_product(dev); + // TODO convert this code into rust std::string klPath = android::getInputDeviceConfigurationFilePathByDeviceIdentifier( identifier, android::InputDeviceConfigurationFileType::KEY_LAYOUT); diff --git a/evdev/src/main/rust/evdev_manager/build.rs b/evdev/src/main/rust/evdev_manager/build.rs index 2851b27740..2f77416a51 100644 --- a/evdev/src/main/rust/evdev_manager/build.rs +++ b/evdev/src/main/rust/evdev_manager/build.rs @@ -13,7 +13,10 @@ fn main() { let is_android = target.contains("android"); if !is_android { - eprintln!("Warning: Building for non-Android target '{}'. Skipping C/C++ compilation.", target); + eprintln!( + "Warning: Building for non-Android target '{}'. Skipping C/C++ compilation.", + target + ); eprintln!("This crate is designed for Android. Use Gradle for actual builds."); // Skip all compilation but succeed to allow cargo check to work return; @@ -62,6 +65,8 @@ fn main() { .file(android_dir.join("input/InputEventLabels.cpp")) .file(android_dir.join("input/InputDevice.cpp")) .file(android_dir.join("input/Input.cpp")) + // C interface wrapper for KeyLayoutMap + .file(cpp_dir.join("keylayoutmap_c.cpp")) // Android base library files .file(android_dir.join("libbase/result.cpp")) .file(android_dir.join("libbase/stringprintf.cpp")) @@ -103,20 +108,33 @@ fn main() { println!("cargo:rustc-link-lib=log"); println!("cargo:rustc-link-lib=binder_ndk"); - // Generate Rust bindings from libevdev headers + // Generate Rust bindings from headers + // We generate C and C++ bindings separately because: + // 1. C headers (libevdev) need C mode to allow implicit void* conversions + // 2. C++ headers (KeyLayoutMap.h) need C++ mode to find cstdint let evdev_headers_path = libevdev_dir.clone(); - // Configure bindgen with Android NDK sysroot and include paths - let mut bindgen_builder = bindgen::Builder::default() - .formatter(bindgen::Formatter::Prettyplease) - // Disable all format/style warnings for generated code - .raw_line("#![allow(clippy::all)]") - .raw_line("#![allow(non_camel_case_types)]") - .raw_line("#![allow(non_snake_case)]") - .raw_line("#![allow(non_upper_case_globals)]") - .raw_line("#![allow(dead_code)]") - .raw_line("#![allow(rustdoc::broken_intra_doc_links)]") - .raw_line("#![allow(rustdoc::private_intra_doc_links)]") + // Common bindgen configuration + let common_allow_attributes = vec![ + "#![allow(clippy::all)]", + "#![allow(non_camel_case_types)]", + "#![allow(non_snake_case)]", + "#![allow(non_upper_case_globals)]", + "#![allow(dead_code)]", + "#![allow(rustdoc::broken_intra_doc_links)]", + "#![allow(rustdoc::private_intra_doc_links)]", + "#![allow(arithmetic_overflow)]", // Needed for bindgen-generated array size calculations + ]; + + // Generate C bindings (libevdev headers) in C mode + let mut bindgen_builder = + bindgen::Builder::default().formatter(bindgen::Formatter::Prettyplease); + + for attr in &common_allow_attributes { + bindgen_builder = bindgen_builder.raw_line(*attr); + } + + bindgen_builder = bindgen_builder .header(evdev_headers_path.join("libevdev.h").display().to_string()) .header( evdev_headers_path @@ -136,6 +154,7 @@ fn main() { .display() .to_string(), ) + .header(cpp_dir.join("keylayoutmap_c.h").display().to_string()) .clang_arg(format!("--sysroot={}", ndk_sysroot.display())) .clang_arg(format!("-I{}", sysroot_include.display())); @@ -147,7 +166,7 @@ fn main() { let bindings = bindgen_builder .generate() - .expect("Unable to generate bindings"); + .expect("Unable to generate C bindings"); let out_path = manifest_dir.join("src"); diff --git a/evdev/src/main/rust/evdev_manager/src/bindings.rs b/evdev/src/main/rust/evdev_manager/src/bindings.rs index 1acd99bfe1..db1a441919 100644 --- a/evdev/src/main/rust/evdev_manager/src/bindings.rs +++ b/evdev/src/main/rust/evdev_manager/src/bindings.rs @@ -7,6 +7,7 @@ #![allow(dead_code)] #![allow(rustdoc::broken_intra_doc_links)] #![allow(rustdoc::private_intra_doc_links)] +#![allow(arithmetic_overflow)] /// If Bindgen could only determine the size and alignment of a /// type, it is represented like this. @@ -7626,4 +7627,62 @@ const _: () = { "Offset of field: libevdev_uinput::ctime", ][::std::mem::offset_of!(libevdev_uinput, ctime) - 32usize]; }; +pub type KeyLayoutMapHandle = *mut ::std::os::raw::c_void; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct AxisInfo { + pub mode: i32, + pub axis: i32, + pub highAxis: i32, + pub splitValue: i32, + pub flatOverride: i32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of AxisInfo"][::std::mem::size_of::() - 20usize]; + ["Alignment of AxisInfo"][::std::mem::align_of::() - 4usize]; + ["Offset of field: AxisInfo::mode"][::std::mem::offset_of!(AxisInfo, mode) - 0usize]; + ["Offset of field: AxisInfo::axis"][::std::mem::offset_of!(AxisInfo, axis) - 4usize]; + [ + "Offset of field: AxisInfo::highAxis", + ][::std::mem::offset_of!(AxisInfo, highAxis) - 8usize]; + [ + "Offset of field: AxisInfo::splitValue", + ][::std::mem::offset_of!(AxisInfo, splitValue) - 12usize]; + [ + "Offset of field: AxisInfo::flatOverride", + ][::std::mem::offset_of!(AxisInfo, flatOverride) - 16usize]; +}; +unsafe extern "C" { + pub fn keylayoutmap_load( + filename: *const ::std::os::raw::c_char, + out_handle: *mut KeyLayoutMapHandle, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn keylayoutmap_load_contents( + filename: *const ::std::os::raw::c_char, + contents: *const ::std::os::raw::c_char, + out_handle: *mut KeyLayoutMapHandle, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn keylayoutmap_map_key( + handle: KeyLayoutMapHandle, + scan_code: i32, + usage_code: i32, + out_key_code: *mut i32, + out_flags: *mut u32, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn keylayoutmap_map_axis( + handle: KeyLayoutMapHandle, + scan_code: i32, + out_axis_info: *mut AxisInfo, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn keylayoutmap_destroy(handle: KeyLayoutMapHandle); +} pub type __uint128_t = u128; diff --git a/evdev/src/main/rust/evdev_manager/src/lib.rs b/evdev/src/main/rust/evdev_manager/src/lib.rs index 598de37b42..7cffe6e098 100644 --- a/evdev/src/main/rust/evdev_manager/src/lib.rs +++ b/evdev/src/main/rust/evdev_manager/src/lib.rs @@ -1,4 +1,4 @@ -mod bindings; +mod bindings; // libevdev C bindings + KeyLayoutMap C interface bindings mod enums; mod evdev; From 325facba57c0588c2f55ff624d63b621be24e20a Mon Sep 17 00:00:00 2001 From: sds100 Date: Sat, 15 Nov 2025 12:38:40 +0100 Subject: [PATCH 006/199] style: reformat --- .../sds100/keymapper/base/trigger/TriggerErrorSnapshotTest.kt | 1 + evdev/src/main/cpp/keylayoutmap_c.cpp | 1 + evdev/src/main/cpp/keylayoutmap_c.h | 1 + 3 files changed, 3 insertions(+) diff --git a/base/src/test/java/io/github/sds100/keymapper/base/trigger/TriggerErrorSnapshotTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/trigger/TriggerErrorSnapshotTest.kt index 3466ea36fc..86c79e1889 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/trigger/TriggerErrorSnapshotTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/trigger/TriggerErrorSnapshotTest.kt @@ -204,3 +204,4 @@ class TriggerErrorSnapshotTest { ) } } + diff --git a/evdev/src/main/cpp/keylayoutmap_c.cpp b/evdev/src/main/cpp/keylayoutmap_c.cpp index e4342aa93e..eb1fceddab 100644 --- a/evdev/src/main/cpp/keylayoutmap_c.cpp +++ b/evdev/src/main/cpp/keylayoutmap_c.cpp @@ -98,3 +98,4 @@ void keylayoutmap_destroy(KeyLayoutMapHandle handle) { } // extern "C" + diff --git a/evdev/src/main/cpp/keylayoutmap_c.h b/evdev/src/main/cpp/keylayoutmap_c.h index 4787f6611c..618a3410b3 100644 --- a/evdev/src/main/cpp/keylayoutmap_c.h +++ b/evdev/src/main/cpp/keylayoutmap_c.h @@ -55,3 +55,4 @@ void keylayoutmap_destroy(KeyLayoutMapHandle handle); #endif // KEYLAYOUTMAP_C_H + From d122e26d8fe672403746451b0fa02742d8840e03 Mon Sep 17 00:00:00 2001 From: sds100 Date: Sat, 15 Nov 2025 15:50:30 +0100 Subject: [PATCH 007/199] #1897 manage the IEvdevCallback in C++ with JNI world and expose simple bindings to Rust --- .../main/cpp/evdev_callback_jni_manager.cpp | 142 ++++++++++++++++++ .../src/main/cpp/evdev_callback_jni_manager.h | 62 ++++++++ .../cpp/{ => wrappers}/keylayoutmap_c.cpp | 0 .../main/cpp/{ => wrappers}/keylayoutmap_c.h | 0 evdev/src/main/rust/evdev_manager/build.rs | 10 +- .../main/rust/evdev_manager/src/bindings.rs | 33 ++++ 6 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 evdev/src/main/cpp/evdev_callback_jni_manager.cpp create mode 100644 evdev/src/main/cpp/evdev_callback_jni_manager.h rename evdev/src/main/cpp/{ => wrappers}/keylayoutmap_c.cpp (100%) rename evdev/src/main/cpp/{ => wrappers}/keylayoutmap_c.h (100%) diff --git a/evdev/src/main/cpp/evdev_callback_jni_manager.cpp b/evdev/src/main/cpp/evdev_callback_jni_manager.cpp new file mode 100644 index 0000000000..0afb140a13 --- /dev/null +++ b/evdev/src/main/cpp/evdev_callback_jni_manager.cpp @@ -0,0 +1,142 @@ +/* + * JNI Manager implementation for IEvdevCallback + * Handles JNI binder registration and lifecycle management + * Exposes simple C-style functions for interacting with the callback + */ + +#include "evdev_callback_jni_manager.h" +#include "aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.h" +#include "logging.h" +#include +#include +#include +#include +#include + +using aidl::io::github::sds100::keymapper::evdev::IEvdevCallback; + +// Static storage for the callback instance +static std::shared_ptr g_callback = nullptr; +static std::mutex g_callback_mutex; + +extern "C" { + +// JNI method to register the callback from Java +JNIEXPORT jint JNICALL +Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_registerEvdevCallbackNative( + JNIEnv *env, + jobject thiz, + jobject jCallbackBinder) { + + if (!jCallbackBinder) { + return EVDEV_CALLBACK_ERROR_INVALID_ARG; + } + + AIBinder *callbackAIBinder = AIBinder_fromJavaBinder(env, jCallbackBinder); + if (!callbackAIBinder) { + return EVDEV_CALLBACK_ERROR_BINDER_CONVERSION_FAILED; + } + + const ::ndk::SpAIBinder spBinder(callbackAIBinder); + std::shared_ptr callback = IEvdevCallback::fromBinder(spBinder); + + if (!callback) { + return EVDEV_CALLBACK_ERROR_CALLBACK_CREATION_FAILED; + } + + std::lock_guard lock(g_callback_mutex); + g_callback = callback; + LOGI("Registered evdev callback via JNI"); + return EVDEV_CALLBACK_SUCCESS; +} + +// JNI method to unregister the callback +JNIEXPORT void JNICALL +Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_unregisterEvdevCallbackNative( + JNIEnv *env, + jobject thiz) { + + std::lock_guard lock(g_callback_mutex); + g_callback = nullptr; + LOGI("Unregistered evdev callback via JNI"); +} + +int evdev_callback_on_evdev_event_loop_started() { + std::lock_guard lock(g_callback_mutex); + if (!g_callback) { + return EVDEV_CALLBACK_ERROR_NO_CALLBACK; + } + + ndk::ScopedAStatus status = g_callback->onEvdevEventLoopStarted(); + if (!status.isOk()) { + return EVDEV_CALLBACK_ERROR_CALLBACK_FAILED; + } + + return EVDEV_CALLBACK_SUCCESS; +} + +int evdev_callback_on_evdev_event( + const char* device_path, + int64_t time_sec, + int64_t time_usec, + int32_t type, + int32_t code, + int32_t value, + int32_t android_code +) { + if (!device_path) { + return EVDEV_CALLBACK_ERROR_INVALID_ARG; + } + + std::lock_guard lock(g_callback_mutex); + if (!g_callback) { + return EVDEV_CALLBACK_ERROR_NO_CALLBACK; + } + + std::string device_path_str(device_path); + bool return_value = false; + ndk::ScopedAStatus status = g_callback->onEvdevEvent( + device_path_str, + time_sec, + time_usec, + type, + code, + value, + android_code, + &return_value + ); + + if (!status.isOk()) { + return EVDEV_CALLBACK_ERROR_CALLBACK_FAILED; + } + + return EVDEV_CALLBACK_SUCCESS; +} + +int evdev_callback_on_emergency_kill_system_bridge() { + std::lock_guard lock(g_callback_mutex); + if (!g_callback) { + return EVDEV_CALLBACK_ERROR_NO_CALLBACK; + } + + ndk::ScopedAStatus status = g_callback->onEmergencyKillSystemBridge(); + if (!status.isOk()) { + return EVDEV_CALLBACK_ERROR_CALLBACK_FAILED; + } + + return EVDEV_CALLBACK_SUCCESS; +} + +void evdev_callback_destroy(IEvdevCallbackHandle handle) { + if (!handle) { + return; + } + + std::shared_ptr* callback_ptr = + reinterpret_cast*>(handle); + + delete callback_ptr; +} + +} // extern "C" + diff --git a/evdev/src/main/cpp/evdev_callback_jni_manager.h b/evdev/src/main/cpp/evdev_callback_jni_manager.h new file mode 100644 index 0000000000..15a2c51c6b --- /dev/null +++ b/evdev/src/main/cpp/evdev_callback_jni_manager.h @@ -0,0 +1,62 @@ +/* + * JNI Manager for IEvdevCallback + * Manages the registration and lifecycle of the evdev callback via JNI + * and provides a simple C API for interacting with the callback + */ + +#ifndef EVDEV_CALLBACK_JNI_MANAGER_H +#define EVDEV_CALLBACK_JNI_MANAGER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Error codes for evdev callback operations +enum EvdevCallbackError { + EVDEV_CALLBACK_SUCCESS = 0, // Operation succeeded + EVDEV_CALLBACK_ERROR_INVALID_ARG = -1, // Invalid argument (null pointer, etc.) + EVDEV_CALLBACK_ERROR_BINDER_CONVERSION_FAILED = -2, // Failed to convert Java binder to AIBinder + EVDEV_CALLBACK_ERROR_CALLBACK_CREATION_FAILED = -3, // Failed to create callback from binder + EVDEV_CALLBACK_ERROR_NO_CALLBACK = -4, // No callback registered + EVDEV_CALLBACK_ERROR_INVALID_HANDLE = -5, // Invalid callback handle + EVDEV_CALLBACK_ERROR_CALLBACK_FAILED = -6, // Callback method returned error +}; + +// Opaque handle to IEvdevCallback +typedef void* IEvdevCallbackHandle; + +// Opaque handle to AIBinder (from Android NDK) +typedef void* AIBinderHandle; + +// Call onEvdevEventLoopStarted using stored callback +// Returns 0 on success, non-zero error code on failure +int evdev_callback_on_evdev_event_loop_started(); + +// Call onEvdevEvent using stored callback +// Returns 0 on success, non-zero error code on failure +int evdev_callback_on_evdev_event( + const char* device_path, + int64_t time_sec, + int64_t time_usec, + int32_t type, + int32_t code, + int32_t value, + int32_t android_code +); + +// Call onEmergencyKillSystemBridge using stored callback +// Returns 0 on success, non-zero error code on failure +int evdev_callback_on_emergency_kill_system_bridge(); + +// Destroy an IEvdevCallback handle +// Safe to call with NULL handle +void evdev_callback_destroy(IEvdevCallbackHandle handle); + +#ifdef __cplusplus +} +#endif + +#endif // EVDEV_CALLBACK_JNI_MANAGER_H + diff --git a/evdev/src/main/cpp/keylayoutmap_c.cpp b/evdev/src/main/cpp/wrappers/keylayoutmap_c.cpp similarity index 100% rename from evdev/src/main/cpp/keylayoutmap_c.cpp rename to evdev/src/main/cpp/wrappers/keylayoutmap_c.cpp diff --git a/evdev/src/main/cpp/keylayoutmap_c.h b/evdev/src/main/cpp/wrappers/keylayoutmap_c.h similarity index 100% rename from evdev/src/main/cpp/keylayoutmap_c.h rename to evdev/src/main/cpp/wrappers/keylayoutmap_c.h diff --git a/evdev/src/main/rust/evdev_manager/build.rs b/evdev/src/main/rust/evdev_manager/build.rs index 2f77416a51..31f7f8f6af 100644 --- a/evdev/src/main/rust/evdev_manager/build.rs +++ b/evdev/src/main/rust/evdev_manager/build.rs @@ -66,7 +66,9 @@ fn main() { .file(android_dir.join("input/InputDevice.cpp")) .file(android_dir.join("input/Input.cpp")) // C interface wrapper for KeyLayoutMap - .file(cpp_dir.join("keylayoutmap_c.cpp")) + .file(cpp_dir.join("wrappers/keylayoutmap_c.cpp")) + // JNI manager for IEvdevCallback so don't need to use a Rust binder library + .file(cpp_dir.join("evdev_callback_jni_manager.cpp")) // Android base library files .file(android_dir.join("libbase/result.cpp")) .file(android_dir.join("libbase/stringprintf.cpp")) @@ -81,6 +83,7 @@ fn main() { .file(aidl_dir.join("io/github/sds100/keymapper/evdev/IEvdevCallback.cpp")) // Include directories .include(&cpp_dir) + .include(&cpp_dir.join("wrappers")) .include(&android_dir) .include(&android_dir.join("input")) .include(&android_dir.join("libbase")) @@ -154,7 +157,10 @@ fn main() { .display() .to_string(), ) - .header(cpp_dir.join("keylayoutmap_c.h").display().to_string()) + .header(cpp_dir.join("evdev_callback_jni_manager.h").display().to_string()) + .header(cpp_dir.join("wrappers/keylayoutmap_c.h").display().to_string()) + // Generate a proper Rust enum for EvdevCallbackError + .rustified_enum("EvdevCallbackError") .clang_arg(format!("--sysroot={}", ndk_sysroot.display())) .clang_arg(format!("-I{}", sysroot_include.display())); diff --git a/evdev/src/main/rust/evdev_manager/src/bindings.rs b/evdev/src/main/rust/evdev_manager/src/bindings.rs index db1a441919..22fb0fd0f2 100644 --- a/evdev/src/main/rust/evdev_manager/src/bindings.rs +++ b/evdev/src/main/rust/evdev_manager/src/bindings.rs @@ -7627,6 +7627,39 @@ const _: () = { "Offset of field: libevdev_uinput::ctime", ][::std::mem::offset_of!(libevdev_uinput, ctime) - 32usize]; }; +#[repr(i32)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum EvdevCallbackError { + EVDEV_CALLBACK_SUCCESS = 0, + EVDEV_CALLBACK_ERROR_INVALID_ARG = -1, + EVDEV_CALLBACK_ERROR_BINDER_CONVERSION_FAILED = -2, + EVDEV_CALLBACK_ERROR_CALLBACK_CREATION_FAILED = -3, + EVDEV_CALLBACK_ERROR_NO_CALLBACK = -4, + EVDEV_CALLBACK_ERROR_INVALID_HANDLE = -5, + EVDEV_CALLBACK_ERROR_CALLBACK_FAILED = -6, +} +pub type IEvdevCallbackHandle = *mut ::std::os::raw::c_void; +pub type AIBinderHandle = *mut ::std::os::raw::c_void; +unsafe extern "C" { + pub fn evdev_callback_on_evdev_event_loop_started() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn evdev_callback_on_evdev_event( + device_path: *const ::std::os::raw::c_char, + time_sec: i64, + time_usec: i64, + type_: i32, + code: i32, + value: i32, + android_code: i32, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn evdev_callback_on_emergency_kill_system_bridge() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn evdev_callback_destroy(handle: IEvdevCallbackHandle); +} pub type KeyLayoutMapHandle = *mut ::std::os::raw::c_void; #[repr(C)] #[derive(Debug, Copy, Clone)] From 5bf7870e0df59e5e287ce7694eb9f06b544e3529 Mon Sep 17 00:00:00 2001 From: sds100 Date: Sat, 15 Nov 2025 17:05:37 +0100 Subject: [PATCH 008/199] #1897 WIP: refactor libevdev_jni.cpp into Rust --- evdev/.idea/gradle.xml | 1 + evdev/src/main/cpp/keylayoutmap_c.cpp | 102 +++ evdev/src/main/cpp/keylayoutmap_c.h | 58 ++ evdev/src/main/cpp/libevdev_jni.cpp | 708 ------------------ .../wrappers/evdev_callback_jni_manager.cpp | 277 +++++++ .../cpp/wrappers/evdev_callback_jni_manager.h | 110 +++ .../main/cpp/wrappers/ievdevcallback_c.cpp | 275 +++++++ .../src/main/cpp/wrappers/ievdevcallback_c.h | 99 +++ .../src/main/cpp/wrappers/keylayoutmap_c.cpp | 5 +- evdev/src/main/cpp/wrappers/keylayoutmap_c.h | 4 +- evdev/src/main/rust/evdev_manager/Cargo.lock | 39 +- evdev/src/main/rust/evdev_manager/Cargo.toml | 3 +- .../main/rust/evdev_manager/src/bindings.rs | 1 - .../rust/evdev_manager/src/device_manager.rs | 92 +++ .../src/main/rust/evdev_manager/src/evdev.rs | 12 +- .../src/evdevcallback_binder_observer.rs | 125 ++++ .../main/rust/evdev_manager/src/event_loop.rs | 402 ++++++++++ .../evdev_manager/src/input_event_lookup.rs | 539 +++++++++++++ .../main/rust/evdev_manager/src/jni_bridge.rs | 362 +++++++++ .../rust/evdev_manager/src/key_layout_map.rs | 449 +++++++++++ .../src/key_layout_map_manager.rs | 232 ++++++ evdev/src/main/rust/evdev_manager/src/lib.rs | 6 + .../main/rust/evdev_manager/src/observer.rs | 61 ++ .../main/rust/evdev_manager/src/tokenizer.rs | 289 +++++++ .../tests/key_layout_map_integration.rs | 165 ++++ 25 files changed, 3686 insertions(+), 730 deletions(-) create mode 100644 evdev/src/main/cpp/keylayoutmap_c.cpp create mode 100644 evdev/src/main/cpp/keylayoutmap_c.h delete mode 100644 evdev/src/main/cpp/libevdev_jni.cpp create mode 100644 evdev/src/main/cpp/wrappers/evdev_callback_jni_manager.cpp create mode 100644 evdev/src/main/cpp/wrappers/evdev_callback_jni_manager.h create mode 100644 evdev/src/main/cpp/wrappers/ievdevcallback_c.cpp create mode 100644 evdev/src/main/cpp/wrappers/ievdevcallback_c.h create mode 100644 evdev/src/main/rust/evdev_manager/src/device_manager.rs create mode 100644 evdev/src/main/rust/evdev_manager/src/evdevcallback_binder_observer.rs create mode 100644 evdev/src/main/rust/evdev_manager/src/event_loop.rs create mode 100644 evdev/src/main/rust/evdev_manager/src/input_event_lookup.rs create mode 100644 evdev/src/main/rust/evdev_manager/src/jni_bridge.rs create mode 100644 evdev/src/main/rust/evdev_manager/src/key_layout_map.rs create mode 100644 evdev/src/main/rust/evdev_manager/src/key_layout_map_manager.rs create mode 100644 evdev/src/main/rust/evdev_manager/src/observer.rs create mode 100644 evdev/src/main/rust/evdev_manager/src/tokenizer.rs create mode 100644 evdev/src/main/rust/evdev_manager/tests/key_layout_map_integration.rs diff --git a/evdev/.idea/gradle.xml b/evdev/.idea/gradle.xml index 1b387c73e6..e77e5a3224 100644 --- a/evdev/.idea/gradle.xml +++ b/evdev/.idea/gradle.xml @@ -1,5 +1,6 @@ + 0\'dan büyük olmalı! %d veya daha az olmalı! UI öğesi bulunamadı! + Komut %1$d saniye sonra zaman aşımına uğradı PRO Modunun başlatılması gerekiyor + Hız sınırına ulaşıldı. Saniyede yalnızca bir kez gönderebilirsiniz. WiFi\'yi aç/kapat @@ -718,6 +727,9 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Sesi kapat Sesi aç/kapat Sesi aç + Mikrofonu sessize al + Mikrofonun sesini aç + Mikrofon sesini aç/kapat Ses diyaloğunu göster Akışı artır %s akışını artır @@ -743,6 +755,9 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Mobil veriyi aç/kapat Mobil veriyi aç Mobil veriyi kapat + Hotspot\'u aç/kapat + Hotspot\'u aç + Hotspot\'u kapat Otomatik parlaklığı aç/kapat Otomatik parlaklığı kapat Otomatik parlaklığı aç @@ -866,9 +881,32 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Telefon araması başlat Telefon aramasını cevapla Telefon aramasını sonlandır + SMS gönder + SMS gönder: \"%s\"\" - %s + SMS oluştur + SMS oluştur: \"%s\" - %s + Ayarı ayarla: %1$s = %2$s + Sistem + Güvenli + Genel + Ayarı değiştir Ses çal En son bildirimi kaldır Tüm bildirimleri kaldır + Bildirim oluştur + Bildirimi göster: %1$s + Bildirim başlığı + Bildirim başlığını girin + Başlık boş olamaz + Bildirim içeriği + Bildirim içeriğini girin + İçerik boş olamaz + Bildirimi otomatik kapat + Şu süre sonra otomatik kapat + %d saniye + Test et + Test ediliyor… + Bildirim başarıyla gösterildi Cihaz kontrol ekranı HTTP isteği HTTP Yöntemi @@ -880,6 +918,31 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. İstek gövdesi (opsiyonel) Yetkilendirme başlığı (opsiyonel) Gerekirse \'Bearer\' ön ekini kullanın + Shell komutu + Shell komutu eylemi + Betik + Betik boş olamaz! + \'adb shell\' yazmayın! + Root olarak çalıştır + Standart + Root + ADB + Çalıştırma Modu + ADB modu akış çıktısını desteklemiyor + PRO Modunu Kur + PRO Modunu Kur (Desteklenmiyor) + Yapılandırma + Çıktı + Henüz çıktı yok. Komutu çalıştırmak için Test\'e tıklayın. + Test et + Çıktı + Çalıştırılıyor… + Başarılı + Başarısız + Çıkış kodu: %1$d + Root ile çalıştır: %s + ADB ile çalıştır: %s + Çalıştır: %s Uygulama öğesiyle etkileşime geç Key Mapper, menüler, sekmeler, düğmeler ve onay kutuları gibi uygulama öğelerini algılayabilir ve bunlarla etkileşime girebilir. Key Mapper\'ın ne yapmak istediğinizi bilmesi için, uygulama öğesiyle etkileşiminizi kaydetmeniz gerekmektedir. Kaydetmeye başla @@ -916,6 +979,20 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Kaynak kimliğini görüntüle Özgün kimlik Etkileşim türü + Uygulamayı durmaya zorla + Uygulamayı son uygulamalardan kapat ve temizle + Ayarı değiştir + Anahtar + Değer + Ayar anahtarı boş olamaz + Ayar değeri boş olamaz + Not: Yalnızca ayar değerlerini değiştirmek, sistemin değişikliği işlemesi için yeterli olmayabilir. Bazı ayarların etkili olması için ek eylemler veya yayınlar gerekir. + Test et + Ayar başarıyla değiştirildi + İzin ver + Ayar seçin + Ayar bulunamadı + Mevcut ayarı seçin Navigasyon @@ -932,6 +1009,20 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Bildirimler Özel + + Uygulamalar + Medya + Bluetooth + Ekran + El feneri + WiFi + Klavye + Kilit + Telefon + Güç + Cihaz + Zaman + Boolean Boolean dizisi @@ -972,15 +1063,6 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Java programlama dilinde geçerli Kısa sayıların virgülle ayrılmış bir listesi. Örn. 3242,12354 Intent bayrakları bit bayrakları olarak saklanır. Bu bayraklar Intent\'in nasıl işleneceğini değiştirir. Bir Etkinlik Intent\'i için bu alan boş bırakılırsa, Key Mapper varsayılan olarak FLAG_ACTIVITY_NEW_TASK kullanır. Daha fazla bilgi için Android geliştirici belgelerini görmek üzere \'dokümanlar\'a dokunun. - - Öğreticiyi geç - İlk anahtar haritanı oluştur! - Bir anahtar haritası, bir düğmeye basıldığında cihazınızın ne yapacağını belirten bir kuraldır. - Bir eylem seçin - Bir eylem, tetikleyiciye bastığınızda olması gereken şeydir. - Bir kısıtlama seç (isteğe bağlı) - Anahtar haritasının yalnızca belirli durumlarda çalışmasını istiyorsanız, örneğin, bir uygulama açıkken. - GitHub Web Sitesi @@ -1001,24 +1083,42 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Çevirmen (Çekçe) Çevirmen (İspanyolca) + + Key Mapper\'ı Destekleyin ❤️ + Deneyiminizi yükseltmek için eklentileri seçin + Desteğiniz Key Mapper\'ı ayakta tutuyor! + "Kayan düğme özelliği ezber bozan bir yenilik" + Google Play yorumcusu + Kayan düğmeler ezber bozan bir yenilik! + Kayan düğmeler özelliğini keyifle kullanıyorum. + Kayan düğmeler harika bir eklenti. + Telefon deneyimimin vazgeçilmez bir parçası. + Bir kez öde, sonsuza dek kullan + Videoyu izle + Şimdi satın al (%s) + Daha fazla bilgi al + Ekran üstü kayan düğmeler + Yeterli düğmeniz yok mu? Herhangi bir uygulamada, oyunda veya kilit ekranınızda anında kısayollar ve makrolar oluşturun! + Oyunlarda kullan + Kilit ekranında kullan + Yan tuş ve Asistan tetikleyicisi + İşe yaramaz yan tuş mu? Asistan düğmesini veya cihazınızın yan tuş kısayolunu istediğiniz her şeye yeniden atayın! + Ekran kapalı + Bixby + Gemini Key Mapper: Yan Tuş Herhangi bir asistan Yan tuş/güç düğmesi Sesli asistan - Gelişmiş tetikleyiciler - Geliştirici, reklamların sürdürülebilir veya kullanıcı dostu bir gelir modeli olduğuna inanmıyor, bu nedenle bu ücretli tetikleyiciler geliştirmeyi desteklemeye yardımcı oluyor ❤️. Ayrıca öncelikli destek de alacaksınız. - Yan tuş ve Asistan tetikleyicisi - Yan tuşunuzu, güç düğmenizi veya cihaz asistanınızı yeniden eşleyebileceğinizi biliyor muydunuz? Asistanı veya güç menüsünü başlatmak yerine, cihazınız seçtiğiniz bir eylemi gerçekleştirebilir. Ekran kapalıyken bile çalışır! Yan tuş tetikleyici özelliğini satın almanız gerekiyor. - Daha fazla bilgi Kayan düğmeleri satın almanız gerekiyor. Düğme silindi. Kayan düğme Satın alma doğrulanamıyor. İnternet bağlantınız var mı? Kilidi aç (%s) - Kullan + Kullan Yükleniyor… Satın alındı! Fiyatı tekrar almayı dene @@ -1033,25 +1133,12 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Bir şeyler ters gitti 😕 Tekrar dene Geliştiriciyle iletişime geç - Yan tuş tetikleyici özelliğini satın almanız gerekiyor! Tuş eşlemesine dokunun ve ardından \'Gelişmiş tetikleyiciler\'e tıklayarak satın alın. - Kayan düğmeler özelliğini satın almanız gerekiyor! Tuş eşlemesine dokunun ve ardından \'Gelişmiş tetikleyiciler\'e tıklayarak satın alın. - Uygulamayı desteklediğiniz için teşekkürler ❤️! + Yan tuş tetikleyici özelliğini satın almalısınız! Tuş eşlemesine dokunun ve ardından mağazaya tıklayarak satın alın. + Kayan düğmeler özelliğini satın almalısınız! Tuş eşlemesine dokunun ve ardından mağazaya tıklayarak satın alın. + Uygulamayı desteklediğiniz için teşekkürler! Satın alımınız başarılı oldu. Key Mapper\'ın ücretli bir kullanıcısı olarak uygulamayı kullanmanıza yardımcı olmak için öncelikli destek alacaksınız. Artık mağazada geliştiriciyle iletişime geçmek için bir düğme bulunmaktadır. - Gelişmiş tetikleyiciler ücretli bir özelliktir ancak siz FOSS yapısını indirdiniz ve bu yapı Google Play faturalandırmasını içermiyor. Bu özelliğe erişmek için lütfen Key Mapper\'ı Google Play\'den indirin. + Gelişmiş tetikleyiciler ücretli bir özelliktir ancak siz, bu kapalı kaynak modülünü veya Google Play faturalandırmasını içermeyen Key Mapper\'ın FOSS sürümünü indirdiniz. Bu özelliğe erişmek için lütfen Key Mapper\'ı Google Play\'den indirin. Play sürümünü indir - - DPAD düğmelerini yeniden eşlemek ister misiniz? - Aşağıdaki adımları izleyerek Key Mapper GUI Klavyesini ayarlamanız gerekiyor. - 1. Klavye uygulamasını yükle - Yükle - Yüklendi - 2. Klavyeyi etkinleştir - Etkinleştir - Etkin - 3. Klavyeyi kullan - Klavyeyi değiştir - Klavye seçildi - Kurulum tamamlandı! \'Bitti\'ye dokunun ve DPAD tetikleyiciniz çalışmalı. Düğme algılanmadı mı? Erişilebilirlik servisi yerine tetikleyicinizi kaydetmek için Key Mapper GUI Klavye uygulamasını deneyebilirsiniz. @@ -1078,12 +1165,27 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. İptal Bitti Düğmenin metni olmalı! + Bildirim panelinin üzerinde göster + Klavyenin üzerinde göster Kayan düğmeler Kayan düğmeler istediğiniz uygulamaların üzerinde görünür. Gerçek düğmeler gibi çalışırlar ve onları istediğiniz gibi yerleştirebilir, stil verebilir ve eşleyebilirsiniz. Kayan düğme %s (%s) Silinen kayan düğme - Kısıtlamalar - Bu düğmenin yalnızca bazı uygulamalarda ekranda olmasını ister misiniz? Bu tuş eşlemesi için “Kısıtlamalar” sekmesinde bir “Ön plandaki uygulama” kısıtlaması ekleyin. + Daha iyi Caps Lock uyumluluğu + PRO modu, Caps Lock yeniden eşlemesi için daha iyi uyumluluk sağlar. \'PRO modunu kullan\' seçeneğine dokunun ve tekrar kaydedin. + PRO modunu kullan + Ekran kapalıyken yeniden eşleme? + PRO modu ile artık herhangi bir tetikleyici ekran kapalıyken ücretsiz olarak çalışabilir! Tekrar kaydetmeniz gerekecek. + PRO modunu kullan + Uygulama sabitleme uyarısı + Geri düğmesini tetikleyici olarak kullanmak uygulama sabitleme ile çakışabilir ve kilit açıldığında siyah ekrana neden olabilir. Yeniden başlatma sorunu çözecektir. + Klavye simgesi + ⌨ simgesi, bir Android kısıtlaması nedeniyle bu tetikleyicinin çalışması için Key Mapper giriş yöntemini kullanmanız gerektiği anlamına gelir. + Zil modu eylemleri + Rahatsız Etmeyin ayarlarıyla çakışmaları önlemek için zil modu eylemleri için PRO modunu kullanmayı düşünün. + PRO modunu kullan + Belirli uygulamalarla sınırla? + Kısıtlamalar sekmesinde kısıtlamalar ekleyin. Bir düzen seçin Arka Yardım @@ -1096,8 +1198,6 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Düğmeyi yapılandır Düzeni düzenle Android 11 veya daha yeni bir sürüm gerektiriyor. - Yeterince düğmeniz yok mu? Artık kendi düğmelerinizi yapabilirsiniz! - Kayan düğmeler istediğiniz uygulamaların üzerinde görünür. Gerçek düğmeler gibi çalışırlar ve onları istediğiniz gibi yerleştirebilir, stil verebilir ve eşleyebilirsiniz. Tetikleyici Eylemler @@ -1108,7 +1208,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Yeni tuş eşlemesi Yeni düzen Bir tuş eşlemesini yapılandırmak için dokunun.\nDaha fazla seçenek için uzun basın. - Bir tuş eşlemesi oluşturun! + Bir tuş eşlemesi oluşturun!\n\nTuş eşlemesi, bir düğmeye basıldığında cihazınıza ne yapacağını söyleyen bir kuraldır. Yeterince düğmeniz yok mu? Kendi düğmelerinizi yapın! Kayan düğmeler istediğiniz uygulamaların üzerinde görünür. Gerçek düğmeler gibi çalışırlar ve onları istediğiniz gibi yerleştirebilir, stil verebilir ve eşleyebilirsiniz. @@ -1131,7 +1231,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Evet, sil İptal Kayan düzenleri gizle - Kayan düğmeleri, bir tetikleyici oluştururken Gelişmiş Tetikleyiciler düğmesinde bulabilirsiniz. + Tetikleyici oluştururken Mağaza düğmesinde kayan düğmeleri bulabilirsiniz. Kayan düğmeler Menü Sırala @@ -1213,7 +1313,6 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Parmak izi okuyucuda aşağı kaydır Parmak izi okuyucuda sola kaydır Parmak izi okuyucuda sağa kaydır - Gelişmiş tetikleyiciler Tuş kodu %d Tarama kodu %d Kodu tara @@ -1250,7 +1349,11 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Android 13 veya daha yeni bir sürüm gerektiriyor. Bu cihaz parlaklık değişikliğine izin vermiyor. Parlaklık değişikliği + Ses akışı + Varsayılan (sistem kontrollü) + Seçenekler Desteklenmiyor + Desteklenmiyor Tuş eşlemelerinin yalnızca belirli durumlarda çalışmasını istiyorsanız kısıtlamalar ekleyin. Son kullanılan kısıtlamalar @@ -1279,6 +1382,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. PRO modu + Kurulum Önemli! PRO modu ile tuşları yeniden eşlemek tehlikelidir ve yanlış eşlerseniz çalışmalarını durdurabilir.\n\nBir hata yaparsanız, güç ve ses düğmelerini 30 saniye basılı tutarak cihazınızı zorla yeniden başlatmanız gerekebilir — bunun nasıl yapılacağı konusunda cihazınızın kılavuzuna veya internete başvurun. %d… @@ -1286,7 +1390,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Anladım Kurulum Root algılandı - Key Mapper\'a root izni vererek kurulum sürecini atlayabilirsiniz. Bu, Key Mapper\'ın açılışta PRO modunu otomatik olarak başlatmasını da sağlayacaktır. + Key Mapper\'a root izni vererek kurulum sürecini atlayabilirsiniz. Bu, Key Mapper\'ın WiFi bağlantısını beklemeden PRO modunu otomatik olarak başlatmasını da sağlar. PRO modunu başlat Shizuku algılandı Key Mapper\'a Shizuku izni vererek kurulum sürecini atlayabilirsiniz. @@ -1302,8 +1406,10 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Bu ayarlar, uyarıyı kabul edene kadar kullanılamaz. PRO modu hizmeti çalışıyor Durdur - Açılışta otomatik olarak başlat - Cihazınızı her açtığınızda veya yeniden başlattığınızda PRO Modu kendini başlatacaktır + Otomatik başlat ve çalışır durumda tut + PRO Modu, cihazınızı başlattığınızda, Key Mapper\'ı açtığınızda veya beklenmedik bir şekilde kapandığında kendini başlatır. + Tuş olayı eylemleri + Tuş olayı eylemlerinin nasıl gerçekleştirileceğini seçin Acil durum ipucu Güç düğmeniz çalışmazsa, PRO Modu\'nu devre dışı bırakmak için güç düğmesini 10 saniye basılı tutun ve bırakın. Kurulum sihirbazı @@ -1316,9 +1422,9 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Ayarlara git Servisi başlat Erişilebilirlik servisini etkinleştir - Key Mapper, PRO modunu kurmanıza yardımcı olmak için bir servis kullanır. Sıradan tuş eşlemeleri için de kullanışlıdır. + Key Mapper bu servisi kurulumda yardımcı olması için kullanır. Ayrıca sıradan tuş eşlemeleri için de gereklidir. Geliştirici seçeneklerini etkinleştir - Key Mapper\'ın PRO modunu başlatmak için Android Debug Bridge\'i kullanması gerekir ve bunun için geliştirici seçeneklerini etkinleştirmeniz gerekir. + Key Mapper\'ın Android Hata Ayıklama Köprüsü\'nü (ADB) kullanması gerekir, bu nedenle geliştirici seçeneklerini etkinleştirmelisiniz. Bir WiFi ağına bağlan Key Mapper\'ın ADB\'yi etkinleştirmek için bir WiFi ağına ihtiyacı var. İnternet bağlantısına ihtiyacınız yok.\n\nWiFi ağı yok mu? Başka birinin telefonundan bir hotspot kullanın. Kablosuz hata ayıklamayı etkinleştir @@ -1326,10 +1432,12 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Kablosuz hata ayıklamayı eşleştir Key Mapper\'ın yeniden eşleme ve giriş hizmetini başlatabilmesi için kablosuz hata ayıklama ile eşleşmesi gerekir. Servisi başlat - Key Mapper\'ın PRO modu hizmetini başlatmak için Android Debug Bridge\'e bağlanması gerekir. + Key Mapper\'ın servisi başlatmak için ADB\'ye bağlanması gerekir. Bildirimlere izin ver Key Mapper\'ın kurulum sürecinde herhangi bir sorun olması durumunda sizi bilgilendirmek için izne ihtiyacı var. İzin ver + Uyumsuz USB yapılandırması + PRO Modunun cihazınızı her kilitlediğinizde sonlandırılmaması için varsayılan USB yapılandırmanız olarak \'Veri aktarımı yok\' seçeneğini seçmelisiniz. Kurulum asistanı PRO modu çalışıyor Artık ekran kapalıyken tuşları yeniden eşleyebilir ve daha fazla eylem kullanabilirsiniz. @@ -1352,12 +1460,12 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Eşleştirme kodunu gönderirken eşleştirme kodu açılır penceresini ekranda tutun Eşleştirme kodunu gir PRO modu ile ne yapabilirim? - 📲 Güç düğmeniz gibi daha fazla tuşu yeniden eşleyebilirsiniz.\n⌨️ Tuş kodu eylemleriyle herhangi bir klavyeyi kullanın.\n⭐️ Aşağıdaki eylemlerin kilidi açılır: WiFi, Bluetooth, mobil veri, NFC ve uçak modu, durum çubuğunu daralt ve cihazı uyut/uyandır. + 📲 Güç düğmeniz gibi daha fazla düğmeyi yeniden eşleyebilirsiniz.\n⌨️ Tuş kodu eylemleriyle herhangi bir ekran klavyesini kullanabilirsiniz.\n⭐️ Ve daha birçok eylemi kullanabilirsiniz. PRO modu bilgilerini göster Kapat PRO modu beklenmedik şekilde durdu Otomatik olarak yeniden başlatılıyor… - Otomatik olarak yeniden başlatılmıyor. Servisi siz durdurmuyorsanız, sorunu geliştiriciye bildirin. + Son otomatik başlatma 5 dakikadan daha kısa bir süre önce olduğu için otomatik olarak yeniden başlatılmıyor. Servisi siz sonlandırmıyorsanız, sorunu geliştiriciye bildirin. Keşfet Neyi yeniden eşlemek istersiniz? @@ -1375,7 +1483,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Özel Çentik Kilit ekranı - Telefonunuzu bir üst seviyeye taşıyın + Deneyiminizi yükseltin Kayan düğmeler, herhangi bir uygulama, oyun veya kilit ekranınızdaki anlık kısayollardır. Desteğiniz Key Mapper\'ı hayatta tutar ❤️ Çalışırken görün Kapat @@ -1404,6 +1512,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Bu cihazı yeniden eşleyebilirsiniz Bu özelliği kullanabilirsiniz Ekran kapalıyken yeniden eşle + Ekran kapalıyken yeniden eşle Seçenekler Gereksinimler Düğmeleriniz hala algılanamıyorsa, lütfen Discord sunucumuza katılın ve bize bildirin. Düğmelerinizi yeniden eşlemenize yardımcı olmak için elimizden gelenin en iyisini yapacağız. @@ -1426,9 +1535,32 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Yön tuşu (D-Pad) düğmesi Diğer basit düğmeler Yön tuşu (DPAD) düğmelerini yeniden eşlerken normal ekran klavyenizi kullanamayacaksınız. - Key Mapper Klavyesi - Klavyeyi etkinleştir - Klavye seç + Key Mapper giriş yöntemi + Etkinleştir + Seç Çalışıyor + + Tuş olayı eylemini düzelt + Bu eylemi kullanmak için ek adımlar vardır. Hangi yöntemi kullanmak istediğinizi seçin: + Key Mapper giriş yöntemi + Ekran klavyesi yok + Normal ekran klavyenizi her zaman kullanabilirsiniz + Uygulamalar ve oyunlarla daha iyi uyumluluk + Key Mapper giriş yöntemini etkinleştir + Key Mapper giriş yöntemini kullan + Yazarken normal ekran klavyesine otomatik olarak geç + Bunu istediğiniz zaman Ayarlar\'dan değiştirebilirsiniz + Kurulum + Seçenekler + SMS Testi + Başarıyla gönderildi! + SMS göndermek operatör veya dolaşım ücretlerine neden olabilir. Key Mapper geliştiricileri herhangi bir maliyetten sorumlu değildir. + Açıklama + Zaman aşımı + + Kayan düğmeleri beğendiniz mi? + Görüşlerinizi merak ediyoruz! Lütfen başkalarının da bu özelliği keşfetmesine yardımcı olmak için Google Play\'de bir yorum bırakmayı düşünün. ❤️ + Kapat + Evdev olayı yazılamadı From b67de8183332243389c039ded5c1f8cd5f0cdb9f Mon Sep 17 00:00:00 2001 From: sds100 Date: Sat, 20 Dec 2025 21:07:29 +0000 Subject: [PATCH 161/199] #1926 WIP: refactor code to use new EvdevGrabController --- app/proguard-rules.pro | 4 +- .../detection/KeyMapDetectionController.kt | 18 +- .../base/input/EvdevDevicesDelegate.kt | 12 +- .../keymapper/base/input/InputEventHub.kt | 45 ++- .../KeyMapDetectionControllerTest.kt | 114 +++++--- ...iceRequest.aidl => GrabTargetKeyCode.aidl} | 2 +- .../common/models/GrabDeviceRequest.kt | 15 +- .../common/models/GrabTargetKeyCode.kt | 46 +++ .../keylayout/key_layout_map_manager.rs | 7 +- .../core/src/android/keylayout/mod.rs | 2 +- .../core/src/evdev_grab_controller.rs | 181 +++++++----- .../rust/evdev_manager/core/src/event_loop.rs | 273 ++++++++++-------- .../core/src/grab_device_request.rs | 7 - .../core/src/grab_target_key_code.rs | 8 + .../evdev_manager/core/src/grabbed_device.rs | 9 +- .../core/src/grabbed_device_handle.rs | 14 + .../main/rust/evdev_manager/core/src/lib.rs | 3 +- .../jni/src/evdev_jni_observer.rs | 72 +++++ .../rust/evdev_manager/jni/src/jni_bridge.rs | 162 +++++------ .../keymapper/evdev/IEvdevCallback.aidl | 3 +- .../keymapper/sysbridge/ISystemBridge.aidl | 6 +- .../sysbridge/service/SystemBridge.kt | 12 +- 22 files changed, 641 insertions(+), 374 deletions(-) rename common/src/main/aidl/io/github/sds100/keymapper/common/models/{GrabDeviceRequest.aidl => GrabTargetKeyCode.aidl} (62%) create mode 100644 common/src/main/java/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.kt delete mode 100644 evdev/src/main/rust/evdev_manager/core/src/grab_device_request.rs create mode 100644 evdev/src/main/rust/evdev_manager/core/src/grab_target_key_code.rs create mode 100644 evdev/src/main/rust/evdev_manager/core/src/grabbed_device_handle.rs diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index b28fae908c..1e905036f2 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -140,7 +140,7 @@ # Keep parcelable classes used in AIDL -keep class io.github.sds100.keymapper.common.models.GrabbedDeviceHandle { *; } -keep class io.github.sds100.keymapper.common.models.EvdevDeviceInfo { *; } --keep class io.github.sds100.keymapper.common.models.GrabDeviceRequest { *; } +-keep class io.github.sds100.keymapper.common.models.GrabTargetKeyCode { *; } -keep class io.github.sds100.keymapper.common.models.ShellResult { *; } # Keep all rikka.hidden classes and interfaces as they contain AIDL files @@ -244,4 +244,4 @@ -dontwarn android.view.DisplayInfo -dontwarn android.view.IWindowManager** -dontwarn com.android.internal.app.** --dontwarn com.android.internal.policy.** \ No newline at end of file +-dontwarn com.android.internal.policy.** diff --git a/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt b/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt index 9b4893eac1..1e83e8a149 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt @@ -13,7 +13,7 @@ import io.github.sds100.keymapper.base.trigger.EvdevTriggerKey import io.github.sds100.keymapper.base.trigger.RecordTriggerController import io.github.sds100.keymapper.base.trigger.RecordTriggerState import io.github.sds100.keymapper.common.models.EvdevDeviceInfo -import io.github.sds100.keymapper.common.models.GrabDeviceRequest +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode import io.github.sds100.keymapper.system.inputevents.KMEvdevEvent import io.github.sds100.keymapper.system.inputevents.KMInputEvent import kotlinx.coroutines.CoroutineScope @@ -37,7 +37,7 @@ class KeyMapDetectionController( companion object { private const val INPUT_EVENT_HUB_ID = "key_map_controller" - fun getEvdevGrabRequests(algorithm: KeyMapAlgorithm): List { + fun getEvdevGrabRequests(algorithm: KeyMapAlgorithm): List { val deviceKeyEventMap = mutableMapOf>() for ((index, trigger) in algorithm.triggers.withIndex()) { @@ -64,7 +64,13 @@ class KeyMapDetectionController( } return deviceKeyEventMap.map { (device, keyEvents) -> - GrabDeviceRequest(device, keyEvents.toIntArray()) + GrabTargetKeyCode( + name = device.name, + bus = device.bus, + vendor = device.vendor, + product = device.product, + extraKeyCodes = keyEvents.toIntArray(), + ) } } } @@ -92,7 +98,7 @@ class KeyMapDetectionController( if (isPaused) { algorithm.loadKeyMaps(emptyList()) - inputEventHub.setGrabbedEvdevDevices(INPUT_EVENT_HUB_ID, emptyList()) + inputEventHub.setGrabTargets(INPUT_EVENT_HUB_ID, emptyList()) } else { algorithm.loadKeyMaps(keyMapList) // Determine which evdev devices need to be grabbed depending on the state @@ -103,7 +109,7 @@ class KeyMapDetectionController( "Grab evdev devices for key map detection: ${grabRequests.joinToString()}", ) - inputEventHub.setGrabbedEvdevDevices( + inputEventHub.setGrabTargets( INPUT_EVENT_HUB_ID, grabRequests, ) @@ -143,7 +149,7 @@ class KeyMapDetectionController( fun teardown() { algorithm.reset() - inputEventHub.setGrabbedEvdevDevices(INPUT_EVENT_HUB_ID, emptyList()) + inputEventHub.setGrabTargets(INPUT_EVENT_HUB_ID, emptyList()) inputEventHub.unregisterClient(INPUT_EVENT_HUB_ID) } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt index de8133f38d..dd6a7c41de 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt @@ -2,7 +2,7 @@ package io.github.sds100.keymapper.base.input import androidx.annotation.RequiresApi import io.github.sds100.keymapper.common.models.EvdevDeviceInfo -import io.github.sds100.keymapper.common.models.GrabDeviceRequest +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle import io.github.sds100.keymapper.common.utils.Constants import io.github.sds100.keymapper.common.utils.onFailure @@ -45,7 +45,7 @@ class EvdevDevicesDelegate @Inject constructor( // Use a channel so there are no race conditions when grabbing and that all // grab operations finish in the correct order to completion. - private val grabDevicesChannel: Channel> = Channel(capacity = 16) + private val grabDevicesChannel: Channel> = Channel(capacity = 16) // All the evdev devices on the device, regardless of whether they are grabbed. val allDevices: MutableStateFlow> = MutableStateFlow(emptyList()) @@ -80,9 +80,9 @@ class EvdevDevicesDelegate @Inject constructor( } } - private fun invalidateGrabbedDevices(devices: List) { + private fun invalidateGrabbedDevices(devices: List) { systemBridgeConnectionManager - .run { bridge -> bridge.setGrabbedDevices(devices.toTypedArray()) } + .run { bridge -> bridge.setGrabTargets(devices.toTypedArray()) } .onSuccess { grabbedDevices -> onGrabbedDevicesChanged(grabbedDevices?.filterNotNull() ?: emptyList()) }.onFailure { error -> @@ -92,7 +92,7 @@ class EvdevDevicesDelegate @Inject constructor( } } - fun setGrabbedDevices(devices: List) { + fun setGrabTargets(devices: List) { grabDevicesChannel.trySend(devices) } @@ -104,7 +104,7 @@ class EvdevDevicesDelegate @Inject constructor( return grabbedDevicesById.value.values.toList() } - private fun onGrabbedDevicesChanged(devices: List) { + fun onGrabbedDevicesChanged(devices: List) { Timber.i("Grabbed devices changed: [${devices.joinToString { it.name }}]") grabbedDevicesById.value = diff --git a/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt b/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt index 77e5de20c9..a3e096b56d 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt @@ -6,7 +6,8 @@ import androidx.annotation.RequiresApi import io.github.sds100.keymapper.base.BuildConfig import io.github.sds100.keymapper.base.system.inputmethod.ImeInputEventInjector import io.github.sds100.keymapper.common.models.EvdevDeviceInfo -import io.github.sds100.keymapper.common.models.GrabDeviceRequest +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode +import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle import io.github.sds100.keymapper.common.utils.Constants import io.github.sds100.keymapper.common.utils.KMError import io.github.sds100.keymapper.common.utils.KMResult @@ -182,17 +183,21 @@ class InputEventHubImpl @Inject constructor( when (event.action) { KeyEvent.ACTION_DOWN -> { Timber.d( - "Key down ${KeyEvent.keyCodeToString( - event.keyCode, - )}: keyCode=${event.keyCode}, scanCode=${event.scanCode}, deviceId=${event.deviceId}, metaState=${event.metaState}, source=${event.source}", + "Key down ${ + KeyEvent.keyCodeToString( + event.keyCode, + ) + }: keyCode=${event.keyCode}, scanCode=${event.scanCode}, deviceId=${event.deviceId}, metaState=${event.metaState}, source=${event.source}", ) } KeyEvent.ACTION_UP -> { Timber.d( - "Key up ${KeyEvent.keyCodeToString( - event.keyCode, - )}: keyCode=${event.keyCode}, scanCode=${event.scanCode}, deviceId=${event.deviceId}, metaState=${event.metaState}, source=${event.source}", + "Key up ${ + KeyEvent.keyCodeToString( + event.keyCode, + ) + }: keyCode=${event.keyCode}, scanCode=${event.scanCode}, deviceId=${event.deviceId}, metaState=${event.metaState}, source=${event.source}", ) } @@ -229,7 +234,7 @@ class InputEventHubImpl @Inject constructor( } @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) - override fun setGrabbedEvdevDevices(clientId: String, devices: List) { + override fun setGrabTargets(clientId: String, devices: List) { if (!clients.containsKey(clientId)) { throw IllegalArgumentException( "This client $clientId is not registered when trying to grab devices!", @@ -255,7 +260,13 @@ class InputEventHubImpl @Inject constructor( val devices = evdevDevicesDelegate.allDevices.value val grabRequests = devices.map { - GrabDeviceRequest(device = it, extraKeyCodes = intArrayOf()) + GrabTargetKeyCode( + name = it.name, + bus = it.bus, + vendor = it.vendor, + product = it.product, + extraKeyCodes = intArrayOf(), + ) }.toSet() clients[clientId] = clients[clientId]!!.copy(grabRequests = grabRequests) @@ -359,10 +370,16 @@ class InputEventHubImpl @Inject constructor( .firstBlocking() } + @RequiresApi(Build.VERSION_CODES.Q) + override fun onGrabbedDevicesChanged(devices: Array?) { + val devicesList = devices?.filterNotNull()?.toList() ?: emptyList() + evdevDevicesDelegate.onGrabbedDevicesChanged(devicesList) + } + @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) private fun invalidateGrabbedDevices() { val devicesToGrab = clients.values.flatMap { it.grabRequests }.toSet() - evdevDevicesDelegate.setGrabbedDevices(devicesToGrab.toList()) + evdevDevicesDelegate.setGrabTargets(devicesToGrab.toList()) } private data class ClientContext( @@ -370,10 +387,12 @@ class InputEventHubImpl @Inject constructor( /** * The evdev devices that this client wants to grab. */ - val grabRequests: Set, + val grabRequests: Set, val evdevEventTypes: Set, ) { - private val devicesSet: Set = grabRequests.map { it.device }.toSet() + private val devicesSet: Set = grabRequests.map { + EvdevDeviceInfo(name = it.name, bus = it.bus, vendor = it.vendor, product = it.product) + }.toSet() fun grabbedDevice(device: EvdevDeviceInfo): Boolean { return devicesSet.contains(device) @@ -397,7 +416,7 @@ interface InputEventHub { fun unregisterClient(clientId: String) fun getGrabbedDevices(): List - fun setGrabbedEvdevDevices(clientId: String, devices: List) + fun setGrabTargets(clientId: String, devices: List) fun grabAllEvdevDevices(clientId: String) /** diff --git a/base/src/test/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionControllerTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionControllerTest.kt index fda233d480..4c7227a03a 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionControllerTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionControllerTest.kt @@ -12,7 +12,7 @@ import io.github.sds100.keymapper.base.trigger.Trigger import io.github.sds100.keymapper.base.utils.parallelTrigger import io.github.sds100.keymapper.base.utils.singleKeyTrigger import io.github.sds100.keymapper.common.models.EvdevDeviceInfo -import io.github.sds100.keymapper.common.models.GrabDeviceRequest +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode import io.github.sds100.keymapper.system.inputevents.Scancode import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope @@ -106,8 +106,11 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf( KeyEvent.KEYCODE_BUTTON_X, KeyEvent.KEYCODE_BUTTON_Y, @@ -154,11 +157,11 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE, extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_X), ), - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE_2, extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_Y), ), @@ -198,11 +201,14 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf(), ), - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE_2, extraKeyCodes = intArrayOf(), ), @@ -237,11 +243,14 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf(), ), - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE_2, extraKeyCodes = intArrayOf(), ), @@ -269,8 +278,11 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf(), ), ), @@ -318,7 +330,7 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE, extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_B), ), @@ -379,11 +391,14 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf(), ), - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE_2, extraKeyCodes = intArrayOf(), ), @@ -412,8 +427,11 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf(), ), ), @@ -441,8 +459,11 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf(), ), ), @@ -492,8 +513,11 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf(), ), ), @@ -526,8 +550,11 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf(), ), ), @@ -561,7 +588,7 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE, extraKeyCodes = intArrayOf( KeyEvent.KEYCODE_BUTTON_X, @@ -598,7 +625,7 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE, extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_X), ), @@ -629,7 +656,7 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE, extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_A), ), @@ -662,8 +689,11 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf(), ), ), @@ -698,7 +728,7 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE, extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_C), ), @@ -733,7 +763,7 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE, extraKeyCodes = intArrayOf( KeyEvent.KEYCODE_BUTTON_X, @@ -802,8 +832,11 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf(), ), ), @@ -844,7 +877,7 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE, extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_X), ), @@ -900,7 +933,7 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE, extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_X), ), @@ -966,7 +999,7 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( + GrabTargetKeyCode( device = FAKE_CONTROLLER_EVDEV_DEVICE, extraKeyCodes = expectedExtraKeyCodes, ), @@ -1007,8 +1040,11 @@ class KeyMapDetectionControllerTest { assertThat( grabRequests, contains( - GrabDeviceRequest( - device = FAKE_CONTROLLER_EVDEV_DEVICE, + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, extraKeyCodes = intArrayOf(), ), ), diff --git a/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabDeviceRequest.aidl b/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.aidl similarity index 62% rename from common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabDeviceRequest.aidl rename to common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.aidl index 86bb02cd0b..1959d02203 100644 --- a/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabDeviceRequest.aidl +++ b/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.aidl @@ -1,3 +1,3 @@ package io.github.sds100.keymapper.common.models; -parcelable GrabDeviceRequest; \ No newline at end of file +parcelable GrabTargetKeyCode; diff --git a/common/src/main/java/io/github/sds100/keymapper/common/models/GrabDeviceRequest.kt b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabDeviceRequest.kt index 94d6f08628..b1d07c5d7a 100644 --- a/common/src/main/java/io/github/sds100/keymapper/common/models/GrabDeviceRequest.kt +++ b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabDeviceRequest.kt @@ -7,7 +7,10 @@ import kotlinx.serialization.Serializable @Serializable @Parcelize data class GrabDeviceRequest( - val device: EvdevDeviceInfo, + val name: String, + val bus: Int, + val vendor: Int, + val product: Int, /** * The Android key codes to add support for from this device. * @@ -23,14 +26,20 @@ data class GrabDeviceRequest( other as GrabDeviceRequest - if (device != other.device) return false + if (name != other.name) return false + if (bus != other.bus) return false + if (vendor != other.vendor) return false + if (product != other.product) return false if (!extraKeyCodes.contentEquals(other.extraKeyCodes)) return false return true } override fun hashCode(): Int { - var result = device.hashCode() + var result = name.hashCode() + result = 31 * result + bus.hashCode() + result = 31 * result + vendor.hashCode() + result = 31 * result + product.hashCode() result = 31 * result + extraKeyCodes.contentHashCode() return result } diff --git a/common/src/main/java/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.kt b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.kt new file mode 100644 index 0000000000..5e5e9a94a1 --- /dev/null +++ b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.kt @@ -0,0 +1,46 @@ +package io.github.sds100.keymapper.common.models + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable + +@Serializable +@Parcelize +data class GrabTargetKeyCode( + val name: String, + val bus: Int, + val vendor: Int, + val product: Int, + /** + * The Android key codes to add support for from this device. + * + * One can request the duplicated uinput device to support extra key codes that the + * original device doesn't support. This is needed when a key map for this device + * inputs a key code that isn't supported by the original evdev device. + */ + val extraKeyCodes: IntArray, +) : Parcelable { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as GrabTargetKeyCode + + if (name != other.name) return false + if (bus != other.bus) return false + if (vendor != other.vendor) return false + if (product != other.product) return false + if (!extraKeyCodes.contentEquals(other.extraKeyCodes)) return false + + return true + } + + override fun hashCode(): Int { + var result = name.hashCode() + result = 31 * result + bus.hashCode() + result = 31 * result + vendor.hashCode() + result = 31 * result + product.hashCode() + result = 31 * result + extraKeyCodes.contentHashCode() + return result + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map_manager.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map_manager.rs index 09c3f0d667..632c72d970 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map_manager.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map_manager.rs @@ -7,12 +7,9 @@ use crate::evdev_device_info::EvdevDeviceInfo; use evdev::enums::{EventCode, EventType}; use evdev::util::int_to_event_code; use libc::c_uint; -use log::{debug, error, info}; +use log::{error, info}; use std::collections::HashMap; use std::error::Error; -use std::fs; -use std::fs::File; -use std::io::ErrorKind; use std::path::PathBuf; use std::sync::{Arc, Mutex, OnceLock}; @@ -221,7 +218,7 @@ impl KeyLayoutMapManager { paths } - fn map_key_codes_to_event_codes(key_codes: &[u32]) -> Vec { + pub fn map_key_codes_to_event_codes(key_codes: &[u32]) -> Vec { let generic_key_layout = get_generic_key_layout_map(); key_codes diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/mod.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/mod.rs index 0a8fd8e542..fa19befdd8 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/mod.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/mod.rs @@ -1,6 +1,6 @@ pub mod generic_key_layout; pub mod input_event_lookup; +pub mod key_layout_file_finder; pub mod key_layout_map; pub mod key_layout_map_manager; pub mod tokenizer; -pub mod key_layout_file_finder; \ No newline at end of file diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs index e16722a2cf..eef255395f 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs @@ -1,7 +1,7 @@ use std::{ - collections::HashMap, error::Error, fs::read_dir, + io, os::fd::AsRawFd, path::PathBuf, sync::{Arc, Mutex, RwLock}, @@ -9,26 +9,24 @@ use std::{ use bimap::BiHashMap; use evdev::{enums::EventCode, DeviceWrapper}; -use mio::{unix::SourceFd, Registry}; +use mio::{unix::SourceFd, Interest, Registry, Token}; use slab::Slab; use crate::{ - evdev_device_info::EvdevDeviceInfo, evdev_error::EvdevError, grab_target::GrabTarget, - grabbed_device::GrabbedDevice, + evdev_device_info::EvdevDeviceInfo, evdev_error::EvdevError, event_loop::EvdevCallback, + grab_target::GrabTarget, grabbed_device::GrabbedDevice, + grabbed_device_handle::GrabbedDeviceHandle, }; pub struct EvdevGrabController { poll_registry: Arc, - callback: fn(grabbed_devices: Vec), + callback: Arc, grab_targets: Mutex>, grabbed_devices: RwLock>, } impl EvdevGrabController { - fn new( - poll_registry: Arc, - callback: fn(grabbed_devices: Vec), - ) -> Self { + pub fn new(poll_registry: Arc, callback: Arc) -> Self { Self { poll_registry, callback, @@ -37,7 +35,7 @@ impl EvdevGrabController { } } - pub fn set_grab_targets(&self, targets: Vec) { + pub fn set_grab_targets(&self, targets: Vec) -> Vec { let mut grab_targets = self.grab_targets.lock().unwrap(); grab_targets.clear(); @@ -45,7 +43,7 @@ impl EvdevGrabController { grab_targets.push(target); } - self.invalidate(grab_targets.as_ref()); + self.invalidate(grab_targets.as_ref()) } // TODO call this function. Isnt this going to be called many times when grabbing/ungrabbing? @@ -54,28 +52,59 @@ impl EvdevGrabController { self.invalidate(grab_targets.as_ref()); } - fn invalidate(&self, grab_targets: &[GrabTarget]) { + fn invalidate(&self, grab_targets: &[GrabTarget]) -> Vec { let mut grabbed_devices = self.grabbed_devices.write().unwrap(); + let real_device_paths = Self::get_real_device_paths(&grabbed_devices).expect("Unable to evdev device paths"); let device_info_path_map = Self::build_device_info_path_map(&real_device_paths); - let keys_to_remove = - Self::get_devices_to_ungrab(grab_targets, &grabbed_devices, device_info_path_map); + let device_keys_to_ungrab = Self::get_devices_to_ungrab( + grab_targets, + &grabbed_devices, + device_info_path_map.clone(), + ); // Ungrab devices that are no longer requested - for key in keys_to_remove { + for key in device_keys_to_ungrab { let grabbed_device = grabbed_devices.remove(key); self.ungrab_device(grabbed_device); } - for grab_target in grab_targets {} + let devices_to_grab = + Self::get_targets_to_grab(grab_targets, &grabbed_devices, device_info_path_map); + + for (path, extra_event_codes) in devices_to_grab { + self.try_grab_target(&path, &extra_event_codes, &mut grabbed_devices) + .inspect_err(|err| error!("Failed to grab device {:?}: {:?}", path, err)) + .ok(); + } + + let grabbed_device_handles: Vec = grabbed_devices + .iter() + .map(|(key, device)| GrabbedDeviceHandle::new(key, device.device_info.clone())) + .collect(); + + // Release the lock before calling the callback + drop(grabbed_devices); - // TODO grab devices - // 5. Try grabbing devices that are not already grabbed - // 6. Call the callback with the new list of grabbed devices. + self.callback + .on_grabbed_devices_changed(grabbed_device_handles.clone()); + + grabbed_device_handles + } + + /// Access a grabbed device by ID through a closure. + /// Returns None if the device is not found, otherwise returns the result of the closure. + pub fn with_grabbed_device(&self, device_id: usize, f: F) -> Option + where + F: FnOnce(&GrabbedDevice) -> R, + { + let grabbed_devices = self.grabbed_devices.read().unwrap(); + grabbed_devices.get(device_id).map(f) } + // TODO test fn get_devices_to_ungrab( grab_targets: &[GrabTarget], grabbed_devices: &Slab, @@ -152,80 +181,82 @@ impl EvdevGrabController { info!("Ungrabbed device: {:?}", device.device_path); } - - fn is_target_grabbable() {} - - fn grab_new_devices(&self, requested_devices: Vec) { - let uinput_paths: Vec = devices_slab - .iter() - .map(|(_, device)| device.uinput.devnode().unwrap().to_string()) - .collect(); - let device_path_map = build_device_path_map(&uinput_paths); - - for grab_request in requested_devices { - let extra_events = Self::map_key_codes_to_event_codes(&grab_request.extra_key_codes); + // TODO test + fn get_targets_to_grab( + grab_targets: &[GrabTarget], + grabbed_devices: &Slab, + device_info_path_map: BiHashMap, + ) -> Vec<(PathBuf, Vec)> { + let mut targets_to_grab: Vec<(PathBuf, Vec)> = Vec::new(); - // Check whether the device is already grabbed with the same extra events. - // Otherwise it should be regrabbed with the updated information. - let already_grabbed = devices_slab.iter().any(|(_, device)| { - device_id_matches_jni_fields(&device.device_info, &grab_request.device_identifier) - && device.extra_events == extra_events - }); + for target in grab_targets { + let already_grabbed = grabbed_devices + .iter() + .any(|(_, device)| target.matches_device_info(&device.device_info)); if already_grabbed { - info!( - "Device {} is already grabbed with the same extra events", - grab_request.device_identifier.name - ); continue; } - let key = DeviceIdentifierKey::from(&grab_request.device_identifier); + let device_info = device_info_path_map + .left_values() + .find(|device_info| target.matches_device_info(device_info)); - match device_path_map.get(&key) { - Some(path) => match self.grab_device(devices_slab, path, &extra_events) { - Ok(_) => { - KeyLayoutMapManager::get() - .preload_key_layout_map(&grab_request.device_identifier) - .ok(); - } + match device_info { + // Target device not connected + None => continue, + // Device is connected + Some(device_info) => { + let path = device_info_path_map.get_by_left(&device_info).unwrap(); - Err(e) => error!("Failed to grab device {}: {:?}", path, e), - }, - None => { - warn!("Device not found: {:?}", grab_request); + targets_to_grab.push((path.clone(), target.extra_event_codes.clone())); } } } + + targets_to_grab } - /// Internal method to grab a device and register it with the poll - fn grab_device( + fn try_grab_target( &self, - devices_slab: &mut Slab, - path: &str, - extra_events: &[EventCode], + device_path: &PathBuf, + extra_event_codes: &[EventCode], + grabbed_devices: &mut Slab, ) -> Result> { - let device = GrabbedDevice::new(path, extra_events)?; + let device = GrabbedDevice::new(device_path, extra_event_codes)?; let fd = device.evdev.lock().unwrap().as_raw_fd(); - let key = devices_slab.insert(device); + let key = self.grabbed_devices.write().unwrap().insert(device); let mut source_fd = SourceFd(&fd); // Register with key + 1 because 0 is reserved for the waker - self.registry + self.poll_registry .register(&mut source_fd, Token(key + 1), Interest::READABLE) .inspect_err(|e| { // Remove device on registration failure - devices_slab.remove(key); - error!("Failed to register device {}: {}", path, e); + grabbed_devices.remove(key); + error!("Failed to register device {:?}: {}", device_path, e); })?; - info!("Grabbed device: {}", path); + info!("Grabbed device: {:?}", device_path); Ok(key) } + pub fn get_real_devices(&self) -> Result, EvdevError> { + let grabbed_devices = self.grabbed_devices.read().unwrap(); + + let mut list: Vec = Vec::new(); + + for path in Self::get_real_device_paths(&grabbed_devices)? { + if let Ok(info) = Self::get_device_info(&path) { + list.push(info); + } + } + + Ok(list) + } + /// Get the paths to all the real (non uinput) connected devices. fn get_real_device_paths( grabbed_devices: &Slab, @@ -267,19 +298,21 @@ impl EvdevGrabController { let mut map: BiHashMap = BiHashMap::new(); for path in paths { - if let Ok(device) = evdev::Device::new_from_path(&path) { - let key = EvdevDeviceInfo { - name: device.name().unwrap_or("").to_string(), - bus: device.bustype(), - vendor: device.vendor_id(), - product: device.product_id(), - version: device.version(), - }; - - map.insert(key, path) + if let Ok(info) = Self::get_device_info(path) { + map.insert(info, path.clone()); } } map } + + fn get_device_info(path: &PathBuf) -> Result { + evdev::Device::new_from_path(path).map(|device| EvdevDeviceInfo { + name: device.name().unwrap_or("").to_string(), + bus: device.bustype(), + vendor: device.vendor_id(), + product: device.product_id(), + version: device.version(), + }) + } } diff --git a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs index 5221ba2608..633b161a07 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs @@ -1,37 +1,44 @@ -use crate::android::keylayout::key_layout_map_manager::{ - get_generic_key_layout_map, KeyLayoutMapManager, -}; +use crate::android::keylayout::key_layout_map_manager::KeyLayoutMapManager; use crate::evdev_device_info::EvdevDeviceInfo; -use crate::evdev_error::EvdevError; +use crate::evdev_error::{EvdevError, EvdevErrorCode}; use crate::evdev_grab_controller::EvdevGrabController; -use crate::grab_device_request::GrabDeviceRequest; +use crate::grab_target::GrabTarget; +use crate::grab_target_key_code::GrabTargetKeyCode; use crate::grabbed_device::GrabbedDevice; +use crate::grabbed_device_handle::GrabbedDeviceHandle; use crate::runtime::get_runtime; -use evdev::enums::{EventCode, EventType, EV_SYN}; -use evdev::util::{event_code_to_int, int_to_event_code}; -use evdev::{DeviceWrapper, InputEvent, ReadFlag, ReadStatus}; +use evdev::enums::{EventType, EV_SYN}; +use evdev::util::event_code_to_int; +use evdev::{InputEvent, ReadFlag, ReadStatus}; use libc::c_uint; use mio::event::Event; -use mio::unix::SourceFd; -use mio::{Events, Interest, Poll, Registry, Token, Waker}; +use mio::{Events, Poll, Token, Waker}; use slab::Slab; -use std::collections::HashMap; use std::error::Error; -use std::fs::read_dir; use std::io; use std::io::ErrorKind; -use std::os::fd::AsRawFd; -use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{mpsc, Arc, OnceLock, RwLock}; use std::time::{Duration, Instant}; use std::{fmt, usize}; use tokio::task::JoinHandle; -/// This callback returns true if the observer consumed the input event. -/// Parameters: device_id (slab key), device_identifier, event -pub type EvdevObserver = - fn(device_id: usize, device_identifier: &EvdevDeviceInfo, event: &InputEvent) -> bool; +/// Callback interface for evdev events and device changes +pub trait EvdevCallback: Send + Sync { + /// Called when an input event is received from a grabbed device. + /// Returns true if the callback consumed the event, false to pass through. + /// Parameters: device_id (slab key), device_identifier, event + fn on_evdev_event( + &self, + device_id: usize, + device_identifier: &EvdevDeviceInfo, + event: &InputEvent, + ) -> bool; + + /// Called when the list of grabbed devices changes. + /// Parameters: grabbed_devices list with their assigned IDs + fn on_grabbed_devices_changed(&self, grabbed_devices: Vec); +} static EVENT_LOOP_MANAGER: OnceLock = OnceLock::new(); @@ -40,19 +47,32 @@ const WAKER_TOKEN: Token = Token(usize::MAX - 1); pub struct EventLoopManager { stop_flag: Arc, poll: Arc>, - registry: Arc, join_handle: RwLock>>, waker: Waker, - observer: EvdevObserver, - poll_controller: EvdevGrabController + callback: Arc, + grab_controller: EvdevGrabController, +} + +impl fmt::Debug for EventLoopManager { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("EventLoopManager") + .field("stop_flag", &self.stop_flag.load(Ordering::SeqCst)) + .field("poll", &"") + .field("registry", &"") + .field("join_handle", &"") + .field("waker", &"") + .field("callback", &"") + .field("grab_controller", &"") + .finish() + } } impl EventLoopManager { - /// Initialize the EventLoopManager with an observer. Must be called once before `get()`. + /// Initialize the EventLoopManager with a callback. Must be called once before `get()`. /// Panics if called more than once. - pub fn init(observer: EvdevObserver) { + pub fn init(callback: Arc) { EVENT_LOOP_MANAGER - .set(Self::new(observer)) + .set(Self::new(callback)) .expect("EventLoopManager already initialized"); } @@ -63,20 +83,21 @@ impl EventLoopManager { .expect("EventLoopManager not initialized. Call init() first.") } - fn new(observer: EvdevObserver) -> Self { + fn new(callback: Arc) -> Self { let poll = Poll::new().unwrap(); let registry = poll.registry().try_clone().unwrap(); let waker = Waker::new(®istry, WAKER_TOKEN).unwrap(); let poll_lock = Arc::new(RwLock::new(poll)); - EventLoopManager { + let registry_arc = Arc::new(registry); + + Self { stop_flag: Arc::new(AtomicBool::new(false)), poll: poll_lock, - registry: Arc::new(registry), join_handle: RwLock::new(None), waker, - observer, - grabbed_devices: Arc::new(RwLock::new(Slab::with_capacity(32))), + callback: callback.clone(), + grab_controller: EvdevGrabController::new(registry_arc.clone(), callback), } } @@ -86,35 +107,33 @@ impl EventLoopManager { if is_running { // Do nothing. The event loop is already started. info!("EvdevManager event loop is already running"); - Ok(()) - } else { - self.stop_flag.store(false, Ordering::Relaxed); + return Ok(()); + } - let observer = self.observer; - let grabbed_devices = self.grabbed_devices.clone(); + self.stop_flag.store(false, Ordering::Relaxed); - let (tx, rx) = mpsc::channel(); + let callback = self.callback.clone(); - let poll_lock_clone = self.poll.clone(); - let stop_flag_clone = self.stop_flag.clone(); + let (tx, rx) = mpsc::channel(); - let join_handle = get_runtime().spawn(async move { - tx.send(()).unwrap(); - EventLoopThread::new(stop_flag_clone, poll_lock_clone, observer, grabbed_devices) - .start(); - }); + let poll_lock_clone = self.poll.clone(); + let stop_flag_clone = self.stop_flag.clone(); - match rx.recv_timeout(Duration::from_secs(2)) { - Ok(_) => { - self.join_handle.write().unwrap().replace(join_handle); + let join_handle = get_runtime().spawn(async move { + tx.send(()).unwrap(); + EventLoopThread::new(stop_flag_clone, poll_lock_clone, callback).start(); + }); - Ok(()) - } - Err(e) => { - error!("Failed to wait for event loop start: {}", e); - join_handle.abort(); - Err(EvdevError::new(-libc::ETIMEDOUT)) - } + match rx.recv_timeout(Duration::from_secs(2)) { + Ok(_) => { + self.join_handle.write().unwrap().replace(join_handle); + + Ok(()) + } + Err(e) => { + error!("Failed to wait for event loop start: {}", e); + join_handle.abort(); + Err(EvdevError::new(-libc::ETIMEDOUT)) } } } @@ -153,22 +172,28 @@ impl EventLoopManager { /// Set the list of grabbed devices. This will ungrab any devices that are no longer in the list /// and grab any new devices. Devices are matched by DeviceIdentifier (name, bus, vendor, product). /// Returns: A list of (device_id, DeviceIdentifier) for all successfully grabbed devices. - pub fn set_grabbed_devices( - &self, - requested_devices: Vec, - ) -> Vec<(usize, EvdevDeviceInfo)> { - let mut devices_slab = self.grabbed_devices.write().unwrap(); + pub fn set_grab_targets(&self, targets: Vec) -> Vec { + let internal_grab_targets = targets.iter().map(Self::convert_grab_target).collect(); - self.ungrab_unused_devices(&mut devices_slab, &requested_devices); + let handles = self.grab_controller.set_grab_targets(internal_grab_targets); - self.grab_new_devices(&mut devices_slab, requested_devices); + for handle in handles.clone() { + KeyLayoutMapManager::get() + .preload_key_layout_map(&handle.device_info) + .inspect_err(|err| { + error!( + "Failed to preload key layout map for device {:?}: {}", + handle.device_info, err + ); + }) + .ok(); + } + + handles + } - // Return all currently grabbed devices as (slab_key, DeviceIdentifier) tuples - // The slab_key is used as device_id for O(1) lookup when writing events - devices_slab - .iter() - .map(|(slab_key, device)| (slab_key, device.device_info.clone())) - .collect() + pub fn get_real_devices(&self) -> Result, EvdevError> { + self.grab_controller.get_real_devices() } /// Write an event to a grabbed device's uinput. @@ -180,27 +205,19 @@ impl EventLoopManager { code: u32, value: i32, ) -> Result<(), EvdevError> { - let devices = self.grabbed_devices.read().unwrap(); - - let device = devices - .get(device_id) // O(1) slab lookup - .ok_or_else(|| EvdevError::new(-libc::ENODEV))?; - debug!( "Write evdev event: device_id={} event_type={} code={} value={}", device_id, event_type, code, value ); - device - .uinput - .write_event(EventType::EV_KEY as c_uint, code, value) - .map_err(EvdevError::from)?; - - // Send SYN_REPORT - device - .uinput - .write_syn_event(EV_SYN::SYN_REPORT) - .map_err(|err| err.into()) + self.grab_controller + .with_grabbed_device(device_id, |device| { + device + .uinput + .write_event(event_type, code, value) + .map_err(EvdevError::from) + }) + .ok_or(EvdevError::from_enum(EvdevErrorCode::NoSuchDevice))? } pub fn write_key_code_event( @@ -209,37 +226,60 @@ impl EventLoopManager { key_code: u32, value: i32, ) -> Result<(), Box> { - let devices = self.grabbed_devices.read().unwrap(); - - let device = devices - .get(device_id) - .ok_or_else(|| EvdevError::new(-libc::ENODEV))?; - - let scan_code_result = - KeyLayoutMapManager::get().find_scan_code_for_key(&device.device_info, key_code)?; + let result = self + .grab_controller + .with_grabbed_device(device_id, |device| { + let scan_code_result = KeyLayoutMapManager::get() + .find_scan_code_for_key(&device.device_info, key_code); + + match scan_code_result { + Err(e) => Err(e.into()), + Ok(None) => { + error!("Failed to find scan code for key: {}", key_code); + Err(Box::new(EvdevError::new(-libc::ENODATA)) as Box) + } + Ok(Some(code)) => { + debug!( + "Write key code evdev event: key_code={} value={}", + key_code, value + ); + + device + .uinput + .write_event(EventType::EV_KEY as c_uint, code, value) + .map_err(|err| Box::new(err) as Box)?; + + // Send SYN_REPORT + device + .uinput + .write_event( + EventType::EV_SYN as c_uint, + EV_SYN::SYN_REPORT as c_uint, + 0, + ) + .map_err(|err| Box::new(err) as Box) + } + } + }); - match scan_code_result { - None => { - error!("Failed to find scan code for key: {}", key_code); - Err(Box::new(EvdevError::new(-libc::ENODATA))) - } - Some(code) => { - debug!( - "Write key code evdev event: key_code={} value={}", - key_code, value - ); + match result { + Some(inner_result) => inner_result, + None => Err(Box::new(EvdevError::from_enum( + EvdevErrorCode::NoSuchDevice, + ))), + } + } - device - .uinput - .write_event(EventType::EV_KEY as c_uint, code, value) - .map_err(EvdevError::from)?; + fn convert_grab_target(target: &GrabTargetKeyCode) -> GrabTarget { + let event_codes = + KeyLayoutMapManager::map_key_codes_to_event_codes(&target.extra_key_codes); - // Send SYN_REPORT - device - .uinput - .write_syn_event(EV_SYN::SYN_REPORT) - .map_err(|err| err.into()) - } + GrabTarget { + name: target.name.clone(), + bus: target.bus, + vendor: target.vendor, + product: target.product, + extra_event_codes: event_codes, } } } @@ -247,7 +287,7 @@ impl EventLoopManager { struct EventLoopThread { stop_flag: Arc, poll: Arc>, - observer: EvdevObserver, + callback: Arc, grabbed_devices: Arc>>, } @@ -255,14 +295,13 @@ impl EventLoopThread { pub fn new( stop_flag: Arc, poll: Arc>, - observer: EvdevObserver, - grabbed_devices: Arc>>, + callback: Arc, ) -> Self { EventLoopThread { stop_flag, poll, - observer, - grabbed_devices, + callback, + grabbed_devices: Arc::new(RwLock::new(Slab::with_capacity(64))), } } @@ -340,7 +379,9 @@ impl EventLoopThread { } fn process_event(&self, device_id: usize, event: &InputEvent, grabbed_device: &GrabbedDevice) { - let consumed = (self.observer)(device_id, &grabbed_device.device_info, event); + let consumed = self + .callback + .on_evdev_event(device_id, &grabbed_device.device_info, event); if !consumed { let (event_type, event_code) = event_code_to_int(&event.event_code); @@ -349,7 +390,7 @@ impl EventLoopThread { .write_event(event_type, event_code, event.value) .inspect_err(|e| { error!( - "Failed to passthrough event to {}. Event: {:?}. Error: {:?}", + "Failed to passthrough event to {:?}. Event: {:?}. Error: {:?}", grabbed_device.device_path, event, e ) }) diff --git a/evdev/src/main/rust/evdev_manager/core/src/grab_device_request.rs b/evdev/src/main/rust/evdev_manager/core/src/grab_device_request.rs deleted file mode 100644 index a0137f64f7..0000000000 --- a/evdev/src/main/rust/evdev_manager/core/src/grab_device_request.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::evdev_device_info::EvdevDeviceInfo; - -#[derive(Debug)] -pub struct GrabDeviceRequest { - pub device_identifier: EvdevDeviceInfo, - pub extra_key_codes: Vec, -} diff --git a/evdev/src/main/rust/evdev_manager/core/src/grab_target_key_code.rs b/evdev/src/main/rust/evdev_manager/core/src/grab_target_key_code.rs new file mode 100644 index 0000000000..26d81c1358 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/grab_target_key_code.rs @@ -0,0 +1,8 @@ +#[derive(Debug)] +pub struct GrabTargetKeyCode { + pub name: String, + pub bus: u16, + pub vendor: u16, + pub product: u16, + pub extra_key_codes: Vec, +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs b/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs index a803b504ae..cba7435d25 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs @@ -21,7 +21,7 @@ pub struct GrabbedDevice { impl GrabbedDevice { /// Create a grabbed device that also enables the given EventCodes in the uinput device. - pub fn new(device_path: PathBuf, extra_events: &[EventCode]) -> Result { + pub fn new(device_path: &PathBuf, extra_events: &[EventCode]) -> Result { let mut evdev = Self::open_evdev_device(&device_path)?; for event in extra_events { @@ -40,7 +40,7 @@ impl GrabbedDevice { }; Ok(Self { - device_path, + device_path: device_path.clone(), device_info, evdev: Mutex::new(evdev), uinput, @@ -67,6 +67,9 @@ impl Drop for GrabbedDevice { fn drop(&mut self) { let mut evdev = self.evdev.lock().unwrap(); // Ungrab the device - evdev.grab(GrabMode::Ungrab); + evdev + .grab(GrabMode::Ungrab) + .inspect_err(|err| error!("Failed to ungrab device {:?}; {}", self.device_info, err)) + .ok(); } } diff --git a/evdev/src/main/rust/evdev_manager/core/src/grabbed_device_handle.rs b/evdev/src/main/rust/evdev_manager/core/src/grabbed_device_handle.rs new file mode 100644 index 0000000000..5c1d334250 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/grabbed_device_handle.rs @@ -0,0 +1,14 @@ +use crate::evdev_device_info::EvdevDeviceInfo; + +/// Handle to a grabbed device with its assigned ID for O(1) lookup +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GrabbedDeviceHandle { + pub id: usize, + pub device_info: EvdevDeviceInfo, +} + +impl GrabbedDeviceHandle { + pub fn new(id: usize, device_info: EvdevDeviceInfo) -> Self { + Self { id, device_info } + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/lib.rs b/evdev/src/main/rust/evdev_manager/core/src/lib.rs index 21ae886407..5f5ea52144 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/lib.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/lib.rs @@ -5,7 +5,8 @@ pub mod evdev_device_info; pub mod evdev_error; pub mod evdev_grab_controller; pub mod event_loop; -pub mod grab_device_request; pub mod grab_target; +pub mod grab_target_key_code; pub mod grabbed_device; +pub mod grabbed_device_handle; pub mod runtime; diff --git a/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs b/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs index 03b714659d..15b6ada093 100644 --- a/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs +++ b/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs @@ -3,6 +3,7 @@ use evdev_manager_core::android::android_codes; use evdev_manager_core::android::android_codes::AKEYCODE_UNKNOWN; use evdev_manager_core::android::keylayout::key_layout_map_manager::KeyLayoutMapManager; use evdev_manager_core::evdev_device_info::EvdevDeviceInfo; +use evdev_manager_core::grabbed_device_handle::GrabbedDeviceHandle; use jni::objects::{GlobalRef, JValue}; use jni::JavaVM; use std::process; @@ -74,6 +75,77 @@ impl EvdevJniObserver { } } + pub fn on_grabbed_devices_changed(&self, grabbed_devices: Vec) { + let mut env = self + .jvm + .attach_current_thread_permanently() + .expect("Failed to attach to JVM thread"); + + // Convert Vec to Java array of GrabbedDeviceHandle + let handle_class = + match env.find_class("io/github/sds100/keymapper/common/models/GrabbedDeviceHandle") { + Ok(c) => c, + Err(e) => { + error!("Failed to find GrabbedDeviceHandle class: {:?}", e); + return; + } + }; + + let array = match env.new_object_array( + grabbed_devices.len() as i32, + &handle_class, + jni::objects::JObject::null(), + ) { + Ok(a) => a, + Err(e) => { + error!("Failed to create GrabbedDeviceHandle array: {:?}", e); + return; + } + }; + + for (i, device_handle) in grabbed_devices.iter().enumerate() { + let name_str = match env.new_string(&device_handle.device_info.name) { + Ok(s) => s, + Err(e) => { + error!("Failed to create device name string: {:?}", e); + continue; + } + }; + + let handle = match env.new_object( + &handle_class, + "(ILjava/lang/String;III)V", + &[ + JValue::Int(device_handle.id as i32), + JValue::Object(&name_str.into()), + JValue::Int(device_handle.device_info.bus as i32), + JValue::Int(device_handle.device_info.vendor as i32), + JValue::Int(device_handle.device_info.product as i32), + ], + ) { + Ok(h) => h, + Err(e) => { + error!("Failed to create GrabbedDeviceHandle: {:?}", e); + continue; + } + }; + + if let Err(e) = env.set_object_array_element(&array, i as i32, &handle) { + error!("Failed to set array element: {:?}", e); + } + } + + // Call BaseSystemBridge.onGrabbedDevicesChanged() via JNI + if let Err(e) = env.call_method( + &self.system_bridge, + "onGrabbedDevicesChanged", + "([Lio/github/sds100/keymapper/common/models/GrabbedDeviceHandle;)V", + &[JValue::Object(&array.into())], + ) { + error!("Failed to call onGrabbedDevicesChanged: {:?}", e); + } + } + pub fn on_event( &self, device_id: usize, diff --git a/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs b/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs index bb2cdb8bf9..bf1bd28eb4 100644 --- a/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs +++ b/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs @@ -1,14 +1,14 @@ use crate::evdev_jni_observer::EvdevJniObserver; -use evdev::{Device, DeviceWrapper}; +use evdev::InputEvent; use evdev_manager_core::android::keylayout::key_layout_map_manager::KeyLayoutMapManager; use evdev_manager_core::evdev_device_info::EvdevDeviceInfo; -use evdev_manager_core::event_loop::EventLoopManager; -use evdev_manager_core::grab_device_request::GrabDeviceRequest; +use evdev_manager_core::event_loop::{EvdevCallback, EventLoopManager}; +use evdev_manager_core::grab_target_key_code::GrabTargetKeyCode; +use evdev_manager_core::grabbed_device_handle::GrabbedDeviceHandle; use jni::objects::{JClass, JIntArray, JObject, JObjectArray, JString, JValue}; use jni::sys::{jboolean, jint, jobject, jobjectArray}; use jni::JNIEnv; use log::LevelFilter; -use std::path::PathBuf; use std::ptr; use std::sync::{Arc, OnceLock}; @@ -18,6 +18,24 @@ fn get_jni_observer() -> &'static EvdevJniObserver { JNI_OBSERVER.get().expect("JNI observer not initialized") } +/// Wrapper struct that implements EvdevCallback trait +struct JniEvdevCallback; + +impl EvdevCallback for JniEvdevCallback { + fn on_evdev_event( + &self, + device_id: usize, + device_identifier: &EvdevDeviceInfo, + event: &InputEvent, + ) -> bool { + get_jni_observer().on_event(device_id, device_identifier, event) + } + + fn on_grabbed_devices_changed(&self, grabbed_devices: Vec) { + get_jni_observer().on_grabbed_devices_changed(grabbed_devices) + } +} + /// MUST only be called once in the lifetime of the process. #[no_mangle] pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_initEvdevManager( @@ -52,10 +70,8 @@ pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemB panic!("JNI observer already initialized"); } - // Initialize and start the event loop with the observer - EventLoopManager::init(|device_id, device_identifier, event| { - get_jni_observer().on_event(device_id, device_identifier, event) - }); + // Initialize and start the event loop with the callback + EventLoopManager::init(Arc::new(JniEvdevCallback)); EventLoopManager::get() .start() @@ -76,15 +92,15 @@ pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemB .unwrap(); } -/// Set the list of grabbed devices. Takes an array of GrabDeviceRequest and returns an array of GrabbedDeviceHandle. +/// Set the list of grabbed devices. Takes an array of GrabTargetKeyCode and returns an array of GrabbedDeviceHandle. #[no_mangle] -pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_setGrabbedDevicesNative( +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_setGrabTargetsNative( mut env: JNIEnv, _class: JClass, j_devices: jobjectArray, ) -> jobjectArray { - // Parse the input array of GrabDeviceRequest - let mut requested_devices: Vec = Vec::new(); + // Parse the input array of GrabTargetKeyCode + let mut requested_devices: Vec = Vec::new(); // Convert raw jobjectArray to JObjectArray let devices_array: JObjectArray = unsafe { JObjectArray::from_raw(j_devices) }; @@ -106,15 +122,15 @@ pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemB } }; - match parse_grab_device_request(&mut env, &obj) { + match parse_grab_target_key_code(&mut env, &obj) { Ok(grab_request) => requested_devices.push(grab_request), Err(e) => { - error!("Failed to parse GrabDeviceRequest at index {}: {:?}", i, e); + error!("Failed to parse GrabTargetKeyCode at index {}: {:?}", i, e); } } } - let grabbed_devices = EventLoopManager::get().set_grabbed_devices(requested_devices); + let grabbed_devices = EventLoopManager::get().set_grab_targets(requested_devices); create_java_grabbed_device_handle_array(&mut env, grabbed_devices) } @@ -154,32 +170,28 @@ pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemB _class: JClass, ) -> jobjectArray { let mut device_infos = Vec::new(); - let device_paths_result = EventLoopManager::get().get_all_real_devices(); - - match device_paths_result { - Ok(paths) => { - for path in paths { - match get_evdev_from_path(path.clone()) { - Some(device) => { - let name = device.name().unwrap_or(""); - let bus = device.bustype() as i32; - let vendor = device.vendor_id() as i32; - let product = device.product_id() as i32; - - // Create EvdevDeviceInfo - match create_java_evdev_device_info(&mut env, name, bus, vendor, product) { - Ok(info) => device_infos.push(info), - Err(e) => { - error!("Failed to create EvdevDeviceInfo: {:?}", e); - } - } + let devices_result = EventLoopManager::get().get_real_devices(); + + match devices_result { + Ok(devices) => { + for device in devices { + // Create EvdevDeviceInfo + match create_java_evdev_device_info( + &mut env, + &device.name, + device.bus as i32, + device.vendor as i32, + device.product as i32, + ) { + Ok(info) => device_infos.push(info), + Err(e) => { + error!("Failed to create EvdevDeviceInfo: {:?}", e); } - None => continue, } } } Err(e) => { - error!("Failed to get input device paths: {:?}", e); + error!("Failed to get input devices: {:?}", e); } } @@ -212,18 +224,11 @@ pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemB array.into_raw() } -fn get_evdev_from_path(path: PathBuf) -> Option { - Device::new_from_path(path.clone()) - .inspect_err(|e| warn!("Failed to open evdev device {:?}: {:?}", path, e)) - .ok() -} - -/// Parse a Java EvdevDeviceInfo object into a Rust DeviceIdentifier -/// Note: version is set to 0 as it's not exposed in the Java API -fn parse_evdev_device_info( +/// Parse a Java GrabTargetKeyCode object into a Rust GrabTargetKeyCode +fn parse_grab_target_key_code( env: &mut JNIEnv, obj: &JObject, -) -> Result { +) -> Result { // Get name field let name_field = env.get_field(obj, "name", "Ljava/lang/String;")?; let name_obj = name_field.l()?; @@ -234,55 +239,38 @@ fn parse_evdev_device_info( .into_owned(); // Get bus field - let bus = env.get_field(obj, "bus", "I")?.i()? as u16; + let bus = env.get_field(obj, "bus", "I")?.i()?; // Get vendor field - let vendor = env.get_field(obj, "vendor", "I")?.i()? as u16; + let vendor = env.get_field(obj, "vendor", "I")?.i()?; // Get product field - let product = env.get_field(obj, "product", "I")?.i()? as u16; + let product = env.get_field(obj, "product", "I")?.i()?; - Ok(EvdevDeviceInfo { - name, - bus, - vendor, - product, - version: 0, // Version is not exposed in Java API, set to 0 - }) -} - -/// Parse a Java GrabDeviceRequest object into a Rust GrabDeviceRequest -fn parse_grab_device_request( - env: &mut JNIEnv, - obj: &JObject, -) -> Result { - // Get the device field (EvdevDeviceInfo) - let device_field = env.get_field( - obj, - "device", - "Lio/github/sds100/keymapper/common/models/EvdevDeviceInfo;", - )?; - let device_obj = device_field.l()?; - let device_identifier = parse_evdev_device_info(env, &device_obj)?; - - // Get the extraEventCodes field (int[]) + // Get the extraKeyCodes field (int[]) let extra_codes_field = env.get_field(obj, "extraKeyCodes", "[I")?; let extra_codes_obj = extra_codes_field.l()?; let extra_codes_array = JIntArray::from(extra_codes_obj); - // Convert Java int[] to Vec + // Convert Java int[] to Vec let array_length = env.get_array_length(&extra_codes_array)? as usize; - let extra_key_codes: Vec = Vec::with_capacity(array_length); + let mut extra_key_codes: Vec = Vec::with_capacity(array_length); + let mut buffer = vec![0i32; array_length]; + env.get_int_array_region(&extra_codes_array, 0, &mut buffer)?; + extra_key_codes.extend(buffer.iter().map(|&v| v as u32)); - Ok(GrabDeviceRequest { - device_identifier, - extra_key_codes: extra_key_codes, + Ok(GrabTargetKeyCode { + name, + bus: bus as u16, + vendor: vendor as u16, + product: product as u16, + extra_key_codes, }) } fn create_java_grabbed_device_handle_array( - mut env: &mut JNIEnv, - grabbed_devices: Vec<(usize, EvdevDeviceInfo)>, + env: &mut JNIEnv, + grabbed_devices: Vec, ) -> jobjectArray { let handle_class = match env.find_class("io/github/sds100/keymapper/common/models/GrabbedDeviceHandle") { @@ -302,14 +290,14 @@ fn create_java_grabbed_device_handle_array( // grabbed_devices contains (slab_key, DeviceIdentifier) tuples // The slab_key is used as the device_id for O(1) lookup when writing events - for (i, (slab_key, device_identifier)) in grabbed_devices.iter().enumerate() { + for (i, handle) in grabbed_devices.iter().enumerate() { match create_java_grabbed_device_handle( - &mut env, - *slab_key as i32, // slab key = device_id for O(1) lookup - &device_identifier.name, - device_identifier.bus as i32, - device_identifier.vendor as i32, - device_identifier.product as i32, + env, + handle.id as i32, // slab key = device_id for O(1) lookup + &handle.device_info.name, + handle.device_info.bus as i32, + handle.device_info.vendor as i32, + handle.device_info.product as i32, ) { Ok(handle) => { if let Err(e) = env.set_object_array_element(&array, i as i32, unsafe { diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl index fb8ce05be0..12f35c3633 100644 --- a/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl @@ -9,4 +9,5 @@ interface IEvdevCallback { */ boolean onEvdevEvent(int deviceId, long timeSec, long timeUsec, int type, int code, int value, int androidCode); void onEmergencyKillSystemBridge(); -} \ No newline at end of file + void onGrabbedDevicesChanged(in GrabbedDeviceHandle[] devices); +} diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl index af1de44ca7..fc7b931157 100644 --- a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl @@ -2,7 +2,7 @@ package io.github.sds100.keymapper.sysbridge; import io.github.sds100.keymapper.evdev.IEvdevCallback; import io.github.sds100.keymapper.common.models.EvdevDeviceInfo; -import io.github.sds100.keymapper.common.models.GrabDeviceRequest; +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode; import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle; import io.github.sds100.keymapper.common.models.ShellResult; import android.view.InputEvent; @@ -13,7 +13,7 @@ interface ISystemBridge { int getVersionCode() = 16777112; ShellResult executeCommand(String command, long timeoutMillis) = 16777111; - GrabbedDeviceHandle[] setGrabbedDevices(in GrabDeviceRequest[] devices) = 1; + GrabbedDeviceHandle[] setGrabTargets(in GrabTargetKeyCode[] devices) = 1; void registerEvdevCallback(IEvdevCallback callback) = 5; void unregisterEvdevCallback() = 6; @@ -48,4 +48,4 @@ interface ISystemBridge { long getUsbScreenUnlockedFunctions() = 21; boolean writeEvdevEventKeyCode(int deviceId, int keyCode, int value) = 22; -} \ No newline at end of file +} diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt index 09abeb8053..a36591ca36 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt @@ -41,7 +41,7 @@ import android.view.InputEvent import androidx.annotation.RequiresApi import com.android.internal.telephony.ITelephony import io.github.sds100.keymapper.common.models.EvdevDeviceInfo -import io.github.sds100.keymapper.common.models.GrabDeviceRequest +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle import io.github.sds100.keymapper.common.models.ShellResult import io.github.sds100.keymapper.common.utils.UserHandleUtils @@ -68,8 +68,8 @@ import rikka.hidden.compat.adapter.ProcessObserverAdapter class SystemBridge : ISystemBridge.Stub() { @Suppress("KotlinJniMissingFunction") - external fun setGrabbedDevicesNative( - devices: Array, + external fun setGrabTargetsNative( + devices: Array, ): Array @Suppress("KotlinJniMissingFunction") @@ -426,10 +426,10 @@ class SystemBridge : ISystemBridge.Stub() { } } - override fun setGrabbedDevices( - devices: Array?, + override fun setGrabTargets( + devices: Array?, ): Array? { - return setGrabbedDevicesNative(devices?.filterNotNull()?.toTypedArray() ?: emptyArray()) + return setGrabTargetsNative(devices?.filterNotNull()?.toTypedArray() ?: emptyArray()) } override fun injectInputEvent(event: InputEvent?, mode: Int): Boolean { From 98e4c036ab76f081ef66ed66da6dfaab6bc6dc0a Mon Sep 17 00:00:00 2001 From: sds100 Date: Sat, 20 Dec 2025 21:33:19 +0000 Subject: [PATCH 162/199] #1926 fix calling SystemBridge onGrabbedDevicesChanged --- .../sds100/keymapper/base/promode/StepContent.kt | 1 + .../core/src/android/keylayout/generic_key_layout.rs | 1 + .../rust/evdev_manager/jni/src/evdev_jni_observer.rs | 2 +- .../keymapper/sysbridge/service/SystemBridge.kt | 12 ++++++++++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/StepContent.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/StepContent.kt index 15b1e8f122..8390ca3db3 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/StepContent.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/promode/StepContent.kt @@ -9,3 +9,4 @@ data class StepContent( val buttonText: String, ) + diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/generic_key_layout.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/generic_key_layout.rs index ac7f35eb89..b454076831 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/generic_key_layout.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/generic_key_layout.rs @@ -286,3 +286,4 @@ axis 0x0a LTRIGGER axis 0x10 HAT_X axis 0x11 HAT_Y "#; + diff --git a/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs b/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs index 15b6ada093..6a64ba1277 100644 --- a/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs +++ b/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs @@ -135,7 +135,7 @@ impl EvdevJniObserver { } } - // Call BaseSystemBridge.onGrabbedDevicesChanged() via JNI + // Call SystemBridge.onGrabbedDevicesChanged() via JNI if let Err(e) = env.call_method( &self.system_bridge, "onGrabbedDevicesChanged", diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt index a36591ca36..ed4c6c16a5 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt @@ -112,6 +112,18 @@ class SystemBridge : ISystemBridge.Stub() { } } + @Suppress("unused") + fun onGrabbedDevicesChanged(devices: Array) { + synchronized(evdevCallbackLock) { + val callback = evdevCallback ?: return + try { + callback.onGrabbedDevicesChanged(devices) + } catch (e: Exception) { + Log.e(TAG, "Error calling evdev callback", e) + } + } + } + /** * Called from Rust via JNI when the power button is held for 10+ seconds. * Forwards the call to the registered IEvdevCallback for emergency system bridge kill. From fc4ebf25b91a1b50405c9a107b0c4e283accc467 Mon Sep 17 00:00:00 2001 From: sds100 Date: Sat, 20 Dec 2025 23:46:05 +0000 Subject: [PATCH 163/199] #1926 listen to inotify events in /dev/input --- .../base/promode/SystemBridgeAutoStarter.kt | 3 - evdev/src/main/rust/evdev_manager/Cargo.lock | 172 +++++++++++++++++- .../main/rust/evdev_manager/core/Cargo.toml | 1 + .../core/src/evdev_grab_controller.rs | 1 - .../rust/evdev_manager/core/src/event_loop.rs | 77 +++++--- 5 files changed, 218 insertions(+), 36 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt index 9a84bf7d9c..fe90c1e784 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt @@ -127,9 +127,6 @@ class SystemBridgeAutoStarter @Inject constructor( @OptIn(ExperimentalCoroutinesApi::class) private val autoStartFlow: Flow = connectionManager.connectionState.flatMapLatest { connectionState -> - - Timber.i("LATEST CONNECTION STATE: $connectionState") - // Do not autostart if it is connected or it was killed from the user if (connectionState !is SystemBridgeConnectionState.Disconnected || connectionState.isStoppedByUser || diff --git a/evdev/src/main/rust/evdev_manager/Cargo.lock b/evdev/src/main/rust/evdev_manager/Cargo.lock index b7ec1576e9..b8d5185cdf 100644 --- a/evdev/src/main/rust/evdev_manager/Cargo.lock +++ b/evdev/src/main/rust/evdev_manager/Cargo.lock @@ -33,6 +33,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.10.0" @@ -87,7 +93,7 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" name = "evdev" version = "0.1.0" dependencies = [ - "bitflags", + "bitflags 2.10.0", "cc", "libc 1.0.0-alpha.1", "log 0.4.28", @@ -104,6 +110,7 @@ dependencies = [ "libc 0.2.177", "log 0.4.28", "mio", + "notify", "pretty_assertions", "slab", "tokio", @@ -127,12 +134,41 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc 0.2.177", +] + [[package]] name = "glob" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "inotify" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" +dependencies = [ + "bitflags 2.10.0", + "inotify-sys", + "libc 0.2.177", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc 0.2.177", +] + [[package]] name = "jni" version = "0.21.1" @@ -155,6 +191,26 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc 0.2.177", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc 0.2.177", +] + [[package]] name = "libc" version = "0.2.177" @@ -207,6 +263,30 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags 2.10.0", + "fsevent-sys", + "inotify", + "kqueue", + "libc 0.2.177", + "log 0.4.28", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + +[[package]] +name = "notify-types" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -357,7 +437,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -375,13 +464,30 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -390,42 +496,90 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "yansi" version = "1.0.1" diff --git a/evdev/src/main/rust/evdev_manager/core/Cargo.toml b/evdev/src/main/rust/evdev_manager/core/Cargo.toml index 83667a3b88..854d5b5bae 100644 --- a/evdev/src/main/rust/evdev_manager/core/Cargo.toml +++ b/evdev/src/main/rust/evdev_manager/core/Cargo.toml @@ -15,6 +15,7 @@ mio = { version = "1.1.0", features = ["os-poll", "os-ext"] } slab = "0.4.11" tokio = { version = "1.48.0", features = ["rt", "macros", "rt-multi-thread"] } bimap = "0.6.3" +notify = "8.2.0" [dev-dependencies] glob = "0.3" diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs index eef255395f..a60f229355 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs @@ -46,7 +46,6 @@ impl EvdevGrabController { self.invalidate(grab_targets.as_ref()) } - // TODO call this function. Isnt this going to be called many times when grabbing/ungrabbing? pub fn on_inotify_dev_input(&self) { let grab_targets = self.grab_targets.lock().unwrap(); self.invalidate(grab_targets.as_ref()); diff --git a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs index 633b161a07..172757b592 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs @@ -13,10 +13,12 @@ use evdev::{InputEvent, ReadFlag, ReadStatus}; use libc::c_uint; use mio::event::Event; use mio::{Events, Poll, Token, Waker}; +use notify::{EventKind, Watcher}; use slab::Slab; use std::error::Error; use std::io; use std::io::ErrorKind; +use std::path::Path; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{mpsc, Arc, OnceLock, RwLock}; use std::time::{Duration, Instant}; @@ -47,10 +49,11 @@ const WAKER_TOKEN: Token = Token(usize::MAX - 1); pub struct EventLoopManager { stop_flag: Arc, poll: Arc>, - join_handle: RwLock>>, + event_loop_handle: RwLock>>, + inotify_handle: RwLock>>, waker: Waker, callback: Arc, - grab_controller: EvdevGrabController, + grab_controller: Arc, } impl fmt::Debug for EventLoopManager { @@ -90,22 +93,23 @@ impl EventLoopManager { let poll_lock = Arc::new(RwLock::new(poll)); let registry_arc = Arc::new(registry); + let grab_controller = EvdevGrabController::new(registry_arc.clone(), callback.clone()); Self { stop_flag: Arc::new(AtomicBool::new(false)), poll: poll_lock, - join_handle: RwLock::new(None), + event_loop_handle: RwLock::new(None), + inotify_handle: RwLock::new(None), waker, - callback: callback.clone(), - grab_controller: EvdevGrabController::new(registry_arc.clone(), callback), + callback, + grab_controller: Arc::new(grab_controller), } } pub fn start(&self) -> Result<(), EvdevError> { - let is_running = { self.join_handle.read().unwrap().is_some() }; + let is_running = { self.event_loop_handle.read().unwrap().is_some() }; if is_running { - // Do nothing. The event loop is already started. info!("EvdevManager event loop is already running"); return Ok(()); } @@ -114,32 +118,33 @@ impl EventLoopManager { let callback = self.callback.clone(); - let (tx, rx) = mpsc::channel(); - let poll_lock_clone = self.poll.clone(); let stop_flag_clone = self.stop_flag.clone(); - let join_handle = get_runtime().spawn(async move { - tx.send(()).unwrap(); + let event_loop_handle = get_runtime().spawn(async move { EventLoopThread::new(stop_flag_clone, poll_lock_clone, callback).start(); }); - match rx.recv_timeout(Duration::from_secs(2)) { - Ok(_) => { - self.join_handle.write().unwrap().replace(join_handle); + self.event_loop_handle + .write() + .unwrap() + .replace(event_loop_handle); - Ok(()) - } - Err(e) => { - error!("Failed to wait for event loop start: {}", e); - join_handle.abort(); - Err(EvdevError::new(-libc::ETIMEDOUT)) - } - } + let grab_controller = self.grab_controller.clone(); + + let inotify_handle = get_runtime().spawn(async move { + Self::watch_dev_input(&grab_controller) + .inspect_err(|err| error!("Failed to watch device input: {}", err)) + .unwrap_err(); + }); + + self.inotify_handle.write().unwrap().replace(inotify_handle); + + Ok(()) } pub fn stop(&self) -> Result<(), io::Error> { - let handle_option = self.join_handle.write().unwrap().take(); + let handle_option = self.event_loop_handle.write().unwrap().take(); match handle_option { None => { @@ -282,6 +287,32 @@ impl EventLoopManager { extra_event_codes: event_codes, } } + + fn watch_dev_input(grab_controller: &EvdevGrabController) -> notify::Result<()> { + let (tx, rx) = mpsc::channel::>(); + + let mut watcher = notify::recommended_watcher(tx) + .inspect_err(|err| error!("Failed to create inotify watcher: {}", err))?; + + watcher.watch(Path::new("/dev/input"), notify::RecursiveMode::Recursive)?; + + for event_result in rx { + match event_result { + Ok(event) => { + if event.kind == EventKind::Create(notify::event::CreateKind::File) + || event.kind == EventKind::Remove(notify::event::RemoveKind::File) + { + grab_controller.on_inotify_dev_input(); + } + } + Err(err) => { + error!("Failed to receive inotify event: {}", err); + } + } + } + + Ok(()) + } } struct EventLoopThread { From 32da562ec0b6e459ca7c62870805c3b937c319a1 Mon Sep 17 00:00:00 2001 From: sds100 Date: Sat, 20 Dec 2025 23:58:44 +0000 Subject: [PATCH 164/199] restart system bridge on debug builds with shorter delay --- .../keymapper/base/promode/SystemBridgeAutoStarter.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt index fe90c1e784..4c007e0956 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt @@ -161,19 +161,19 @@ class SystemBridgeAutoStarter @Inject constructor( "SystemBridgeAutoStarter init: time since boot=${clock.elapsedRealtime() / 1000} seconds", ) - // Wait 5 seconds for the system bridge to potentially connect itself to Key Mapper - // before deciding whether to start it. - delay(5000) - if (BuildConfig.DEBUG && connectionManager.isConnected()) { + delay(1000) // This is useful when developing and need to restart the system bridge // after making changes to it. Timber.w("Restarting system bridge on debug build.") connectionManager.restartSystemBridge() - delay(5000) } + // Wait 5 seconds for the system bridge to potentially connect itself to Key Mapper + // before deciding whether to start it. + delay(5000) + handleAutoStartFromPreVersion4() autoStartFlow From 7c50b0bf0213c423f8714b9353a450c9ed5750ae Mon Sep 17 00:00:00 2001 From: sds100 Date: Sun, 21 Dec 2025 00:17:13 +0000 Subject: [PATCH 165/199] #1926 debounce inotify events when grabbing devices --- .../core/src/evdev_grab_controller.rs | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs index a60f229355..ddd6981eab 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs @@ -4,7 +4,10 @@ use std::{ io, os::fd::AsRawFd, path::PathBuf, - sync::{Arc, Mutex, RwLock}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, RwLock, + }, }; use bimap::BiHashMap; @@ -23,6 +26,7 @@ pub struct EvdevGrabController { callback: Arc, grab_targets: Mutex>, grabbed_devices: RwLock>, + setting_grab_targets: AtomicBool, } impl EvdevGrabController { @@ -32,10 +36,15 @@ impl EvdevGrabController { callback, grab_targets: Mutex::new(Vec::with_capacity(64)), grabbed_devices: RwLock::new(Slab::with_capacity(64)), + // This stores whether new devices are being grabbed due to new targets. + setting_grab_targets: AtomicBool::new(false), } } pub fn set_grab_targets(&self, targets: Vec) -> Vec { + info!("Setting grab targets: {:?}", targets); + self.setting_grab_targets.store(true, Ordering::Relaxed); + let mut grab_targets = self.grab_targets.lock().unwrap(); grab_targets.clear(); @@ -43,10 +52,21 @@ impl EvdevGrabController { grab_targets.push(target); } - self.invalidate(grab_targets.as_ref()) + let handles = self.invalidate(grab_targets.as_ref()); + + self.setting_grab_targets.store(false, Ordering::Relaxed); + + handles } pub fn on_inotify_dev_input(&self) { + // When setting the grabbed devices, the files in /dev/input + // so debounce the inotify calls while grabbing new devices. + if self.setting_grab_targets.load(Ordering::Relaxed) { + return; + } + + info!("inotify /dev/input event received"); let grab_targets = self.grab_targets.lock().unwrap(); self.invalidate(grab_targets.as_ref()); } @@ -84,6 +104,8 @@ impl EvdevGrabController { .map(|(key, device)| GrabbedDeviceHandle::new(key, device.device_info.clone())) .collect(); + info!("Grabbed devices: {:?}", grabbed_device_handles); + // Release the lock before calling the callback drop(grabbed_devices); @@ -177,8 +199,6 @@ impl EvdevGrabController { ) }) .ok(); - - info!("Ungrabbed device: {:?}", device.device_path); } // TODO test @@ -207,7 +227,7 @@ impl EvdevGrabController { None => continue, // Device is connected Some(device_info) => { - let path = device_info_path_map.get_by_left(&device_info).unwrap(); + let path = device_info_path_map.get_by_left(device_info).unwrap(); targets_to_grab.push((path.clone(), target.extra_event_codes.clone())); } @@ -225,7 +245,7 @@ impl EvdevGrabController { ) -> Result> { let device = GrabbedDevice::new(device_path, extra_event_codes)?; let fd = device.evdev.lock().unwrap().as_raw_fd(); - let key = self.grabbed_devices.write().unwrap().insert(device); + let key = grabbed_devices.insert(device); let mut source_fd = SourceFd(&fd); @@ -238,7 +258,6 @@ impl EvdevGrabController { error!("Failed to register device {:?}: {}", device_path, e); })?; - info!("Grabbed device: {:?}", device_path); Ok(key) } From a3745808ef79d87f5ca0f575eb2c8265d51b9cdd Mon Sep 17 00:00:00 2001 From: sds100 Date: Sun, 21 Dec 2025 22:25:20 +0000 Subject: [PATCH 166/199] #1926 fix lock contention between methods when grabbing devices --- .../base/input/EvdevDevicesDelegate.kt | 1 + .../core/src/evdev_grab_controller.rs | 45 ++++++----- .../rust/evdev_manager/core/src/event_loop.rs | 75 ++++++++++--------- 3 files changed, 60 insertions(+), 61 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt index dd6a7c41de..45192e5b82 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt @@ -57,6 +57,7 @@ class EvdevDevicesDelegate @Inject constructor( systemBridgeConnectionManager.connectionState.flatMapLatest { connectionState -> when (connectionState) { is SystemBridgeConnectionState.Connected -> { + // TODO replace with callback from system bridge devicesAdapter.connectedInputDevices.onEach { allDevices.value = fetchAllDevices() } diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs index ddd6981eab..98672dd8d5 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs @@ -26,7 +26,6 @@ pub struct EvdevGrabController { callback: Arc, grab_targets: Mutex>, grabbed_devices: RwLock>, - setting_grab_targets: AtomicBool, } impl EvdevGrabController { @@ -36,51 +35,53 @@ impl EvdevGrabController { callback, grab_targets: Mutex::new(Vec::with_capacity(64)), grabbed_devices: RwLock::new(Slab::with_capacity(64)), - // This stores whether new devices are being grabbed due to new targets. - setting_grab_targets: AtomicBool::new(false), } } pub fn set_grab_targets(&self, targets: Vec) -> Vec { info!("Setting grab targets: {:?}", targets); - self.setting_grab_targets.store(true, Ordering::Relaxed); let mut grab_targets = self.grab_targets.lock().unwrap(); + grab_targets.clear(); for target in targets { grab_targets.push(target); } - let handles = self.invalidate(grab_targets.as_ref()); - - self.setting_grab_targets.store(false, Ordering::Relaxed); + let mut grabbed_devices = self.grabbed_devices.write().unwrap(); + let handles = self.invalidate(grab_targets.as_ref(), &mut grabbed_devices); handles } - pub fn on_inotify_dev_input(&self) { - // When setting the grabbed devices, the files in /dev/input - // so debounce the inotify calls while grabbing new devices. - if self.setting_grab_targets.load(Ordering::Relaxed) { + pub fn on_inotify_dev_input(&self, paths: &[PathBuf]) { + let mut grabbed_devices = self.grabbed_devices.write().unwrap(); + let is_uinput_device = grabbed_devices + .iter() + .any(|(_, device)| paths.contains(&device.uinput.devnode().unwrap().into())); + + if is_uinput_device { return; } info!("inotify /dev/input event received"); let grab_targets = self.grab_targets.lock().unwrap(); - self.invalidate(grab_targets.as_ref()); + self.invalidate(grab_targets.as_ref(), &mut grabbed_devices); } - fn invalidate(&self, grab_targets: &[GrabTarget]) -> Vec { - let mut grabbed_devices = self.grabbed_devices.write().unwrap(); - + fn invalidate( + &self, + grab_targets: &[GrabTarget], + grabbed_devices: &mut Slab, + ) -> Vec { let real_device_paths = - Self::get_real_device_paths(&grabbed_devices).expect("Unable to evdev device paths"); + Self::get_real_device_paths(grabbed_devices).expect("Unable to evdev device paths"); let device_info_path_map = Self::build_device_info_path_map(&real_device_paths); let device_keys_to_ungrab = Self::get_devices_to_ungrab( grab_targets, - &grabbed_devices, + grabbed_devices, device_info_path_map.clone(), ); @@ -91,10 +92,10 @@ impl EvdevGrabController { } let devices_to_grab = - Self::get_targets_to_grab(grab_targets, &grabbed_devices, device_info_path_map); + Self::get_targets_to_grab(grab_targets, grabbed_devices, device_info_path_map); for (path, extra_event_codes) in devices_to_grab { - self.try_grab_target(&path, &extra_event_codes, &mut grabbed_devices) + self.try_grab_target(&path, &extra_event_codes, grabbed_devices) .inspect_err(|err| error!("Failed to grab device {:?}: {:?}", path, err)) .ok(); } @@ -106,9 +107,6 @@ impl EvdevGrabController { info!("Grabbed devices: {:?}", grabbed_device_handles); - // Release the lock before calling the callback - drop(grabbed_devices); - self.callback .on_grabbed_devices_changed(grabbed_device_handles.clone()); @@ -249,9 +247,8 @@ impl EvdevGrabController { let mut source_fd = SourceFd(&fd); - // Register with key + 1 because 0 is reserved for the waker self.poll_registry - .register(&mut source_fd, Token(key + 1), Interest::READABLE) + .register(&mut source_fd, Token(key), Interest::READABLE) .inspect_err(|e| { // Remove device on registration failure grabbed_devices.remove(key); diff --git a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs index 172757b592..6f318c9d08 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs @@ -120,9 +120,16 @@ impl EventLoopManager { let poll_lock_clone = self.poll.clone(); let stop_flag_clone = self.stop_flag.clone(); + let grab_controller_event_loop = self.grab_controller.clone(); let event_loop_handle = get_runtime().spawn(async move { - EventLoopThread::new(stop_flag_clone, poll_lock_clone, callback).start(); + EventLoopThread::new( + stop_flag_clone, + poll_lock_clone, + callback, + grab_controller_event_loop, + ) + .start(); }); self.event_loop_handle @@ -130,10 +137,10 @@ impl EventLoopManager { .unwrap() .replace(event_loop_handle); - let grab_controller = self.grab_controller.clone(); + let grab_controller_inotify = self.grab_controller.clone(); let inotify_handle = get_runtime().spawn(async move { - Self::watch_dev_input(&grab_controller) + Self::watch_dev_input(&grab_controller_inotify) .inspect_err(|err| error!("Failed to watch device input: {}", err)) .unwrap_err(); }); @@ -302,7 +309,7 @@ impl EventLoopManager { if event.kind == EventKind::Create(notify::event::CreateKind::File) || event.kind == EventKind::Remove(notify::event::RemoveKind::File) { - grab_controller.on_inotify_dev_input(); + grab_controller.on_inotify_dev_input(&event.paths); } } Err(err) => { @@ -319,7 +326,7 @@ struct EventLoopThread { stop_flag: Arc, poll: Arc>, callback: Arc, - grabbed_devices: Arc>>, + grab_controller: Arc, } impl EventLoopThread { @@ -327,12 +334,13 @@ impl EventLoopThread { stop_flag: Arc, poll: Arc>, callback: Arc, + grab_controller: Arc, ) -> Self { EventLoopThread { stop_flag, poll, callback, - grabbed_devices: Arc::new(RwLock::new(Slab::with_capacity(64))), + grab_controller, } } @@ -375,38 +383,31 @@ impl EventLoopThread { let Token(key) = event.token(); let slab_key = key; - let devices = self.grabbed_devices.read().unwrap(); - - let grabbed_device = match devices.get(slab_key) { - Some(device) => device, - None => { - debug!("Device with key {} no longer exists", slab_key); - return; - } - }; - - let evdev = grabbed_device.evdev.lock().unwrap(); - let mut flags: ReadFlag = ReadFlag::NORMAL; - - loop { - match evdev.next_event(flags) { - Ok((ReadStatus::Success, input_event)) => { - flags = ReadFlag::NORMAL; - // Keep this logging line. Debug/verbose events will be disabled in production. - debug!("Evdev event: {:?}", input_event); - self.process_event(slab_key, &input_event, grabbed_device); - } - Ok((ReadStatus::Sync, _event)) => { - // Continue reading sync events - flags = ReadFlag::NORMAL | ReadFlag::SYNC; - } - Err(_error) => { - // Break if it's EAGAIN (no more events) or any other error. - // Do not log these errors because it is expected - break; + self.grab_controller + .with_grabbed_device(slab_key, |device| { + let evdev = device.evdev.lock().unwrap(); + let mut flags: ReadFlag = ReadFlag::NORMAL; + + loop { + match evdev.next_event(flags) { + Ok((ReadStatus::Success, input_event)) => { + flags = ReadFlag::NORMAL; + // Keep this logging line. Debug/verbose events will be disabled in production. + debug!("Evdev event: {:?}", input_event); + self.process_event(slab_key, &input_event, device); + } + Ok((ReadStatus::Sync, _event)) => { + // Continue reading sync events + flags = ReadFlag::NORMAL | ReadFlag::SYNC; + } + Err(_error) => { + // Break if it's EAGAIN (no more events) or any other error. + // Do not log these errors because it is expected + break; + } + } } - } - } + }); } fn process_event(&self, device_id: usize, event: &InputEvent, grabbed_device: &GrabbedDevice) { From 53db006b272747249e7337f7ba03238adf39f7fc Mon Sep 17 00:00:00 2001 From: sds100 Date: Sun, 21 Dec 2025 22:33:51 +0000 Subject: [PATCH 167/199] style: reformat --- .../core/src/android/keylayout/generic_key_layout.rs | 1 - .../evdev_manager/core/src/evdev_grab_controller.rs | 11 ++++++----- .../main/rust/evdev_manager/core/src/event_loop.rs | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/generic_key_layout.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/generic_key_layout.rs index b454076831..ac7f35eb89 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/generic_key_layout.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/generic_key_layout.rs @@ -286,4 +286,3 @@ axis 0x0a LTRIGGER axis 0x10 HAT_X axis 0x11 HAT_Y "#; - diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs index 98672dd8d5..ba663ca21b 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs @@ -5,7 +5,6 @@ use std::{ os::fd::AsRawFd, path::PathBuf, sync::{ - atomic::{AtomicBool, Ordering}, Arc, Mutex, RwLock, }, }; @@ -123,8 +122,9 @@ impl EvdevGrabController { grabbed_devices.get(device_id).map(f) } - // TODO test - fn get_devices_to_ungrab( + /// Get devices that should be ungrabed based on current grab targets and device state. + /// This function is public for testing purposes. + pub fn get_devices_to_ungrab( grab_targets: &[GrabTarget], grabbed_devices: &Slab, device_info_path_map: BiHashMap, @@ -199,8 +199,9 @@ impl EvdevGrabController { .ok(); } - // TODO test - fn get_targets_to_grab( + /// Get targets that should be grabbed based on current grab targets and device state. + /// This function is public for testing purposes. + pub fn get_targets_to_grab( grab_targets: &[GrabTarget], grabbed_devices: &Slab, device_info_path_map: BiHashMap, diff --git a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs index 6f318c9d08..b071726e03 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs @@ -14,7 +14,6 @@ use libc::c_uint; use mio::event::Event; use mio::{Events, Poll, Token, Waker}; use notify::{EventKind, Watcher}; -use slab::Slab; use std::error::Error; use std::io; use std::io::ErrorKind; From 4a8d2f097ff7766caaf8430d43370ecfbf4c0539 Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 00:03:40 +0000 Subject: [PATCH 168/199] #1926 WIP: notify Kotlin code when evdev devices change and stop watching when invalidating grabbed devices --- .../base/input/EvdevDevicesDelegate.kt | 30 ++--- .../keymapper/base/input/InputEventHub.kt | 6 + .../core/src/evdev_devices_watcher.rs | 126 ++++++++++++++++++ .../core/src/evdev_grab_controller.rs | 74 +++++++--- .../rust/evdev_manager/core/src/event_loop.rs | 54 ++------ .../main/rust/evdev_manager/core/src/lib.rs | 1 + .../jni/src/evdev_jni_observer.rs | 70 ++++++++++ .../rust/evdev_manager/jni/src/jni_bridge.rs | 4 + .../keymapper/evdev/IEvdevCallback.aidl | 2 + .../sysbridge/service/SystemBridge.kt | 12 ++ 10 files changed, 299 insertions(+), 80 deletions(-) create mode 100644 evdev/src/main/rust/evdev_manager/core/src/evdev_devices_watcher.rs diff --git a/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt index 45192e5b82..9dc7ea17d5 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt @@ -6,11 +6,9 @@ import io.github.sds100.keymapper.common.models.GrabTargetKeyCode import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle import io.github.sds100.keymapper.common.utils.Constants import io.github.sds100.keymapper.common.utils.onFailure -import io.github.sds100.keymapper.common.utils.onSuccess import io.github.sds100.keymapper.common.utils.valueIfFailure import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState -import io.github.sds100.keymapper.system.devices.DevicesAdapter import javax.inject.Inject import javax.inject.Singleton import kotlinx.coroutines.CoroutineScope @@ -18,10 +16,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -37,7 +31,6 @@ import timber.log.Timber @Singleton class EvdevDevicesDelegate @Inject constructor( private val coroutineScope: CoroutineScope, - private val devicesAdapter: DevicesAdapter, private val systemBridgeConnectionManager: SystemBridgeConnectionManager, ) { private val grabbedDevicesById: MutableStateFlow> = @@ -52,24 +45,18 @@ class EvdevDevicesDelegate @Inject constructor( init { coroutineScope.launch { - // Only listen to changes in the connected input devices if the system bridge - // is connected. - systemBridgeConnectionManager.connectionState.flatMapLatest { connectionState -> + systemBridgeConnectionManager.connectionState.collect { connectionState -> when (connectionState) { is SystemBridgeConnectionState.Connected -> { - // TODO replace with callback from system bridge - devicesAdapter.connectedInputDevices.onEach { - allDevices.value = fetchAllDevices() - } + allDevices.value = fetchAllDevices() } is SystemBridgeConnectionState.Disconnected -> { allDevices.value = emptyList() grabbedDevicesById.value = emptyMap() - emptyFlow() } } - }.collect() + } } coroutineScope.launch { @@ -84,9 +71,8 @@ class EvdevDevicesDelegate @Inject constructor( private fun invalidateGrabbedDevices(devices: List) { systemBridgeConnectionManager .run { bridge -> bridge.setGrabTargets(devices.toTypedArray()) } - .onSuccess { grabbedDevices -> - onGrabbedDevicesChanged(grabbedDevices?.filterNotNull() ?: emptyList()) - }.onFailure { error -> + // The callback will respond with the new grabbed devices. + .onFailure { error -> Timber.w( "Grabbing devices failed in system bridge: $error", ) @@ -115,6 +101,12 @@ class EvdevDevicesDelegate @Inject constructor( } } + fun onEvdevDevicesChanged(devices: List) { + Timber.i("Evdev devices changed: [${devices.joinToString { it.name }}]") + + allDevices.value = devices + } + private suspend fun fetchAllDevices(): List { // Do it on a separate thread in case there is deadlock return withContext(Dispatchers.IO) { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt b/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt index a3e096b56d..62680729c1 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt @@ -376,6 +376,12 @@ class InputEventHubImpl @Inject constructor( evdevDevicesDelegate.onGrabbedDevicesChanged(devicesList) } + @RequiresApi(Build.VERSION_CODES.Q) + override fun onEvdevDevicesChanged(devices: Array?) { + val devicesList = devices?.filterNotNull()?.toList() ?: emptyList() + evdevDevicesDelegate.onEvdevDevicesChanged(devicesList) + } + @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) private fun invalidateGrabbedDevices() { val devicesToGrab = clients.values.flatMap { it.grabRequests }.toSet() diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_devices_watcher.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_devices_watcher.rs new file mode 100644 index 0000000000..365a80347a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_devices_watcher.rs @@ -0,0 +1,126 @@ +use std::path::{Path, PathBuf}; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + mpsc, Arc, Mutex, RwLock, +}; + +use notify::{EventKind, RecommendedWatcher, Watcher}; +use tokio::task::JoinHandle; + +use crate::evdev_error::{EvdevError, EvdevErrorCode}; +use crate::runtime::get_runtime; + +/// Callback for when inotify events occur +pub trait InotifyCallback: Send + Sync { + fn on_inotify_dev_input(&self, paths: &[PathBuf]); +} + +pub struct EvdevDevicesWatcher { + watcher: Arc>>, + inotify_handle: RwLock>>, + enabled: Arc, +} + +impl EvdevDevicesWatcher { + pub fn new() -> Self { + Self { + watcher: Arc::new(Mutex::new(None)), + inotify_handle: RwLock::new(None), + enabled: Arc::new(AtomicBool::new(true)), + } + } + + /// Start the thread to watch /dev/input for device changes + pub fn start(&self, callback: Arc) -> Result<(), EvdevError> { + let is_running = { self.inotify_handle.read().unwrap().is_some() }; + + if is_running { + info!("Inotify watcher is already running"); + return Ok(()); + } + + // Create the channel and watcher + let (tx, rx) = mpsc::channel::>(); + + let mut watcher = notify::recommended_watcher(tx).map_err(|e| { + error!("Failed to create inotify watcher: {}", e); + EvdevError::from_enum(EvdevErrorCode::IoError) + })?; + + watcher + .watch(Path::new("/dev/input"), notify::RecursiveMode::Recursive) + .map_err(|e| { + error!("Failed to watch /dev/input: {}", e); + EvdevError::from_enum(EvdevErrorCode::IoError) + })?; + + // Store the watcher in Arc + { + let mut watcher_guard = self.watcher.lock().unwrap(); + *watcher_guard = Some(watcher); + } + + // Start the event processing loop + let callback_clone = callback.clone(); + let enabled_clone = self.enabled.clone(); + + let handle = get_runtime().spawn(async move { + for event_result in rx { + // Skip processing if disabled + if !enabled_clone.load(Ordering::Relaxed) { + continue; + } + + match event_result { + Ok(event) => { + if event.kind == EventKind::Create(notify::event::CreateKind::File) + || event.kind == EventKind::Remove(notify::event::RemoveKind::File) + { + callback_clone.on_inotify_dev_input(&event.paths); + } + } + Err(err) => { + error!("Failed to receive inotify event: {}", err); + } + } + } + }); + + self.inotify_handle.write().unwrap().replace(handle); + + Ok(()) + } + + /// Stop the thread watching /dev/input for device changes + pub fn stop(&self) -> Result<(), EvdevError> { + self.enabled.store(false, Ordering::Relaxed); + + let handle_option = self.inotify_handle.write().unwrap().take(); + + if let Some(handle) = handle_option { + handle.abort(); + } + + // Clear the watcher + let mut watcher_guard = self.watcher.lock().unwrap(); + *watcher_guard = None; + + Ok(()) + } + + /// Enable processing of inotify events + pub fn enable(&self) { + self.enabled.store(true, Ordering::Relaxed); + } + + /// Disable processing of inotify events (temporarily skip events) + pub fn disable(&self) { + self.enabled.store(false, Ordering::Relaxed); + } +} + +impl Default for EvdevDevicesWatcher { + fn default() -> Self { + Self::new() + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs index ba663ca21b..848b0d0f9c 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs @@ -4,9 +4,7 @@ use std::{ io, os::fd::AsRawFd, path::PathBuf, - sync::{ - Arc, Mutex, RwLock, - }, + sync::{Arc, Mutex, RwLock}, }; use bimap::BiHashMap; @@ -15,8 +13,12 @@ use mio::{unix::SourceFd, Interest, Registry, Token}; use slab::Slab; use crate::{ - evdev_device_info::EvdevDeviceInfo, evdev_error::EvdevError, event_loop::EvdevCallback, - grab_target::GrabTarget, grabbed_device::GrabbedDevice, + evdev_device_info::EvdevDeviceInfo, + evdev_devices_watcher::{EvdevDevicesWatcher, InotifyCallback}, + evdev_error::EvdevError, + event_loop::EvdevCallback, + grab_target::GrabTarget, + grabbed_device::GrabbedDevice, grabbed_device_handle::GrabbedDeviceHandle, }; @@ -25,6 +27,7 @@ pub struct EvdevGrabController { callback: Arc, grab_targets: Mutex>, grabbed_devices: RwLock>, + devices_watcher: EvdevDevicesWatcher, } impl EvdevGrabController { @@ -34,6 +37,7 @@ impl EvdevGrabController { callback, grab_targets: Mutex::new(Vec::with_capacity(64)), grabbed_devices: RwLock::new(Slab::with_capacity(64)), + devices_watcher: EvdevDevicesWatcher::new(), } } @@ -54,26 +58,14 @@ impl EvdevGrabController { handles } - pub fn on_inotify_dev_input(&self, paths: &[PathBuf]) { - let mut grabbed_devices = self.grabbed_devices.write().unwrap(); - let is_uinput_device = grabbed_devices - .iter() - .any(|(_, device)| paths.contains(&device.uinput.devnode().unwrap().into())); - - if is_uinput_device { - return; - } - - info!("inotify /dev/input event received"); - let grab_targets = self.grab_targets.lock().unwrap(); - self.invalidate(grab_targets.as_ref(), &mut grabbed_devices); - } - fn invalidate( &self, grab_targets: &[GrabTarget], grabbed_devices: &mut Slab, ) -> Vec { + // Disable inotify event processing during invalidate to avoid race conditions + self.devices_watcher.disable(); + let real_device_paths = Self::get_real_device_paths(grabbed_devices).expect("Unable to evdev device paths"); let device_info_path_map = Self::build_device_info_path_map(&real_device_paths); @@ -109,6 +101,9 @@ impl EvdevGrabController { self.callback .on_grabbed_devices_changed(grabbed_device_handles.clone()); + // Re-enable inotify event processing after invalidate is complete + self.devices_watcher.enable(); + grabbed_device_handles } @@ -306,7 +301,6 @@ impl EvdevGrabController { } } - debug!("EvdevManager: get real devices: {:?}", paths); Ok(paths) } @@ -331,4 +325,42 @@ impl EvdevGrabController { version: device.version(), }) } + + /// Start watching /dev/input for device changes + pub fn start_watching(self: &Arc) -> Result<(), EvdevError> { + self.devices_watcher.start(self.clone()) + } + + /// Stop watching /dev/input for device changes + pub fn stop_watching(&self) -> Result<(), EvdevError> { + self.devices_watcher.stop() + } +} + +impl InotifyCallback for EvdevGrabController { + fn on_inotify_dev_input(&self, paths: &[PathBuf]) { + let mut grabbed_devices = self.grabbed_devices.write().unwrap(); + let is_uinput_device = grabbed_devices + .iter() + .any(|(_, device)| paths.contains(&device.uinput.devnode().unwrap().into())); + + if is_uinput_device { + return; + } + + info!("inotify /dev/input event received"); + let grab_targets = self.grab_targets.lock().unwrap(); + self.invalidate(grab_targets.as_ref(), &mut grabbed_devices); + + // Notify callback about device list changes + drop(grabbed_devices); // Release the write lock before calling get_real_devices + match self.get_real_devices() { + Ok(devices) => { + self.callback.on_evdev_devices_changed(devices); + } + Err(e) => { + error!("Failed to get real devices for callback: {:?}", e); + } + } + } } diff --git a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs index b071726e03..df87d1de29 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs @@ -13,13 +13,11 @@ use evdev::{InputEvent, ReadFlag, ReadStatus}; use libc::c_uint; use mio::event::Event; use mio::{Events, Poll, Token, Waker}; -use notify::{EventKind, Watcher}; use std::error::Error; use std::io; use std::io::ErrorKind; -use std::path::Path; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{mpsc, Arc, OnceLock, RwLock}; +use std::sync::{Arc, OnceLock, RwLock}; use std::time::{Duration, Instant}; use std::{fmt, usize}; use tokio::task::JoinHandle; @@ -39,6 +37,10 @@ pub trait EvdevCallback: Send + Sync { /// Called when the list of grabbed devices changes. /// Parameters: grabbed_devices list with their assigned IDs fn on_grabbed_devices_changed(&self, grabbed_devices: Vec); + + /// Called when the list of available evdev devices changes. + /// Parameters: devices list of all available evdev devices + fn on_evdev_devices_changed(&self, devices: Vec); } static EVENT_LOOP_MANAGER: OnceLock = OnceLock::new(); @@ -49,7 +51,6 @@ pub struct EventLoopManager { stop_flag: Arc, poll: Arc>, event_loop_handle: RwLock>>, - inotify_handle: RwLock>>, waker: Waker, callback: Arc, grab_controller: Arc, @@ -98,7 +99,6 @@ impl EventLoopManager { stop_flag: Arc::new(AtomicBool::new(false)), poll: poll_lock, event_loop_handle: RwLock::new(None), - inotify_handle: RwLock::new(None), waker, callback, grab_controller: Arc::new(grab_controller), @@ -136,20 +136,20 @@ impl EventLoopManager { .unwrap() .replace(event_loop_handle); - let grab_controller_inotify = self.grab_controller.clone(); - - let inotify_handle = get_runtime().spawn(async move { - Self::watch_dev_input(&grab_controller_inotify) - .inspect_err(|err| error!("Failed to watch device input: {}", err)) - .unwrap_err(); - }); - - self.inotify_handle.write().unwrap().replace(inotify_handle); + self.grab_controller + .start_watching() + .inspect_err(|err| error!("Failed to start inotify watching: {:?}", err))?; Ok(()) } pub fn stop(&self) -> Result<(), io::Error> { + // Stop inotify watching + self.grab_controller + .stop_watching() + .inspect_err(|err| error!("Failed to stop inotify watching: {:?}", err)) + .ok(); + let handle_option = self.event_loop_handle.write().unwrap().take(); match handle_option { @@ -293,32 +293,6 @@ impl EventLoopManager { extra_event_codes: event_codes, } } - - fn watch_dev_input(grab_controller: &EvdevGrabController) -> notify::Result<()> { - let (tx, rx) = mpsc::channel::>(); - - let mut watcher = notify::recommended_watcher(tx) - .inspect_err(|err| error!("Failed to create inotify watcher: {}", err))?; - - watcher.watch(Path::new("/dev/input"), notify::RecursiveMode::Recursive)?; - - for event_result in rx { - match event_result { - Ok(event) => { - if event.kind == EventKind::Create(notify::event::CreateKind::File) - || event.kind == EventKind::Remove(notify::event::RemoveKind::File) - { - grab_controller.on_inotify_dev_input(&event.paths); - } - } - Err(err) => { - error!("Failed to receive inotify event: {}", err); - } - } - } - - Ok(()) - } } struct EventLoopThread { diff --git a/evdev/src/main/rust/evdev_manager/core/src/lib.rs b/evdev/src/main/rust/evdev_manager/core/src/lib.rs index 5f5ea52144..40b0c8b813 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/lib.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/lib.rs @@ -2,6 +2,7 @@ extern crate log; pub mod android; pub mod evdev_device_info; +pub mod evdev_devices_watcher; pub mod evdev_error; pub mod evdev_grab_controller; pub mod event_loop; diff --git a/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs b/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs index 6a64ba1277..5bf31d541f 100644 --- a/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs +++ b/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs @@ -207,4 +207,74 @@ impl EvdevJniObserver { } } } + + pub fn on_evdev_devices_changed(&self, devices: Vec) { + let mut env = self + .jvm + .attach_current_thread_permanently() + .expect("Failed to attach to JVM thread"); + + // Convert Vec to Java array of EvdevDeviceInfo + let info_class = + match env.find_class("io/github/sds100/keymapper/common/models/EvdevDeviceInfo") { + Ok(c) => c, + Err(e) => { + error!("Failed to find EvdevDeviceInfo class: {:?}", e); + return; + } + }; + + let array = match env.new_object_array( + devices.len() as i32, + &info_class, + jni::objects::JObject::null(), + ) { + Ok(a) => a, + Err(e) => { + error!("Failed to create EvdevDeviceInfo array: {:?}", e); + return; + } + }; + + for (i, device_info) in devices.iter().enumerate() { + let name_str = match env.new_string(&device_info.name) { + Ok(s) => s, + Err(e) => { + error!("Failed to create device name string: {:?}", e); + continue; + } + }; + + let info = match env.new_object( + &info_class, + "(Ljava/lang/String;III)V", + &[ + JValue::Object(&name_str.into()), + JValue::Int(device_info.bus as i32), + JValue::Int(device_info.vendor as i32), + JValue::Int(device_info.product as i32), + ], + ) { + Ok(i) => i, + Err(e) => { + error!("Failed to create EvdevDeviceInfo: {:?}", e); + continue; + } + }; + + if let Err(e) = env.set_object_array_element(&array, i as i32, &info) { + error!("Failed to set array element: {:?}", e); + } + } + + // Call SystemBridge.onEvdevDevicesChanged() via JNI + if let Err(e) = env.call_method( + &self.system_bridge, + "onEvdevDevicesChanged", + "([Lio/github/sds100/keymapper/common/models/EvdevDeviceInfo;)V", + &[JValue::Object(&array.into())], + ) { + error!("Failed to call onEvdevDevicesChanged: {:?}", e); + } + } } diff --git a/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs b/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs index bf1bd28eb4..b39c54cd82 100644 --- a/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs +++ b/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs @@ -34,6 +34,10 @@ impl EvdevCallback for JniEvdevCallback { fn on_grabbed_devices_changed(&self, grabbed_devices: Vec) { get_jni_observer().on_grabbed_devices_changed(grabbed_devices) } + + fn on_evdev_devices_changed(&self, devices: Vec) { + get_jni_observer().on_evdev_devices_changed(devices) + } } /// MUST only be called once in the lifetime of the process. diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl index 12f35c3633..1ffea8fb99 100644 --- a/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl @@ -1,6 +1,7 @@ package io.github.sds100.keymapper.evdev; import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle; +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo; interface IEvdevCallback { /** @@ -10,4 +11,5 @@ interface IEvdevCallback { boolean onEvdevEvent(int deviceId, long timeSec, long timeUsec, int type, int code, int value, int androidCode); void onEmergencyKillSystemBridge(); void onGrabbedDevicesChanged(in GrabbedDeviceHandle[] devices); + void onEvdevDevicesChanged(in EvdevDeviceInfo[] devices); } diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt index ed4c6c16a5..7ce2659465 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt @@ -124,6 +124,18 @@ class SystemBridge : ISystemBridge.Stub() { } } + @Suppress("unused") + fun onEvdevDevicesChanged(devices: Array) { + synchronized(evdevCallbackLock) { + val callback = evdevCallback ?: return + try { + callback.onEvdevDevicesChanged(devices) + } catch (e: Exception) { + Log.e(TAG, "Error calling evdev callback", e) + } + } + } + /** * Called from Rust via JNI when the power button is held for 10+ seconds. * Forwards the call to the registered IEvdevCallback for emergency system bridge kill. From 4b9d9a33d0bfa37d56548a98df3add854fe88d48 Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 00:20:41 +0000 Subject: [PATCH 169/199] fix Rust test --- .../evdev_manager/core/tests/key_layout_map_manager_test.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_manager_test.rs b/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_manager_test.rs index 5d24430977..5f80f13d0a 100644 --- a/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_manager_test.rs +++ b/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_manager_test.rs @@ -3,8 +3,9 @@ use assertables::{assert_iter_eq, assert_some, assert_some_eq}; use evdev_manager_core::android::android_codes::{ AKEYCODE_ESCAPE, AKEYCODE_HOME, AKEYCODE_MINUS, AKEYCODE_MOVE_HOME, AKEYCODE_SPACE, }; +use evdev_manager_core::android::keylayout::key_layout_file_finder::KeyLayoutFileFinder; use evdev_manager_core::android::keylayout::key_layout_map_manager::{ - get_generic_key_layout_map, KeyLayoutFileFinder, KeyLayoutMapManager, + get_generic_key_layout_map, KeyLayoutMapManager, }; use evdev_manager_core::evdev_device_info::EvdevDeviceInfo; #[cfg(test)] From f312105facea62dc2bcb481f33cd8f6395c8e35b Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 00:23:23 +0000 Subject: [PATCH 170/199] fix Kotlin test --- .../keymapper/common/models/GrabTargetKeyCode.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/common/src/main/java/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.kt b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.kt index 5e5e9a94a1..c1e436d9a7 100644 --- a/common/src/main/java/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.kt +++ b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.kt @@ -20,6 +20,18 @@ data class GrabTargetKeyCode( */ val extraKeyCodes: IntArray, ) : Parcelable { + + constructor( + device: EvdevDeviceInfo, + extraKeyCodes: IntArray, + ) : this( + device.name, + device.bus, + device.vendor, + device.product, + extraKeyCodes, + ) + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false From c2f27d117b43934a4b564f16cce83a573dd3a18f Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 00:31:09 +0000 Subject: [PATCH 171/199] #1934 fix: hold down option for tap screen is added back --- .../keymapper/base/actions/ActionUtils.kt | 153 +++++++++++++++--- 1 file changed, 132 insertions(+), 21 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUtils.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUtils.kt index b051aa498d..0385eedbb0 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUtils.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUtils.kt @@ -121,7 +121,6 @@ object ActionUtils { ActionId.SWIPE_SCREEN -> ActionCategory.INPUT ActionId.PINCH_SCREEN -> ActionCategory.INPUT ActionId.TEXT -> ActionCategory.INPUT - ActionId.OPEN_VOICE_ASSISTANT -> ActionCategory.APPS ActionId.OPEN_DEVICE_ASSISTANT -> ActionCategory.APPS ActionId.OPEN_CAMERA -> ActionCategory.APPS @@ -132,30 +131,24 @@ object ActionUtils { ActionId.URL -> ActionCategory.APPS ActionId.HTTP_REQUEST -> ActionCategory.APPS ActionId.SHELL_COMMAND -> ActionCategory.APPS - ActionId.TOGGLE_WIFI -> ActionCategory.CONNECTIVITY ActionId.ENABLE_WIFI -> ActionCategory.CONNECTIVITY ActionId.DISABLE_WIFI -> ActionCategory.CONNECTIVITY - ActionId.TOGGLE_BLUETOOTH -> ActionCategory.CONNECTIVITY ActionId.ENABLE_BLUETOOTH -> ActionCategory.CONNECTIVITY ActionId.DISABLE_BLUETOOTH -> ActionCategory.CONNECTIVITY - ActionId.TOGGLE_MOBILE_DATA -> ActionCategory.CONNECTIVITY ActionId.ENABLE_MOBILE_DATA -> ActionCategory.CONNECTIVITY ActionId.DISABLE_MOBILE_DATA -> ActionCategory.CONNECTIVITY - ActionId.TOGGLE_HOTSPOT -> ActionCategory.CONNECTIVITY ActionId.ENABLE_HOTSPOT -> ActionCategory.CONNECTIVITY ActionId.DISABLE_HOTSPOT -> ActionCategory.CONNECTIVITY - ActionId.TOGGLE_AUTO_BRIGHTNESS -> ActionCategory.DISPLAY ActionId.DISABLE_AUTO_BRIGHTNESS -> ActionCategory.DISPLAY ActionId.ENABLE_AUTO_BRIGHTNESS -> ActionCategory.DISPLAY ActionId.INCREASE_BRIGHTNESS -> ActionCategory.DISPLAY ActionId.DECREASE_BRIGHTNESS -> ActionCategory.DISPLAY ActionId.SCREENSHOT -> ActionCategory.DISPLAY - ActionId.TOGGLE_AUTO_ROTATE -> ActionCategory.INTERFACE ActionId.ENABLE_AUTO_ROTATE -> ActionCategory.INTERFACE ActionId.DISABLE_AUTO_ROTATE -> ActionCategory.INTERFACE @@ -163,7 +156,6 @@ object ActionUtils { ActionId.LANDSCAPE_MODE -> ActionCategory.INTERFACE ActionId.SWITCH_ORIENTATION -> ActionCategory.INTERFACE ActionId.CYCLE_ROTATIONS -> ActionCategory.INTERFACE - ActionId.VOLUME_UP -> ActionCategory.VOLUME ActionId.VOLUME_DOWN -> ActionCategory.VOLUME ActionId.VOLUME_SHOW_DIALOG -> ActionCategory.VOLUME @@ -181,13 +173,11 @@ object ActionUtils { ActionId.MUTE_MICROPHONE -> ActionCategory.VOLUME ActionId.UNMUTE_MICROPHONE -> ActionCategory.VOLUME ActionId.TOGGLE_MUTE_MICROPHONE -> ActionCategory.VOLUME - ActionId.EXPAND_NOTIFICATION_DRAWER -> ActionCategory.NAVIGATION ActionId.TOGGLE_NOTIFICATION_DRAWER -> ActionCategory.NAVIGATION ActionId.EXPAND_QUICK_SETTINGS -> ActionCategory.NAVIGATION ActionId.TOGGLE_QUICK_SETTINGS -> ActionCategory.NAVIGATION ActionId.COLLAPSE_STATUS_BAR -> ActionCategory.NAVIGATION - ActionId.SOUND -> ActionCategory.MEDIA ActionId.PAUSE_MEDIA -> ActionCategory.MEDIA ActionId.PAUSE_MEDIA_PACKAGE -> ActionCategory.MEDIA @@ -209,27 +199,22 @@ object ActionUtils { ActionId.STEP_FORWARD_PACKAGE -> ActionCategory.MEDIA ActionId.STEP_BACKWARD -> ActionCategory.MEDIA ActionId.STEP_BACKWARD_PACKAGE -> ActionCategory.MEDIA - ActionId.GO_BACK -> ActionCategory.NAVIGATION ActionId.GO_HOME -> ActionCategory.NAVIGATION ActionId.OPEN_RECENTS -> ActionCategory.NAVIGATION ActionId.TOGGLE_SPLIT_SCREEN -> ActionCategory.NAVIGATION ActionId.GO_LAST_APP -> ActionCategory.NAVIGATION ActionId.OPEN_MENU -> ActionCategory.NAVIGATION - ActionId.TOGGLE_FLASHLIGHT -> ActionCategory.FLASHLIGHT ActionId.ENABLE_FLASHLIGHT -> ActionCategory.FLASHLIGHT ActionId.DISABLE_FLASHLIGHT -> ActionCategory.FLASHLIGHT ActionId.CHANGE_FLASHLIGHT_STRENGTH -> ActionCategory.FLASHLIGHT - ActionId.ENABLE_NFC -> ActionCategory.CONNECTIVITY ActionId.DISABLE_NFC -> ActionCategory.CONNECTIVITY ActionId.TOGGLE_NFC -> ActionCategory.CONNECTIVITY - ActionId.TOGGLE_AIRPLANE_MODE -> ActionCategory.CONNECTIVITY ActionId.ENABLE_AIRPLANE_MODE -> ActionCategory.CONNECTIVITY ActionId.DISABLE_AIRPLANE_MODE -> ActionCategory.CONNECTIVITY - ActionId.TEXT_CUT -> ActionCategory.KEYBOARD ActionId.TEXT_COPY -> ActionCategory.KEYBOARD ActionId.TEXT_PASTE -> ActionCategory.KEYBOARD @@ -240,162 +225,283 @@ object ActionUtils { ActionId.SHOW_KEYBOARD_PICKER -> ActionCategory.KEYBOARD ActionId.SELECT_WORD_AT_CURSOR -> ActionCategory.KEYBOARD ActionId.SWITCH_KEYBOARD -> ActionCategory.KEYBOARD - ActionId.LOCK_DEVICE -> ActionCategory.INTERFACE ActionId.POWER_ON_OFF_DEVICE -> ActionCategory.INTERFACE ActionId.SECURE_LOCK_DEVICE -> ActionCategory.INTERFACE ActionId.SHOW_POWER_MENU -> ActionCategory.INTERFACE - ActionId.PHONE_CALL -> ActionCategory.TELEPHONY ActionId.ANSWER_PHONE_CALL -> ActionCategory.TELEPHONY ActionId.END_PHONE_CALL -> ActionCategory.TELEPHONY ActionId.SEND_SMS -> ActionCategory.TELEPHONY ActionId.COMPOSE_SMS -> ActionCategory.TELEPHONY - ActionId.DISMISS_MOST_RECENT_NOTIFICATION -> ActionCategory.NOTIFICATIONS ActionId.DISMISS_ALL_NOTIFICATIONS -> ActionCategory.NOTIFICATIONS ActionId.CREATE_NOTIFICATION -> ActionCategory.NOTIFICATIONS ActionId.DEVICE_CONTROLS -> ActionCategory.APPS - ActionId.INTERACT_UI_ELEMENT -> ActionCategory.APPS ActionId.FORCE_STOP_APP -> ActionCategory.APPS ActionId.CLEAR_RECENT_APP -> ActionCategory.APPS ActionId.MODIFY_SETTING -> ActionCategory.APPS - ActionId.CONSUME_KEY_EVENT -> ActionCategory.SPECIAL } @StringRes fun getTitle(id: ActionId): Int = when (id) { ActionId.TOGGLE_WIFI -> R.string.action_toggle_wifi + ActionId.ENABLE_WIFI -> R.string.action_enable_wifi + ActionId.DISABLE_WIFI -> R.string.action_disable_wifi + ActionId.TOGGLE_BLUETOOTH -> R.string.action_toggle_bluetooth + ActionId.ENABLE_BLUETOOTH -> R.string.action_enable_bluetooth + ActionId.DISABLE_BLUETOOTH -> R.string.action_disable_bluetooth + ActionId.TOGGLE_MOBILE_DATA -> R.string.action_toggle_mobile_data + ActionId.ENABLE_MOBILE_DATA -> R.string.action_enable_mobile_data + ActionId.DISABLE_MOBILE_DATA -> R.string.action_disable_mobile_data + ActionId.TOGGLE_AUTO_BRIGHTNESS -> R.string.action_toggle_auto_brightness + ActionId.DISABLE_AUTO_BRIGHTNESS -> R.string.action_disable_auto_brightness + ActionId.ENABLE_AUTO_BRIGHTNESS -> R.string.action_enable_auto_brightness + ActionId.INCREASE_BRIGHTNESS -> R.string.action_increase_brightness + ActionId.DECREASE_BRIGHTNESS -> R.string.action_decrease_brightness + ActionId.TOGGLE_AUTO_ROTATE -> R.string.action_toggle_auto_rotate + ActionId.ENABLE_AUTO_ROTATE -> R.string.action_enable_auto_rotate + ActionId.DISABLE_AUTO_ROTATE -> R.string.action_disable_auto_rotate + ActionId.PORTRAIT_MODE -> R.string.action_portrait_mode + ActionId.LANDSCAPE_MODE -> R.string.action_landscape_mode + ActionId.SWITCH_ORIENTATION -> R.string.action_switch_orientation + ActionId.CYCLE_ROTATIONS -> R.string.action_cycle_rotations + ActionId.VOLUME_UP -> R.string.action_volume_up + ActionId.VOLUME_DOWN -> R.string.action_volume_down + ActionId.VOLUME_SHOW_DIALOG -> R.string.action_volume_show_dialog + ActionId.VOLUME_DECREASE_STREAM -> R.string.action_decrease_stream + ActionId.VOLUME_INCREASE_STREAM -> R.string.action_increase_stream + ActionId.CYCLE_RINGER_MODE -> R.string.action_cycle_ringer_mode + ActionId.CHANGE_RINGER_MODE -> R.string.action_change_ringer_mode + ActionId.CYCLE_VIBRATE_RING -> R.string.action_cycle_vibrate_ring + ActionId.TOGGLE_DND_MODE -> R.string.action_toggle_dnd_mode + ActionId.ENABLE_DND_MODE -> R.string.action_enable_dnd_mode + ActionId.DISABLE_DND_MODE -> R.string.action_disable_dnd_mode + ActionId.VOLUME_UNMUTE -> R.string.action_volume_unmute + ActionId.VOLUME_MUTE -> R.string.action_volume_mute + ActionId.VOLUME_TOGGLE_MUTE -> R.string.action_toggle_mute + ActionId.MUTE_MICROPHONE -> R.string.action_mute_microphone + ActionId.UNMUTE_MICROPHONE -> R.string.action_unmute_microphone + ActionId.TOGGLE_MUTE_MICROPHONE -> R.string.action_toggle_mute_microphone + ActionId.EXPAND_NOTIFICATION_DRAWER -> R.string.action_expand_notification_drawer + ActionId.TOGGLE_NOTIFICATION_DRAWER -> R.string.action_toggle_notification_drawer + ActionId.EXPAND_QUICK_SETTINGS -> R.string.action_expand_quick_settings + ActionId.TOGGLE_QUICK_SETTINGS -> R.string.action_toggle_quick_settings + ActionId.COLLAPSE_STATUS_BAR -> R.string.action_collapse_status_bar + ActionId.PAUSE_MEDIA -> R.string.action_pause_media + ActionId.PAUSE_MEDIA_PACKAGE -> R.string.action_pause_media_package + ActionId.PLAY_MEDIA -> R.string.action_play_media + ActionId.PLAY_MEDIA_PACKAGE -> R.string.action_play_media_package + ActionId.PLAY_PAUSE_MEDIA -> R.string.action_play_pause_media + ActionId.PLAY_PAUSE_MEDIA_PACKAGE -> R.string.action_play_pause_media_package + ActionId.NEXT_TRACK -> R.string.action_next_track + ActionId.NEXT_TRACK_PACKAGE -> R.string.action_next_track_package + ActionId.PREVIOUS_TRACK -> R.string.action_previous_track + ActionId.PREVIOUS_TRACK_PACKAGE -> R.string.action_previous_track_package + ActionId.FAST_FORWARD -> R.string.action_fast_forward + ActionId.FAST_FORWARD_PACKAGE -> R.string.action_fast_forward_package + ActionId.REWIND -> R.string.action_rewind + ActionId.REWIND_PACKAGE -> R.string.action_rewind_package + ActionId.STOP_MEDIA -> R.string.action_stop_media + ActionId.STOP_MEDIA_PACKAGE -> R.string.action_stop_media_package + ActionId.STEP_FORWARD -> R.string.action_step_forward_media + ActionId.STEP_FORWARD_PACKAGE -> R.string.action_step_forward_media_package + ActionId.STEP_BACKWARD -> R.string.action_step_backward_media + ActionId.STEP_BACKWARD_PACKAGE -> R.string.action_step_backward_media_package + ActionId.GO_BACK -> R.string.action_go_back + ActionId.GO_HOME -> R.string.action_go_home + ActionId.OPEN_RECENTS -> R.string.action_open_recents + ActionId.TOGGLE_SPLIT_SCREEN -> R.string.action_toggle_split_screen + ActionId.GO_LAST_APP -> R.string.action_go_last_app + ActionId.OPEN_MENU -> R.string.action_open_menu + ActionId.TOGGLE_FLASHLIGHT -> R.string.action_toggle_flashlight + ActionId.ENABLE_FLASHLIGHT -> R.string.action_enable_flashlight + ActionId.DISABLE_FLASHLIGHT -> R.string.action_disable_flashlight + ActionId.CHANGE_FLASHLIGHT_STRENGTH -> R.string.action_flashlight_change_strength + ActionId.ENABLE_NFC -> R.string.action_nfc_enable + ActionId.DISABLE_NFC -> R.string.action_nfc_disable + ActionId.TOGGLE_NFC -> R.string.action_nfc_toggle + ActionId.MOVE_CURSOR -> R.string.action_move_cursor + ActionId.TOGGLE_KEYBOARD -> R.string.action_toggle_keyboard + ActionId.SHOW_KEYBOARD -> R.string.action_show_keyboard + ActionId.HIDE_KEYBOARD -> R.string.action_hide_keyboard + ActionId.SHOW_KEYBOARD_PICKER -> R.string.action_show_keyboard_picker + ActionId.TEXT_CUT -> R.string.action_text_cut + ActionId.TEXT_COPY -> R.string.action_text_copy + ActionId.TEXT_PASTE -> R.string.action_text_paste + ActionId.SELECT_WORD_AT_CURSOR -> R.string.action_select_word_at_cursor + ActionId.SWITCH_KEYBOARD -> R.string.action_switch_keyboard + ActionId.TOGGLE_AIRPLANE_MODE -> R.string.action_toggle_airplane_mode + ActionId.ENABLE_AIRPLANE_MODE -> R.string.action_enable_airplane_mode + ActionId.DISABLE_AIRPLANE_MODE -> R.string.action_disable_airplane_mode + ActionId.SCREENSHOT -> R.string.action_screenshot + ActionId.OPEN_VOICE_ASSISTANT -> R.string.action_open_assistant + ActionId.OPEN_DEVICE_ASSISTANT -> R.string.action_open_device_assistant + ActionId.OPEN_CAMERA -> R.string.action_open_camera + ActionId.LOCK_DEVICE -> R.string.action_lock_device + ActionId.POWER_ON_OFF_DEVICE -> R.string.action_power_on_off_device + ActionId.SECURE_LOCK_DEVICE -> R.string.action_secure_lock_device + ActionId.CONSUME_KEY_EVENT -> R.string.action_consume_keyevent + ActionId.OPEN_SETTINGS -> R.string.action_open_settings + ActionId.SHOW_POWER_MENU -> R.string.action_show_power_menu + ActionId.APP -> R.string.action_open_app + ActionId.APP_SHORTCUT -> R.string.action_open_app_shortcut + ActionId.KEY_CODE -> R.string.action_input_key_code + ActionId.KEY_EVENT -> R.string.action_input_key_event + ActionId.TAP_SCREEN -> R.string.action_tap_screen + ActionId.SWIPE_SCREEN -> R.string.action_swipe_screen + ActionId.PINCH_SCREEN -> R.string.action_pinch_screen + ActionId.TEXT -> R.string.action_input_text + ActionId.URL -> R.string.action_open_url + ActionId.INTENT -> R.string.action_send_intent + ActionId.PHONE_CALL -> R.string.action_phone_call + ActionId.SOUND -> R.string.action_play_sound + ActionId.DISMISS_MOST_RECENT_NOTIFICATION -> R.string.action_dismiss_most_recent_notification + ActionId.DISMISS_ALL_NOTIFICATIONS -> R.string.action_dismiss_all_notifications + ActionId.CREATE_NOTIFICATION -> R.string.action_create_notification + ActionId.ANSWER_PHONE_CALL -> R.string.action_answer_call + ActionId.END_PHONE_CALL -> R.string.action_end_call + ActionId.SEND_SMS -> R.string.action_send_sms + ActionId.COMPOSE_SMS -> R.string.action_compose_sms + ActionId.DEVICE_CONTROLS -> R.string.action_device_controls + ActionId.HTTP_REQUEST -> R.string.action_http_request + ActionId.SHELL_COMMAND -> R.string.action_shell_command + ActionId.INTERACT_UI_ELEMENT -> R.string.action_interact_ui_element_title + ActionId.FORCE_STOP_APP -> R.string.action_force_stop_app + ActionId.CLEAR_RECENT_APP -> R.string.action_clear_recent_app ActionId.MODIFY_SETTING -> R.string.action_modify_setting + ActionId.TOGGLE_HOTSPOT -> R.string.action_toggle_hotspot + ActionId.ENABLE_HOTSPOT -> R.string.action_enable_hotspot + ActionId.DISABLE_HOTSPOT -> R.string.action_disable_hotspot } @@ -525,10 +631,13 @@ object ActionUtils { fun getMinApi(id: ActionId): Int = when (id) { ActionId.ANSWER_PHONE_CALL -> Build.VERSION_CODES.O + ActionId.END_PHONE_CALL -> Build.VERSION_CODES.P ActionId.TOGGLE_SPLIT_SCREEN -> Build.VERSION_CODES.N + ActionId.GO_LAST_APP -> Build.VERSION_CODES.N + ActionId.TAP_SCREEN -> Build.VERSION_CODES.N ActionId.VOLUME_MUTE, @@ -562,6 +671,7 @@ object ActionUtils { -> Build.VERSION_CODES.JELLY_BEAN_MR2 ActionId.SHOW_POWER_MENU -> Build.VERSION_CODES.LOLLIPOP + ActionId.DEVICE_CONTROLS -> Build.VERSION_CODES.S // It could be supported on older versions but system bridge min API is Q and its extra @@ -761,6 +871,7 @@ object ActionUtils { } ActionId.SECURE_LOCK_DEVICE -> return listOf(Permission.DEVICE_ADMIN) + ActionId.POWER_ON_OFF_DEVICE -> return if (isSystemBridgeSupported) { emptyList() } else { @@ -923,7 +1034,6 @@ object ActionUtils { ActionId.INTERACT_UI_ELEMENT -> KeyMapperIcons.JumpToElement ActionId.FORCE_STOP_APP -> Icons.Outlined.Dangerous ActionId.CLEAR_RECENT_APP -> Icons.Outlined.VerticalSplit - ActionId.MODIFY_SETTING -> Icons.Outlined.Settings ActionId.TOGGLE_HOTSPOT -> Icons.Outlined.WifiTethering ActionId.ENABLE_HOTSPOT -> Icons.Outlined.WifiTethering @@ -933,6 +1043,7 @@ object ActionUtils { fun ActionData.canBeHeldDown(): Boolean = when (this) { is ActionData.InputKeyEvent -> true + is ActionData.TapScreen -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.O else -> false } From c46190bdeda5ad02844f3b7d34682b4999ea9833 Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 00:33:47 +0000 Subject: [PATCH 172/199] chore: update CHANGELOG --- CHANGELOG.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48defe2158..a1075a1fcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,12 +13,16 @@ - #1913 actually save the option to detect with scan code - #1931 fix Close and Remove From Recents action on some Android 13 revisions +- #1926 PRO mode triggers for external devices work when the device reconnects. +- #1918 PRO mode key maps can input key codes that aren't originally supported by the trigger device. +- #1934 hold down option for Tap Screen action is added back. ## [4.0.0 Beta 3](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.03) #### 25 November 2025 ## Added + - #1871 action to modify any system settings. - #1221 action to show a custom notification. - #1491 action to toggle/enable/disable hotspot. @@ -30,7 +34,8 @@ ## Bug fixes - #1901 prompt user to set default USB configuration to 'No data transfer' after starting pro mode. -- #1898 do not launch directly into the Wireless Debugging activity on Xiaomi devices due to a bug they introduced. +- #1898 do not launch directly into the Wireless Debugging activity on Xiaomi devices due to a bug + they introduced. ## [4.0.0 Beta 2](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.02) @@ -38,12 +43,15 @@ ## Added -- #1890 add button to save log to file and share it. The clipboard button now cuts off older entries and keeps newest ones. +- #1890 add button to save log to file and share it. The clipboard button now cuts off older entries + and keeps newest ones. ## Fixed -- Only autostart PRO mode with Shizuku if Shizuku permission is granted. Otherwise fallback to method with Wireless Debugging and WRITE_SECURE_SETTINGS permission. -- Starting system bridge for the first time would be janky because granting READ_LOGS kills the app process. Only grant for READ_LOGS when sharing logcat from settings. +- Only autostart PRO mode with Shizuku if Shizuku permission is granted. Otherwise fallback to + method with Wireless Debugging and WRITE_SECURE_SETTINGS permission. +- Starting system bridge for the first time would be janky because granting READ_LOGS kills the app + process. Only grant for READ_LOGS when sharing logcat from settings. - #1886 mobile data actions work in PRO mode. ## [4.0.0 Beta 1](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.01) From e9124155f738e36fd45f0a7c1dcae4d121a82810 Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 00:34:01 +0000 Subject: [PATCH 173/199] chore: bump version code --- app/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/version.properties b/app/version.properties index a7ecf2efdd..bd966a4c62 100644 --- a/app/version.properties +++ b/app/version.properties @@ -1,2 +1,2 @@ VERSION_NAME=4.0.0-beta.04 -VERSION_CODE=210 +VERSION_CODE=212 From 5f0f0bfc44e2df4bc85c5efb843535beaf9d6082 Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 00:34:06 +0000 Subject: [PATCH 174/199] style: reformat --- .../java/io/github/sds100/keymapper/base/promode/StepContent.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/StepContent.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/StepContent.kt index 8390ca3db3..c8945da19f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/StepContent.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/promode/StepContent.kt @@ -8,5 +8,3 @@ data class StepContent( val icon: ImageVector, val buttonText: String, ) - - From b294a75f3c5175c211fe04c82cc1c78e13e1293f Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 21:21:01 +0000 Subject: [PATCH 175/199] #1905 feat: log to Key Mapper from system bridge --- .../sds100/keymapper/base/BaseKeyMapperApp.kt | 37 ++-- .../base/logging/SystemBridgeLogger.kt | 85 ++++++++ evdev/src/main/rust/evdev_manager/Cargo.lock | 41 +--- .../rust/evdev_manager/core/src/event_loop.rs | 26 ++- .../evdev_manager/core/src/grabbed_device.rs | 2 +- .../main/rust/evdev_manager/jni/Cargo.toml | 1 - .../rust/evdev_manager/jni/src/jni_bridge.rs | 71 +++---- .../main/rust/evdev_manager/jni/src/lib.rs | 2 +- .../rust/evdev_manager/jni/src/logging.rs | 197 ++++++++++++++++++ .../keymapper/sysbridge/ILogCallback.aidl | 10 + .../keymapper/sysbridge/ISystemBridge.aidl | 5 + .../sysbridge/service/SystemBridge.kt | 56 +++++ 12 files changed, 432 insertions(+), 101 deletions(-) create mode 100644 base/src/main/java/io/github/sds100/keymapper/base/logging/SystemBridgeLogger.kt create mode 100644 evdev/src/main/rust/evdev_manager/jni/src/logging.rs create mode 100644 sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ILogCallback.aidl diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt index 0b7e7e8627..5888c9c31d 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt @@ -17,6 +17,7 @@ import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ProcessLifecycleOwner import androidx.multidex.MultiDexApplication import io.github.sds100.keymapper.base.logging.KeyMapperLoggingTree +import io.github.sds100.keymapper.base.logging.SystemBridgeLogger import io.github.sds100.keymapper.base.promode.SystemBridgeAutoStarter import io.github.sds100.keymapper.base.settings.Theme import io.github.sds100.keymapper.base.system.accessibility.AccessibilityServiceAdapterImpl @@ -90,6 +91,9 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { @Inject lateinit var systemBridgeConnectionManager: SystemBridgeConnectionManagerImpl + @Inject + lateinit var systemBridgeLogger: SystemBridgeLogger + private val processLifecycleOwner by lazy { ProcessLifecycleOwner.get() } private val userManager: UserManager? by lazy { getSystemService() } @@ -186,20 +190,22 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { notificationController.init() - processLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver { - @Suppress("DEPRECATION") - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - fun onResume() { - // when the user returns to the app let everything know that the permissions could have changed - notificationController.onOpenApp() - - if (BuildConfig.DEBUG && - permissionAdapter.isGranted(Permission.WRITE_SECURE_SETTINGS) - ) { - accessibilityServiceAdapter.start() + processLifecycleOwner.lifecycle.addObserver( + object : LifecycleObserver { + @Suppress("DEPRECATION") + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + fun onResume() { + // when the user returns to the app let everything know that the permissions could have changed + notificationController.onOpenApp() + + if (BuildConfig.DEBUG && + permissionAdapter.isGranted(Permission.WRITE_SECURE_SETTINGS) + ) { + accessibilityServiceAdapter.start() + } } - } - }) + }, + ) appCoroutineScope.launch { notificationController.openApp.collectLatest { intentAction -> @@ -228,6 +234,11 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { systemBridgeAutoStarter.init() + // Initialize SystemBridgeLogger to start receiving log messages from SystemBridge. + // Using Lazy<> to avoid circular dependency issues and ensure it's only created + // when the API level requirement is met. + systemBridgeLogger.start() + appCoroutineScope.launch { systemBridgeConnectionManager.connectionState.collect { state -> if (state is SystemBridgeConnectionState.Connected) { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/logging/SystemBridgeLogger.kt b/base/src/main/java/io/github/sds100/keymapper/base/logging/SystemBridgeLogger.kt new file mode 100644 index 0000000000..1deffaf591 --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/logging/SystemBridgeLogger.kt @@ -0,0 +1,85 @@ +package io.github.sds100.keymapper.base.logging + +import android.util.Log +import androidx.annotation.RequiresApi +import io.github.sds100.keymapper.common.utils.Constants +import io.github.sds100.keymapper.data.Keys +import io.github.sds100.keymapper.data.repositories.PreferenceRepository +import io.github.sds100.keymapper.sysbridge.ILogCallback +import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager +import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState +import javax.inject.Inject +import javax.inject.Singleton +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import timber.log.Timber + +/** + * Listens for SystemBridge connection and registers a log callback to receive + * log messages from the Rust SystemBridge code. Respects the "extra logging" + * preference to control the log level. + */ +@Singleton +@RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) +class SystemBridgeLogger @Inject constructor( + private val coroutineScope: CoroutineScope, + private val systemBridgeConnManager: SystemBridgeConnectionManager, + private val preferenceRepository: PreferenceRepository, +) : ILogCallback.Stub() { + + private val extraLoggingEnabled: StateFlow = + preferenceRepository.get(Keys.log) + .map { it ?: false } + .stateIn(coroutineScope, SharingStarted.Eagerly, false) + + fun start() { + // Listen for connection state changes + coroutineScope.launch { + systemBridgeConnManager.connectionState + .filterIsInstance() + .collect { + registerWithSystemBridge() + } + } + + // Listen for preference changes and update log level + coroutineScope.launch { + extraLoggingEnabled.collect { enabled -> + updateLogLevel(enabled) + } + } + } + + private fun registerWithSystemBridge() { + systemBridgeConnManager.run { bridge -> + bridge.registerLogCallback(this@SystemBridgeLogger) + bridge.setLogLevel(getSystemBridgeLogLevel(extraLoggingEnabled.value)) + } + } + + private fun updateLogLevel(extraLogging: Boolean) { + systemBridgeConnManager.run { bridge -> + bridge.setLogLevel(getSystemBridgeLogLevel(extraLogging)) + } + } + + override fun onLog(level: Int, message: String) { + // Log with Timber so the messages appear under the Key Mapper package name + // in logcat, and the KeyMapperLoggingTree will then save it to the LogRepository. + Timber.log(priority = level, message = "systembridge: $message") + } + + private fun getSystemBridgeLogLevel(extraLogging: Boolean): Int { + val level = if (extraLogging) { + Log.DEBUG + } else { + Log.INFO + } + return level + } +} diff --git a/evdev/src/main/rust/evdev_manager/Cargo.lock b/evdev/src/main/rust/evdev_manager/Cargo.lock index b8d5185cdf..efe3500145 100644 --- a/evdev/src/main/rust/evdev_manager/Cargo.lock +++ b/evdev/src/main/rust/evdev_manager/Cargo.lock @@ -2,25 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "android_liblog-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf82c031178ca72b38595a54d16df8a257df9deea7d97a8992870e5c6a738e7" -dependencies = [ - "libc 0.2.177", -] - -[[package]] -name = "android_log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc00e0d3a060cce3fa338f9644ce9a93901c79f5405330891aeca69c9957009a" -dependencies = [ - "android_liblog-sys", - "log 0.3.9", -] - [[package]] name = "assertables" version = "9.8.2" @@ -96,7 +77,7 @@ dependencies = [ "bitflags 2.10.0", "cc", "libc 1.0.0-alpha.1", - "log 0.4.28", + "log", ] [[package]] @@ -108,7 +89,7 @@ dependencies = [ "evdev", "glob", "libc 0.2.177", - "log 0.4.28", + "log", "mio", "notify", "pretty_assertions", @@ -120,12 +101,11 @@ dependencies = [ name = "evdev_manager_jni" version = "0.1.0" dependencies = [ - "android_log", "evdev", "evdev_manager_core", "jni", "libc 0.2.177", - "log 0.4.28", + "log", ] [[package]] @@ -179,7 +159,7 @@ dependencies = [ "cfg-if", "combine", "jni-sys", - "log 0.4.28", + "log", "thiserror", "walkdir", "windows-sys 0.45.0", @@ -223,15 +203,6 @@ version = "1.0.0-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7222002e5385b4d9327755661e3847c970e8fbf9dea6da8c57f16e8cfbff53a8" -[[package]] -name = "log" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -dependencies = [ - "log 0.4.28", -] - [[package]] name = "log" version = "0.4.28" @@ -258,7 +229,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc 0.2.177", - "log 0.4.28", + "log", "wasi", "windows-sys 0.61.2", ] @@ -274,7 +245,7 @@ dependencies = [ "inotify", "kqueue", "libc 0.2.177", - "log 0.4.28", + "log", "mio", "notify-types", "walkdir", diff --git a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs index df87d1de29..ef374aaa5a 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs @@ -11,6 +11,7 @@ use evdev::enums::{EventType, EV_SYN}; use evdev::util::event_code_to_int; use evdev::{InputEvent, ReadFlag, ReadStatus}; use libc::c_uint; +use log::Level; use mio::event::Event; use mio::{Events, Poll, Token, Waker}; use std::error::Error; @@ -216,10 +217,12 @@ impl EventLoopManager { code: u32, value: i32, ) -> Result<(), EvdevError> { - debug!( - "Write evdev event: device_id={} event_type={} code={} value={}", - device_id, event_type, code, value - ); + if log_enabled!(Level::Debug) { + debug!( + "Write evdev event: device_id={} event_type={} code={} value={}", + device_id, event_type, code, value + ); + } self.grab_controller .with_grabbed_device(device_id, |device| { @@ -250,10 +253,12 @@ impl EventLoopManager { Err(Box::new(EvdevError::new(-libc::ENODATA)) as Box) } Ok(Some(code)) => { - debug!( - "Write key code evdev event: key_code={} value={}", - key_code, value - ); + if log_enabled!(Level::Debug) { + debug!( + "Write key code evdev event: key_code={} value={}", + key_code, value + ); + } device .uinput @@ -365,8 +370,9 @@ impl EventLoopThread { match evdev.next_event(flags) { Ok((ReadStatus::Success, input_event)) => { flags = ReadFlag::NORMAL; - // Keep this logging line. Debug/verbose events will be disabled in production. - debug!("Evdev event: {:?}", input_event); + if log_enabled!(Level::Debug) { + debug!("Evdev event: {:?}", input_event); + } self.process_event(slab_key, &input_event, device); } Ok((ReadStatus::Sync, _event)) => { diff --git a/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs b/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs index cba7435d25..861d995705 100644 --- a/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs +++ b/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs @@ -22,7 +22,7 @@ pub struct GrabbedDevice { impl GrabbedDevice { /// Create a grabbed device that also enables the given EventCodes in the uinput device. pub fn new(device_path: &PathBuf, extra_events: &[EventCode]) -> Result { - let mut evdev = Self::open_evdev_device(&device_path)?; + let mut evdev = Self::open_evdev_device(device_path)?; for event in extra_events { evdev.enable(*event)?; diff --git a/evdev/src/main/rust/evdev_manager/jni/Cargo.toml b/evdev/src/main/rust/evdev_manager/jni/Cargo.toml index 3f852ea73f..932490aa99 100644 --- a/evdev/src/main/rust/evdev_manager/jni/Cargo.toml +++ b/evdev/src/main/rust/evdev_manager/jni/Cargo.toml @@ -10,7 +10,6 @@ crate-type = ["rlib"] [dependencies] jni = "0.21.1" log = "0.4.28" -android_log = "0.1.3" evdev = { path = "../../evdev" } evdev_manager_core = { path = "../core" } libc = "0.2.177" diff --git a/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs b/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs index b39c54cd82..9e9cf4b53a 100644 --- a/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs +++ b/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs @@ -1,4 +1,5 @@ use crate::evdev_jni_observer::EvdevJniObserver; +use crate::logging::{AndroidLogLevel, KeyMapperLogger}; use evdev::InputEvent; use evdev_manager_core::android::keylayout::key_layout_map_manager::KeyLayoutMapManager; use evdev_manager_core::evdev_device_info::EvdevDeviceInfo; @@ -8,7 +9,7 @@ use evdev_manager_core::grabbed_device_handle::GrabbedDeviceHandle; use jni::objects::{JClass, JIntArray, JObject, JObjectArray, JString, JValue}; use jni::sys::{jboolean, jint, jobject, jobjectArray}; use jni::JNIEnv; -use log::LevelFilter; +use std::ffi::CString; use std::ptr; use std::sync::{Arc, OnceLock}; @@ -46,29 +47,34 @@ pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemB env: JNIEnv, this: JObject, ) { - android_log::init("KeyMapperSystemBridge").unwrap(); - // Set log level: Info for production builds, Debug for debug builds - let log_level = if cfg!(debug_assertions) { - LevelFilter::Debug - } else { - LevelFilter::Info - }; - log::set_max_level(log_level); - set_log_panic_hook(); - - info!("Initializing evdev manager"); - // Get the JavaVM let jvm = env.get_java_vm().expect("Failed to get JavaVM"); + let jvm_arc = Arc::new(jvm); + + // Create a global reference to the SystemBridge instance for logging + let system_bridge_for_log = env + .new_global_ref(&this) + .expect("Failed to create global reference to SystemBridge for logging"); - // Create a global reference to the SystemBridge instance + // Initialize logging to Key Mapper and Android logcat. + KeyMapperLogger::init( + jvm_arc.clone(), + system_bridge_for_log, + CString::new("KeyMapperSystemBridge").unwrap(), + ); + + KeyMapperLogger::set_log_panic_hook(); + + info!("Initializing evdev manager"); + + // Create a global reference to the SystemBridge instance for evdev observer let system_bridge = env .new_global_ref(this) .expect("Failed to create global reference to SystemBridge"); // Initialize the JNI observer let key_layout_manager = KeyLayoutMapManager::get(); - let observer = EvdevJniObserver::new(Arc::new(jvm), system_bridge, key_layout_manager); + let observer = EvdevJniObserver::new(jvm_arc, system_bridge, key_layout_manager); if JNI_OBSERVER.set(observer).is_err() { panic!("JNI observer already initialized"); @@ -96,6 +102,16 @@ pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemB .unwrap(); } +/// Set the log level from Kotlin. +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_setLogLevelNative( + _env: JNIEnv, + _class: JClass, + level: jint, +) { + KeyMapperLogger::set_level(AndroidLogLevel::from(level as i32)); +} + /// Set the list of grabbed devices. Takes an array of GrabTargetKeyCode and returns an array of GrabbedDeviceHandle. #[no_mangle] pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_setGrabTargetsNative( @@ -370,28 +386,3 @@ fn create_java_grabbed_device_handle( Ok(obj.into_raw()) } - -fn set_log_panic_hook() { - std::panic::set_hook(Box::new(|panic_info| { - error!("PANIC in Rust code!"); - - if let Some(location) = panic_info.location() { - error!( - "Panic at {}:{}:{}", - location.file(), - location.line(), - location.column() - ); - } else { - error!("Panic at unknown location"); - } - - if let Some(payload) = panic_info.payload().downcast_ref::<&str>() { - error!("Panic message: {}", payload); - } else if let Some(payload) = panic_info.payload().downcast_ref::() { - error!("Panic message: {}", payload); - } else { - error!("Panic with unknown payload"); - } - })); -} diff --git a/evdev/src/main/rust/evdev_manager/jni/src/lib.rs b/evdev/src/main/rust/evdev_manager/jni/src/lib.rs index 5ed013c32f..4fcf152074 100644 --- a/evdev/src/main/rust/evdev_manager/jni/src/lib.rs +++ b/evdev/src/main/rust/evdev_manager/jni/src/lib.rs @@ -1,6 +1,6 @@ mod evdev_jni_observer; mod jni_bridge; +mod logging; #[macro_use] extern crate log; -extern crate android_log; diff --git a/evdev/src/main/rust/evdev_manager/jni/src/logging.rs b/evdev/src/main/rust/evdev_manager/jni/src/logging.rs new file mode 100644 index 0000000000..145cb1c4b0 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/src/logging.rs @@ -0,0 +1,197 @@ +use jni::objects::{GlobalRef, JValue}; +use jni::JavaVM; +use libc::{c_char, c_int}; +use log::LevelFilter; +use std::ffi::CString; +use std::str::FromStr; +use std::sync::{Arc, OnceLock}; + +/// Holds the JVM and SystemBridge reference for logging callbacks +static KEY_MAPPER_LOGGER: OnceLock = OnceLock::new(); + +#[link(name = "log")] +extern "C" { + pub fn __android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int; +} + +/// Custom logger that forwards log messages to Kotlin via JNI +pub struct KeyMapperLogger { + jvm: Arc, + system_bridge: GlobalRef, + tag: CString, +} + +impl KeyMapperLogger { + pub fn init(jvm: Arc, system_bridge: GlobalRef, tag: CString) { + let logger = Self { + jvm, + system_bridge, + tag, + }; + + if KEY_MAPPER_LOGGER.set(logger).is_err() { + panic!("Log callback holder already initialized"); + } + + // Set up the custom JNI logger + // Note: log::set_logger can only be called once per process + if log::set_logger(KEY_MAPPER_LOGGER.get().unwrap()).is_err() { + panic!("Failed to set logger"); + } + + // Set default log level: Info for production builds, Debug for debug builds + let log_level = if cfg!(debug_assertions) { + LevelFilter::Debug + } else { + LevelFilter::Info + }; + + log::set_max_level(log_level); + } + + pub fn set_level(level: AndroidLogLevel) { + log::set_max_level(level.into()); + } + + pub fn set_log_panic_hook() { + std::panic::set_hook(Box::new(|panic_info| { + let mut message = String::from("PANIC in Rust code!"); + + if let Some(location) = panic_info.location() { + message.push_str(&format!( + " at {}:{}:{}", + location.file(), + location.line(), + location.column() + )); + } else { + message.push_str(" at unknown location"); + } + + if let Some(payload) = panic_info.payload().downcast_ref::<&str>() { + message.push_str(&format!(" - {}", payload)); + } else if let Some(payload) = panic_info.payload().downcast_ref::() { + message.push_str(&format!(" - {}", payload)); + } else { + message.push_str(" - unknown payload"); + } + + // Also log via the standard logger for logcat + error!("{}", message); + })); + } + + /// Send a log message to Java via JNI + fn send_log_to_java(&self, level: i32, message: &str) { + let mut env = self + .jvm + .attach_current_thread_permanently() + .expect("Failed to attach to JVM thread"); + + if let Ok(msg) = env.new_string(message) { + let _ = env.call_method( + &self.system_bridge, + "onLogMessage", + "(ILjava/lang/String;)V", + &[JValue::Int(level), JValue::Object(&msg.into())], + ); + } + } +} + +// Safety: JavaVM and GlobalRef are thread-safe +unsafe impl Send for KeyMapperLogger {} +unsafe impl Sync for KeyMapperLogger {} + +impl log::Log for KeyMapperLogger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + metadata.level() <= log::max_level() + } + + fn log(&self, record: &log::Record) { + if !self.enabled(record.metadata()) { + return; + } + + let msg_level: AndroidLogLevel = match record.level() { + log::Level::Error => AndroidLogLevel::Error, + log::Level::Warn => AndroidLogLevel::Warn, + log::Level::Info => AndroidLogLevel::Info, + log::Level::Debug => AndroidLogLevel::Debug, + log::Level::Trace => AndroidLogLevel::Verbose, + }; + + let message = format!( + "({}:{}): {}", + record.module_path().unwrap_or("unknown"), + record.line().unwrap_or(0), + record.args() + ); + + let c_message = CString::from_str(&message).unwrap(); + + // This is taken from the android_log crate. https://crates.io/crates/android_log + unsafe { + __android_log_write(msg_level as c_int, self.tag.as_ptr(), c_message.as_ptr()); + } + + self.send_log_to_java(msg_level as i32, &message); + } + + fn flush(&self) {} +} + +#[derive(Clone, Copy)] +pub enum AndroidLogLevel { + Unknown = 0, + Default, + Verbose, + Debug, + Info, + Warn, + Error, + Fatal, + Silent, +} + +impl From for log::LevelFilter { + fn from(level: AndroidLogLevel) -> Self { + match level { + AndroidLogLevel::Verbose => log::LevelFilter::Trace, + AndroidLogLevel::Debug => log::LevelFilter::Debug, + AndroidLogLevel::Info => log::LevelFilter::Info, + AndroidLogLevel::Warn => log::LevelFilter::Warn, + AndroidLogLevel::Error => log::LevelFilter::Error, + _ => log::LevelFilter::Off, + } + } +} + +impl From for AndroidLogLevel { + fn from(level: log::Level) -> Self { + match level { + log::Level::Trace => AndroidLogLevel::Verbose, + log::Level::Debug => AndroidLogLevel::Debug, + log::Level::Info => AndroidLogLevel::Info, + log::Level::Warn => AndroidLogLevel::Warn, + log::Level::Error => AndroidLogLevel::Error, + } + } +} + +impl From for AndroidLogLevel { + fn from(value: i32) -> Self { + match value { + 0 => AndroidLogLevel::Unknown, + 1 => AndroidLogLevel::Default, + 2 => AndroidLogLevel::Verbose, + 3 => AndroidLogLevel::Debug, + 4 => AndroidLogLevel::Info, + 5 => AndroidLogLevel::Warn, + 6 => AndroidLogLevel::Error, + 7 => AndroidLogLevel::Fatal, + 8 => AndroidLogLevel::Silent, + _ => AndroidLogLevel::Debug, + } + } +} diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ILogCallback.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ILogCallback.aidl new file mode 100644 index 0000000000..eb1adb4835 --- /dev/null +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ILogCallback.aidl @@ -0,0 +1,10 @@ +package io.github.sds100.keymapper.sysbridge; + +interface ILogCallback { + /** + * Called when a log message is emitted from the SystemBridge. + * @param level The log level: 0=ERROR, 1=WARN, 2=INFO, 3=DEBUG + * @param message The log message (already prefixed with "systembridge:") + */ + void onLog(int level, String message); +} diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl index fc7b931157..ee04b124c8 100644 --- a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl @@ -1,6 +1,7 @@ package io.github.sds100.keymapper.sysbridge; import io.github.sds100.keymapper.evdev.IEvdevCallback; +import io.github.sds100.keymapper.sysbridge.ILogCallback; import io.github.sds100.keymapper.common.models.EvdevDeviceInfo; import io.github.sds100.keymapper.common.models.GrabTargetKeyCode; import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle; @@ -48,4 +49,8 @@ interface ISystemBridge { long getUsbScreenUnlockedFunctions() = 21; boolean writeEvdevEventKeyCode(int deviceId, int keyCode, int value) = 22; + + void registerLogCallback(ILogCallback callback) = 23; + void unregisterLogCallback() = 24; + void setLogLevel(int level) = 25; } diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt index 7ce2659465..edfbbe4ab6 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt @@ -46,6 +46,7 @@ import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle import io.github.sds100.keymapper.common.models.ShellResult import io.github.sds100.keymapper.common.utils.UserHandleUtils import io.github.sds100.keymapper.evdev.IEvdevCallback +import io.github.sds100.keymapper.sysbridge.ILogCallback import io.github.sds100.keymapper.sysbridge.ISystemBridge import io.github.sds100.keymapper.sysbridge.provider.BinderContainer import io.github.sds100.keymapper.sysbridge.provider.SystemBridgeBinderProvider @@ -87,6 +88,9 @@ class SystemBridge : ISystemBridge.Stub() { @Suppress("KotlinJniMissingFunction") external fun destroyEvdevManager() + @Suppress("KotlinJniMissingFunction") + external fun setLogLevelNative(level: Int) + /** * Called from Rust via JNI when an evdev event occurs. * Forwards the call to the registered IEvdevCallback and returns whether the event was consumed. @@ -147,6 +151,22 @@ class SystemBridge : ISystemBridge.Stub() { } } + /** + * Called from Rust via JNI when a log message is emitted. + * Forwards the call to the registered ILogCallback. + */ + @Suppress("unused") + fun onLogMessage(level: Int, message: String) { + synchronized(logCallbackLock) { + val callback = logCallback ?: return + try { + callback.onLog(level, message) + } catch (e: Exception) { + Log.e(TAG, "Error calling log callback", e) + } + } + } + companion object { private const val TAG: String = "KeyMapperSystemBridge" private val systemBridgePackageName: String? by lazy { @@ -243,6 +263,14 @@ class SystemBridge : ISystemBridge.Stub() { startKeyMapperPeriodicCheck() } + private val logCallbackLock: Any = Any() + private var logCallback: ILogCallback? = null + private val logCallbackDeathRecipient: IBinder.DeathRecipient = IBinder.DeathRecipient { + synchronized(logCallbackLock) { + logCallback = null + } + } + private val inputManager: IInputManager private val wifiManager: IWifiManager? private val permissionManager: IPermissionManager @@ -833,4 +861,32 @@ class SystemBridge : ISystemBridge.Stub() { -1 } } + + override fun registerLogCallback(callback: ILogCallback?) { + callback ?: return + + Log.i(TAG, "Register log callback") + + val binder = callback.asBinder() + + if (this.logCallback != null) { + unregisterLogCallback() + } + + synchronized(logCallbackLock) { + this.logCallback = callback + binder.linkToDeath(logCallbackDeathRecipient, 0) + } + } + + override fun unregisterLogCallback() { + synchronized(logCallbackLock) { + logCallback?.asBinder()?.unlinkToDeath(logCallbackDeathRecipient, 0) + logCallback = null + } + } + + override fun setLogLevel(level: Int) { + setLogLevelNative(level) + } } From ca2555f1b550926f1c2bdceb085eb8c95eab0756 Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 21:29:28 +0000 Subject: [PATCH 176/199] #1905 do not log file and line from rust log messages --- evdev/src/main/rust/evdev_manager/jni/src/logging.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/evdev/src/main/rust/evdev_manager/jni/src/logging.rs b/evdev/src/main/rust/evdev_manager/jni/src/logging.rs index 145cb1c4b0..6f71eeb8ad 100644 --- a/evdev/src/main/rust/evdev_manager/jni/src/logging.rs +++ b/evdev/src/main/rust/evdev_manager/jni/src/logging.rs @@ -121,13 +121,7 @@ impl log::Log for KeyMapperLogger { log::Level::Trace => AndroidLogLevel::Verbose, }; - let message = format!( - "({}:{}): {}", - record.module_path().unwrap_or("unknown"), - record.line().unwrap_or(0), - record.args() - ); - + let message = format!("{}", record.args()); let c_message = CString::from_str(&message).unwrap(); // This is taken from the android_log crate. https://crates.io/crates/android_log @@ -141,7 +135,7 @@ impl log::Log for KeyMapperLogger { fn flush(&self) {} } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] pub enum AndroidLogLevel { Unknown = 0, Default, From f18e66156445c69d4b130504c372f6154b5bfb3e Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 21:37:16 +0000 Subject: [PATCH 177/199] fix: do not invalidate grabbed devices when registering evdev callback fails --- .../io/github/sds100/keymapper/base/input/InputEventHub.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt b/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt index 62680729c1..3feaad3905 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt @@ -13,6 +13,7 @@ import io.github.sds100.keymapper.common.utils.KMError import io.github.sds100.keymapper.common.utils.KMResult import io.github.sds100.keymapper.common.utils.Success import io.github.sds100.keymapper.common.utils.firstBlocking +import io.github.sds100.keymapper.common.utils.onSuccess import io.github.sds100.keymapper.common.utils.then import io.github.sds100.keymapper.data.Keys import io.github.sds100.keymapper.data.repositories.PreferenceRepository @@ -80,9 +81,9 @@ class InputEventHubImpl @Inject constructor( // Whenever the system bridge is connected systemBridgeConnManager.run { bridge -> bridge.registerEvdevCallback(this@InputEventHubImpl) + }.onSuccess { + invalidateGrabbedDevices() } - - invalidateGrabbedDevices() } } } From dde3d28d48c3a6489707db31c8ed9fe33ce7e687 Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 21:39:07 +0000 Subject: [PATCH 178/199] #1943 fix: do not log error when cancelling invalidating root detection --- .../java/io/github/sds100/keymapper/system/root/SuAdapter.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/system/src/main/java/io/github/sds100/keymapper/system/root/SuAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/root/SuAdapter.kt index 81b7503ad5..5ad4444b46 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/root/SuAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/root/SuAdapter.kt @@ -5,6 +5,7 @@ import io.github.sds100.keymapper.system.shell.BaseShellAdapter import io.github.sds100.keymapper.system.shell.ShellAdapter import javax.inject.Inject import javax.inject.Singleton +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -48,7 +49,9 @@ class SuAdapterImpl @Inject constructor(private val coroutineScope: CoroutineSco val isRooted = getIsRooted() isRootGranted.update { isRooted } } catch (e: Exception) { - Timber.e("Exception invalidating root detection: $e") + if (e !is CancellationException) { + Timber.e("Exception invalidating root detection: $e") + } } } From 83a1aee990c18dceb348910cbbf38683bf4799cd Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 21:40:01 +0000 Subject: [PATCH 179/199] chore: update changelog --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1075a1fcd..9040fd31fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,15 +7,18 @@ - #1915 ask user to remove "adb shell" from Shell command. - #1904 inform the user how to enable the accessibility service with PRO mode or ADB. - #1911 constraint for physical device orientation that ignores auto rotate setting. -- #1918 improve how key event actions are performed with system bridge +- #1918 improve how key event actions are performed with system bridge. +- #1905 system bridge log is now visible in Key Mapper log. ## Bug fixes - #1913 actually save the option to detect with scan code - #1931 fix Close and Remove From Recents action on some Android 13 revisions - #1926 PRO mode triggers for external devices work when the device reconnects. -- #1918 PRO mode key maps can input key codes that aren't originally supported by the trigger device. +- #1918 PRO mode key maps can input key codes that aren't originally supported by the trigger + device. - #1934 hold down option for Tap Screen action is added back. +- Log less verbose. ## [4.0.0 Beta 3](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.03) From 8389b22c9f3c9de97e45a5c32cf1501117c175da Mon Sep 17 00:00:00 2001 From: sds100 Date: Mon, 22 Dec 2025 21:40:50 +0000 Subject: [PATCH 180/199] #1942 update string for start system bridge button on incompatible Android versions --- base/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index 990aee3153..26b8a0d5aa 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -1670,7 +1670,7 @@ Start PRO mode Set up with Key Mapper Continue - Continue (Android 11+) + (Requires Android 11+) Options Enable PRO mode for all key maps Key Mapper will use the ADB Shell for remapping From 4059743ac4fc482418d4c26c4cf3228c1bb458cd Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 11:47:31 +0000 Subject: [PATCH 181/199] #1941 feat: show loading indicator when starting system bridge --- CHANGELOG.md | 1 + .../keymapper/base/promode/ProModeScreen.kt | 17 +- .../base/promode/ProModeSetupScreen.kt | 31 +++- .../base/promode/ProModeSetupState.kt | 1 + .../base/promode/ProModeViewModel.kt | 8 +- .../SystemBridgeSetupAssistantController.kt | 21 +-- .../base/promode/SystemBridgeSetupDelegate.kt | 15 +- .../base/promode/SystemBridgeSetupUseCase.kt | 27 ++- .../manager/SystemBridgeConnectionManager.kt | 77 ++++---- .../service/SystemBridgeSetupController.kt | 168 ++++++++++++------ .../sysbridge/starter/SystemBridgeStarter.kt | 128 +++++++------ 11 files changed, 302 insertions(+), 192 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9040fd31fe..f0c77fb6c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - #1911 constraint for physical device orientation that ignores auto rotate setting. - #1918 improve how key event actions are performed with system bridge. - #1905 system bridge log is now visible in Key Mapper log. +- #1941 show loading indicator when starting system bridge. ## Bug fixes diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeScreen.kt index 5d35f0fc90..f20ec183f0 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeScreen.kt @@ -44,6 +44,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Scaffold @@ -344,6 +345,7 @@ private fun LoadedContent( ), onButtonClick = onRootButtonClick, enabled = state.isNotificationPermissionGranted, + isLoading = state.isStarting, ) Spacer(modifier = Modifier.height(8.dp)) @@ -384,6 +386,7 @@ private fun LoadedContent( buttonText = shizukuButtonText, onButtonClick = onShizukuButtonClick, enabled = state.isNotificationPermissionGranted, + isLoading = state.isStarting, ) } @@ -415,6 +418,7 @@ private fun LoadedContent( enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && state.isNotificationPermissionGranted, + isLoading = state.isStarting, ) } } @@ -612,6 +616,7 @@ private fun SetupCard( buttonText: String, onButtonClick: () -> Unit = {}, enabled: Boolean = true, + isLoading: Boolean = false, ) { OutlinedCard(modifier = modifier) { Spacer(modifier = Modifier.height(16.dp)) @@ -645,12 +650,20 @@ private fun SetupCard( .align(Alignment.End) .padding(horizontal = 16.dp), onClick = onButtonClick, - enabled = enabled, + enabled = enabled && !isLoading, colors = ButtonDefaults.filledTonalButtonColors( containerColor = color, contentColor = LocalCustomColorsPalette.current.contentColorFor(color), ), ) { + if (isLoading) { + CircularProgressIndicator( + modifier = Modifier.size(18.dp), + strokeWidth = 2.dp, + color = LocalContentColor.current, + ) + Spacer(modifier = Modifier.width(8.dp)) + } Text(buttonText) } @@ -726,6 +739,7 @@ private fun Preview() { isRootGranted = false, shizukuSetupState = ShizukuSetupState.PERMISSION_GRANTED, isNotificationPermissionGranted = true, + isStarting = false, ), ), showInfoCard = true, @@ -802,6 +816,7 @@ private fun PreviewNotificationPermissionNotGranted() { isRootGranted = true, shizukuSetupState = ShizukuSetupState.PERMISSION_GRANTED, isNotificationPermissionGranted = false, + isStarting = false, ), ), showInfoCard = false, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupScreen.kt index 8ad5167038..f55b820352 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons @@ -38,6 +39,7 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect @@ -206,10 +208,11 @@ fun ProModeSetupScreenContent( .fillMaxWidth() .weight(1f) .padding(horizontal = 16.dp), - stepContent, - onWatchTutorialClick, - onStepButtonClick, + stepContent = stepContent, + onWatchTutorialClick = onWatchTutorialClick, + onButtonClick = onStepButtonClick, iconTint = iconTint, + isLoading = state.data.isStarting, ) } } @@ -223,6 +226,7 @@ private fun StepContent( onWatchTutorialClick: () -> Unit, onButtonClick: () -> Unit, iconTint: Color = Color.Unspecified, + isLoading: Boolean = false, ) { Column( modifier, @@ -269,7 +273,18 @@ private fun StepContent( // TextButton(onClick = onWatchTutorialClick) { // Text(text = stringResource(R.string.pro_mode_setup_wizard_watch_tutorial_button)) // } - Button(onClick = onButtonClick) { + Button( + onClick = onButtonClick, + enabled = !isLoading, + ) { + if (isLoading) { + CircularProgressIndicator( + modifier = Modifier.size(18.dp), + strokeWidth = 2.dp, + color = LocalContentColor.current, + ) + Spacer(modifier = Modifier.width(8.dp)) + } Text(text = stepContent.buttonText) } } @@ -438,6 +453,7 @@ private fun ProModeSetupScreenAccessibilityServicePreview() { stepContent = createPreviewStepContent(step), isSetupAssistantChecked = false, isSetupAssistantButtonEnabled = false, + isStarting = false, ), ), ) @@ -458,6 +474,7 @@ private fun ProModeSetupScreenNotificationPermissionPreview() { stepContent = createPreviewStepContent(step), isSetupAssistantChecked = false, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -478,6 +495,7 @@ private fun ProModeSetupScreenDeveloperOptionsPreview() { stepContent = createPreviewStepContent(step), isSetupAssistantChecked = false, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -498,6 +516,7 @@ private fun ProModeSetupScreenWifiNetworkPreview() { stepContent = createPreviewStepContent(step), isSetupAssistantChecked = false, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -518,6 +537,7 @@ private fun ProModeSetupScreenWirelessDebuggingPreview() { stepContent = createPreviewStepContent(step), isSetupAssistantChecked = false, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -538,6 +558,7 @@ private fun ProModeSetupScreenAdbPairingPreview() { stepContent = createPreviewStepContent(step), isSetupAssistantChecked = true, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -558,6 +579,7 @@ private fun ProModeSetupScreenStartServicePreview() { stepContent = createPreviewStepContent(step), isSetupAssistantChecked = true, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -578,6 +600,7 @@ private fun ProModeSetupScreenStartedPreview() { stepContent = createPreviewStepContent(step), isSetupAssistantChecked = true, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupState.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupState.kt index e1fc812a19..aae4de9c2a 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupState.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupState.kt @@ -9,4 +9,5 @@ data class ProModeSetupState( val stepContent: StepContent, val isSetupAssistantChecked: Boolean, val isSetupAssistantButtonEnabled: Boolean, + val isStarting: Boolean, ) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeViewModel.kt index 415b7163f0..20f67ae4d9 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeViewModel.kt @@ -13,7 +13,6 @@ import io.github.sds100.keymapper.base.utils.ui.DialogProvider import io.github.sds100.keymapper.base.utils.ui.ResourceProvider import io.github.sds100.keymapper.common.utils.State import io.github.sds100.keymapper.common.utils.valueOrNull -import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow @@ -26,6 +25,7 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch +import javax.inject.Inject @HiltViewModel class ProModeViewModel @Inject constructor( @@ -60,6 +60,7 @@ class ProModeViewModel @Inject constructor( useCase.isRootGranted, useCase.shizukuSetupState, useCase.isNotificationPermissionGranted, + useCase.isSystemBridgeStarting, ::buildSetupState, ).stateIn(viewModelScope, SharingStarted.Eagerly, State.Loading) @@ -155,12 +156,13 @@ class ProModeViewModel @Inject constructor( isRootGranted: Boolean, shizukuSetupState: ShizukuSetupState, isNotificationPermissionGranted: Boolean, + isSystemBridgeStarting: Boolean, ): State { if (isSystemBridgeConnected) { return State.Data( ProModeState.Started( isDefaultUsbModeCompatible = - useCase.isCompatibleUsbModeSelected().valueOrNull() ?: false, + useCase.isCompatibleUsbModeSelected().valueOrNull() ?: false, ), ) } else { @@ -169,6 +171,7 @@ class ProModeViewModel @Inject constructor( isRootGranted = isRootGranted, shizukuSetupState = shizukuSetupState, isNotificationPermissionGranted = isNotificationPermissionGranted, + isStarting = isSystemBridgeStarting, ), ) } @@ -186,6 +189,7 @@ sealed class ProModeState { val isRootGranted: Boolean, val shizukuSetupState: ShizukuSetupState, val isNotificationPermissionGranted: Boolean, + val isStarting: Boolean, ) : ProModeState() data class Started(val isDefaultUsbModeCompatible: Boolean) : ProModeState() diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupAssistantController.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupAssistantController.kt index 464524777f..0df53276a4 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupAssistantController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupAssistantController.kt @@ -25,23 +25,20 @@ import io.github.sds100.keymapper.data.Keys import io.github.sds100.keymapper.data.PreferenceDefaults import io.github.sds100.keymapper.data.repositories.PreferenceRepository import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager -import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState +import io.github.sds100.keymapper.sysbridge.manager.awaitConnected import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupController import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupStep import io.github.sds100.keymapper.system.notifications.NotificationModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.delay import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import kotlinx.coroutines.withTimeout +import kotlinx.coroutines.withTimeoutOrNull import timber.log.Timber @Suppress("KotlinConstantConditions") @@ -219,17 +216,9 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( private suspend fun onPairingSuccess() { setupController.startWithAdb() - val isStarted = try { - withTimeout(10000L) { - systemBridgeConnectionManager.connectionState - .filterIsInstance() - .first() - } - - true - } catch (_: TimeoutCancellationException) { - false - } + val isStarted = withTimeoutOrNull(10000L) { + systemBridgeConnectionManager.awaitConnected() + } != null if (isStarted) { Timber.i("System bridge started after pairing. Going back to Key Mapper.") diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupDelegate.kt index e11789ef64..484d31362d 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupDelegate.kt @@ -20,7 +20,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch + abstract class SystemBridgeSetupDelegateImpl( val viewModelScope: CoroutineScope, @@ -29,7 +29,12 @@ abstract class SystemBridgeSetupDelegateImpl( ) : SystemBridgeSetupDelegate, ResourceProvider by resourceProvider { override val setupState: StateFlow> = - combine(useCase.nextSetupStep, useCase.isSetupAssistantEnabled, ::buildState).stateIn( + combine( + useCase.nextSetupStep, + useCase.isSetupAssistantEnabled, + useCase.isSystemBridgeStarting, + ::buildState, + ).stateIn( viewModelScope, SharingStarted.Eagerly, State.Loading, @@ -47,9 +52,7 @@ abstract class SystemBridgeSetupDelegateImpl( SystemBridgeSetupStep.WIFI_NETWORK -> useCase.connectWifiNetwork() SystemBridgeSetupStep.WIRELESS_DEBUGGING -> useCase.enableWirelessDebugging() SystemBridgeSetupStep.ADB_PAIRING -> useCase.pairWirelessAdb() - SystemBridgeSetupStep.START_SERVICE -> viewModelScope.launch { - useCase.startSystemBridgeWithAdb() - } + SystemBridgeSetupStep.START_SERVICE -> useCase.startSystemBridgeWithAdb() SystemBridgeSetupStep.STARTED -> onFinishClick() } @@ -172,6 +175,7 @@ abstract class SystemBridgeSetupDelegateImpl( private fun buildState( step: SystemBridgeSetupStep, isSetupAssistantUserEnabled: Boolean, + isStarting: Boolean, ): State.Data { // Uncheck the setup assistant if the accessibility service is disabled since it is // required for the setup assistant to work @@ -193,6 +197,7 @@ abstract class SystemBridgeSetupDelegateImpl( isSetupAssistantButtonEnabled = step != SystemBridgeSetupStep.ACCESSIBILITY_SERVICE && step != SystemBridgeSetupStep.STARTED, + isStarting = isStarting, ), ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCase.kt index 9a79257dc2..2746c1eb88 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCase.kt @@ -93,6 +93,9 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( systemBridgeConnectionManager.connectionState .map { it is SystemBridgeConnectionState.Connected } + override val isSystemBridgeStarting: Flow = + systemBridgeSetupController.isStarting + override val isNotificationPermissionGranted: Flow = permissionAdapter.isGrantedFlow(Permission.POST_NOTIFICATIONS) @@ -188,14 +191,16 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( systemBridgeSetupController.startWithShizuku() } - override suspend fun startSystemBridgeWithAdb() { + override fun startSystemBridgeWithAdb() { preferences.set(Keys.isSystemBridgeEmergencyKilled, false) preferences.set(Keys.isSystemBridgeStoppedByUser, false) - if (isAdbAutoStartAllowed.first()) { - systemBridgeSetupController.autoStartWithAdb() - } else { - systemBridgeSetupController.startWithAdb() - } + systemBridgeSetupController.startWithAdb() + } + + override fun autoStartSystemBridgeWithAdb() { + preferences.set(Keys.isSystemBridgeEmergencyKilled, false) + preferences.set(Keys.isSystemBridgeStoppedByUser, false) + systemBridgeSetupController.autoStartWithAdb() } override fun isInfoDismissed(): Boolean { @@ -245,11 +250,17 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( return when { accessibilityServiceState != AccessibilityServiceState.ENABLED -> SystemBridgeSetupStep.ACCESSIBILITY_SERVICE + !isNotificationPermissionGranted -> SystemBridgeSetupStep.NOTIFICATION_PERMISSION + !isDeveloperOptionsEnabled -> SystemBridgeSetupStep.DEVELOPER_OPTIONS + !isWifiConnected -> SystemBridgeSetupStep.WIFI_NETWORK + !isWirelessDebuggingEnabled -> SystemBridgeSetupStep.WIRELESS_DEBUGGING + isWirelessDebuggingEnabled -> SystemBridgeSetupStep.ADB_PAIRING + else -> SystemBridgeSetupStep.START_SERVICE } } @@ -269,6 +280,7 @@ interface SystemBridgeSetupUseCase { fun toggleSetupAssistant() val isSystemBridgeConnected: Flow + val isSystemBridgeStarting: Flow val nextSetupStep: Flow val isRootGranted: Flow @@ -288,7 +300,8 @@ interface SystemBridgeSetupUseCase { fun pairWirelessAdb() fun startSystemBridgeWithRoot() fun startSystemBridgeWithShizuku() - suspend fun startSystemBridgeWithAdb() + fun startSystemBridgeWithAdb() + fun autoStartSystemBridgeWithAdb() fun isCompatibleUsbModeSelected(): KMResult } diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt index 026362e325..c8ed2865ac 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt @@ -33,9 +33,10 @@ import javax.inject.Inject import javax.inject.Singleton import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import timber.log.Timber @@ -65,7 +66,7 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( time = SystemClock.elapsedRealtime(), // Get whether the user previously stopped the system bridge. isStoppedByUser = - preferences.get(Keys.isSystemBridgeStoppedByUser).firstBlocking() ?: false, + preferences.get(Keys.isSystemBridgeStoppedByUser).firstBlocking() ?: false, ), ) private var isExpectedDeath: Boolean = false @@ -87,8 +88,6 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( } } - private var startJob: Job? = null - fun pingBinder(): Boolean { synchronized(systemBridgeLock) { return systemBridgeFlow.value?.asBinder()?.pingBinder() == true @@ -154,25 +153,27 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( @SuppressLint("LogNotTimber") private suspend fun restartSystemBridge(systemBridge: ISystemBridge) { - starter.startSystemBridge(executeCommand = { command -> - try { - val result = systemBridge.executeCommand(command, 10000L)!! - if (result.isSuccess()) { - Success(result.stdout) - } else { - KMError.Exception( - Exception( - "Command failed with exit code ${result.exitCode}: ${result.stdout}", - ), - ) + starter.startSystemBridgeWithLock( + commandExecutor = { command -> + try { + val result = systemBridge.executeCommand(command, 10000L)!! + if (result.isSuccess()) { + Success(result.stdout) + } else { + KMError.Exception( + Exception( + "Command failed with exit code ${result.exitCode}: ${result.stdout}", + ), + ) + } + } catch (_: DeadObjectException) { + // This exception is expected since it is killing the system bridge + Success("") + } catch (e: Exception) { + KMError.Exception(e) } - } catch (_: DeadObjectException) { - // This exception is expected since it is killing the system bridge - Success("") - } catch (e: Exception) { - KMError.Exception(e) - } - }).onFailure { error -> + }, + ).onFailure { error -> Log.e(TAG, "Failed to restart System Bridge: $error") } } @@ -202,15 +203,8 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( } @RequiresApi(Build.VERSION_CODES.R) - override fun startWithAdb() { - if (startJob?.isActive == true) { - Timber.i("System Bridge is already starting") - return - } - - startJob = coroutineScope.launch { - starter.startWithAdb() - } + override suspend fun startWithAdb() { + starter.startWithAdb() } private fun preventSystemBridgeKilling(systemBridge: ISystemBridge) { @@ -249,15 +243,8 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( } } - override fun startWithRoot() { - if (startJob?.isActive == true) { - Timber.i("System Bridge is already starting") - return - } - - startJob = coroutineScope.launch { - starter.startWithRoot() - } + override suspend fun startWithRoot() { + starter.startWithRoot() } override fun startWithShizuku() { @@ -274,11 +261,17 @@ interface SystemBridgeConnectionManager { fun stopSystemBridge() fun restartSystemBridge() - fun startWithRoot() + suspend fun startWithRoot() fun startWithShizuku() - fun startWithAdb() + suspend fun startWithAdb() } fun SystemBridgeConnectionManager.isConnected(): Boolean { return connectionState.value is SystemBridgeConnectionState.Connected } + +suspend fun SystemBridgeConnectionManager.awaitConnected() { + connectionState + .filterIsInstance() + .first() +} diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt index d78a260353..36af474114 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt @@ -12,7 +12,6 @@ import android.os.Build import android.provider.Settings import android.service.quicksettings.TileService import androidx.annotation.RequiresApi -import androidx.annotation.RequiresPermission import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import dagger.hilt.android.qualifiers.ApplicationContext @@ -25,8 +24,7 @@ import io.github.sds100.keymapper.common.utils.onSuccess import io.github.sds100.keymapper.sysbridge.adb.AdbManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState -import javax.inject.Inject -import javax.inject.Singleton +import io.github.sds100.keymapper.sysbridge.manager.awaitConnected import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.TimeoutCancellationException @@ -34,7 +32,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.stateIn @@ -43,6 +41,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeoutOrNull import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton @Singleton class SystemBridgeSetupControllerImpl @Inject constructor( @@ -60,6 +60,10 @@ class SystemBridgeSetupControllerImpl @Inject constructor( private val activityManager: ActivityManager by lazy { ctx.getSystemService()!! } + private val _isStarting: MutableStateFlow = MutableStateFlow(false) + override val isStarting: StateFlow = _isStarting + private var startJob: Job? = null + override val isDeveloperOptionsEnabled: MutableStateFlow = MutableStateFlow(getDeveloperOptionsEnabled()) @@ -74,8 +78,6 @@ class SystemBridgeSetupControllerImpl @Inject constructor( private val isAdbPairedResult: MutableStateFlow = MutableStateFlow(null) private var isAdbPairedJob: Job? = null - private var autoStartJob: Job? = null - init { // Automatically go back to the Key Mapper app when turning on wireless debugging coroutineScope.launch { @@ -104,13 +106,65 @@ class SystemBridgeSetupControllerImpl @Inject constructor( } override fun startWithRoot() { - coroutineScope.launch { - connectionManager.startWithRoot() + if (startJob?.isActive == true) { + Timber.i("System Bridge is already starting") + return + } + + startJob = coroutineScope.launch { + _isStarting.value = true + try { + connectionManager.startWithRoot() + // Wait for the service to bind and start system bridge + withTimeoutOrNull(10000L) { + connectionManager.awaitConnected() + } + } finally { + _isStarting.value = false + } } } override fun startWithShizuku() { - connectionManager.startWithShizuku() + if (startJob?.isActive == true) { + Timber.i("System Bridge is already starting") + return + } + + startJob = coroutineScope.launch { + _isStarting.value = true + try { + connectionManager.startWithShizuku() + + // Wait for the service to bind and start system bridge + withTimeoutOrNull(10000L) { + connectionManager.awaitConnected() + } + } finally { + _isStarting.value = false + } + } + } + + @RequiresApi(Build.VERSION_CODES.R) + override fun startWithAdb() { + if (startJob?.isActive == true) { + Timber.i("System Bridge is already starting") + return + } + + startJob = coroutineScope.launch { + _isStarting.value = true + try { + connectionManager.startWithAdb() + // Wait for the service to bind and start system bridge + withTimeoutOrNull(10000L) { + connectionManager.awaitConnected() + } + } finally { + _isStarting.value = false + } + } } /** @@ -119,65 +173,68 @@ class SystemBridgeSetupControllerImpl @Inject constructor( */ @RequiresApi(Build.VERSION_CODES.R) override fun autoStartWithAdb() { - autoStartJob?.cancel() - - autoStartJob = coroutineScope.launch { - if (!canWriteGlobalSettings()) { - Timber.w("Cannot auto start with ADB. WRITE_SECURE_SETTINGS permission not granted") - return@launch - } - - if (connectionManager.connectionState.value - !is SystemBridgeConnectionState.Disconnected - ) { - Timber.w("Not auto starting. System Bridge is already connected.") - return@launch - } - - SettingsUtils.putGlobalSetting(ctx, DEVELOPER_OPTIONS_SETTING, 1) + if (startJob?.isActive == true) { + Timber.i("System Bridge is already starting") + return + } + startJob = coroutineScope.launch { + _isStarting.value = true try { - withTimeout(5000L) { isDeveloperOptionsEnabled.first { it } } - } catch (_: TimeoutCancellationException) { - return@launch - } + if (!canWriteGlobalSettings()) { + Timber.w( + "Cannot auto start with ADB. WRITE_SECURE_SETTINGS permission not granted", + ) + return@launch + } - if (isAdbPaired()) { - // This is IMPORTANT. First turn on ADB before enabling wireless debugging because - // turning on developer options just before can cause the Shell to be killed once - // the system bridge is started. - SettingsUtils.putGlobalSetting(ctx, Settings.Global.ADB_ENABLED, 1) - SettingsUtils.putGlobalSetting(ctx, ADB_WIRELESS_SETTING, 1) + if (connectionManager.connectionState.value + !is SystemBridgeConnectionState.Disconnected + ) { + Timber.w("Not auto starting. System Bridge is already connected.") + return@launch + } + + SettingsUtils.putGlobalSetting(ctx, DEVELOPER_OPTIONS_SETTING, 1) - // Wait for wireless debugging to be enabled before starting with ADB try { - withTimeout(5000L) { isWirelessDebuggingEnabled.first { it } } + withTimeout(5000L) { isDeveloperOptionsEnabled.first { it } } } catch (_: TimeoutCancellationException) { return@launch } - startWithAdb() - - // Wait for the service to connect before turning off wireless debugging - withTimeoutOrNull(5000L) { - connectionManager.connectionState - .filterIsInstance() - .first() + if (isAdbPaired()) { + // This is IMPORTANT. First turn on ADB before enabling wireless debugging because + // turning on developer options just before can cause the Shell to be killed once + // the system bridge is started. + SettingsUtils.putGlobalSetting(ctx, Settings.Global.ADB_ENABLED, 1) + SettingsUtils.putGlobalSetting(ctx, ADB_WIRELESS_SETTING, 1) + + // Wait for wireless debugging to be enabled before starting with ADB + try { + withTimeout(5000L) { isWirelessDebuggingEnabled.first { it } } + } catch (_: TimeoutCancellationException) { + return@launch + } + + connectionManager.startWithAdb() + + // Wait for the service to connect before turning off wireless debugging + withTimeoutOrNull(10000L) { + connectionManager.awaitConnected() + } + + // Disable wireless debugging when done + SettingsUtils.putGlobalSetting(ctx, ADB_WIRELESS_SETTING, 0) + } else { + Timber.e("Autostart failed. ADB not paired successfully.") } - - // Disable wireless debugging when done - SettingsUtils.putGlobalSetting(ctx, ADB_WIRELESS_SETTING, 0) - } else { - Timber.e("Autostart failed. ADB not paired successfully.") + } finally { + _isStarting.value = false } } } - @RequiresApi(Build.VERSION_CODES.R) - override fun startWithAdb() { - connectionManager.startWithAdb() - } - @RequiresApi(Build.VERSION_CODES.R) override fun launchPairingAssistant() { launchWirelessDebuggingActivity() @@ -341,6 +398,7 @@ class SystemBridgeSetupControllerImpl @Inject constructor( @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) interface SystemBridgeSetupController { val setupAssistantStep: Flow + val isStarting: StateFlow val isDeveloperOptionsEnabled: Flow fun enableDeveloperOptions() @@ -359,7 +417,5 @@ interface SystemBridgeSetupController { fun startWithRoot() fun startWithShizuku() fun startWithAdb() - - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) fun autoStartWithAdb() } diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/starter/SystemBridgeStarter.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/starter/SystemBridgeStarter.kt index 01bd4c92c5..317b87ecbd 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/starter/SystemBridgeStarter.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/starter/SystemBridgeStarter.kt @@ -74,15 +74,17 @@ class SystemBridgeStarter @Inject constructor( Timber.i("Starting System Bridge with Shizuku starter service") try { runBlocking { - startSystemBridge(executeCommand = { command -> - val output = service.executeCommand(command) - - if (output == null) { - KMError.UnknownIOError - } else { - Success(output) - } - }) + startSystemBridgeWithLock( + commandExecutor = { command -> + val output = service.executeCommand(command) + + if (output == null) { + KMError.UnknownIOError + } else { + Success(output) + } + }, + ) } } catch (e: RemoteException) { Timber.e("Exception starting with Shizuku starter service: $e") @@ -134,7 +136,7 @@ class SystemBridgeStarter @Inject constructor( return KMError.Exception(IllegalStateException("User is locked")) } - return startSystemBridge(executeCommand = adbManager::executeCommand) + return startSystemBridgeWithLock(commandExecutor = adbManager::executeCommand) .onFailure { error -> Timber.e("Failed to start system bridge with ADB: $error") } @@ -147,66 +149,74 @@ class SystemBridgeStarter @Inject constructor( } Timber.i("Starting System Bridge with root") - startSystemBridge(executeCommand = { command -> - val output = withContext(Dispatchers.IO) { - Shell.cmd(command).exec() - } + startSystemBridgeWithLock( + commandExecutor = { command -> + val output = withContext(Dispatchers.IO) { + Shell.cmd(command).exec() + } - if (output.isSuccess) { - Success(output.out.plus(output.err).joinToString("\n")) - } else { - KMError.UnknownIOError - } - }) + if (output.isSuccess) { + Success(output.out.plus(output.err).joinToString("\n")) + } else { + KMError.UnknownIOError + } + }, + ) } - suspend fun startSystemBridge( - executeCommand: suspend (String) -> KMResult, + suspend fun startSystemBridgeWithLock( + commandExecutor: suspend (String) -> KMResult, ): KMResult { startMutex.withLock { - val externalFilesParent = try { - ctx.getExternalFilesDir(null)?.parentFile - } catch (e: IOException) { - return KMError.UnknownIOError - } + return startSystemBridge(commandExecutor) + } + } + + private suspend fun startSystemBridge( + commandExecutor: suspend (String) -> KMResult, + ): KMResult { + val externalFilesParent = try { + ctx.getExternalFilesDir(null)?.parentFile + } catch (e: IOException) { + return KMError.UnknownIOError + } - Timber.i("Copy starter files to ${externalFilesParent?.absolutePath}") + Timber.i("Copy starter files to ${externalFilesParent?.absolutePath}") - val outputStarterBinary = File(externalFilesParent, "starter") - val outputStarterScript = File(externalFilesParent, "start.sh") + val outputStarterBinary = File(externalFilesParent, "starter") + val outputStarterScript = File(externalFilesParent, "start.sh") - val copyFilesResult = withContext(Dispatchers.IO) { - copyNativeLibrary(outputStarterBinary).then { - // Create the start.sh shell script - writeStarterScript( - outputStarterScript, - outputStarterBinary.absolutePath, - ) - Success(Unit) - } + val copyFilesResult = withContext(Dispatchers.IO) { + copyNativeLibrary(outputStarterBinary).then { + // Create the start.sh shell script + writeStarterScript( + outputStarterScript, + outputStarterBinary.absolutePath, + ) + Success(Unit) } + } - val startCommand = - "sh ${outputStarterScript.absolutePath} --apk=$baseApkPath --lib=$libPath --package=$packageName --version=${buildConfigProvider.versionCode}" - - return copyFilesResult - .then { executeCommand(startCommand) } - .then { output -> - // Adb on Android 11 has no permission to access Android/data so use /data/user_de. - if (output.contains( - "/Android/data/${ctx.packageName}/start.sh: Permission denied", - ) - ) { - Timber.w( - "ADB has no permission to access Android/data/${ctx.packageName}/start.sh. Trying to use /data/user_de instead...", - ) - - startSystemBridgeFromProtectedStorage(executeCommand) - } else { - Success(output) - } + val startCommand = + "sh ${outputStarterScript.absolutePath} --apk=$baseApkPath --lib=$libPath --package=$packageName --version=${buildConfigProvider.versionCode}" + + return copyFilesResult + .then { commandExecutor(startCommand) } + .then { output -> + // Adb on Android 11 has no permission to access Android/data so use /data/user_de. + if (output.contains( + "/Android/data/${ctx.packageName}/start.sh: Permission denied", + ) + ) { + Timber.w( + "ADB has no permission to access Android/data/${ctx.packageName}/start.sh. Trying to use /data/user_de instead...", + ) + + startSystemBridgeFromProtectedStorage(commandExecutor) + } else { + Success(output) } - } + } } private suspend fun startSystemBridgeFromProtectedStorage( From 85f17b440218e199238ba3adf68d8ddf792dbf54 Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 11:50:02 +0000 Subject: [PATCH 182/199] #1938 show different log message when system bridge killed by user --- .../sysbridge/manager/SystemBridgeConnectionManager.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt index c8ed2865ac..f9c5941b68 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt @@ -73,7 +73,11 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( private val deathRecipient: DeathRecipient = DeathRecipient { synchronized(systemBridgeLock) { - Timber.e("System Bridge has died") + if (isExpectedDeath) { + Timber.w("System bridge killed by user.") + } else { + Timber.e("System Bridge has died") + } systemBridgeFlow.update { null } From c5c31f431e3633732fcb7a07d3751d80a55f60ca Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 12:31:42 +0000 Subject: [PATCH 183/199] #1923 rename PRO mode to Expert mode --- CHANGELOG.md | 2 + .../AccessibilityServiceController.kt | 2 +- .../trigger/ConfigTriggerViewModel.kt | 4 +- base/src/main/assets/whats-new.txt | 2 +- .../keymapper/base/ActivityViewModel.kt | 4 +- .../sds100/keymapper/base/BaseKeyMapperApp.kt | 2 +- .../sds100/keymapper/base/BaseMainActivity.kt | 2 +- .../sds100/keymapper/base/BaseMainNavHost.kt | 12 +- .../keymapper/base/BaseViewModelHiltModule.kt | 14 +- .../keymapper/base/actions/ActionsScreen.kt | 5 +- .../actions/ConfigShellCommandViewModel.kt | 19 +- .../base/actions/ShellCommandActionScreen.kt | 30 +-- .../keyevent/FixKeyEventActionBottomSheet.kt | 51 ++-- .../keyevent/FixKeyEventActionDelegate.kt | 40 ++-- .../keyevent/FixKeyEventActionState.kt | 10 +- .../ExpertModeScreen.kt} | 153 ++++++------ .../ExpertModeSetupDelegateImpl.kt} | 4 +- .../ExpertModeSetupScreen.kt} | 135 +++++------ .../ExpertModeSetupState.kt} | 4 +- .../ExpertModeSetupViewModel.kt} | 4 +- .../ExpertModeViewModel.kt} | 44 ++-- .../ShizukuSetupState.kt | 2 +- .../{promode => expertmode}/StepContent.kt | 2 +- .../SystemBridgeAutoStarter.kt | 33 +-- .../SystemBridgeSetupAssistantController.kt | 36 +-- .../SystemBridgeSetupDelegate.kt | 60 +++-- .../SystemBridgeSetupUseCase.kt | 23 +- .../base/home/HomeKeyMapListScreen.kt | 4 +- .../base/keymaps/DisplayKeyMapUseCase.kt | 12 +- .../base/onboarding/OnboardingTipDelegate.kt | 40 ++-- .../SetupAccessibilityServiceDialog.kt | 2 +- .../keymapper/base/settings/SettingsScreen.kt | 40 ++-- .../base/settings/SettingsViewModel.kt | 8 +- .../BaseAccessibilityServiceController.kt | 4 +- .../notifications/NotificationController.kt | 14 +- .../permissions/RequestPermissionDelegate.kt | 15 +- .../trigger/BaseConfigTriggerViewModel.kt | 35 ++- .../base/trigger/BaseTriggerScreen.kt | 47 ++-- ...tate.kt => ExpertModeRecordSwitchState.kt} | 2 +- .../base/trigger/RecordTriggerButtonRow.kt | 44 ++-- .../base/trigger/TriggerKeyListItem.kt | 22 +- .../base/trigger/TriggerSetupBottomSheet.kt | 185 ++++++++------- .../base/trigger/TriggerSetupDelegate.kt | 151 +++++++----- .../base/trigger/TriggerSetupState.kt | 32 +-- .../{ProModeStatus.kt => ExpertModeStatus.kt} | 2 +- .../base/utils/navigation/NavDestination.kt | 10 +- .../base/utils/ui/compose/OptionButton.kt | 4 +- .../base/utils/ui/compose/OptionPageButton.kt | 9 +- .../utils/ui/compose/SetupRequirementRow.kt | 22 +- .../utils/ui/compose/icons/ProModeDisabled.kt | 162 ------------- .../utils/ui/compose/icons/ProModeIcon.kt | 150 ------------ .../main/res/drawable/offline_bolt_24px.xml | 10 + base/src/main/res/drawable/pro_mode.xml | 36 --- base/src/main/res/values-pt/strings.xml | 168 ++++++------- base/src/main/res/values-tr/strings.xml | 196 +++++++-------- base/src/main/res/values/strings.xml | 224 +++++++++--------- .../SystemBridgeAutoStarterTest.kt | 16 +- .../SystemBridgeSetupUseCaseTest.kt | 2 +- .../trigger/ConfigTriggerViewModelTest.kt | 52 ++-- .../io/github/sds100/keymapper/data/Keys.kt | 12 +- .../keymapper/data/PreferenceDefaults.kt | 4 +- 61 files changed, 1097 insertions(+), 1338 deletions(-) rename base/src/main/java/io/github/sds100/keymapper/base/{promode/ProModeScreen.kt => expertmode/ExpertModeScreen.kt} (83%) rename base/src/main/java/io/github/sds100/keymapper/base/{promode/ProModeSetupDelegateImpl.kt => expertmode/ExpertModeSetupDelegateImpl.kt} (87%) rename base/src/main/java/io/github/sds100/keymapper/base/{promode/ProModeSetupScreen.kt => expertmode/ExpertModeSetupScreen.kt} (80%) rename base/src/main/java/io/github/sds100/keymapper/base/{promode/ProModeSetupState.kt => expertmode/ExpertModeSetupState.kt} (79%) rename base/src/main/java/io/github/sds100/keymapper/base/{promode/ProModeSetupViewModel.kt => expertmode/ExpertModeSetupViewModel.kt} (87%) rename base/src/main/java/io/github/sds100/keymapper/base/{promode/ProModeViewModel.kt => expertmode/ExpertModeViewModel.kt} (83%) rename base/src/main/java/io/github/sds100/keymapper/base/{promode => expertmode}/ShizukuSetupState.kt (66%) rename base/src/main/java/io/github/sds100/keymapper/base/{promode => expertmode}/StepContent.kt (78%) rename base/src/main/java/io/github/sds100/keymapper/base/{promode => expertmode}/SystemBridgeAutoStarter.kt (92%) rename base/src/main/java/io/github/sds100/keymapper/base/{promode => expertmode}/SystemBridgeSetupAssistantController.kt (89%) rename base/src/main/java/io/github/sds100/keymapper/base/{promode => expertmode}/SystemBridgeSetupDelegate.kt (74%) rename base/src/main/java/io/github/sds100/keymapper/base/{promode => expertmode}/SystemBridgeSetupUseCase.kt (93%) rename base/src/main/java/io/github/sds100/keymapper/base/trigger/{ProModeRecordSwitchState.kt => ExpertModeRecordSwitchState.kt} (77%) rename base/src/main/java/io/github/sds100/keymapper/base/utils/{ProModeStatus.kt => ExpertModeStatus.kt} (75%) delete mode 100644 base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeDisabled.kt delete mode 100644 base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeIcon.kt create mode 100644 base/src/main/res/drawable/offline_bolt_24px.xml delete mode 100644 base/src/main/res/drawable/pro_mode.xml rename base/src/test/java/io/github/sds100/keymapper/base/{promode => expertmode}/SystemBridgeAutoStarterTest.kt (98%) rename base/src/test/java/io/github/sds100/keymapper/base/{promode => expertmode}/SystemBridgeSetupUseCaseTest.kt (98%) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0c77fb6c3..4c7879734f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### TO BE RELEASED +Renamed PRO mode to Expert mode because it sounded like a paid premium feature even though it is free. + ## Added - #1915 ask user to remove "adb shell" from Shell command. diff --git a/app/src/main/java/io/github/sds100/keymapper/system/accessibility/AccessibilityServiceController.kt b/app/src/main/java/io/github/sds100/keymapper/system/accessibility/AccessibilityServiceController.kt index 98d59f8ba1..a275a3197c 100644 --- a/app/src/main/java/io/github/sds100/keymapper/system/accessibility/AccessibilityServiceController.kt +++ b/app/src/main/java/io/github/sds100/keymapper/system/accessibility/AccessibilityServiceController.kt @@ -6,10 +6,10 @@ import dagger.assisted.AssistedInject import io.github.sds100.keymapper.base.actions.PerformActionsUseCaseImpl import io.github.sds100.keymapper.base.constraints.DetectConstraintsUseCaseImpl import io.github.sds100.keymapper.base.detection.DetectKeyMapsUseCaseImpl +import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupAssistantController import io.github.sds100.keymapper.base.input.InputEventHub import io.github.sds100.keymapper.base.keymaps.FingerprintGesturesSupportedUseCase import io.github.sds100.keymapper.base.keymaps.PauseKeyMapsUseCase -import io.github.sds100.keymapper.base.promode.SystemBridgeSetupAssistantController import io.github.sds100.keymapper.base.system.accessibility.AccessibilityNodeRecorder import io.github.sds100.keymapper.base.system.accessibility.BaseAccessibilityServiceController import io.github.sds100.keymapper.base.system.inputmethod.AutoSwitchImeController diff --git a/app/src/main/java/io/github/sds100/keymapper/trigger/ConfigTriggerViewModel.kt b/app/src/main/java/io/github/sds100/keymapper/trigger/ConfigTriggerViewModel.kt index 7ab3c25020..849ad6f261 100644 --- a/app/src/main/java/io/github/sds100/keymapper/trigger/ConfigTriggerViewModel.kt +++ b/app/src/main/java/io/github/sds100/keymapper/trigger/ConfigTriggerViewModel.kt @@ -54,7 +54,7 @@ class ConfigTriggerViewModel @Inject constructor( override fun onEditFloatingLayoutClick() {} - override fun showTriggerSetup(shortcut: TriggerSetupShortcut, forceProMode: Boolean) { + override fun showTriggerSetup(shortcut: TriggerSetupShortcut, forceExpertMode: Boolean) { when (shortcut) { TriggerSetupShortcut.ASSISTANT, TriggerSetupShortcut.FLOATING_BUTTON_CUSTOM, @@ -63,7 +63,7 @@ class ConfigTriggerViewModel @Inject constructor( navigateToAdvancedTriggers("purchase_assistant_trigger") } - else -> super.showTriggerSetup(shortcut, forceProMode) + else -> super.showTriggerSetup(shortcut, forceExpertMode) } } } diff --git a/base/src/main/assets/whats-new.txt b/base/src/main/assets/whats-new.txt index 4a199f4438..4af0dd8f1b 100644 --- a/base/src/main/assets/whats-new.txt +++ b/base/src/main/assets/whats-new.txt @@ -1,5 +1,5 @@ ✨ Screen-off remapping -You can now remap ALL buttons when the screen is off (including the power button) for free with PRO mode. +You can now remap ALL buttons when the screen is off (including the power button) for free with Expert mode. 🎯 New Actions • Run shell commands diff --git a/base/src/main/java/io/github/sds100/keymapper/base/ActivityViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/ActivityViewModel.kt index ac3706a965..78ba5f4ad1 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/ActivityViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/ActivityViewModel.kt @@ -29,9 +29,9 @@ class ActivityViewModel @Inject constructor( setupAccessibilityServiceDelegate.showCantFindAccessibilitySettingsDialog() } - fun launchProModeSetup() { + fun launchExpertModeSetup() { viewModelScope.launch { - navigate("pro_mode_setup", NavDestination.ProModeSetup) + navigate("expert_mode_setup", NavDestination.ExpertModeSetup) } } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt index 5888c9c31d..e989b9748c 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt @@ -16,9 +16,9 @@ import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ProcessLifecycleOwner import androidx.multidex.MultiDexApplication +import io.github.sds100.keymapper.base.expertmode.SystemBridgeAutoStarter import io.github.sds100.keymapper.base.logging.KeyMapperLoggingTree import io.github.sds100.keymapper.base.logging.SystemBridgeLogger -import io.github.sds100.keymapper.base.promode.SystemBridgeAutoStarter import io.github.sds100.keymapper.base.settings.Theme import io.github.sds100.keymapper.base.system.accessibility.AccessibilityServiceAdapterImpl import io.github.sds100.keymapper.base.system.notifications.NotificationController diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt index ff96088bff..b726317db1 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt @@ -254,7 +254,7 @@ abstract class BaseMainActivity : AppCompatActivity() { } ACTION_START_SYSTEM_BRIDGE -> { - viewModel.launchProModeSetup() + viewModel.launchExpertModeSetup() // Only clear the intent if it is handled in case it is used elsewhere this.intent = null diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BaseMainNavHost.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseMainNavHost.kt index 7dbe5c9a35..0b22cc858e 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BaseMainNavHost.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseMainNavHost.kt @@ -25,11 +25,11 @@ import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementScreen import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementViewModel import io.github.sds100.keymapper.base.constraints.ChooseConstraintScreen import io.github.sds100.keymapper.base.constraints.ChooseConstraintViewModel +import io.github.sds100.keymapper.base.expertmode.ExpertModeScreen +import io.github.sds100.keymapper.base.expertmode.ExpertModeSetupScreen import io.github.sds100.keymapper.base.logging.LogScreen import io.github.sds100.keymapper.base.onboarding.HandleAccessibilityServiceDialogs import io.github.sds100.keymapper.base.onboarding.SetupAccessibilityServiceDelegateImpl -import io.github.sds100.keymapper.base.promode.ProModeScreen -import io.github.sds100.keymapper.base.promode.ProModeSetupScreen import io.github.sds100.keymapper.base.settings.AutomaticChangeImeSettingsScreen import io.github.sds100.keymapper.base.settings.DefaultOptionsSettingsScreen import io.github.sds100.keymapper.base.settings.SettingsScreen @@ -135,8 +135,8 @@ fun BaseMainNavHost( ) } - composable { - ProModeScreen( + composable { + ExpertModeScreen( modifier = Modifier .fillMaxSize() .windowInsetsPadding( @@ -151,8 +151,8 @@ fun BaseMainNavHost( ) } - composable { - ProModeSetupScreen( + composable { + ExpertModeSetupScreen( viewModel = hiltViewModel(), ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BaseViewModelHiltModule.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseViewModelHiltModule.kt index 4f316601ad..75ebbc90c7 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BaseViewModelHiltModule.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseViewModelHiltModule.kt @@ -25,6 +25,10 @@ import io.github.sds100.keymapper.base.constraints.ConfigConstraintsUseCaseImpl import io.github.sds100.keymapper.base.constraints.CreateConstraintUseCase import io.github.sds100.keymapper.base.constraints.CreateConstraintUseCaseImpl import io.github.sds100.keymapper.base.constraints.DisplayConstraintUseCase +import io.github.sds100.keymapper.base.expertmode.ExpertModeSetupDelegateImpl +import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupDelegate +import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupUseCase +import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupUseCaseImpl import io.github.sds100.keymapper.base.home.ListKeyMapsUseCase import io.github.sds100.keymapper.base.home.ListKeyMapsUseCaseImpl import io.github.sds100.keymapper.base.home.ShowHomeScreenAlertsUseCase @@ -37,10 +41,6 @@ import io.github.sds100.keymapper.base.logging.ShareLogcatUseCase import io.github.sds100.keymapper.base.logging.ShareLogcatUseCaseImpl import io.github.sds100.keymapper.base.onboarding.OnboardingTipDelegate import io.github.sds100.keymapper.base.onboarding.OnboardingTipDelegateImpl -import io.github.sds100.keymapper.base.promode.ProModeSetupDelegateImpl -import io.github.sds100.keymapper.base.promode.SystemBridgeSetupDelegate -import io.github.sds100.keymapper.base.promode.SystemBridgeSetupUseCase -import io.github.sds100.keymapper.base.promode.SystemBridgeSetupUseCaseImpl import io.github.sds100.keymapper.base.settings.ConfigSettingsUseCase import io.github.sds100.keymapper.base.settings.ConfigSettingsUseCaseImpl import io.github.sds100.keymapper.base.shortcuts.CreateKeyMapShortcutUseCase @@ -153,7 +153,7 @@ abstract class BaseViewModelHiltModule { @Binds @ViewModelScoped - abstract fun bindProModeSetupUseCase( + abstract fun bindExpertModeSetupUseCase( impl: SystemBridgeSetupUseCaseImpl, ): SystemBridgeSetupUseCase @@ -197,5 +197,7 @@ abstract class BaseViewModelHiltModule { @Binds @ViewModelScoped - abstract fun bindProModeSetupDelegate(impl: ProModeSetupDelegateImpl): SystemBridgeSetupDelegate + abstract fun bindExpertModeSetupDelegate( + impl: ExpertModeSetupDelegateImpl, + ): SystemBridgeSetupDelegate } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt index 55c61681ab..b7fec30f2c 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt @@ -79,11 +79,11 @@ fun ActionsScreen(modifier: Modifier = Modifier, viewModel: ConfigActionsViewMod sheetState = sheetState, onDismissRequest = viewModel::dismissFixKeyEventActionBottomSheet, onEnableAccessibilityServiceClick = viewModel::onEnableAccessibilityServiceClick, - onEnableProModeClick = viewModel::onEnableProModeForKeyEventActionsClick, + onEnableExpertModeClick = viewModel::onEnableExpertModeForKeyEventActionsClick, onEnableInputMethodClick = viewModel::onEnableImeClick, onChooseInputMethodClick = viewModel::onChooseImeClick, onDoneClick = viewModel::dismissFixKeyEventActionBottomSheet, - onSelectProMode = viewModel::onSelectProMode, + onSelectExpertMode = viewModel::onSelectExpertMode, onSelectInputMethod = viewModel::onSelectInputMethod, onAutoSwitchImeCheckedChange = viewModel::onAutoSwitchImeCheckedChange, ) @@ -150,6 +150,7 @@ private fun ActionsScreen( when (state) { State.Loading -> Loading() + is State.Data -> Surface(modifier = modifier) { Column { Spacer(Modifier.height(8.dp)) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigShellCommandViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigShellCommandViewModel.kt index 9fc5bc5076..e8262c6c42 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigShellCommandViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigShellCommandViewModel.kt @@ -8,7 +8,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import io.github.sds100.keymapper.base.R -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus import io.github.sds100.keymapper.base.utils.navigation.NavDestination import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider import io.github.sds100.keymapper.base.utils.navigation.navigate @@ -44,16 +44,16 @@ class ConfigShellCommandViewModel @Inject constructor( private var testJob: Job? = null init { - // Update ProModeStatus in state + // Update ExpertModeStatus in state if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { viewModelScope.launch { systemBridgeConnectionManager.connectionState.map { connectionState -> when (connectionState) { - is SystemBridgeConnectionState.Connected -> ProModeStatus.ENABLED - is SystemBridgeConnectionState.Disconnected -> ProModeStatus.DISABLED + is SystemBridgeConnectionState.Connected -> ExpertModeStatus.ENABLED + is SystemBridgeConnectionState.Disconnected -> ExpertModeStatus.DISABLED } - }.collect { proModeStatus -> - state = state.copy(proModeStatus = proModeStatus) + }.collect { expertModeStatus -> + state = state.copy(expertModeStatus = expertModeStatus) } } } @@ -172,9 +172,12 @@ class ConfigShellCommandViewModel @Inject constructor( } } - fun onSetupProModeClick() { + fun onSetupExpertModeClick() { viewModelScope.launch { - navigationProvider.navigate("shell_command_setup_pro_mode", NavDestination.ProModeSetup) + navigationProvider.navigate( + "shell_command_setup_expert_mode", + NavDestination.ExpertModeSetup, + ) } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/ShellCommandActionScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ShellCommandActionScreen.kt index e336db0217..1523ebb6cb 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/ShellCommandActionScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ShellCommandActionScreen.kt @@ -50,7 +50,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.compose.KeyMapperTheme -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus import io.github.sds100.keymapper.base.utils.getFullMessage import io.github.sds100.keymapper.base.utils.ui.compose.KeyMapperSegmentedButtonRow import io.github.sds100.keymapper.base.utils.ui.compose.SliderOptionText @@ -77,7 +77,7 @@ data class ShellCommandActionState( val timeoutSeconds: Int = 10, val isRunning: Boolean = false, val testResult: KMResult? = null, - val proModeStatus: ProModeStatus = ProModeStatus.UNSUPPORTED, + val expertModeStatus: ExpertModeStatus = ExpertModeStatus.UNSUPPORTED, ) @Composable @@ -96,7 +96,7 @@ fun ShellCommandActionScreen( onKillClick = viewModel::onKillClick, onDoneClick = viewModel::onDoneClick, onCancelClick = viewModel::onCancelClick, - onSetupProModeClick = viewModel::onSetupProModeClick, + onSetupExpertModeClick = viewModel::onSetupExpertModeClick, ) } @@ -119,7 +119,7 @@ private fun ShellCommandActionScreen( */ onDoneClick: () -> Boolean = { true }, onCancelClick: () -> Unit = {}, - onSetupProModeClick: () -> Unit = {}, + onSetupExpertModeClick: () -> Unit = {}, ) { val scrollState = rememberScrollState() val scope = rememberCoroutineScope() @@ -225,7 +225,7 @@ private fun ShellCommandActionScreen( } } }, - onSetupProModeClick = onSetupProModeClick, + onSetupExpertModeClick = onSetupExpertModeClick, ) 1 -> ShellCommandOutputContent( @@ -250,7 +250,7 @@ private fun ShellCommandConfigurationContent( onExecutionModeChanged: (ShellExecutionMode) -> Unit, onTimeoutChanged: (Int) -> Unit, onTestClick: () -> Unit, - onSetupProModeClick: () -> Unit, + onSetupExpertModeClick: () -> Unit, ) { val keyboardController = LocalSoftwareKeyboardController.current Column( @@ -327,18 +327,18 @@ private fun ShellCommandConfigurationContent( ) if (state.executionMode == ShellExecutionMode.ADB && - state.proModeStatus != ProModeStatus.ENABLED + state.expertModeStatus != ExpertModeStatus.ENABLED ) { OutlinedButton( modifier = Modifier.fillMaxWidth(), - onClick = onSetupProModeClick, - enabled = state.proModeStatus != ProModeStatus.UNSUPPORTED, + onClick = onSetupExpertModeClick, + enabled = state.expertModeStatus != ExpertModeStatus.UNSUPPORTED, ) { Text( - if (state.proModeStatus == ProModeStatus.UNSUPPORTED) { - stringResource(R.string.action_shell_command_setup_pro_mode_unsupported) + if (state.expertModeStatus == ExpertModeStatus.UNSUPPORTED) { + stringResource(R.string.action_shell_command_setup_expert_mode_unsupported) } else { - stringResource(R.string.action_shell_command_setup_pro_mode) + stringResource(R.string.action_shell_command_setup_expert_mode) }, ) } @@ -355,7 +355,7 @@ private fun ShellCommandConfigurationContent( state.executionMode != ShellExecutionMode.ADB || ( state.executionMode == ShellExecutionMode.ADB && - state.proModeStatus == ProModeStatus.ENABLED + state.expertModeStatus == ExpertModeStatus.ENABLED ) ), ) { @@ -575,14 +575,14 @@ private fun PreviewShellCommandActionScreenTesting() { @Preview @Composable -private fun PreviewShellCommandActionScreenProModeUnsupported() { +private fun PreviewShellCommandActionScreenExpertModeUnsupported() { KeyMapperTheme { ShellCommandActionScreen( state = ShellCommandActionState( description = "ADB command example", command = "echo 'Hello from ADB'", executionMode = ShellExecutionMode.ADB, - proModeStatus = ProModeStatus.UNSUPPORTED, + expertModeStatus = ExpertModeStatus.UNSUPPORTED, ), ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionBottomSheet.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionBottomSheet.kt index b890e9ac19..1bca36c182 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionBottomSheet.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionBottomSheet.kt @@ -16,6 +16,7 @@ import androidx.compose.foundation.text.InlineTextContent import androidx.compose.foundation.text.appendInlineContent import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.OfflineBolt import androidx.compose.material.icons.rounded.Add import androidx.compose.material.icons.rounded.Keyboard import androidx.compose.material.icons.rounded.Remove @@ -44,15 +45,13 @@ import androidx.compose.ui.unit.dp import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.compose.KeyMapperTheme import io.github.sds100.keymapper.base.compose.LocalCustomColorsPalette -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus import io.github.sds100.keymapper.base.utils.ui.compose.AccessibilityServiceRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.CheckBoxText +import io.github.sds100.keymapper.base.utils.ui.compose.ExpertModeRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.HeaderText import io.github.sds100.keymapper.base.utils.ui.compose.InputMethodRequirementRow -import io.github.sds100.keymapper.base.utils.ui.compose.ProModeRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.filledTonalButtonColorsError -import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIcon @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -62,9 +61,9 @@ fun FixKeyEventActionBottomSheet( sheetState: SheetState, onDismissRequest: () -> Unit = {}, onSelectInputMethod: () -> Unit = {}, - onSelectProMode: () -> Unit = {}, + onSelectExpertMode: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onEnableInputMethodClick: () -> Unit = {}, onChooseInputMethodClick: () -> Unit = {}, onDoneClick: () -> Unit = {}, @@ -135,18 +134,18 @@ fun FixKeyEventActionBottomSheet( ) } - val isProModeUnsupported = state.proModeStatus == ProModeStatus.UNSUPPORTED + val isExpertModeUnsupported = state.expertModeStatus == ExpertModeStatus.UNSUPPORTED FixKeyEventActionOptionCard( - onClick = onSelectProMode, - selected = state is FixKeyEventActionState.ProMode, - title = stringResource(R.string.pro_mode_app_bar_title), - icon = KeyMapperIcons.ProModeIcon, - enabled = !isProModeUnsupported, + onClick = onSelectExpertMode, + selected = state is FixKeyEventActionState.ExpertMode, + title = stringResource(R.string.expert_mode_app_bar_title), + icon = Icons.Outlined.OfflineBolt, + enabled = !isExpertModeUnsupported, ) { - if (isProModeUnsupported) { + if (isExpertModeUnsupported) { Text( - stringResource(R.string.trigger_setup_pro_mode_unsupported), + stringResource(R.string.trigger_setup_expert_mode_unsupported), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.error, ) @@ -154,11 +153,11 @@ fun FixKeyEventActionBottomSheet( val annotatedText = buildAnnotatedString { appendInlineContent("icon", "[icon]") append(" ") - append(stringResource(R.string.fix_key_event_action_pro_mode_text_1)) + append(stringResource(R.string.fix_key_event_action_expert_mode_text_1)) appendLine() appendInlineContent("icon", "[icon]") append(" ") - append(stringResource(R.string.fix_key_event_action_pro_mode_text_2)) + append(stringResource(R.string.fix_key_event_action_expert_mode_text_2)) } val inlineContent = mapOf( Pair( @@ -224,13 +223,13 @@ fun FixKeyEventActionBottomSheet( ) } - is FixKeyEventActionState.ProMode -> { - ProModeRequirementRow( + is FixKeyEventActionState.ExpertMode -> { + ExpertModeRequirementRow( modifier = Modifier.fillMaxWidth(), isVisible = true, - proModeStatus = state.proModeStatus, + expertModeStatus = state.expertModeStatus, buttonColors = ButtonDefaults.filledTonalButtonColorsError(), - onClick = onEnableProModeClick, + onClick = onEnableExpertModeClick, ) } } @@ -317,7 +316,7 @@ private fun InputMethodPreview() { enablingRequiresUserInput = true, isAccessibilityServiceEnabled = true, isAutoSwitchImeEnabled = true, - proModeStatus = ProModeStatus.ENABLED, + expertModeStatus = ExpertModeStatus.ENABLED, ), ) } @@ -326,7 +325,7 @@ private fun InputMethodPreview() { @OptIn(ExperimentalMaterial3Api::class) @Preview @Composable -private fun ProModePreview() { +private fun ExpertModePreview() { KeyMapperTheme { val sheetState = SheetState( skipPartiallyExpanded = true, @@ -336,8 +335,8 @@ private fun ProModePreview() { FixKeyEventActionBottomSheet( sheetState = sheetState, - state = FixKeyEventActionState.ProMode( - proModeStatus = ProModeStatus.DISABLED, + state = FixKeyEventActionState.ExpertMode( + expertModeStatus = ExpertModeStatus.DISABLED, isAccessibilityServiceEnabled = true, ), ) @@ -347,7 +346,7 @@ private fun ProModePreview() { @OptIn(ExperimentalMaterial3Api::class) @Preview @Composable -private fun ProModeUnsupportedPreview() { +private fun ExpertModeUnsupportedPreview() { KeyMapperTheme { val sheetState = SheetState( skipPartiallyExpanded = true, @@ -358,7 +357,7 @@ private fun ProModeUnsupportedPreview() { FixKeyEventActionBottomSheet( sheetState = sheetState, state = FixKeyEventActionState.InputMethod( - proModeStatus = ProModeStatus.UNSUPPORTED, + expertModeStatus = ExpertModeStatus.UNSUPPORTED, isEnabled = false, isChosen = false, enablingRequiresUserInput = true, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionDelegate.kt index 48a8a16c5c..61f8cbe818 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionDelegate.kt @@ -5,7 +5,7 @@ import dagger.hilt.android.scopes.ViewModelScoped import io.github.sds100.keymapper.base.onboarding.SetupAccessibilityServiceDelegate import io.github.sds100.keymapper.base.system.accessibility.ControlAccessibilityServiceUseCase import io.github.sds100.keymapper.base.trigger.SetupInputMethodUseCase -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus import io.github.sds100.keymapper.base.utils.navigation.NavDestination import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider import io.github.sds100.keymapper.base.utils.navigation.navigate @@ -52,19 +52,19 @@ class FixKeyEventActionDelegateImpl @Inject constructor( DialogProvider by dialogProvider, NavigationProvider by navigationProvider { - private val proModeStatus: Flow = + private val expertModeStatus: Flow = if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { systemBridgeConnectionManager.connectionState.map { state -> when (state) { - is SystemBridgeConnectionState.Connected -> ProModeStatus.ENABLED - is SystemBridgeConnectionState.Disconnected -> ProModeStatus.DISABLED + is SystemBridgeConnectionState.Connected -> ExpertModeStatus.ENABLED + is SystemBridgeConnectionState.Disconnected -> ExpertModeStatus.DISABLED } } } else { - flowOf(ProModeStatus.UNSUPPORTED) + flowOf(ExpertModeStatus.UNSUPPORTED) } - private val isProModeSelected: Flow = + private val isExpertModeSelected: Flow = preferenceRepository.get(Keys.keyEventActionsUseSystemBridge) .map { it ?: PreferenceDefaults.KEY_EVENT_ACTIONS_USE_SYSTEM_BRIDGE } @@ -82,26 +82,26 @@ class FixKeyEventActionDelegateImpl @Inject constructor( @OptIn(ExperimentalCoroutinesApi::class) private fun buildStateFlow(): Flow { - return isProModeSelected.flatMapLatest { isProModeSelected -> - if (isProModeSelected) { + return isExpertModeSelected.flatMapLatest { isExpertModeSelected -> + if (isExpertModeSelected) { combine( - proModeStatus, + expertModeStatus, controlAccessibilityServiceUseCase.serviceState, - ) { proModeStatus, serviceState -> - FixKeyEventActionState.ProMode( - proModeStatus = proModeStatus, + ) { expertModeStatus, serviceState -> + FixKeyEventActionState.ExpertMode( + expertModeStatus = expertModeStatus, isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, ) } } else { combine( - proModeStatus, + expertModeStatus, setupInputMethodUseCase.isEnabled, setupInputMethodUseCase.isChosen, controlAccessibilityServiceUseCase.serviceState, preferenceRepository.get(Keys.changeImeOnInputFocus), - ) { proModeStatus, isEnabled, isChosen, serviceState, changeImeOnInputFocus -> + ) { expertModeStatus, isEnabled, isChosen, serviceState, changeImeOnInputFocus -> val enablingRequiresUserInput = Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU @@ -111,7 +111,7 @@ class FixKeyEventActionDelegateImpl @Inject constructor( enablingRequiresUserInput = enablingRequiresUserInput, isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - proModeStatus = proModeStatus, + expertModeStatus = expertModeStatus, isAutoSwitchImeEnabled = changeImeOnInputFocus ?: PreferenceDefaults.CHANGE_IME_ON_INPUT_FOCUS, ) @@ -134,9 +134,9 @@ class FixKeyEventActionDelegateImpl @Inject constructor( } } - override fun onEnableProModeForKeyEventActionsClick() { + override fun onEnableExpertModeForKeyEventActionsClick() { viewModelScope.launch { - navigate("fix_key_event_action_pro_mode", NavDestination.ProMode) + navigate("fix_key_event_action_expert_mode", NavDestination.ExpertMode) } } @@ -154,7 +154,7 @@ class FixKeyEventActionDelegateImpl @Inject constructor( } } - override fun onSelectProMode() { + override fun onSelectExpertMode() { preferenceRepository.set(Keys.keyEventActionsUseSystemBridge, true) } @@ -175,10 +175,10 @@ interface FixKeyEventActionDelegate { fun showFixKeyEventActionBottomSheet() fun dismissFixKeyEventActionBottomSheet() fun onEnableAccessibilityServiceClick() - fun onEnableProModeForKeyEventActionsClick() + fun onEnableExpertModeForKeyEventActionsClick() fun onEnableImeClick() fun onChooseImeClick() - fun onSelectProMode() + fun onSelectExpertMode() fun onSelectInputMethod() fun onAutoSwitchImeCheckedChange(checked: Boolean) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionState.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionState.kt index 5bacfc2db5..370dd54e1a 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionState.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionState.kt @@ -1,10 +1,10 @@ package io.github.sds100.keymapper.base.actions.keyevent -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus sealed class FixKeyEventActionState { abstract val isAccessibilityServiceEnabled: Boolean - abstract val proModeStatus: ProModeStatus + abstract val expertModeStatus: ExpertModeStatus data class InputMethod( val isEnabled: Boolean, @@ -16,11 +16,11 @@ sealed class FixKeyEventActionState { val enablingRequiresUserInput: Boolean, val isAutoSwitchImeEnabled: Boolean, override val isAccessibilityServiceEnabled: Boolean, - override val proModeStatus: ProModeStatus, + override val expertModeStatus: ExpertModeStatus, ) : FixKeyEventActionState() - data class ProMode( + data class ExpertMode( override val isAccessibilityServiceEnabled: Boolean, - override val proModeStatus: ProModeStatus, + override val expertModeStatus: ExpertModeStatus, ) : FixKeyEventActionState() } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt similarity index 83% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeScreen.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt index f20ec183f0..496c22c917 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import android.os.Build import android.provider.Settings @@ -76,20 +76,20 @@ import io.github.sds100.keymapper.common.utils.SettingsUtils import io.github.sds100.keymapper.common.utils.State @Composable -fun ProModeScreen(modifier: Modifier = Modifier, viewModel: ProModeViewModel) { - val proModeWarningState by viewModel.warningState.collectAsStateWithLifecycle() - val proModeSetupState by viewModel.setupState.collectAsStateWithLifecycle() +fun ExpertModeScreen(modifier: Modifier = Modifier, viewModel: ExpertModeViewModel) { + val expertModeWarningState by viewModel.warningState.collectAsStateWithLifecycle() + val expertModeSetupState by viewModel.setupState.collectAsStateWithLifecycle() val autoStartBootEnabled by viewModel.autoStartBootEnabled.collectAsStateWithLifecycle() - ProModeScreen( + ExpertModeScreen( modifier = modifier, onBackClick = viewModel::onBackClick, onHelpClick = { viewModel.showInfoCard() }, showHelpIcon = !viewModel.showInfoCard, ) { Content( - warningState = proModeWarningState, - setupState = proModeSetupState, + warningState = expertModeWarningState, + setupState = expertModeSetupState, showInfoCard = viewModel.showInfoCard, onInfoCardDismiss = { viewModel.hideInfoCard() }, onWarningButtonClick = viewModel::onWarningButtonClick, @@ -106,7 +106,7 @@ fun ProModeScreen(modifier: Modifier = Modifier, viewModel: ProModeViewModel) { @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun ProModeScreen( +private fun ExpertModeScreen( modifier: Modifier = Modifier, onBackClick: () -> Unit = {}, onHelpClick: () -> Unit = {}, @@ -117,7 +117,7 @@ private fun ProModeScreen( modifier = modifier.displayCutoutPadding(), topBar = { TopAppBar( - title = { Text(stringResource(R.string.pro_mode_app_bar_title)) }, + title = { Text(stringResource(R.string.expert_mode_app_bar_title)) }, actions = { AnimatedVisibility( visible = showHelpIcon, @@ -128,7 +128,7 @@ private fun ProModeScreen( Icon( imageVector = Icons.AutoMirrored.Rounded.HelpOutline, contentDescription = stringResource( - R.string.pro_mode_info_card_show_content_description, + R.string.expert_mode_info_card_show_content_description, ), ) } @@ -169,8 +169,8 @@ private fun ProModeScreen( @Composable private fun Content( modifier: Modifier = Modifier, - warningState: ProModeWarningState, - setupState: State, + warningState: ExpertModeWarningState, + setupState: State, showInfoCard: Boolean, onInfoCardDismiss: () -> Unit = {}, onWarningButtonClick: () -> Unit = {}, @@ -188,7 +188,7 @@ private fun Content( enter = fadeIn() + expandVertically(), exit = fadeOut() + shrinkVertically(), ) { - ProModeInfoCard( + ExpertModeInfoCard( modifier = Modifier .fillMaxWidth() .padding(horizontal = 8.dp), @@ -210,7 +210,7 @@ private fun Content( Spacer(modifier = Modifier.height(16.dp)) - if (warningState is ProModeWarningState.Understood) { + if (warningState is ExpertModeWarningState.Understood) { when (setupState) { is State.Loading -> { CircularProgressIndicator( @@ -235,7 +235,7 @@ private fun Content( } else { Text( modifier = Modifier.padding(horizontal = 32.dp), - text = stringResource(R.string.pro_mode_settings_unavailable_text), + text = stringResource(R.string.expert_mode_settings_unavailable_text), textAlign = TextAlign.Center, ) } @@ -245,7 +245,7 @@ private fun Content( @Composable private fun LoadedContent( modifier: Modifier, - state: ProModeState, + state: ExpertModeState, onRootButtonClick: () -> Unit = {}, onShizukuButtonClick: () -> Unit, onStopServiceClick: () -> Unit, @@ -258,16 +258,16 @@ private fun LoadedContent( OptionsHeaderRow( modifier = Modifier.padding(horizontal = 16.dp), icon = Icons.Rounded.Checklist, - text = stringResource(R.string.pro_mode_set_up_title), + text = stringResource(R.string.expert_mode_set_up_title), ) Spacer(modifier = Modifier.height(8.dp)) // Show notification permission warning if permission not granted - if (state is ProModeState.Stopped && !state.isNotificationPermissionGranted) { + if (state is ExpertModeState.Stopped && !state.isNotificationPermissionGranted) { val text = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_description, + R.string.expert_mode_setup_wizard_enable_notification_permission_description, ) SetupCard( @@ -283,7 +283,7 @@ private fun LoadedContent( ) }, title = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_title, + R.string.expert_mode_setup_wizard_enable_notification_permission_title, ), content = { Text( @@ -292,7 +292,7 @@ private fun LoadedContent( ) }, buttonText = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_button, + R.string.expert_mode_setup_wizard_enable_notification_permission_button, ), onButtonClick = onRequestNotificationPermissionClick, ) @@ -300,7 +300,7 @@ private fun LoadedContent( } when (state) { - is ProModeState.Started -> { + is ExpertModeState.Started -> { if (!state.isDefaultUsbModeCompatible) { IncompatibleUsbModeCard( modifier = Modifier @@ -311,7 +311,7 @@ private fun LoadedContent( Spacer(Modifier.height(8.dp)) } - ProModeStartedCard( + ExpertModeStartedCard( modifier = Modifier .fillMaxWidth() .padding(horizontal = 8.dp), @@ -319,7 +319,7 @@ private fun LoadedContent( ) } - is ProModeState.Stopped -> { + is ExpertModeState.Stopped -> { if (state.isRootGranted) { SetupCard( modifier = Modifier @@ -333,15 +333,15 @@ private fun LoadedContent( tint = LocalCustomColorsPalette.current.magiskTeal, ) }, - title = stringResource(R.string.pro_mode_root_detected_title), + title = stringResource(R.string.expert_mode_root_detected_title), content = { Text( - text = stringResource(R.string.pro_mode_root_detected_text), + text = stringResource(R.string.expert_mode_root_detected_text), style = MaterialTheme.typography.bodyMedium, ) }, buttonText = stringResource( - R.string.pro_mode_root_detected_button_start_service, + R.string.expert_mode_root_detected_button_start_service, ), onButtonClick = onRootButtonClick, enabled = state.isNotificationPermissionGranted, @@ -353,14 +353,17 @@ private fun LoadedContent( val shizukuButtonText: String? = when (state.shizukuSetupState) { ShizukuSetupState.INSTALLED -> stringResource( - R.string.pro_mode_shizuku_detected_button_start, + R.string.expert_mode_shizuku_detected_button_start, ) + ShizukuSetupState.STARTED -> stringResource( - R.string.pro_mode_shizuku_detected_button_request_permission, + R.string.expert_mode_shizuku_detected_button_request_permission, ) + ShizukuSetupState.PERMISSION_GRANTED -> stringResource( - R.string.pro_mode_shizuku_detected_button_start_service, + R.string.expert_mode_shizuku_detected_button_start_service, ) + ShizukuSetupState.NOT_FOUND -> null } @@ -376,10 +379,10 @@ private fun LoadedContent( contentDescription = null, ) }, - title = stringResource(R.string.pro_mode_shizuku_detected_title), + title = stringResource(R.string.expert_mode_shizuku_detected_title), content = { Text( - text = stringResource(R.string.pro_mode_shizuku_detected_text), + text = stringResource(R.string.expert_mode_shizuku_detected_text), style = MaterialTheme.typography.bodyMedium, ) }, @@ -394,9 +397,10 @@ private fun LoadedContent( val setupKeyMapperText: String = when { Build.VERSION.SDK_INT < Build.VERSION_CODES.R -> stringResource( - R.string.pro_mode_set_up_with_key_mapper_button_incompatible, + R.string.expert_mode_set_up_with_key_mapper_button_incompatible, ) - else -> stringResource(R.string.pro_mode_set_up_with_key_mapper_button) + + else -> stringResource(R.string.expert_mode_set_up_with_key_mapper_button) } SetupCard( @@ -411,7 +415,7 @@ private fun LoadedContent( contentDescription = null, ) }, - title = stringResource(R.string.pro_mode_set_up_with_key_mapper_title), + title = stringResource(R.string.expert_mode_set_up_with_key_mapper_title), content = {}, buttonText = setupKeyMapperText, onButtonClick = onSetupWithKeyMapperClick, @@ -429,15 +433,15 @@ private fun LoadedContent( OptionsHeaderRow( modifier = Modifier.padding(horizontal = 16.dp), icon = Icons.Rounded.Tune, - text = stringResource(R.string.pro_mode_options_title), + text = stringResource(R.string.expert_mode_options_title), ) Spacer(modifier = Modifier.height(8.dp)) SwitchPreferenceCompose( modifier = Modifier.padding(horizontal = 8.dp), - title = stringResource(R.string.title_pref_pro_mode_auto_start), - text = stringResource(R.string.summary_pref_pro_mode_auto_start), + title = stringResource(R.string.title_pref_expert_mode_auto_start), + text = stringResource(R.string.summary_pref_expert_mode_auto_start), icon = Icons.Rounded.RestartAlt, isChecked = autoStartAtBoot, onCheckedChange = onAutoStartAtBootToggled, @@ -461,12 +465,12 @@ private fun IncompatibleUsbModeCard(modifier: Modifier = Modifier) { ) }, title = stringResource( - R.string.pro_mode_setup_wizard_change_default_usb_configuration_title, + R.string.expert_mode_setup_wizard_change_default_usb_configuration_title, ), content = { Text( text = stringResource( - R.string.pro_mode_setup_wizard_change_default_usb_configuration_description, + R.string.expert_mode_setup_wizard_change_default_usb_configuration_description, ), style = MaterialTheme.typography.bodyMedium, ) @@ -488,10 +492,10 @@ private fun IncompatibleUsbModeCard(modifier: Modifier = Modifier) { @Composable private fun WarningCard( modifier: Modifier = Modifier, - state: ProModeWarningState, + state: ExpertModeWarningState, onButtonClick: () -> Unit = {}, ) { - val borderStroke = if (state is ProModeWarningState.Understood) { + val borderStroke = if (state is ExpertModeWarningState.Understood) { CardDefaults.outlinedCardBorder() } else { BorderStroke(1.dp, MaterialTheme.colorScheme.error) @@ -513,7 +517,7 @@ private fun WarningCard( Spacer(modifier = Modifier.width(8.dp)) Text( - text = stringResource(R.string.pro_mode_warning_title), + text = stringResource(R.string.expert_mode_warning_title), style = MaterialTheme.typography.titleMedium, ) } @@ -522,7 +526,7 @@ private fun WarningCard( Text( modifier = Modifier.padding(horizontal = 16.dp), - text = stringResource(R.string.pro_mode_warning_text), + text = stringResource(R.string.expert_mode_warning_text), style = MaterialTheme.typography.bodyMedium, ) @@ -533,29 +537,30 @@ private fun WarningCard( .align(Alignment.End) .padding(horizontal = 16.dp), onClick = onButtonClick, - enabled = state is ProModeWarningState.Idle, + enabled = state is ExpertModeWarningState.Idle, colors = ButtonDefaults.filledTonalButtonColors( containerColor = MaterialTheme.colorScheme.error, contentColor = MaterialTheme.colorScheme.onError, ), ) { - if (state is ProModeWarningState.Understood) { + if (state is ExpertModeWarningState.Understood) { Icon(imageVector = Icons.Rounded.Check, contentDescription = null) Spacer(modifier = Modifier.width(8.dp)) } val text = when (state) { - is ProModeWarningState.CountingDown -> stringResource( - R.string.pro_mode_warning_understand_button_countdown, + is ExpertModeWarningState.CountingDown -> stringResource( + R.string.expert_mode_warning_understand_button_countdown, state.seconds, ) - ProModeWarningState.Idle -> stringResource( - R.string.pro_mode_warning_understand_button_not_completed, + ExpertModeWarningState.Idle -> stringResource( + R.string.expert_mode_warning_understand_button_not_completed, ) - ProModeWarningState.Understood -> stringResource( - R.string.pro_mode_warning_understand_button_completed, + + ExpertModeWarningState.Understood -> stringResource( + R.string.expert_mode_warning_understand_button_completed, ) } @@ -567,7 +572,7 @@ private fun WarningCard( } @Composable -private fun ProModeStartedCard(modifier: Modifier = Modifier, onStopClick: () -> Unit = {}) { +private fun ExpertModeStartedCard(modifier: Modifier = Modifier, onStopClick: () -> Unit = {}) { OutlinedCard(modifier) { Row( verticalAlignment = Alignment.CenterVertically, @@ -586,7 +591,7 @@ private fun ProModeStartedCard(modifier: Modifier = Modifier, onStopClick: () -> modifier = Modifier .weight(1f) .padding(vertical = 8.dp), - text = stringResource(R.string.pro_mode_service_started), + text = stringResource(R.string.expert_mode_service_started), style = MaterialTheme.typography.titleMedium, ) @@ -598,7 +603,7 @@ private fun ProModeStartedCard(modifier: Modifier = Modifier, onStopClick: () -> contentColor = MaterialTheme.colorScheme.error, ), ) { - Text(stringResource(R.string.pro_mode_stop_service_button)) + Text(stringResource(R.string.expert_mode_stop_service_button)) } Spacer(modifier = Modifier.width(16.dp)) @@ -672,7 +677,7 @@ private fun SetupCard( } @Composable -private fun ProModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit = {}) { +private fun ExpertModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit = {}) { OutlinedCard( modifier = modifier, border = BorderStroke(1.dp, MaterialTheme.colorScheme.primary), @@ -695,7 +700,7 @@ private fun ProModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit Spacer(modifier = Modifier.width(8.dp)) Text( - text = stringResource(R.string.pro_mode_info_card_title), + text = stringResource(R.string.expert_mode_info_card_title), style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurface, ) @@ -704,7 +709,7 @@ private fun ProModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit Spacer(modifier = Modifier.height(8.dp)) Text( - text = stringResource(R.string.pro_mode_info_card_description), + text = stringResource(R.string.expert_mode_info_card_description), style = MaterialTheme.typography.bodyMedium, ) } @@ -718,7 +723,7 @@ private fun ProModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit Icon( imageVector = Icons.Rounded.Close, contentDescription = stringResource( - R.string.pro_mode_info_card_dismiss_content_description, + R.string.expert_mode_info_card_dismiss_content_description, ), tint = MaterialTheme.colorScheme.onSurfaceVariant, ) @@ -731,11 +736,11 @@ private fun ProModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit @Composable private fun Preview() { KeyMapperTheme { - ProModeScreen { + ExpertModeScreen { Content( - warningState = ProModeWarningState.Understood, + warningState = ExpertModeWarningState.Understood, setupState = State.Data( - ProModeState.Stopped( + ExpertModeState.Stopped( isRootGranted = false, shizukuSetupState = ShizukuSetupState.PERMISSION_GRANTED, isNotificationPermissionGranted = true, @@ -755,10 +760,10 @@ private fun Preview() { @Composable private fun PreviewDark() { KeyMapperTheme(darkTheme = true) { - ProModeScreen { + ExpertModeScreen { Content( - warningState = ProModeWarningState.Understood, - setupState = State.Data(ProModeState.Started(isDefaultUsbModeCompatible = true)), + warningState = ExpertModeWarningState.Understood, + setupState = State.Data(ExpertModeState.Started(isDefaultUsbModeCompatible = true)), showInfoCard = false, onInfoCardDismiss = {}, autoStartAtBoot = true, @@ -772,9 +777,9 @@ private fun PreviewDark() { @Composable private fun PreviewCountingDown() { KeyMapperTheme { - ProModeScreen { + ExpertModeScreen { Content( - warningState = ProModeWarningState.CountingDown( + warningState = ExpertModeWarningState.CountingDown( seconds = 5, ), setupState = State.Loading, @@ -791,10 +796,12 @@ private fun PreviewCountingDown() { @Composable private fun PreviewStarted() { KeyMapperTheme { - ProModeScreen { + ExpertModeScreen { Content( - warningState = ProModeWarningState.Understood, - setupState = State.Data(ProModeState.Started(isDefaultUsbModeCompatible = false)), + warningState = ExpertModeWarningState.Understood, + setupState = State.Data( + ExpertModeState.Started(isDefaultUsbModeCompatible = false), + ), showInfoCard = false, onInfoCardDismiss = {}, autoStartAtBoot = false, @@ -808,11 +815,11 @@ private fun PreviewStarted() { @Composable private fun PreviewNotificationPermissionNotGranted() { KeyMapperTheme { - ProModeScreen { + ExpertModeScreen { Content( - warningState = ProModeWarningState.Understood, + warningState = ExpertModeWarningState.Understood, setupState = State.Data( - ProModeState.Stopped( + ExpertModeState.Stopped( isRootGranted = true, shizukuSetupState = ShizukuSetupState.PERMISSION_GRANTED, isNotificationPermissionGranted = false, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupDelegateImpl.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupDelegateImpl.kt similarity index 87% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupDelegateImpl.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupDelegateImpl.kt index 59612c1c81..2d1c491ad9 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupDelegateImpl.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupDelegateImpl.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import dagger.hilt.android.scopes.ViewModelScoped import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider @@ -9,7 +9,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @ViewModelScoped -class ProModeSetupDelegateImpl @Inject constructor( +class ExpertModeSetupDelegateImpl @Inject constructor( @Named("viewmodel") viewModelScope: CoroutineScope, useCase: SystemBridgeSetupUseCase, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupScreen.kt similarity index 80% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupScreen.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupScreen.kt index f55b820352..ea913fbe5f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupScreen.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import android.content.res.Configuration import androidx.compose.animation.core.Animatable @@ -39,7 +39,6 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar -import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect @@ -63,10 +62,10 @@ import io.github.sds100.keymapper.common.utils.State import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupStep @Composable -fun ProModeSetupScreen(viewModel: ProModeSetupViewModel) { +fun ExpertModeSetupScreen(viewModel: ExpertModeSetupViewModel) { val state by viewModel.setupState.collectAsStateWithLifecycle() - ProModeSetupScreen( + ExpertModeSetupScreen( state = state, onStepButtonClick = viewModel::onSetupStepButtonClick, onAssistantClick = viewModel::onSetupAssistantClick, @@ -77,8 +76,8 @@ fun ProModeSetupScreen(viewModel: ProModeSetupViewModel) { @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ProModeSetupScreen( - state: State, +fun ExpertModeSetupScreen( + state: State, onBackClick: () -> Unit = {}, onStepButtonClick: () -> Unit = {}, onAssistantClick: () -> Unit = {}, @@ -87,7 +86,7 @@ fun ProModeSetupScreen( Scaffold( topBar = { TopAppBar( - title = { Text(stringResource(R.string.pro_mode_setup_wizard_title)) }, + title = { Text(stringResource(R.string.expert_mode_setup_wizard_title)) }, navigationIcon = { IconButton(onClick = onBackClick) { Icon( @@ -99,7 +98,7 @@ fun ProModeSetupScreen( ) }, ) { paddingValues -> - ProModeSetupScreenContent( + ExpertModeSetupScreenContent( modifier = Modifier .padding(paddingValues) .fillMaxSize(), @@ -112,9 +111,9 @@ fun ProModeSetupScreen( } @Composable -fun ProModeSetupScreenContent( +fun ExpertModeSetupScreenContent( modifier: Modifier = Modifier, - state: State, + state: State, onAssistantClick: () -> Unit, onWatchTutorialClick: () -> Unit, onStepButtonClick: () -> Unit, @@ -177,14 +176,14 @@ fun ProModeSetupScreenContent( ) { Text( text = stringResource( - R.string.pro_mode_setup_wizard_step_n, + R.string.expert_mode_setup_wizard_step_n, state.data.stepNumber, state.data.stepCount, ), style = MaterialTheme.typography.titleLarge, ) Text( - text = stringResource(R.string.pro_mode_setup_title), + text = stringResource(R.string.expert_mode_setup_title), style = MaterialTheme.typography.titleLarge, ) } @@ -271,7 +270,7 @@ private fun StepContent( verticalAlignment = Alignment.CenterVertically, ) { // TextButton(onClick = onWatchTutorialClick) { -// Text(text = stringResource(R.string.pro_mode_setup_wizard_watch_tutorial_button)) +// Text(text = stringResource(R.string.expert_mode_setup_wizard_watch_tutorial_button)) // } Button( onClick = onButtonClick, @@ -322,16 +321,16 @@ private fun AssistantCheckBoxRow( ) val text = if (isEnabled) { - stringResource(R.string.pro_mode_setup_wizard_use_assistant_description) + stringResource(R.string.expert_mode_setup_wizard_use_assistant_description) } else { stringResource( - R.string.pro_mode_setup_wizard_use_assistant_enable_accessibility_service, + R.string.expert_mode_setup_wizard_use_assistant_enable_accessibility_service, ) } Column { Text( - text = stringResource(R.string.pro_mode_setup_wizard_use_assistant), + text = stringResource(R.string.expert_mode_setup_wizard_use_assistant), style = MaterialTheme.typography.titleMedium, ) @@ -365,88 +364,92 @@ private fun createPreviewStepContent(step: SystemBridgeSetupStep): StepContent { return when (step) { SystemBridgeSetupStep.ACCESSIBILITY_SERVICE -> StepContent( title = stringResource( - R.string.pro_mode_setup_wizard_enable_accessibility_service_title, + R.string.expert_mode_setup_wizard_enable_accessibility_service_title, ), message = stringResource( - R.string.pro_mode_setup_wizard_enable_accessibility_service_description, + R.string.expert_mode_setup_wizard_enable_accessibility_service_description, ), icon = icon, buttonText = stringResource( - R.string.pro_mode_setup_wizard_enable_accessibility_service_button, + R.string.expert_mode_setup_wizard_enable_accessibility_service_button, ), ) SystemBridgeSetupStep.NOTIFICATION_PERMISSION -> StepContent( title = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_title, + R.string.expert_mode_setup_wizard_enable_notification_permission_title, ), message = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_description, + R.string.expert_mode_setup_wizard_enable_notification_permission_description, ), icon = icon, buttonText = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_button, + R.string.expert_mode_setup_wizard_enable_notification_permission_button, ), ) SystemBridgeSetupStep.DEVELOPER_OPTIONS -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_enable_developer_options_title), + title = stringResource( + R.string.expert_mode_setup_wizard_enable_developer_options_title, + ), message = stringResource( - R.string.pro_mode_setup_wizard_enable_developer_options_description, + R.string.expert_mode_setup_wizard_enable_developer_options_description, ), icon = icon, - buttonText = stringResource(R.string.pro_mode_setup_wizard_go_to_settings_button), + buttonText = stringResource(R.string.expert_mode_setup_wizard_go_to_settings_button), ) SystemBridgeSetupStep.WIFI_NETWORK -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_connect_wifi_title), - message = stringResource(R.string.pro_mode_setup_wizard_connect_wifi_description), + title = stringResource(R.string.expert_mode_setup_wizard_connect_wifi_title), + message = stringResource(R.string.expert_mode_setup_wizard_connect_wifi_description), icon = icon, - buttonText = stringResource(R.string.pro_mode_setup_wizard_go_to_settings_button), + buttonText = stringResource(R.string.expert_mode_setup_wizard_go_to_settings_button), ) SystemBridgeSetupStep.WIRELESS_DEBUGGING -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_enable_wireless_debugging_title), + title = stringResource( + R.string.expert_mode_setup_wizard_enable_wireless_debugging_title, + ), message = stringResource( - R.string.pro_mode_setup_wizard_enable_wireless_debugging_description, + R.string.expert_mode_setup_wizard_enable_wireless_debugging_description, ), icon = icon, - buttonText = stringResource(R.string.pro_mode_setup_wizard_go_to_settings_button), + buttonText = stringResource(R.string.expert_mode_setup_wizard_go_to_settings_button), ) SystemBridgeSetupStep.ADB_PAIRING -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_pair_wireless_debugging_title), + title = stringResource(R.string.expert_mode_setup_wizard_pair_wireless_debugging_title), message = stringResource( - R.string.pro_mode_setup_wizard_pair_wireless_debugging_description, + R.string.expert_mode_setup_wizard_pair_wireless_debugging_description, ), icon = icon, - buttonText = stringResource(R.string.pro_mode_setup_wizard_go_to_settings_button), + buttonText = stringResource(R.string.expert_mode_setup_wizard_go_to_settings_button), ) SystemBridgeSetupStep.START_SERVICE -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_start_service_title), - message = stringResource(R.string.pro_mode_setup_wizard_start_service_description), + title = stringResource(R.string.expert_mode_setup_wizard_start_service_title), + message = stringResource(R.string.expert_mode_setup_wizard_start_service_description), icon = icon, - buttonText = stringResource(R.string.pro_mode_root_detected_button_start_service), + buttonText = stringResource(R.string.expert_mode_root_detected_button_start_service), ) SystemBridgeSetupStep.STARTED -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_complete_title), - message = stringResource(R.string.pro_mode_setup_wizard_complete_text), + title = stringResource(R.string.expert_mode_setup_wizard_complete_title), + message = stringResource(R.string.expert_mode_setup_wizard_complete_text), icon = icon, - buttonText = stringResource(R.string.pro_mode_setup_wizard_complete_button), + buttonText = stringResource(R.string.expert_mode_setup_wizard_complete_button), ) } } @Preview(name = "Accessibility Service Step") @Composable -private fun ProModeSetupScreenAccessibilityServicePreview() { +private fun ExpertModeSetupScreenAccessibilityServicePreview() { KeyMapperTheme { val step = SystemBridgeSetupStep.ACCESSIBILITY_SERVICE - ProModeSetupScreen( + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 1, stepCount = 6, step = step, @@ -462,12 +465,12 @@ private fun ProModeSetupScreenAccessibilityServicePreview() { @Preview(name = "Notification Permission Step") @Composable -private fun ProModeSetupScreenNotificationPermissionPreview() { +private fun ExpertModeSetupScreenNotificationPermissionPreview() { KeyMapperTheme { val step = SystemBridgeSetupStep.NOTIFICATION_PERMISSION - ProModeSetupScreen( + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 2, stepCount = 6, step = step, @@ -483,12 +486,12 @@ private fun ProModeSetupScreenNotificationPermissionPreview() { @Preview(name = "Developer Options Step") @Composable -private fun ProModeSetupScreenDeveloperOptionsPreview() { +private fun ExpertModeSetupScreenDeveloperOptionsPreview() { KeyMapperTheme { val step = SystemBridgeSetupStep.DEVELOPER_OPTIONS - ProModeSetupScreen( + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 2, stepCount = 6, step = step, @@ -504,12 +507,12 @@ private fun ProModeSetupScreenDeveloperOptionsPreview() { @Preview(name = "WiFi Network Step") @Composable -private fun ProModeSetupScreenWifiNetworkPreview() { +private fun ExpertModeSetupScreenWifiNetworkPreview() { KeyMapperTheme { val step = SystemBridgeSetupStep.WIFI_NETWORK - ProModeSetupScreen( + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 3, stepCount = 6, step = step, @@ -525,12 +528,12 @@ private fun ProModeSetupScreenWifiNetworkPreview() { @Preview(name = "Wireless Debugging Step") @Composable -private fun ProModeSetupScreenWirelessDebuggingPreview() { +private fun ExpertModeSetupScreenWirelessDebuggingPreview() { KeyMapperTheme { val step = SystemBridgeSetupStep.WIRELESS_DEBUGGING - ProModeSetupScreen( + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 4, stepCount = 6, step = step, @@ -546,12 +549,12 @@ private fun ProModeSetupScreenWirelessDebuggingPreview() { @Preview(name = "ADB Pairing Step", widthDp = 400, heightDp = 400) @Composable -private fun ProModeSetupScreenAdbPairingPreview() { +private fun ExpertModeSetupScreenAdbPairingPreview() { KeyMapperTheme { val step = SystemBridgeSetupStep.ADB_PAIRING - ProModeSetupScreen( + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 5, stepCount = 6, step = step, @@ -567,12 +570,12 @@ private fun ProModeSetupScreenAdbPairingPreview() { @Preview(name = "Start Service Step", uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun ProModeSetupScreenStartServicePreview() { +private fun ExpertModeSetupScreenStartServicePreview() { KeyMapperTheme { val step = SystemBridgeSetupStep.START_SERVICE - ProModeSetupScreen( + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 6, stepCount = 6, step = step, @@ -588,12 +591,12 @@ private fun ProModeSetupScreenStartServicePreview() { @Preview(name = "Started", uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun ProModeSetupScreenStartedPreview() { +private fun ExpertModeSetupScreenStartedPreview() { KeyMapperTheme { val step = SystemBridgeSetupStep.STARTED - ProModeSetupScreen( + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 8, stepCount = 8, step = step, @@ -609,9 +612,9 @@ private fun ProModeSetupScreenStartedPreview() { @Preview(name = "Loading", uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun ProModeSetupScreenLoadingPreview() { +private fun ExpertModeSetupScreenLoadingPreview() { KeyMapperTheme { - ProModeSetupScreen( + ExpertModeSetupScreen( state = State.Loading, ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupState.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupState.kt similarity index 79% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupState.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupState.kt index aae4de9c2a..f920a4aad3 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupState.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupState.kt @@ -1,8 +1,8 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupStep -data class ProModeSetupState( +data class ExpertModeSetupState( val stepNumber: Int, val stepCount: Int, val step: SystemBridgeSetupStep, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupViewModel.kt similarity index 87% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupViewModel.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupViewModel.kt index 745271b4e4..bf204bdb93 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupViewModel.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -9,7 +9,7 @@ import javax.inject.Inject import kotlinx.coroutines.launch @HiltViewModel -class ProModeSetupViewModel @Inject constructor( +class ExpertModeSetupViewModel @Inject constructor( delegate: SystemBridgeSetupDelegate, navigationProvider: NavigationProvider, resourceProvider: ResourceProvider, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt similarity index 83% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeViewModel.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt index 20f67ae4d9..87dfc97116 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -13,6 +13,7 @@ import io.github.sds100.keymapper.base.utils.ui.DialogProvider import io.github.sds100.keymapper.base.utils.ui.ResourceProvider import io.github.sds100.keymapper.common.utils.State import io.github.sds100.keymapper.common.utils.valueOrNull +import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow @@ -25,10 +26,9 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import javax.inject.Inject @HiltViewModel -class ProModeViewModel @Inject constructor( +class ExpertModeViewModel @Inject constructor( private val useCase: SystemBridgeSetupUseCase, resourceProvider: ResourceProvider, dialogProvider: DialogProvider, @@ -43,18 +43,18 @@ class ProModeViewModel @Inject constructor( } @OptIn(ExperimentalCoroutinesApi::class) - val warningState: StateFlow = + val warningState: StateFlow = useCase.isWarningUnderstood .flatMapLatest { isUnderstood -> createWarningStateFlow(isUnderstood) } .stateIn( viewModelScope, SharingStarted.Eagerly, - ProModeWarningState.CountingDown( + ExpertModeWarningState.CountingDown( WARNING_COUNT_DOWN_SECONDS, ), ) - val setupState: StateFlow> = + val setupState: StateFlow> = combine( useCase.isSystemBridgeConnected, useCase.isRootGranted, @@ -82,17 +82,17 @@ class ProModeViewModel @Inject constructor( showInfoCard = true } - private fun createWarningStateFlow(isUnderstood: Boolean): Flow = + private fun createWarningStateFlow(isUnderstood: Boolean): Flow = if (isUnderstood) { - flowOf(ProModeWarningState.Understood) + flowOf(ExpertModeWarningState.Understood) } else { flow { repeat(WARNING_COUNT_DOWN_SECONDS) { - emit(ProModeWarningState.CountingDown(WARNING_COUNT_DOWN_SECONDS - it)) + emit(ExpertModeWarningState.CountingDown(WARNING_COUNT_DOWN_SECONDS - it)) delay(1000L) } - emit(ProModeWarningState.Idle) + emit(ExpertModeWarningState.Idle) } } @@ -139,7 +139,7 @@ class ProModeViewModel @Inject constructor( fun onSetupWithKeyMapperClick() { viewModelScope.launch { - navigate("setup_pro_mode_with_key_mapper", NavDestination.ProModeSetup) + navigate("setup_expert_mode_with_key_mapper", NavDestination.ExpertModeSetup) } } @@ -157,17 +157,17 @@ class ProModeViewModel @Inject constructor( shizukuSetupState: ShizukuSetupState, isNotificationPermissionGranted: Boolean, isSystemBridgeStarting: Boolean, - ): State { + ): State { if (isSystemBridgeConnected) { return State.Data( - ProModeState.Started( + ExpertModeState.Started( isDefaultUsbModeCompatible = - useCase.isCompatibleUsbModeSelected().valueOrNull() ?: false, + useCase.isCompatibleUsbModeSelected().valueOrNull() ?: false, ), ) } else { return State.Data( - ProModeState.Stopped( + ExpertModeState.Stopped( isRootGranted = isRootGranted, shizukuSetupState = shizukuSetupState, isNotificationPermissionGranted = isNotificationPermissionGranted, @@ -178,19 +178,19 @@ class ProModeViewModel @Inject constructor( } } -sealed class ProModeWarningState { - data class CountingDown(val seconds: Int) : ProModeWarningState() - data object Idle : ProModeWarningState() - data object Understood : ProModeWarningState() +sealed class ExpertModeWarningState { + data class CountingDown(val seconds: Int) : ExpertModeWarningState() + data object Idle : ExpertModeWarningState() + data object Understood : ExpertModeWarningState() } -sealed class ProModeState { +sealed class ExpertModeState { data class Stopped( val isRootGranted: Boolean, val shizukuSetupState: ShizukuSetupState, val isNotificationPermissionGranted: Boolean, val isStarting: Boolean, - ) : ProModeState() + ) : ExpertModeState() - data class Started(val isDefaultUsbModeCompatible: Boolean) : ProModeState() + data class Started(val isDefaultUsbModeCompatible: Boolean) : ExpertModeState() } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ShizukuSetupState.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ShizukuSetupState.kt similarity index 66% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/ShizukuSetupState.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/ShizukuSetupState.kt index 1c9814a519..2f06044bc0 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ShizukuSetupState.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ShizukuSetupState.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode enum class ShizukuSetupState { NOT_FOUND, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/StepContent.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/StepContent.kt similarity index 78% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/StepContent.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/StepContent.kt index c8945da19f..8aac069b9b 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/StepContent.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/StepContent.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import androidx.compose.ui.graphics.vector.ImageVector diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarter.kt similarity index 92% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarter.kt index 4c007e0956..1a9bb65c99 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarter.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import android.annotation.SuppressLint import android.os.Build @@ -187,7 +187,8 @@ class SystemBridgeAutoStarter @Inject constructor( private suspend fun handleAutoStartFromPreVersion4() { @Suppress("DEPRECATION") - val upgradedFromPreVersion4 = preferences.get(Keys.handledUpgradeToProMode).first() == null + val upgradedFromPreVersion4 = + preferences.get(Keys.handledUpgradeToExpertMode).first() == null if (!upgradedFromPreVersion4) { return @@ -203,7 +204,7 @@ class SystemBridgeAutoStarter @Inject constructor( ) autoStart(AutoStartType.ROOT) - preferences.set(Keys.handledUpgradeToProMode, true) + preferences.set(Keys.handledUpgradeToExpertMode, true) preferences.set(Keys.keyEventActionsUseSystemBridge, true) return } @@ -217,7 +218,7 @@ class SystemBridgeAutoStarter @Inject constructor( ) autoStart(AutoStartType.SHIZUKU) - preferences.set(Keys.handledUpgradeToProMode, true) + preferences.set(Keys.handledUpgradeToExpertMode, true) preferences.set(Keys.keyEventActionsUseSystemBridge, true) return } @@ -236,7 +237,7 @@ class SystemBridgeAutoStarter @Inject constructor( Timber.i("Auto starting system bridge with ADB") showAutoStartNotification( getString( - R.string.pro_mode_setup_notification_auto_start_system_bridge_adb_text, + R.string.expert_mode_setup_notification_auto_start_system_bridge_adb_text, ), ) @@ -247,7 +248,7 @@ class SystemBridgeAutoStarter @Inject constructor( Timber.i("Auto starting system bridge with Shizuku") showAutoStartNotification( getString( - R.string.pro_mode_setup_notification_auto_start_system_bridge_shizuku_text, + R.string.expert_mode_setup_notification_auto_start_system_bridge_shizuku_text, ), ) connectionManager.startWithShizuku() @@ -257,7 +258,7 @@ class SystemBridgeAutoStarter @Inject constructor( Timber.i("Auto starting system bridge with root") showAutoStartNotification( getString( - R.string.pro_mode_setup_notification_auto_start_system_bridge_root_text, + R.string.expert_mode_setup_notification_auto_start_system_bridge_root_text, ), ) connectionManager.startWithRoot() @@ -300,7 +301,7 @@ class SystemBridgeAutoStarter @Inject constructor( private suspend fun isAutoStartEnabled(): Boolean { return preferences.get(Keys.isSystemBridgeKeepAliveEnabled) - .map { it ?: PreferenceDefaults.PRO_MODE_KEEP_ALIVE } + .map { it ?: PreferenceDefaults.EXPERT_MODE_KEEP_ALIVE } .first() } @@ -310,7 +311,7 @@ class SystemBridgeAutoStarter @Inject constructor( channel = CHANNEL_SETUP_ASSISTANT, title = getString(R.string.system_bridge_died_notification_title), text = text, - icon = R.drawable.pro_mode, + icon = R.drawable.offline_bolt_24px, showOnLockscreen = true, onGoing = false, priority = NotificationCompat.PRIORITY_MAX, @@ -326,10 +327,12 @@ class SystemBridgeAutoStarter @Inject constructor( private fun showAutoStartNotification(text: String) { val model = NotificationModel( id = ID_SYSTEM_BRIDGE_STATUS, - title = getString(R.string.pro_mode_setup_notification_auto_start_system_bridge_title), + title = getString( + R.string.expert_mode_setup_notification_auto_start_system_bridge_title, + ), text = text, channel = CHANNEL_SETUP_ASSISTANT, - icon = R.drawable.pro_mode, + icon = R.drawable.offline_bolt_24px, priority = NotificationCompat.PRIORITY_MAX, onGoing = true, showIndeterminateProgress = true, @@ -343,11 +346,13 @@ class SystemBridgeAutoStarter @Inject constructor( val model = NotificationModel( id = ID_SYSTEM_BRIDGE_STATUS, title = getString( - R.string.pro_mode_setup_notification_start_system_bridge_failed_title, + R.string.expert_mode_setup_notification_start_system_bridge_failed_title, + ), + text = getString( + R.string.expert_mode_setup_notification_start_system_bridge_failed_text, ), - text = getString(R.string.pro_mode_setup_notification_start_system_bridge_failed_text), channel = CHANNEL_SETUP_ASSISTANT, - icon = R.drawable.pro_mode, + icon = R.drawable.offline_bolt_24px, onGoing = false, showOnLockscreen = false, autoCancel = true, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupAssistantController.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupAssistantController.kt similarity index 89% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupAssistantController.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupAssistantController.kt index 0df53276a4..0589a80761 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupAssistantController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupAssistantController.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import android.app.ActivityManager import android.os.Build @@ -85,12 +85,12 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( private val activityManager: ActivityManager = accessibilityService.getSystemService()!! private val isInteractive: StateFlow = - preferenceRepository.get(Keys.isProModeInteractiveSetupAssistantEnabled) - .map { it ?: PreferenceDefaults.PRO_MODE_INTERACTIVE_SETUP_ASSISTANT } + preferenceRepository.get(Keys.isExpertModeInteractiveSetupAssistantEnabled) + .map { it ?: PreferenceDefaults.EXPERT_MODE_INTERACTIVE_SETUP_ASSISTANT } .stateIn( coroutineScope, SharingStarted.Eagerly, - PreferenceDefaults.PRO_MODE_INTERACTIVE_SETUP_ASSISTANT, + PreferenceDefaults.EXPERT_MODE_INTERACTIVE_SETUP_ASSISTANT, ) private var interactionStep: InteractionStep? = null @@ -203,11 +203,13 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( stopInteracting() showNotification( - getString(R.string.pro_mode_setup_notification_invalid_pairing_code_title), - getString(R.string.pro_mode_setup_notification_invalid_pairing_code_text), + getString(R.string.expert_mode_setup_notification_invalid_pairing_code_title), + getString(R.string.expert_mode_setup_notification_invalid_pairing_code_text), actions = listOf( KMNotificationAction.RemoteInput.PairingCode to - getString(R.string.pro_mode_setup_notification_action_input_pairing_code), + getString( + R.string.expert_mode_setup_notification_action_input_pairing_code, + ), ), ) } @@ -226,8 +228,8 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( } else { Timber.e("Failed to start system bridge after pairing.") showNotification( - getString(R.string.pro_mode_setup_notification_start_system_bridge_failed_title), - getString(R.string.pro_mode_setup_notification_start_system_bridge_failed_text), + getString(R.string.expert_mode_setup_notification_start_system_bridge_failed_title), + getString(R.string.expert_mode_setup_notification_start_system_bridge_failed_text), onClickAction = KMNotificationAction.Activity.MainActivity( BaseMainActivity.ACTION_START_SYSTEM_BRIDGE, ), @@ -270,7 +272,7 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( channel = NotificationController.Companion.CHANNEL_SETUP_ASSISTANT, title = title, text = text, - icon = R.drawable.pro_mode, + icon = R.drawable.offline_bolt_24px, onGoing = false, showOnLockscreen = false, autoCancel = true, @@ -298,15 +300,15 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( when (step) { SystemBridgeSetupStep.DEVELOPER_OPTIONS -> { showNotification( - getString(R.string.pro_mode_setup_notification_tap_build_number_title), - getString(R.string.pro_mode_setup_notification_tap_build_number_text), + getString(R.string.expert_mode_setup_notification_tap_build_number_title), + getString(R.string.expert_mode_setup_notification_tap_build_number_text), ) } SystemBridgeSetupStep.ADB_PAIRING -> { showNotification( - getString(R.string.pro_mode_setup_notification_pairing_title), - getString(R.string.pro_mode_setup_notification_pairing_text), + getString(R.string.expert_mode_setup_notification_pairing_title), + getString(R.string.expert_mode_setup_notification_pairing_text), ) interactionStep = InteractionStep.PAIR_DEVICE @@ -328,15 +330,15 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( showNotification( title = getString( - R.string.pro_mode_setup_notification_pairing_button_not_found_title, + R.string.expert_mode_setup_notification_pairing_button_not_found_title, ), text = getString( - R.string.pro_mode_setup_notification_pairing_button_not_found_text, + R.string.expert_mode_setup_notification_pairing_button_not_found_text, ), actions = listOf( KMNotificationAction.RemoteInput.PairingCode to getString( - R.string.pro_mode_setup_notification_action_input_pairing_code, + R.string.expert_mode_setup_notification_action_input_pairing_code, ), ), ) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupDelegate.kt similarity index 74% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupDelegate.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupDelegate.kt index 484d31362d..c4ba397200 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupDelegate.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Accessibility @@ -21,14 +21,13 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn - abstract class SystemBridgeSetupDelegateImpl( val viewModelScope: CoroutineScope, private val useCase: SystemBridgeSetupUseCase, private val resourceProvider: ResourceProvider, ) : SystemBridgeSetupDelegate, ResourceProvider by resourceProvider { - override val setupState: StateFlow> = + override val setupState: StateFlow> = combine( useCase.nextSetupStep, useCase.isSetupAssistantEnabled, @@ -53,7 +52,6 @@ abstract class SystemBridgeSetupDelegateImpl( SystemBridgeSetupStep.WIRELESS_DEBUGGING -> useCase.enableWirelessDebugging() SystemBridgeSetupStep.ADB_PAIRING -> useCase.pairWirelessAdb() SystemBridgeSetupStep.START_SERVICE -> useCase.startSystemBridgeWithAdb() - SystemBridgeSetupStep.STARTED -> onFinishClick() } } @@ -68,105 +66,105 @@ abstract class SystemBridgeSetupDelegateImpl( return when (step) { SystemBridgeSetupStep.ACCESSIBILITY_SERVICE -> StepContent( title = getString( - R.string.pro_mode_setup_wizard_enable_accessibility_service_title, + R.string.expert_mode_setup_wizard_enable_accessibility_service_title, ), message = getString( - R.string.pro_mode_setup_wizard_enable_accessibility_service_description, + R.string.expert_mode_setup_wizard_enable_accessibility_service_description, ), icon = Icons.Rounded.Accessibility, buttonText = getString( - R.string.pro_mode_setup_wizard_enable_accessibility_service_button, + R.string.expert_mode_setup_wizard_enable_accessibility_service_button, ), ) SystemBridgeSetupStep.NOTIFICATION_PERMISSION -> StepContent( title = getString( - R.string.pro_mode_setup_wizard_enable_notification_permission_title, + R.string.expert_mode_setup_wizard_enable_notification_permission_title, ), message = getString( - R.string.pro_mode_setup_wizard_enable_notification_permission_description, + R.string.expert_mode_setup_wizard_enable_notification_permission_description, ), icon = Icons.Rounded.Notifications, buttonText = getString( - R.string.pro_mode_setup_wizard_enable_notification_permission_button, + R.string.expert_mode_setup_wizard_enable_notification_permission_button, ), ) SystemBridgeSetupStep.DEVELOPER_OPTIONS -> StepContent( title = getString( - R.string.pro_mode_setup_wizard_enable_developer_options_title, + R.string.expert_mode_setup_wizard_enable_developer_options_title, ), message = getString( - R.string.pro_mode_setup_wizard_enable_developer_options_description, + R.string.expert_mode_setup_wizard_enable_developer_options_description, ), icon = Icons.Rounded.Build, buttonText = getString( - R.string.pro_mode_setup_wizard_go_to_settings_button, + R.string.expert_mode_setup_wizard_go_to_settings_button, ), ) SystemBridgeSetupStep.WIFI_NETWORK -> StepContent( title = getString( - R.string.pro_mode_setup_wizard_connect_wifi_title, + R.string.expert_mode_setup_wizard_connect_wifi_title, ), message = getString( - R.string.pro_mode_setup_wizard_connect_wifi_description, + R.string.expert_mode_setup_wizard_connect_wifi_description, ), icon = KeyMapperIcons.SignalWifiNotConnected, buttonText = getString( - R.string.pro_mode_setup_wizard_go_to_settings_button, + R.string.expert_mode_setup_wizard_go_to_settings_button, ), ) SystemBridgeSetupStep.WIRELESS_DEBUGGING -> StepContent( title = getString( - R.string.pro_mode_setup_wizard_enable_wireless_debugging_title, + R.string.expert_mode_setup_wizard_enable_wireless_debugging_title, ), message = getString( - R.string.pro_mode_setup_wizard_enable_wireless_debugging_description, + R.string.expert_mode_setup_wizard_enable_wireless_debugging_description, ), icon = Icons.Rounded.BugReport, buttonText = getString( - R.string.pro_mode_setup_wizard_go_to_settings_button, + R.string.expert_mode_setup_wizard_go_to_settings_button, ), ) SystemBridgeSetupStep.ADB_PAIRING -> StepContent( title = getString( - R.string.pro_mode_setup_wizard_pair_wireless_debugging_title, + R.string.expert_mode_setup_wizard_pair_wireless_debugging_title, ), message = getString( - R.string.pro_mode_setup_wizard_pair_wireless_debugging_description, + R.string.expert_mode_setup_wizard_pair_wireless_debugging_description, ), icon = Icons.Rounded.Link, buttonText = getString( - R.string.pro_mode_setup_wizard_go_to_settings_button, + R.string.expert_mode_setup_wizard_go_to_settings_button, ), ) SystemBridgeSetupStep.START_SERVICE -> StepContent( title = getString( - R.string.pro_mode_setup_wizard_start_service_title, + R.string.expert_mode_setup_wizard_start_service_title, ), message = getString( - R.string.pro_mode_setup_wizard_start_service_description, + R.string.expert_mode_setup_wizard_start_service_description, ), icon = Icons.Rounded.PlayArrow, buttonText = getString( - R.string.pro_mode_root_detected_button_start_service, + R.string.expert_mode_root_detected_button_start_service, ), ) SystemBridgeSetupStep.STARTED -> StepContent( title = getString( - R.string.pro_mode_setup_wizard_complete_title, + R.string.expert_mode_setup_wizard_complete_title, ), message = getString( - R.string.pro_mode_setup_wizard_complete_text, + R.string.expert_mode_setup_wizard_complete_text, ), icon = Icons.Rounded.CheckCircleOutline, buttonText = getString( - R.string.pro_mode_setup_wizard_complete_button, + R.string.expert_mode_setup_wizard_complete_button, ), ) } @@ -176,7 +174,7 @@ abstract class SystemBridgeSetupDelegateImpl( step: SystemBridgeSetupStep, isSetupAssistantUserEnabled: Boolean, isStarting: Boolean, - ): State.Data { + ): State.Data { // Uncheck the setup assistant if the accessibility service is disabled since it is // required for the setup assistant to work val isSetupAssistantChecked = if (step == SystemBridgeSetupStep.ACCESSIBILITY_SERVICE) { @@ -188,7 +186,7 @@ abstract class SystemBridgeSetupDelegateImpl( val stepContent = getStepContent(step) return State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = step.stepIndex + 1, stepCount = SystemBridgeSetupStep.entries.size, step = step, @@ -204,7 +202,7 @@ abstract class SystemBridgeSetupDelegateImpl( } interface SystemBridgeSetupDelegate { - val setupState: StateFlow> + val setupState: StateFlow> fun onSetupStepButtonClick() fun onSetupAssistantClick() fun getStepContent(step: SystemBridgeSetupStep): StepContent diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCase.kt similarity index 93% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCase.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCase.kt index 2746c1eb88..c8ea53a633 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCase.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import android.os.Build import android.os.Process @@ -26,7 +26,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn @@ -54,7 +53,7 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( } override val isWarningUnderstood: Flow = - preferences.get(Keys.isProModeWarningUnderstood).map { it ?: false } + preferences.get(Keys.isExpertModeWarningUnderstood).map { it ?: false } private val isAdbAutoStartAllowed: Flow = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { @@ -71,18 +70,18 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( } override fun onUnderstoodWarning() { - preferences.set(Keys.isProModeWarningUnderstood, true) + preferences.set(Keys.isExpertModeWarningUnderstood, true) } override val isSetupAssistantEnabled: Flow = - preferences.get(Keys.isProModeInteractiveSetupAssistantEnabled).map { - it ?: PreferenceDefaults.PRO_MODE_INTERACTIVE_SETUP_ASSISTANT + preferences.get(Keys.isExpertModeInteractiveSetupAssistantEnabled).map { + it ?: PreferenceDefaults.EXPERT_MODE_INTERACTIVE_SETUP_ASSISTANT } override fun toggleSetupAssistant() { - preferences.update(Keys.isProModeInteractiveSetupAssistantEnabled) { + preferences.update(Keys.isExpertModeInteractiveSetupAssistantEnabled) { if (it == null) { - !PreferenceDefaults.PRO_MODE_INTERACTIVE_SETUP_ASSISTANT + !PreferenceDefaults.EXPERT_MODE_INTERACTIVE_SETUP_ASSISTANT } else { !it } @@ -204,20 +203,20 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( } override fun isInfoDismissed(): Boolean { - return preferences.get(Keys.isProModeInfoDismissed).map { it ?: false }.firstBlocking() + return preferences.get(Keys.isExpertModeInfoDismissed).map { it ?: false }.firstBlocking() } override fun dismissInfo() { - preferences.set(Keys.isProModeInfoDismissed, true) + preferences.set(Keys.isExpertModeInfoDismissed, true) } override val isAutoStartBootEnabled: Flow = preferences.get(Keys.isSystemBridgeKeepAliveEnabled) - .map { it ?: PreferenceDefaults.PRO_MODE_KEEP_ALIVE } + .map { it ?: PreferenceDefaults.EXPERT_MODE_KEEP_ALIVE } override fun toggleAutoStartBoot() { preferences.update(Keys.isSystemBridgeKeepAliveEnabled) { - !(it ?: PreferenceDefaults.PRO_MODE_KEEP_ALIVE) + !(it ?: PreferenceDefaults.EXPERT_MODE_KEEP_ALIVE) } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/home/HomeKeyMapListScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/home/HomeKeyMapListScreen.kt index 4118c107f2..c5b71421a0 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/home/HomeKeyMapListScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/home/HomeKeyMapListScreen.kt @@ -135,11 +135,11 @@ fun HomeKeyMapListScreen( sheetState = sheetState, onDismissRequest = viewModel::dismissFixKeyEventActionBottomSheet, onEnableAccessibilityServiceClick = viewModel::onEnableAccessibilityServiceClick, - onEnableProModeClick = viewModel::onEnableProModeForKeyEventActionsClick, + onEnableExpertModeClick = viewModel::onEnableExpertModeForKeyEventActionsClick, onEnableInputMethodClick = viewModel::onEnableImeClick, onChooseInputMethodClick = viewModel::onChooseImeClick, onDoneClick = viewModel::dismissFixKeyEventActionBottomSheet, - onSelectProMode = viewModel::onSelectProMode, + onSelectExpertMode = viewModel::onSelectExpertMode, onSelectInputMethod = viewModel::onSelectInputMethod, onAutoSwitchImeCheckedChange = viewModel::onAutoSwitchImeCheckedChange, ) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/keymaps/DisplayKeyMapUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/keymaps/DisplayKeyMapUseCase.kt index 35be4f212d..b21b126d86 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/keymaps/DisplayKeyMapUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/keymaps/DisplayKeyMapUseCase.kt @@ -163,6 +163,7 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( TriggerError.CANT_DETECT_IN_PHONE_CALL -> fixError( KMError.CantDetectKeyEventsInPhoneCall, ) + TriggerError.ASSISTANT_TRIGGER_NOT_PURCHASED -> fixError( ProductNotPurchased( ProductId.ASSISTANT_TRIGGER, @@ -170,6 +171,7 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( ) TriggerError.DPAD_IME_NOT_SELECTED -> fixError(KMError.DpadTriggerImeNotSelected) + TriggerError.FLOATING_BUTTONS_NOT_PURCHASED -> fixError( ProductNotPurchased( ProductId.FLOATING_BUTTONS, @@ -177,7 +179,9 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( ) TriggerError.PURCHASE_VERIFICATION_FAILED -> purchasingManager.refresh() + TriggerError.SYSTEM_BRIDGE_DISCONNECTED -> fixError(SystemBridgeError.Disconnected) + TriggerError.EVDEV_DEVICE_NOT_FOUND, TriggerError.FLOATING_BUTTON_DELETED, TriggerError.SYSTEM_BRIDGE_UNSUPPORTED, @@ -200,18 +204,24 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( override suspend fun fixError(error: KMError) { when (error) { is KMError.AppDisabled -> packageManagerAdapter.enableApp(error.packageName) + is KMError.AppNotFound -> packageManagerAdapter.downloadApp(error.packageName) + KMError.NoCompatibleImeChosen -> keyMapperImeHelper.chooseCompatibleInputMethod().otherwise { inputMethodAdapter.showImePicker(fromForeground = true) } KMError.NoCompatibleImeEnabled -> keyMapperImeHelper.enableCompatibleInputMethods() + is ImeDisabled -> switchImeInterface.enableIme(error.ime.id) + is PermissionDenied -> permissionAdapter.request(error.permission) + is KMError.ShizukuNotStarted -> packageManagerAdapter.openApp( ShizukuUtils.SHIZUKU_PACKAGE, ) + is KMError.CantDetectKeyEventsInPhoneCall -> { if (!keyMapperImeHelper.isCompatibleImeEnabled()) { keyMapperImeHelper.enableCompatibleInputMethods() @@ -227,7 +237,7 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( is SystemBridgeError.Disconnected -> navigationProvider.navigate( "fix_system_bridge", - NavDestination.ProMode, + NavDestination.ExpertMode, ) is KMError.DpadTriggerImeNotSelected -> { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt index 33ced838b9..2f4ffe66d3 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt @@ -49,8 +49,8 @@ class OnboardingTipDelegateImpl @Inject constructor( private const val PARALLEL_TRIGGER_TIP_ID = "parallel_trigger_tip" private const val SEQUENCE_TRIGGER_TIP_ID = "sequence_trigger_tip" private const val TRIGGER_CONSTRAINTS_TIP_ID = "trigger_constraints_tip" - const val CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID = "caps_lock_pro_mode_compatibility_tip" - const val VOLUME_BUTTONS_PRO_MODE_TIP_ID = "volume_buttons_pro_mode_tip" + const val CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID = "caps_lock_expert_mode_compatibility_tip" + const val VOLUME_BUTTONS_PRO_MODE_TIP_ID = "volume_buttons_expert_mode_tip" const val SCREEN_PINNING_TIP_ID = "screen_pinning_tip" const val IME_DETECTION_TIP_ID = "ime_detection_tip" const val RINGER_MODE_TIP_ID = "ringer_mode_tip" @@ -74,13 +74,13 @@ class OnboardingTipDelegateImpl @Inject constructor( false, ) - private var shownCapsLockProModeTip: Boolean by PrefDelegate( - Keys.shownCapsLockProModeTip, + private var shownCapsLockExpertModeTip: Boolean by PrefDelegate( + Keys.shownCapsLockExpertModeTip, false, ) - private var shownVolumeButtonsProModeTip: Boolean by PrefDelegate( - Keys.shownVolumeButtonsProModeTip, + private var shownVolumeButtonsExpertModeTip: Boolean by PrefDelegate( + Keys.shownVolumeButtonsExpertModeTip, false, ) @@ -141,13 +141,13 @@ class OnboardingTipDelegateImpl @Inject constructor( when (tipId) { RINGER_MODE_TIP_ID -> { viewModelScope.launch { - navigate("ringer_mode_tip_pro_mode", NavDestination.ProMode) + navigate("ringer_mode_tip_expert_mode", NavDestination.ExpertMode) } } VOLUME_BUTTONS_PRO_MODE_TIP_ID -> { viewModelScope.launch { - navigate("volume_buttons_pro_mode_tip", NavDestination.ProMode) + navigate("volume_buttons_expert_mode_tip", NavDestination.ExpertMode) } } } @@ -188,8 +188,8 @@ class OnboardingTipDelegateImpl @Inject constructor( showPowerButtonEmergencyTip -> { val tipModel = OnboardingTipModel( id = POWER_BUTTON_EMERGENCY_TIP_ID, - title = getString(R.string.pro_mode_emergency_tip_title), - message = getString(R.string.pro_mode_emergency_tip_text), + title = getString(R.string.expert_mode_emergency_tip_title), + message = getString(R.string.expert_mode_emergency_tip_text), isDismissable = false, ) @@ -218,24 +218,24 @@ class OnboardingTipDelegateImpl @Inject constructor( triggerTip.value = tipModel } - hasVolumeKey && !shownVolumeButtonsProModeTip -> { + hasVolumeKey && !shownVolumeButtonsExpertModeTip -> { val tip = OnboardingTipModel( id = VOLUME_BUTTONS_PRO_MODE_TIP_ID, - title = getString(R.string.tip_volume_buttons_pro_mode_title), - message = getString(R.string.tip_volume_buttons_pro_mode_text), + title = getString(R.string.tip_volume_buttons_expert_mode_title), + message = getString(R.string.tip_volume_buttons_expert_mode_text), isDismissable = true, - buttonText = getString(R.string.tip_volume_buttons_pro_mode_button), + buttonText = getString(R.string.tip_volume_buttons_expert_mode_button), ) triggerTip.value = tip } - hasCapsLockKey && !shownCapsLockProModeTip -> { + hasCapsLockKey && !shownCapsLockExpertModeTip -> { val tip = OnboardingTipModel( id = CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID, - title = getString(R.string.tip_caps_lock_pro_mode_title), - message = getString(R.string.tip_caps_lock_pro_mode_text), + title = getString(R.string.tip_caps_lock_expert_mode_title), + message = getString(R.string.tip_caps_lock_expert_mode_text), isDismissable = true, - buttonText = getString(R.string.tip_caps_lock_pro_mode_button), + buttonText = getString(R.string.tip_caps_lock_expert_mode_button), ) triggerTip.value = tip } @@ -293,11 +293,11 @@ class OnboardingTipDelegateImpl @Inject constructor( } CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID -> { - shownCapsLockProModeTip = true + shownCapsLockExpertModeTip = true } VOLUME_BUTTONS_PRO_MODE_TIP_ID -> { - shownVolumeButtonsProModeTip = true + shownVolumeButtonsExpertModeTip = true } SCREEN_PINNING_TIP_ID -> { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/onboarding/SetupAccessibilityServiceDialog.kt b/base/src/main/java/io/github/sds100/keymapper/base/onboarding/SetupAccessibilityServiceDialog.kt index 72e8750750..272d514f0f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/onboarding/SetupAccessibilityServiceDialog.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/onboarding/SetupAccessibilityServiceDialog.kt @@ -167,7 +167,7 @@ private fun CantFindAccessibilitySettingsDialog( }, confirmButton = { TextButton(onClick = onOpenGuide) { - Text(stringResource(R.string.pos_start_service_with_pro_mode)) + Text(stringResource(R.string.pos_start_service_with_expert_mode)) } }, dismissButton = { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsScreen.kt index 370b194a07..9cbcb0ed7a 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.material.icons.outlined.Android import androidx.compose.material.icons.outlined.BugReport import androidx.compose.material.icons.outlined.FindInPage import androidx.compose.material.icons.outlined.Gamepad +import androidx.compose.material.icons.outlined.OfflineBolt import androidx.compose.material.icons.rounded.Code import androidx.compose.material.icons.rounded.Construction import androidx.compose.material.icons.rounded.Devices @@ -69,14 +70,13 @@ import io.github.sds100.keymapper.base.utils.ui.compose.RadioButtonText import io.github.sds100.keymapper.base.utils.ui.compose.SwitchPreferenceCompose import io.github.sds100.keymapper.base.utils.ui.compose.icons.FolderManaged import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIcon import io.github.sds100.keymapper.base.utils.ui.compose.icons.WandStars import io.github.sds100.keymapper.common.utils.BuildUtils import io.github.sds100.keymapper.common.utils.Constants import io.github.sds100.keymapper.system.files.FileUtils import kotlinx.coroutines.launch -private val isProModeSupported = Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API +private val isExpertModeSupported = Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API private val isAutoSwitchImeSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R @Composable @@ -149,9 +149,9 @@ fun SettingsScreen(modifier: Modifier = Modifier, viewModel: SettingsViewModel) onThemeSelected = viewModel::onThemeSelected, onPauseResumeNotificationClick = viewModel::onPauseResumeNotificationClick, onDefaultOptionsClick = viewModel::onDefaultOptionsClick, - onProModeClick = { - if (isProModeSupported) { - viewModel.onProModeClick() + onExpertModeClick = { + if (isExpertModeSupported) { + viewModel.onExpertModeClick() } else { scope.launch { snackbarHostState.showSnackbar( @@ -245,7 +245,7 @@ private fun Content( onPauseResumeNotificationClick: () -> Unit = { }, onDefaultOptionsClick: () -> Unit = { }, onAutomaticBackupClick: () -> Unit = { }, - onProModeClick: () -> Unit = { }, + onExpertModeClick: () -> Unit = { }, onAutomaticChangeImeClick: () -> Unit = { }, onForceVibrateToggled: (Boolean) -> Unit = { }, onLoggingToggled: (Boolean) -> Unit = { }, @@ -253,7 +253,7 @@ private fun Content( onShareLogcatClick: () -> Unit = { }, onHideHomeScreenAlertsToggled: (Boolean) -> Unit = { }, onShowDeviceDescriptorsToggled: (Boolean) -> Unit = { }, - onKeyEventActionMethodSelected: (isProModeSelected: Boolean) -> Unit = {}, + onKeyEventActionMethodSelected: (isExpertModeSelected: Boolean) -> Unit = {}, ) { Column( modifier @@ -359,23 +359,23 @@ private fun Content( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), - isProModeSelected = state.keyEventActionsUseSystemBridge, + isExpertModeSelected = state.keyEventActionsUseSystemBridge, onSelected = onKeyEventActionMethodSelected, ) OptionPageButton( - title = stringResource(R.string.title_pref_pro_mode), - text = if (isProModeSupported) { - stringResource(R.string.summary_pref_pro_mode) + title = stringResource(R.string.title_pref_expert_mode), + text = if (isExpertModeSupported) { + stringResource(R.string.summary_pref_expert_mode) } else { stringResource( R.string.error_sdk_version_too_low, BuildUtils.getSdkVersionName(Constants.SYSTEM_BRIDGE_MIN_API), ) }, - icon = KeyMapperIcons.ProModeIcon, - onClick = onProModeClick, - enabled = isProModeSupported, + icon = Icons.Outlined.OfflineBolt, + onClick = onExpertModeClick, + enabled = isExpertModeSupported, ) OptionPageButton( @@ -428,13 +428,13 @@ private fun Content( @Composable private fun KeyEventActionMethodRow( modifier: Modifier = Modifier, - isProModeSelected: Boolean, - onSelected: (isProModeSelected: Boolean) -> Unit, + isExpertModeSelected: Boolean, + onSelected: (isExpertModeSelected: Boolean) -> Unit, ) { Column(modifier) { val buttonStates = listOf( false to stringResource(R.string.fix_key_event_action_input_method_title), - true to stringResource(R.string.pro_mode_app_bar_title), + true to stringResource(R.string.expert_mode_app_bar_title), ) Text( @@ -454,11 +454,11 @@ private fun KeyEventActionMethodRow( horizontalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(4.dp), ) { - for ((isProMode, text) in buttonStates) { + for ((isExpertMode, text) in buttonStates) { RadioButtonText( text = text, - isSelected = isProMode == isProModeSelected, - onSelected = { onSelected(isProMode) }, + isSelected = isExpertMode == isExpertModeSelected, + onSelected = { onSelected(isExpertMode) }, ) } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt index 2cf751ad38..7628eb1edc 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt @@ -171,9 +171,9 @@ class SettingsViewModel @Inject constructor( useCase.requestRootPermission() } - fun onProModeClick() { + fun onExpertModeClick() { viewModelScope.launch { - navigate("pro_mode_settings", NavDestination.ProMode) + navigate("expert_mode_settings", NavDestination.ExpertMode) } } @@ -323,9 +323,9 @@ class SettingsViewModel @Inject constructor( } } - fun onKeyEventActionMethodSelected(isProModeSelected: Boolean) { + fun onKeyEventActionMethodSelected(isExpertModeSelected: Boolean) { viewModelScope.launch { - useCase.setPreference(Keys.keyEventActionsUseSystemBridge, isProModeSelected) + useCase.setPreference(Keys.keyEventActionsUseSystemBridge, isExpertModeSelected) } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt index 2a9d5c9b2d..2035cf216e 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt @@ -17,12 +17,12 @@ import io.github.sds100.keymapper.base.constraints.DetectConstraintsUseCaseImpl import io.github.sds100.keymapper.base.detection.DetectKeyMapsUseCaseImpl import io.github.sds100.keymapper.base.detection.KeyMapDetectionController import io.github.sds100.keymapper.base.detection.TriggerKeyMapFromOtherAppsController +import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupAssistantController import io.github.sds100.keymapper.base.input.InputEventDetectionSource import io.github.sds100.keymapper.base.input.InputEventHub import io.github.sds100.keymapper.base.keymaps.FingerprintGesturesSupportedUseCase import io.github.sds100.keymapper.base.keymaps.PauseKeyMapsUseCase import io.github.sds100.keymapper.base.keymaps.TriggerKeyMapEvent -import io.github.sds100.keymapper.base.promode.SystemBridgeSetupAssistantController import io.github.sds100.keymapper.base.system.inputmethod.AutoSwitchImeController import io.github.sds100.keymapper.base.trigger.RecordTriggerController import io.github.sds100.keymapper.common.utils.Constants @@ -461,7 +461,9 @@ abstract class BaseAccessibilityServiceController( } is AccessibilityServiceEvent.HideKeyboard -> service.hideKeyboard() + is AccessibilityServiceEvent.ShowKeyboard -> service.showKeyboard() + is AccessibilityServiceEvent.ChangeIme -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { service.switchIme(event.imeId) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/notifications/NotificationController.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/notifications/NotificationController.kt index 3fa824beee..31054d496d 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/notifications/NotificationController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/notifications/NotificationController.kt @@ -106,7 +106,7 @@ class NotificationController @Inject constructor( manageNotifications.createChannel( NotificationChannelModel( id = CHANNEL_SETUP_ASSISTANT, - name = getString(R.string.pro_mode_setup_assistant_notification_channel), + name = getString(R.string.expert_mode_setup_assistant_notification_channel), importance = NotificationManagerCompat.IMPORTANCE_MAX, ), ) @@ -162,16 +162,21 @@ class NotificationController @Inject constructor( manageNotifications.onActionClick.onEach { actionId -> when (actionId) { KMNotificationAction.IntentAction.RESUME_KEY_MAPS -> pauseMappings.resume() + KMNotificationAction.IntentAction.PAUSE_KEY_MAPS -> pauseMappings.pause() + KMNotificationAction.IntentAction.DISMISS_TOGGLE_KEY_MAPS_NOTIFICATION -> manageNotifications.dismiss(ID_TOGGLE_MAPPINGS) KMNotificationAction.IntentAction.STOP_ACCESSIBILITY_SERVICE -> controlAccessibilityService.stopService() + KMNotificationAction.IntentAction.START_ACCESSIBILITY_SERVICE -> attemptStartAccessibilityService() + KMNotificationAction.IntentAction.RESTART_ACCESSIBILITY_SERVICE -> attemptRestartAccessibilityService() + KMNotificationAction.IntentAction.TOGGLE_KEY_MAPPER_IME -> toggleCompatibleIme.toggle() .onSuccess { @@ -181,6 +186,7 @@ class NotificationController @Inject constructor( } KMNotificationAction.IntentAction.SHOW_KEYBOARD -> hideInputMethod.show() + else -> Unit // Ignore other notification actions } }.launchIn(coroutineScope) @@ -416,10 +422,10 @@ class NotificationController @Inject constructor( private fun showSystemBridgeStartedNotification() { val model = NotificationModel( id = ID_SYSTEM_BRIDGE_STATUS, - title = getString(R.string.pro_mode_setup_notification_system_bridge_started_title), - text = getString(R.string.pro_mode_setup_notification_system_bridge_started_text), + title = getString(R.string.expert_mode_setup_notification_system_bridge_started_title), + text = getString(R.string.expert_mode_setup_notification_system_bridge_started_text), channel = CHANNEL_SETUP_ASSISTANT, - icon = R.drawable.pro_mode, + icon = R.drawable.offline_bolt_24px, onGoing = false, showOnLockscreen = false, autoCancel = true, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/permissions/RequestPermissionDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/permissions/RequestPermissionDelegate.kt index 89f521832f..9fce955b46 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/permissions/RequestPermissionDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/permissions/RequestPermissionDelegate.kt @@ -68,24 +68,35 @@ class RequestPermissionDelegate( fun requestPermission(permission: Permission) { when (permission) { Permission.WRITE_SETTINGS -> requestWriteSettings() + Permission.CAMERA -> requestPermissionLauncher.launch(Manifest.permission.CAMERA) + Permission.DEVICE_ADMIN -> requestDeviceAdmin() + Permission.READ_PHONE_STATE -> requestPermissionLauncher.launch( Manifest.permission.READ_PHONE_STATE, ) + Permission.ACCESS_NOTIFICATION_POLICY -> requestAccessNotificationPolicy() + Permission.WRITE_SECURE_SETTINGS -> requestWriteSecureSettings() + Permission.NOTIFICATION_LISTENER -> notificationReceiverAdapter.start() + Permission.CALL_PHONE -> requestPermissionLauncher.launch( Manifest.permission.CALL_PHONE, ) + Permission.SEND_SMS -> requestPermissionLauncher.launch(Manifest.permission.SEND_SMS) + Permission.ANSWER_PHONE_CALL -> requestPermissionLauncher.launch( Manifest.permission.ANSWER_PHONE_CALLS, ) + Permission.FIND_NEARBY_DEVICES -> requestPermissionLauncher.launch( Manifest.permission.BLUETOOTH_CONNECT, ) + Permission.ROOT -> requestRootPermission() Permission.IGNORE_BATTERY_OPTIMISATION -> @@ -176,11 +187,11 @@ class RequestPermissionDelegate( messageResource = R.string.dialog_message_write_secure_settings positiveButton(R.string.pos_proceed) { - val destination = NavDestination.ProMode + val destination = NavDestination.ExpertMode coroutineScope.launch { navigationProvider.navigate( - "grant_write_secure_settings_pro_mode", + "grant_write_secure_settings_expert_mode", destination, ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseConfigTriggerViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseConfigTriggerViewModel.kt index b94322819c..e55d4413b8 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseConfigTriggerViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseConfigTriggerViewModel.kt @@ -80,14 +80,14 @@ abstract class BaseConfigTriggerViewModel( private const val DEVICE_ID_ANY = "any" private const val DEVICE_ID_INTERNAL = "internal" - fun buildProModeSwitchState( + fun buildExpertModeSwitchState( recordTriggerState: RecordTriggerState, - isProModeRecordingEnabled: Boolean, + isExpertModeRecordingEnabled: Boolean, systemBridgeState: SystemBridgeConnectionState, - ): ProModeRecordSwitchState { - return ProModeRecordSwitchState( + ): ExpertModeRecordSwitchState { + return ExpertModeRecordSwitchState( isVisible = systemBridgeState is SystemBridgeConnectionState.Connected, - isChecked = isProModeRecordingEnabled, + isChecked = isExpertModeRecordingEnabled, isEnabled = recordTriggerState !is RecordTriggerState.CountingDown, ) } @@ -112,18 +112,18 @@ abstract class BaseConfigTriggerViewModel( RecordTriggerState.Idle, ) - val proModeSwitchState: StateFlow = + val expertModeSwitchState: StateFlow = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { combine( recordTrigger.state, recordTrigger.isEvdevRecordingEnabled, systemBridgeConnectionManager.connectionState, - Companion::buildProModeSwitchState, + Companion::buildExpertModeSwitchState, ) .stateIn( viewModelScope, SharingStarted.Eagerly, - ProModeRecordSwitchState( + ExpertModeRecordSwitchState( isVisible = false, isChecked = false, isEnabled = false, @@ -131,7 +131,11 @@ abstract class BaseConfigTriggerViewModel( ) } else { MutableStateFlow( - ProModeRecordSwitchState(isVisible = false, isChecked = false, isEnabled = false), + ExpertModeRecordSwitchState( + isVisible = false, + isChecked = false, + isEnabled = false, + ), ) } @@ -298,6 +302,7 @@ abstract class BaseConfigTriggerViewModel( when (keyMapState) { State.Loading -> return null + is State.Data -> { val trigger = keyMapState.data.trigger val key = trigger.keys.find { it.uid == triggerKeyUid } @@ -478,7 +483,9 @@ abstract class BaseConfigTriggerViewModel( triggerKeyOptionsUid.value?.let { triggerKeyUid -> val device = when (descriptor) { DEVICE_ID_ANY -> KeyEventTriggerDevice.Any + DEVICE_ID_INTERNAL -> KeyEventTriggerDevice.Internal + else -> { val device = config.getAvailableTriggerKeyDevices() .filterIsInstance() @@ -537,7 +544,7 @@ abstract class BaseConfigTriggerViewModel( } } - fun onProModeSwitchChange(isChecked: Boolean) = + fun onExpertModeSwitchChange(isChecked: Boolean) = recordTrigger.setEvdevRecordingEnabled(isChecked) fun handleServiceEventResult(result: KMResult<*>) { @@ -549,11 +556,11 @@ abstract class BaseConfigTriggerViewModel( override fun onTipButtonClick(tipId: String) { when (tipId) { OnboardingTipDelegateImpl.CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID -> { - showTriggerSetup(TriggerSetupShortcut.KEYBOARD, forceProMode = true) + showTriggerSetup(TriggerSetupShortcut.KEYBOARD, forceExpertMode = true) } OnboardingTipDelegateImpl.VOLUME_BUTTONS_PRO_MODE_TIP_ID -> { - showTriggerSetup(TriggerSetupShortcut.VOLUME, forceProMode = true) + showTriggerSetup(TriggerSetupShortcut.VOLUME, forceExpertMode = true) } } } @@ -587,7 +594,7 @@ abstract class BaseConfigTriggerViewModel( val response = showDialog("migrate_screen_off", dialog) if (response == DialogResponse.POSITIVE) { - showTriggerSetup(TriggerSetupShortcut.OTHER, forceProMode = true) + showTriggerSetup(TriggerSetupShortcut.OTHER, forceExpertMode = true) } } @@ -715,7 +722,9 @@ abstract class BaseConfigTriggerViewModel( showDeviceDescriptors: Boolean, ): String = when (device) { is KeyEventTriggerDevice.Internal -> getString(R.string.this_device) + is KeyEventTriggerDevice.Any -> getString(R.string.any_device) + is KeyEventTriggerDevice.External -> { if (showDeviceDescriptors) { InputDeviceUtils.appendDeviceDescriptorToName( diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseTriggerScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseTriggerScreen.kt index fa33295b6c..6be1f4e4a4 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseTriggerScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseTriggerScreen.kt @@ -59,7 +59,7 @@ fun BaseTriggerScreen( val scope = rememberCoroutineScope() val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val recordTriggerState by viewModel.recordTriggerState.collectAsStateWithLifecycle() - val proModeSwitchState by viewModel.proModeSwitchState.collectAsStateWithLifecycle() + val expertModeSwitchState by viewModel.expertModeSwitchState.collectAsStateWithLifecycle() val showFingerprintGestures: Boolean by viewModel.showFingerprintGesturesShortcut.collectAsStateWithLifecycle() @@ -110,6 +110,7 @@ fun BaseTriggerScreen( when (val state = configState) { is State.Loading -> Loading(modifier = modifier) + is State.Data -> { val tipContent: @Composable () -> Unit = { tipModel?.let { tip -> @@ -134,11 +135,11 @@ fun BaseTriggerScreen( modifier = modifier, configState = state.data, recordTriggerState = recordTriggerState, - proModeSwitchState = proModeSwitchState, + expertModeSwitchState = expertModeSwitchState, onRemoveClick = viewModel::onRemoveKeyClick, onEditClick = viewModel::onTriggerKeyOptionsClick, onRecordTriggerClick = viewModel::onRecordTriggerButtonClick, - onProModeSwitchChange = viewModel::onProModeSwitchChange, + onExpertModeSwitchChange = viewModel::onExpertModeSwitchChange, onAdvancedTriggersClick = viewModel::onAdvancedTriggersClick, onSelectClickType = viewModel::onClickTypeRadioButtonChecked, onSelectParallelMode = viewModel::onParallelRadioButtonChecked, @@ -156,11 +157,11 @@ fun BaseTriggerScreen( modifier = modifier, configState = state.data, recordTriggerState = recordTriggerState, - proModeSwitchState = proModeSwitchState, + expertModeSwitchState = expertModeSwitchState, onRemoveClick = viewModel::onRemoveKeyClick, onEditClick = viewModel::onTriggerKeyOptionsClick, onRecordTriggerClick = viewModel::onRecordTriggerButtonClick, - onProModeSwitchChange = viewModel::onProModeSwitchChange, + onExpertModeSwitchChange = viewModel::onExpertModeSwitchChange, onAdvancedTriggersClick = viewModel::onAdvancedTriggersClick, onSelectClickType = viewModel::onClickTypeRadioButtonChecked, onSelectParallelMode = viewModel::onParallelRadioButtonChecked, @@ -205,14 +206,14 @@ private fun TriggerScreenVertical( modifier: Modifier = Modifier, configState: ConfigTriggerState, recordTriggerState: RecordTriggerState, - proModeSwitchState: ProModeRecordSwitchState, + expertModeSwitchState: ExpertModeRecordSwitchState, onRemoveClick: (String) -> Unit = {}, onEditClick: (String) -> Unit = {}, onSelectClickType: (ClickType) -> Unit = {}, onSelectParallelMode: () -> Unit = {}, onSelectSequenceMode: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, - onProModeSwitchChange: (Boolean) -> Unit = {}, + onExpertModeSwitchChange: (Boolean) -> Unit = {}, onAdvancedTriggersClick: () -> Unit = {}, onMoveTriggerKey: (fromIndex: Int, toIndex: Int) -> Unit = { _, _ -> }, onFixErrorClick: (TriggerError) -> Unit = {}, @@ -239,8 +240,8 @@ private fun TriggerScreenVertical( modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp), onRecordTriggerClick = onRecordTriggerClick, recordTriggerState = recordTriggerState, - proModeRecordSwitchState = proModeSwitchState, - onProModeSwitchChange = onProModeSwitchChange, + expertModeRecordSwitchState = expertModeSwitchState, + onExpertModeSwitchChange = onExpertModeSwitchChange, onAdvancedTriggersClick = onAdvancedTriggersClick, ) } @@ -301,8 +302,8 @@ private fun TriggerScreenVertical( .padding(start = 8.dp, end = 8.dp, bottom = 8.dp), onRecordTriggerClick = onRecordTriggerClick, recordTriggerState = recordTriggerState, - proModeRecordSwitchState = proModeSwitchState, - onProModeSwitchChange = onProModeSwitchChange, + expertModeRecordSwitchState = expertModeSwitchState, + onExpertModeSwitchChange = onExpertModeSwitchChange, onAdvancedTriggersClick = onAdvancedTriggersClick, ) } @@ -316,14 +317,14 @@ private fun TriggerScreenHorizontal( modifier: Modifier = Modifier, configState: ConfigTriggerState, recordTriggerState: RecordTriggerState, - proModeSwitchState: ProModeRecordSwitchState, + expertModeSwitchState: ExpertModeRecordSwitchState, onRemoveClick: (String) -> Unit = {}, onEditClick: (String) -> Unit = {}, onSelectClickType: (ClickType) -> Unit = {}, onSelectParallelMode: () -> Unit = {}, onSelectSequenceMode: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, - onProModeSwitchChange: (Boolean) -> Unit = {}, + onExpertModeSwitchChange: (Boolean) -> Unit = {}, onAdvancedTriggersClick: () -> Unit = {}, onMoveTriggerKey: (fromIndex: Int, toIndex: Int) -> Unit = { _, _ -> }, onFixErrorClick: (TriggerError) -> Unit = {}, @@ -349,8 +350,8 @@ private fun TriggerScreenHorizontal( .padding(start = 8.dp, end = 8.dp, bottom = 8.dp), onRecordTriggerClick = onRecordTriggerClick, recordTriggerState = recordTriggerState, - proModeRecordSwitchState = proModeSwitchState, - onProModeSwitchChange = onProModeSwitchChange, + expertModeRecordSwitchState = expertModeSwitchState, + onExpertModeSwitchChange = onExpertModeSwitchChange, onAdvancedTriggersClick = onAdvancedTriggersClick, ) } @@ -418,8 +419,8 @@ private fun TriggerScreenHorizontal( modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp), onRecordTriggerClick = onRecordTriggerClick, recordTriggerState = recordTriggerState, - proModeRecordSwitchState = proModeSwitchState, - onProModeSwitchChange = onProModeSwitchChange, + expertModeRecordSwitchState = expertModeSwitchState, + onExpertModeSwitchChange = onExpertModeSwitchChange, onAdvancedTriggersClick = onAdvancedTriggersClick, ) } @@ -625,7 +626,7 @@ private fun VerticalPreview() { TriggerScreenVertical( configState = previewState, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, @@ -644,7 +645,7 @@ private fun VerticalPreviewTiny() { TriggerScreenVertical( configState = previewState, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = true, isEnabled = true, @@ -663,7 +664,7 @@ private fun VerticalEmptyPreview() { TriggerScreenVertical( configState = ConfigTriggerState.Empty, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = false, isChecked = false, isEnabled = true, @@ -682,7 +683,7 @@ private fun VerticalEmptyDarkPreview() { TriggerScreenVertical( configState = ConfigTriggerState.Empty, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = true, isEnabled = false, @@ -701,7 +702,7 @@ private fun HorizontalPreview() { TriggerScreenHorizontal( configState = previewState, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, @@ -735,7 +736,7 @@ private fun HorizontalEmptyPreview() { TriggerScreenHorizontal( configState = ConfigTriggerState.Empty, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ProModeRecordSwitchState.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ExpertModeRecordSwitchState.kt similarity index 77% rename from base/src/main/java/io/github/sds100/keymapper/base/trigger/ProModeRecordSwitchState.kt rename to base/src/main/java/io/github/sds100/keymapper/base/trigger/ExpertModeRecordSwitchState.kt index 37d2add993..6f43f789be 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ProModeRecordSwitchState.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ExpertModeRecordSwitchState.kt @@ -1,6 +1,6 @@ package io.github.sds100.keymapper.base.trigger -data class ProModeRecordSwitchState( +data class ExpertModeRecordSwitchState( val isVisible: Boolean, val isChecked: Boolean, val isEnabled: Boolean, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt index 64bff3dd91..87f4aad736 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt @@ -19,6 +19,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.TextAutoSize import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.OfflineBolt import androidx.compose.material.icons.outlined.ShoppingCart import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.FilledTonalButton @@ -42,25 +43,22 @@ import androidx.compose.ui.unit.sp import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.compose.KeyMapperTheme import io.github.sds100.keymapper.base.compose.LocalCustomColorsPalette -import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIcon -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIconDisabled @Composable fun RecordTriggerButtonRow( modifier: Modifier = Modifier, onRecordTriggerClick: () -> Unit = {}, recordTriggerState: RecordTriggerState, - proModeRecordSwitchState: ProModeRecordSwitchState, - onProModeSwitchChange: (Boolean) -> Unit = {}, + expertModeRecordSwitchState: ExpertModeRecordSwitchState, + onExpertModeSwitchChange: (Boolean) -> Unit = {}, onAdvancedTriggersClick: () -> Unit = {}, ) { Column(modifier = modifier) { Row(verticalAlignment = Alignment.CenterVertically) { - if (proModeRecordSwitchState.isVisible) { - ProModeSwitch( - state = proModeRecordSwitchState, - onCheckedChange = onProModeSwitchChange, + if (expertModeRecordSwitchState.isVisible) { + ExpertModeSwitch( + state = expertModeRecordSwitchState, + onCheckedChange = onExpertModeSwitchChange, ) Spacer(modifier = Modifier.width(8.dp)) @@ -80,9 +78,9 @@ fun RecordTriggerButtonRow( } @Composable -private fun ProModeSwitch( +private fun ExpertModeSwitch( modifier: Modifier = Modifier, - state: ProModeRecordSwitchState, + state: ExpertModeRecordSwitchState, onCheckedChange: (Boolean) -> Unit, ) { Switch( @@ -99,11 +97,7 @@ private fun ProModeSwitch( thumbContent = { Icon( modifier = Modifier.padding(2.dp), - imageVector = if (state.isChecked) { - KeyMapperIcons.ProModeIcon - } else { - KeyMapperIcons.ProModeIconDisabled - }, + imageVector = Icons.Outlined.OfflineBolt, contentDescription = null, ) }, @@ -196,7 +190,7 @@ private fun PreviewCountingDown() { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.CountingDown(3), - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = true, isEnabled = true, @@ -214,7 +208,7 @@ private fun PreviewStopped() { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Idle, - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, @@ -232,7 +226,7 @@ private fun PreviewStoppedDark() { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Idle, - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = true, isEnabled = true, @@ -250,7 +244,7 @@ private fun PreviewStoppedCompact() { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Idle, - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, @@ -262,13 +256,13 @@ private fun PreviewStoppedCompact() { @Preview(widthDp = 400) @Composable -private fun PreviewProModeSwitchHidden() { +private fun PreviewExpertModeSwitchHidden() { KeyMapperTheme { Surface { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Idle, - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = false, isChecked = false, isEnabled = true, @@ -280,13 +274,13 @@ private fun PreviewProModeSwitchHidden() { @Preview(widthDp = 400) @Composable -private fun PreviewProModeSwitchDisabled() { +private fun PreviewExpertModeSwitchDisabled() { KeyMapperTheme { Surface { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Idle, - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = true, isEnabled = false, @@ -304,7 +298,7 @@ private fun PreviewCompleted() { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Completed(emptyList()), - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerKeyListItem.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerKeyListItem.kt index 3fb455de52..51af4cc2fb 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerKeyListItem.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerKeyListItem.kt @@ -18,6 +18,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Assistant import androidx.compose.material.icons.outlined.BubbleChart import androidx.compose.material.icons.outlined.Fingerprint +import androidx.compose.material.icons.outlined.OfflineBolt import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.rounded.Add import androidx.compose.material.icons.rounded.ArrowDownward @@ -46,8 +47,6 @@ import io.github.sds100.keymapper.base.keymaps.ClickType import io.github.sds100.keymapper.base.system.accessibility.FingerprintGestureType import io.github.sds100.keymapper.base.utils.ui.LinkType import io.github.sds100.keymapper.base.utils.ui.compose.DragDropState -import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIcon @Composable fun TriggerKeyListItem( @@ -114,7 +113,7 @@ fun TriggerKeyListItem( is TriggerKeyListItemModel.Assistant -> Icons.Outlined.Assistant is TriggerKeyListItemModel.FloatingButton -> Icons.Outlined.BubbleChart is TriggerKeyListItemModel.FingerprintGesture -> Icons.Outlined.Fingerprint - is TriggerKeyListItemModel.EvdevEvent -> KeyMapperIcons.ProModeIcon + is TriggerKeyListItemModel.EvdevEvent -> Icons.Outlined.OfflineBolt else -> null } @@ -133,9 +132,11 @@ fun TriggerKeyListItem( AssistantTriggerType.ANY -> stringResource( R.string.assistant_any_trigger_name, ) + AssistantTriggerType.VOICE -> stringResource( R.string.assistant_voice_trigger_name, ) + AssistantTriggerType.DEVICE -> stringResource( R.string.assistant_device_trigger_name, ) @@ -147,6 +148,7 @@ fun TriggerKeyListItem( ) is TriggerKeyListItemModel.KeyEvent -> model.keyName + is TriggerKeyListItemModel.EvdevEvent -> model.keyName is TriggerKeyListItemModel.FloatingButtonDeleted -> stringResource( @@ -157,12 +159,15 @@ fun TriggerKeyListItem( FingerprintGestureType.SWIPE_UP -> stringResource( R.string.trigger_key_fingerprint_gesture_up, ) + FingerprintGestureType.SWIPE_DOWN -> stringResource( R.string.trigger_key_fingerprint_gesture_down, ) + FingerprintGestureType.SWIPE_LEFT -> stringResource( R.string.trigger_key_fingerprint_gesture_left, ) + FingerprintGestureType.SWIPE_RIGHT -> stringResource( R.string.trigger_key_fingerprint_gesture_right, ) @@ -182,7 +187,6 @@ fun TriggerKeyListItem( is TriggerKeyListItemModel.KeyEvent -> model.extraInfo is TriggerKeyListItemModel.EvdevEvent -> model.extraInfo is TriggerKeyListItemModel.FloatingButton -> model.layoutName - else -> null } @@ -278,33 +282,43 @@ fun TriggerKeyListItem( private fun getErrorMessage(error: TriggerError): String { return when (error) { TriggerError.DND_ACCESS_DENIED -> stringResource(R.string.trigger_error_dnd_access_denied) + TriggerError.CANT_DETECT_IN_PHONE_CALL -> stringResource( R.string.trigger_error_cant_detect_in_phone_call, ) + TriggerError.ASSISTANT_TRIGGER_NOT_PURCHASED -> stringResource( R.string.trigger_error_assistant_not_purchased, ) + TriggerError.DPAD_IME_NOT_SELECTED -> stringResource( R.string.trigger_error_dpad_ime_not_selected, ) + TriggerError.FLOATING_BUTTON_DELETED -> stringResource( R.string.trigger_error_floating_button_deleted, ) + TriggerError.FLOATING_BUTTONS_NOT_PURCHASED -> stringResource( R.string.trigger_error_floating_buttons_not_purchased, ) + TriggerError.PURCHASE_VERIFICATION_FAILED -> stringResource( R.string.trigger_error_product_verification_failed, ) + TriggerError.SYSTEM_BRIDGE_UNSUPPORTED -> stringResource( R.string.trigger_error_system_bridge_unsupported, ) + TriggerError.SYSTEM_BRIDGE_DISCONNECTED -> stringResource( R.string.trigger_error_system_bridge_disconnected, ) + TriggerError.EVDEV_DEVICE_NOT_FOUND -> stringResource( R.string.trigger_error_evdev_device_not_found, ) + TriggerError.MIGRATE_SCREEN_OFF_TRIGGER -> stringResource( R.string.trigger_error_migrate_screen_off_key_map, ) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt index 5aaf8bdefa..66410b1ca1 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt @@ -52,13 +52,13 @@ import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.compose.KeyMapperTheme import io.github.sds100.keymapper.base.compose.LocalCustomColorsPalette import io.github.sds100.keymapper.base.system.accessibility.FingerprintGestureType -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus import io.github.sds100.keymapper.base.utils.ui.compose.AccessibilityServiceRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.CheckBoxText +import io.github.sds100.keymapper.base.utils.ui.compose.ExpertModeRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.HeaderText import io.github.sds100.keymapper.base.utils.ui.compose.InputMethodRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.KeyMapperSegmentedButtonRow -import io.github.sds100.keymapper.base.utils.ui.compose.ProModeRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.RadioButtonText import io.github.sds100.keymapper.base.utils.ui.compose.icons.IndeterminateQuestionBox import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons @@ -79,9 +79,9 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.Volume, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, - onUseProModeCheckedChange = delegate::onUseProModeCheckedChange, + onUseExpertModeCheckedChange = delegate::onUseExpertModeCheckedChange, ) is TriggerSetupState.Power -> PowerTriggerSetupBottomSheet( @@ -89,7 +89,7 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.Power, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, ) @@ -107,9 +107,9 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.Keyboard, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, - onUseProModeCheckedChange = delegate::onUseProModeCheckedChange, + onUseExpertModeCheckedChange = delegate::onUseExpertModeCheckedChange, ) is TriggerSetupState.Mouse -> MouseTriggerSetupBottomSheet( @@ -117,7 +117,7 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.Mouse, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, ) @@ -126,9 +126,9 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.Other, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, - onUseProModeCheckedChange = delegate::onUseProModeCheckedChange, + onUseExpertModeCheckedChange = delegate::onUseExpertModeCheckedChange, ) is TriggerSetupState.NotDetected -> NotDetectedSetupBottomSheet( @@ -136,7 +136,7 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.NotDetected, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, ) @@ -149,8 +149,8 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { onRecordTriggerClick = delegate::onTriggerSetupRecordClick, onEnableInputMethodClick = delegate::onEnableImeClick, onChooseInputMethodClick = delegate::onChooseImeClick, - onUseProModeCheckedChange = delegate::onUseProModeCheckedChange, - onEnableProModeClick = delegate::onEnableProModeClick, + onUseExpertModeCheckedChange = delegate::onUseExpertModeCheckedChange, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, ) null -> {} @@ -167,10 +167,10 @@ private fun GamepadTriggerSetupBottomSheet( onRecordTriggerClick: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, onSelectButtonType: (TriggerSetupState.Gamepad.Type) -> Unit = { }, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onEnableInputMethodClick: () -> Unit = { }, onChooseInputMethodClick: () -> Unit = { }, - onUseProModeCheckedChange: (Boolean) -> Unit = {}, + onUseExpertModeCheckedChange: (Boolean) -> Unit = {}, ) { TriggerSetupBottomSheet( modifier = modifier, @@ -208,6 +208,7 @@ private fun GamepadTriggerSetupBottomSheet( val selectedState = when (state) { is TriggerSetupState.Gamepad.Dpad -> TriggerSetupState.Gamepad.Type.DPAD + is TriggerSetupState.Gamepad.SimpleButtons -> TriggerSetupState.Gamepad.Type.SIMPLE_BUTTONS } @@ -219,12 +220,12 @@ private fun GamepadTriggerSetupBottomSheet( onStateSelected = onSelectButtonType, ) - val isUseProModeChecked = when (state) { + val isUseExpertModeChecked = when (state) { is TriggerSetupState.Gamepad.Dpad -> false - is TriggerSetupState.Gamepad.SimpleButtons -> state.isUseProModeChecked + is TriggerSetupState.Gamepad.SimpleButtons -> state.isUseExpertModeChecked } - val isUseProModeEnabled = when (state) { + val isUseExpertModeEnabled = when (state) { is TriggerSetupState.Gamepad.Dpad -> false is TriggerSetupState.Gamepad.SimpleButtons -> true } @@ -232,9 +233,9 @@ private fun GamepadTriggerSetupBottomSheet( CheckBoxText( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.trigger_setup_screen_off_option), - isChecked = isUseProModeChecked, - isEnabled = isUseProModeEnabled, - onCheckedChange = onUseProModeCheckedChange, + isChecked = isUseExpertModeChecked, + isEnabled = isUseExpertModeEnabled, + onCheckedChange = onUseExpertModeCheckedChange, ) HeaderText(text = stringResource(R.string.trigger_setup_requirements_title)) @@ -256,11 +257,11 @@ private fun GamepadTriggerSetupBottomSheet( } is TriggerSetupState.Gamepad.SimpleButtons -> { - ProModeRequirementRow( + ExpertModeRequirementRow( modifier = Modifier.fillMaxWidth(), - isVisible = state.isUseProModeChecked, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + isVisible = state.isUseExpertModeChecked, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) } } @@ -283,7 +284,7 @@ private fun MouseTriggerSetupBottomSheet( state: TriggerSetupState.Mouse, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, ) { TriggerSetupBottomSheet( @@ -323,10 +324,10 @@ private fun MouseTriggerSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( + ExpertModeRequirementRow( isVisible = true, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) } } @@ -338,7 +339,7 @@ private fun PowerTriggerSetupBottomSheet( state: TriggerSetupState.Power, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, ) { TriggerSetupBottomSheet( @@ -378,10 +379,10 @@ private fun PowerTriggerSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( + ExpertModeRequirementRow( isVisible = true, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) HeaderText(text = stringResource(R.string.trigger_setup_information_title)) @@ -423,9 +424,9 @@ private fun VolumeTriggerSetupBottomSheet( state: TriggerSetupState.Volume, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, - onUseProModeCheckedChange: (Boolean) -> Unit = {}, + onUseExpertModeCheckedChange: (Boolean) -> Unit = {}, ) { TriggerSetupBottomSheet( modifier = modifier, @@ -457,9 +458,9 @@ private fun VolumeTriggerSetupBottomSheet( CheckBoxText( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.trigger_setup_screen_off_option), - isChecked = state.isUseProModeChecked, - isEnabled = !state.forceProMode, - onCheckedChange = onUseProModeCheckedChange, + isChecked = state.isUseExpertModeChecked, + isEnabled = !state.forceExpertMode, + onCheckedChange = onUseExpertModeCheckedChange, ) HeaderText(text = stringResource(R.string.trigger_setup_requirements_title)) @@ -469,10 +470,10 @@ private fun VolumeTriggerSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( - isVisible = state.isUseProModeChecked, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + ExpertModeRequirementRow( + isVisible = state.isUseExpertModeChecked, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) } } @@ -484,7 +485,7 @@ private fun NotDetectedSetupBottomSheet( state: TriggerSetupState.NotDetected, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, ) { TriggerSetupBottomSheet( @@ -531,10 +532,10 @@ private fun NotDetectedSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( + ExpertModeRequirementRow( isVisible = true, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) HeaderText(text = stringResource(R.string.trigger_setup_information_title)) @@ -569,9 +570,9 @@ private fun OtherTriggerSetupBottomSheet( state: TriggerSetupState.Other, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, - onUseProModeCheckedChange: (Boolean) -> Unit = {}, + onUseExpertModeCheckedChange: (Boolean) -> Unit = {}, ) { TriggerSetupBottomSheet( modifier = modifier, @@ -603,9 +604,9 @@ private fun OtherTriggerSetupBottomSheet( CheckBoxText( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.trigger_setup_screen_off_option), - isChecked = state.isUseProModeChecked, - isEnabled = !state.forceProMode, - onCheckedChange = onUseProModeCheckedChange, + isChecked = state.isUseExpertModeChecked, + isEnabled = !state.forceExpertMode, + onCheckedChange = onUseExpertModeCheckedChange, ) HeaderText(text = stringResource(R.string.trigger_setup_requirements_title)) @@ -615,10 +616,10 @@ private fun OtherTriggerSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( - isVisible = state.isUseProModeChecked, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + ExpertModeRequirementRow( + isVisible = state.isUseExpertModeChecked, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) HeaderText(text = stringResource(R.string.trigger_setup_information_title)) @@ -653,9 +654,9 @@ private fun KeyboardTriggerSetupBottomSheet( state: TriggerSetupState.Keyboard, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, - onUseProModeCheckedChange: (Boolean) -> Unit = {}, + onUseExpertModeCheckedChange: (Boolean) -> Unit = {}, ) { TriggerSetupBottomSheet( modifier = modifier, @@ -687,9 +688,9 @@ private fun KeyboardTriggerSetupBottomSheet( CheckBoxText( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.trigger_setup_screen_off_option), - isChecked = state.isUseProModeChecked, - isEnabled = !state.forceProMode, - onCheckedChange = onUseProModeCheckedChange, + isChecked = state.isUseExpertModeChecked, + isEnabled = !state.forceExpertMode, + onCheckedChange = onUseExpertModeCheckedChange, ) HeaderText(text = stringResource(R.string.trigger_setup_requirements_title)) @@ -699,10 +700,10 @@ private fun KeyboardTriggerSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( - isVisible = state.isUseProModeChecked, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + ExpertModeRequirementRow( + isVisible = state.isUseExpertModeChecked, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) } } @@ -910,7 +911,7 @@ private fun PowerButtonPreview() { sheetState = sheetState, state = TriggerSetupState.Power( isAccessibilityServiceEnabled = true, - proModeStatus = ProModeStatus.ENABLED, + expertModeStatus = ExpertModeStatus.ENABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, remapStatus = RemapStatus.SUPPORTED, @@ -934,7 +935,7 @@ private fun PowerButtonDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Power( isAccessibilityServiceEnabled = false, - proModeStatus = ProModeStatus.UNSUPPORTED, + expertModeStatus = ExpertModeStatus.UNSUPPORTED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, remapStatus = RemapStatus.UNSUPPORTED, @@ -958,11 +959,11 @@ private fun VolumeButtonPreview() { sheetState = sheetState, state = TriggerSetupState.Volume( isAccessibilityServiceEnabled = true, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.ENABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.ENABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -983,11 +984,11 @@ private fun VolumeButtonDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Volume( isAccessibilityServiceEnabled = false, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.DISABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.DISABLED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1052,11 +1053,11 @@ private fun KeyboardButtonEnabledPreview() { sheetState = sheetState, state = TriggerSetupState.Keyboard( isAccessibilityServiceEnabled = true, - isUseProModeChecked = false, - proModeStatus = ProModeStatus.DISABLED, + isUseExpertModeChecked = false, + expertModeStatus = ExpertModeStatus.DISABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1077,11 +1078,11 @@ private fun KeyboardButtonDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Keyboard( isAccessibilityServiceEnabled = false, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.DISABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.DISABLED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1102,7 +1103,7 @@ private fun MouseButtonPreview() { sheetState = sheetState, state = TriggerSetupState.Mouse( isAccessibilityServiceEnabled = true, - proModeStatus = ProModeStatus.ENABLED, + expertModeStatus = ExpertModeStatus.ENABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, remapStatus = RemapStatus.SUPPORTED, @@ -1126,7 +1127,7 @@ private fun MouseButtonDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Mouse( isAccessibilityServiceEnabled = false, - proModeStatus = ProModeStatus.UNSUPPORTED, + expertModeStatus = ExpertModeStatus.UNSUPPORTED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, remapStatus = RemapStatus.UNSUPPORTED, @@ -1150,11 +1151,11 @@ private fun OtherButtonPreview() { sheetState = sheetState, state = TriggerSetupState.Other( isAccessibilityServiceEnabled = true, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.ENABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.ENABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1175,11 +1176,11 @@ private fun OtherButtonDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Other( isAccessibilityServiceEnabled = false, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.DISABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.DISABLED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1250,11 +1251,11 @@ private fun GamepadSimpleButtonsPreview() { sheetState = sheetState, state = TriggerSetupState.Gamepad.SimpleButtons( isAccessibilityServiceEnabled = true, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.ENABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.ENABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1275,11 +1276,11 @@ private fun GamepadSimpleButtonsDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Gamepad.SimpleButtons( isAccessibilityServiceEnabled = false, - isUseProModeChecked = false, - proModeStatus = ProModeStatus.DISABLED, + isUseExpertModeChecked = false, + expertModeStatus = ExpertModeStatus.DISABLED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupDelegate.kt index c5af7ca363..9eaec3e10c 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupDelegate.kt @@ -4,7 +4,7 @@ import android.os.Build import dagger.hilt.android.scopes.ViewModelScoped import io.github.sds100.keymapper.base.onboarding.SetupAccessibilityServiceDelegate import io.github.sds100.keymapper.base.system.accessibility.FingerprintGestureType -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus import io.github.sds100.keymapper.base.utils.navigation.NavDestination import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider import io.github.sds100.keymapper.base.utils.navigation.navigate @@ -58,8 +58,8 @@ class TriggerSetupDelegateImpl @Inject constructor( MutableStateFlow(null) private val isScreenOffChecked: MutableStateFlow = MutableStateFlow(false) - private val isProModeLocked: MutableStateFlow = MutableStateFlow(false) - private val forceProMode: MutableStateFlow = MutableStateFlow(false) + private val isExpertModeLocked: MutableStateFlow = MutableStateFlow(false) + private val forceExpertMode: MutableStateFlow = MutableStateFlow(false) private val selectedFingerprintGestureType: MutableStateFlow = MutableStateFlow(FingerprintGestureType.SWIPE_DOWN) @@ -67,16 +67,16 @@ class TriggerSetupDelegateImpl @Inject constructor( private val selectedGamePadType: MutableStateFlow = MutableStateFlow(TriggerSetupState.Gamepad.Type.DPAD) - private val proModeStatus: Flow = + private val expertModeStatus: Flow = if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { systemBridgeConnectionManager.connectionState.map { state -> when (state) { - is SystemBridgeConnectionState.Connected -> ProModeStatus.ENABLED - is SystemBridgeConnectionState.Disconnected -> ProModeStatus.DISABLED + is SystemBridgeConnectionState.Connected -> ExpertModeStatus.ENABLED + is SystemBridgeConnectionState.Disconnected -> ExpertModeStatus.DISABLED } } } else { - flowOf(ProModeStatus.UNSUPPORTED) + flowOf(ExpertModeStatus.UNSUPPORTED) } override val triggerSetupState: StateFlow = @@ -93,17 +93,16 @@ class TriggerSetupDelegateImpl @Inject constructor( TriggerSetupShortcut.GAMEPAD -> buildSetupGamepadTriggerFlow() TriggerSetupShortcut.OTHER -> buildSetupOtherTriggerFlow() TriggerSetupShortcut.NOT_DETECTED -> buildSetupNotDetectedFlow() - else -> throw UnsupportedOperationException("Unhandled shortcut: $shortcut") } } }.stateIn(viewModelScope, SharingStarted.Lazily, null) - override fun showTriggerSetup(shortcut: TriggerSetupShortcut, forceProMode: Boolean) { - isProModeLocked.value = true - this.forceProMode.value = forceProMode + override fun showTriggerSetup(shortcut: TriggerSetupShortcut, forceExpertMode: Boolean) { + isExpertModeLocked.value = true + this.forceExpertMode.value = forceExpertMode // If force pro mode is enabled, automatically check the screen off option - if (forceProMode) { + if (forceExpertMode) { isScreenOffChecked.value = true } currentSetupShortcut.value = shortcut @@ -114,23 +113,29 @@ class TriggerSetupDelegateImpl @Inject constructor( accessibilityServiceState, isScreenOffChecked, recordTriggerController.state, - proModeStatus, - forceProMode, - ) { serviceState, isScreenOffChecked, recordTriggerState, proModeStatus, forceProMode -> + expertModeStatus, + forceExpertMode, + ) { + serviceState, + isScreenOffChecked, + recordTriggerState, + expertModeStatus, + forceExpertMode, + -> val areRequirementsMet = if (isScreenOffChecked) { serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED } else { serviceState == AccessibilityServiceState.ENABLED } TriggerSetupState.Volume( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - isUseProModeChecked = isScreenOffChecked, - proModeStatus = proModeStatus, + isUseExpertModeChecked = isScreenOffChecked, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, - forceProMode = forceProMode, + forceExpertMode = forceExpertMode, ) } } @@ -168,18 +173,18 @@ class TriggerSetupDelegateImpl @Inject constructor( accessibilityServiceState, isScreenOffChecked, recordTriggerController.state, - proModeStatus, - forceProMode, + expertModeStatus, + forceExpertMode, ) { serviceState, isScreenOffChecked, recordTriggerState, - proModeStatus, - forceProMode, + expertModeStatus, + forceExpertMode, -> val areRequirementsMet = if (isScreenOffChecked) { serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED } else { serviceState == AccessibilityServiceState.ENABLED } @@ -187,11 +192,11 @@ class TriggerSetupDelegateImpl @Inject constructor( TriggerSetupState.Gamepad.SimpleButtons( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - isUseProModeChecked = isScreenOffChecked, - proModeStatus = proModeStatus, + isUseExpertModeChecked = isScreenOffChecked, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, - forceProMode = forceProMode, + forceExpertMode = forceExpertMode, ) } } @@ -204,23 +209,29 @@ class TriggerSetupDelegateImpl @Inject constructor( accessibilityServiceState, isScreenOffChecked, recordTriggerController.state, - proModeStatus, - forceProMode, - ) { serviceState, isScreenOffChecked, recordTriggerState, proModeStatus, forceProMode -> + expertModeStatus, + forceExpertMode, + ) { + serviceState, + isScreenOffChecked, + recordTriggerState, + expertModeStatus, + forceExpertMode, + -> val areRequirementsMet = if (isScreenOffChecked) { serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED } else { serviceState == AccessibilityServiceState.ENABLED } TriggerSetupState.Other( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - isUseProModeChecked = isScreenOffChecked, - proModeStatus = proModeStatus, + isUseExpertModeChecked = isScreenOffChecked, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, - forceProMode = forceProMode, + forceExpertMode = forceExpertMode, ) } } @@ -229,15 +240,15 @@ class TriggerSetupDelegateImpl @Inject constructor( return combine( accessibilityServiceState, recordTriggerController.state, - proModeStatus, - ) { serviceState, recordTriggerState, proModeStatus -> + expertModeStatus, + ) { serviceState, recordTriggerState, expertModeStatus -> val areRequirementsMet = serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED TriggerSetupState.NotDetected( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - proModeStatus = proModeStatus, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, ) @@ -249,23 +260,29 @@ class TriggerSetupDelegateImpl @Inject constructor( accessibilityServiceState, isScreenOffChecked, recordTriggerController.state, - proModeStatus, - forceProMode, - ) { serviceState, isScreenOffChecked, recordTriggerState, proModeStatus, forceProMode -> + expertModeStatus, + forceExpertMode, + ) { + serviceState, + isScreenOffChecked, + recordTriggerState, + expertModeStatus, + forceExpertMode, + -> val areRequirementsMet = if (isScreenOffChecked) { serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED } else { serviceState == AccessibilityServiceState.ENABLED } TriggerSetupState.Keyboard( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - isUseProModeChecked = isScreenOffChecked, - proModeStatus = proModeStatus, + isUseExpertModeChecked = isScreenOffChecked, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, - forceProMode = forceProMode, + forceExpertMode = forceExpertMode, ) } } @@ -289,11 +306,11 @@ class TriggerSetupDelegateImpl @Inject constructor( return combine( accessibilityServiceState, recordTriggerController.state, - proModeStatus, - ) { serviceState, recordTriggerState, proModeStatus -> + expertModeStatus, + ) { serviceState, recordTriggerState, expertModeStatus -> val areRequirementsMet = serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED val remapStatus = if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { if (areRequirementsMet) { @@ -307,7 +324,7 @@ class TriggerSetupDelegateImpl @Inject constructor( TriggerSetupState.Power( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - proModeStatus = proModeStatus, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, remapStatus = remapStatus, @@ -319,11 +336,11 @@ class TriggerSetupDelegateImpl @Inject constructor( return combine( accessibilityServiceState, recordTriggerController.state, - proModeStatus, - ) { serviceState, recordTriggerState, proModeStatus -> + expertModeStatus, + ) { serviceState, recordTriggerState, expertModeStatus -> val areRequirementsMet = serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED val remapStatus = if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { if (areRequirementsMet) { @@ -337,7 +354,7 @@ class TriggerSetupDelegateImpl @Inject constructor( TriggerSetupState.Mouse( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - proModeStatus = proModeStatus, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, remapStatus = remapStatus, @@ -351,9 +368,9 @@ class TriggerSetupDelegateImpl @Inject constructor( } } - override fun onEnableProModeClick() { + override fun onEnableExpertModeClick() { viewModelScope.launch { - navigate("trigger_setup_enable_pro_mode", NavDestination.ProMode) + navigate("trigger_setup_enable_expert_mode", NavDestination.ExpertMode) } } @@ -361,7 +378,7 @@ class TriggerSetupDelegateImpl @Inject constructor( isScreenOffChecked.value = isChecked } - override fun onUseProModeCheckedChange(isChecked: Boolean) { + override fun onUseExpertModeCheckedChange(isChecked: Boolean) { isScreenOffChecked.value = isChecked } @@ -373,14 +390,22 @@ class TriggerSetupDelegateImpl @Inject constructor( val setupState = triggerSetupState.value ?: return val enableEvdevRecording = when (setupState) { - is TriggerSetupState.Volume -> setupState.isUseProModeChecked - is TriggerSetupState.Keyboard -> setupState.isUseProModeChecked + is TriggerSetupState.Volume -> setupState.isUseExpertModeChecked + + is TriggerSetupState.Keyboard -> setupState.isUseExpertModeChecked + is TriggerSetupState.Power -> true + is TriggerSetupState.FingerprintGesture -> false + is TriggerSetupState.Mouse -> true - is TriggerSetupState.Other -> setupState.isUseProModeChecked + + is TriggerSetupState.Other -> setupState.isUseExpertModeChecked + is TriggerSetupState.Gamepad.Dpad -> false - is TriggerSetupState.Gamepad.SimpleButtons -> setupState.isUseProModeChecked + + is TriggerSetupState.Gamepad.SimpleButtons -> setupState.isUseExpertModeChecked + // Always enable pro mode recording to increase the chances of detecting // the key is TriggerSetupState.NotDetected -> true @@ -442,12 +467,12 @@ class TriggerSetupDelegateImpl @Inject constructor( interface TriggerSetupDelegate { val triggerSetupState: StateFlow - fun showTriggerSetup(shortcut: TriggerSetupShortcut, forceProMode: Boolean = false) + fun showTriggerSetup(shortcut: TriggerSetupShortcut, forceExpertMode: Boolean = false) fun onDismissTriggerSetup() fun onEnableAccessibilityServiceClick() - fun onEnableProModeClick() + fun onEnableExpertModeClick() fun onScreenOffTriggerSetupCheckedChange(isChecked: Boolean) - fun onUseProModeCheckedChange(isChecked: Boolean) + fun onUseExpertModeCheckedChange(isChecked: Boolean) fun onTriggerSetupRecordClick() fun onFingerprintGestureTypeSelected(type: FingerprintGestureType) fun onAddFingerprintGestureClick() diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupState.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupState.kt index e94c8749de..ac7d09402b 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupState.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupState.kt @@ -1,30 +1,30 @@ package io.github.sds100.keymapper.base.trigger import io.github.sds100.keymapper.base.system.accessibility.FingerprintGestureType -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus sealed class TriggerSetupState { data class Volume( val isAccessibilityServiceEnabled: Boolean, - val isUseProModeChecked: Boolean, - val proModeStatus: ProModeStatus, + val isUseExpertModeChecked: Boolean, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, - val forceProMode: Boolean = false, + val forceExpertMode: Boolean = false, ) : TriggerSetupState() data class Keyboard( val isAccessibilityServiceEnabled: Boolean, - val isUseProModeChecked: Boolean, - val proModeStatus: ProModeStatus, + val isUseExpertModeChecked: Boolean, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, - val forceProMode: Boolean = false, + val forceExpertMode: Boolean = false, ) : TriggerSetupState() data class Power( val isAccessibilityServiceEnabled: Boolean, - val proModeStatus: ProModeStatus, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, val remapStatus: RemapStatus, @@ -32,7 +32,7 @@ sealed class TriggerSetupState { data class Mouse( val isAccessibilityServiceEnabled: Boolean, - val proModeStatus: ProModeStatus, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, val remapStatus: RemapStatus, @@ -40,16 +40,16 @@ sealed class TriggerSetupState { data class Other( val isAccessibilityServiceEnabled: Boolean, - val isUseProModeChecked: Boolean, - val proModeStatus: ProModeStatus, + val isUseExpertModeChecked: Boolean, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, - val forceProMode: Boolean = false, + val forceExpertMode: Boolean = false, ) : TriggerSetupState() data class NotDetected( val isAccessibilityServiceEnabled: Boolean, - val proModeStatus: ProModeStatus, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, ) : TriggerSetupState() @@ -75,11 +75,11 @@ sealed class TriggerSetupState { data class SimpleButtons( override val isAccessibilityServiceEnabled: Boolean, - val isUseProModeChecked: Boolean, - val proModeStatus: ProModeStatus, + val isUseExpertModeChecked: Boolean, + val expertModeStatus: ExpertModeStatus, override val areRequirementsMet: Boolean, override val recordTriggerState: RecordTriggerState, - val forceProMode: Boolean = false, + val forceExpertMode: Boolean = false, ) : Gamepad() } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ProModeStatus.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ExpertModeStatus.kt similarity index 75% rename from base/src/main/java/io/github/sds100/keymapper/base/utils/ProModeStatus.kt rename to base/src/main/java/io/github/sds100/keymapper/base/utils/ExpertModeStatus.kt index 4bc9c674ec..e927d35e2f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ProModeStatus.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ExpertModeStatus.kt @@ -1,6 +1,6 @@ package io.github.sds100.keymapper.base.utils -enum class ProModeStatus { +enum class ExpertModeStatus { UNSUPPORTED, DISABLED, ENABLED, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/navigation/NavDestination.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/navigation/NavDestination.kt index 0c7f95c588..f13888bccd 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/navigation/NavDestination.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/navigation/NavDestination.kt @@ -42,7 +42,7 @@ abstract class NavDestination(val isCompose: Boolean = false) { const val ID_SHELL_COMMAND_ACTION = "shell_command_action" const val ID_CHOOSE_SETTING = "choose_setting" const val ID_CREATE_NOTIFICATION_ACTION = "create_notification_action" - const val ID_PRO_MODE = "pro_mode" + const val ID_EXPERT_MODE = "expert_mode" const val ID_LOG = "log" const val ID_ADVANCED_TRIGGERS = "advanced_triggers" } @@ -189,13 +189,13 @@ abstract class NavDestination(val isCompose: Boolean = false) { } @Serializable - data object ProMode : NavDestination(isCompose = true) { - override val id: String = ID_PRO_MODE + data object ExpertMode : NavDestination(isCompose = true) { + override val id: String = ID_EXPERT_MODE } @Serializable - data object ProModeSetup : NavDestination(isCompose = true) { - const val ID_PRO_MODE_SETUP = "pro_mode_setup_wizard" + data object ExpertModeSetup : NavDestination(isCompose = true) { + const val ID_PRO_MODE_SETUP = "expert_mode_setup_wizard" override val id: String = ID_PRO_MODE_SETUP } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionButton.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionButton.kt index 835108fffd..cf099840c3 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionButton.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionButton.kt @@ -41,8 +41,8 @@ private fun Preview() { KeyMapperTheme { OptionButton( modifier = Modifier.fillMaxWidth(), - title = stringResource(R.string.title_pref_pro_mode), - text = stringResource(R.string.summary_pref_pro_mode), + title = stringResource(R.string.title_pref_expert_mode), + text = stringResource(R.string.summary_pref_expert_mode), onClick = {}, ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionPageButton.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionPageButton.kt index f85de7a9d3..b01a3d0343 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionPageButton.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionPageButton.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.OfflineBolt import androidx.compose.material.icons.rounded.ChevronRight import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -23,8 +24,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.compose.KeyMapperTheme -import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIcon @Composable fun OptionPageButton( @@ -84,9 +83,9 @@ private fun Preview() { KeyMapperTheme { OptionPageButton( modifier = Modifier.fillMaxWidth(), - title = stringResource(R.string.title_pref_pro_mode), - text = stringResource(R.string.summary_pref_pro_mode), - icon = KeyMapperIcons.ProModeIcon, + title = stringResource(R.string.title_pref_expert_mode), + text = stringResource(R.string.summary_pref_expert_mode), + icon = Icons.Outlined.OfflineBolt, onClick = {}, ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SetupRequirementRow.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SetupRequirementRow.kt index f65b906060..ee835b2c0f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SetupRequirementRow.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SetupRequirementRow.kt @@ -22,13 +22,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.github.sds100.keymapper.base.R -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus @Composable -fun ProModeRequirementRow( +fun ExpertModeRequirementRow( modifier: Modifier = Modifier, isVisible: Boolean, - proModeStatus: ProModeStatus, + expertModeStatus: ExpertModeStatus, buttonColors: ButtonColors = ButtonDefaults.filledTonalButtonColors(), onClick: () -> Unit, ) { @@ -39,19 +39,21 @@ fun ProModeRequirementRow( ) { SetupRequirementRow( modifier = modifier, - text = stringResource(R.string.trigger_setup_pro_mode_title), + text = stringResource(R.string.trigger_setup_expert_mode_title), ) { - if (proModeStatus == ProModeStatus.UNSUPPORTED) { + if (expertModeStatus == ExpertModeStatus.UNSUPPORTED) { Text( - text = stringResource(R.string.trigger_setup_pro_mode_unsupported), + text = stringResource(R.string.trigger_setup_expert_mode_unsupported), color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodyMedium, ) } else { SetupRequirementButton( - enabledText = stringResource(R.string.trigger_setup_pro_mode_enable_button), - disabledText = stringResource(R.string.trigger_setup_pro_mode_running_button), - isEnabled = proModeStatus != ProModeStatus.ENABLED, + enabledText = stringResource(R.string.trigger_setup_expert_mode_enable_button), + disabledText = stringResource( + R.string.trigger_setup_expert_mode_running_button, + ), + isEnabled = expertModeStatus != ExpertModeStatus.ENABLED, colors = buttonColors, onClick = onClick, ) @@ -103,7 +105,9 @@ fun InputMethodRequirementRow( !isEnabled && enablingRequiresUserInput -> stringResource( R.string.trigger_setup_input_method_enable_button, ) + !isChosen -> stringResource(R.string.trigger_setup_input_method_choose_button) + else -> "" } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeDisabled.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeDisabled.kt deleted file mode 100644 index 7e6435500b..0000000000 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeDisabled.kt +++ /dev/null @@ -1,162 +0,0 @@ -package io.github.sds100.keymapper.base.utils.ui.compose.icons - -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.PathFillType -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.StrokeCap -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.graphics.vector.PathData -import androidx.compose.ui.graphics.vector.group -import androidx.compose.ui.graphics.vector.path -import androidx.compose.ui.unit.dp - -val KeyMapperIcons.ProModeIconDisabled: ImageVector - get() { - if (_ProModeDisabled != null) { - return _ProModeDisabled!! - } - _ProModeDisabled = ImageVector.Builder( - name = "ProModeDisabled", - defaultWidth = 32.dp, - defaultHeight = 32.dp, - viewportWidth = 32f, - viewportHeight = 32f, - ).apply { - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, -0f) - lineTo(32f, -0f) - lineTo(32f, 32f) - lineTo(-0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(4f, 11f) - verticalLineToRelative(10f) - horizontalLineToRelative(2f) - verticalLineToRelative(-4f) - horizontalLineToRelative(2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, -2f) - verticalLineTo(13f) - arcTo(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 8f, 11f) - horizontalLineTo(4f) - moveToRelative(2f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(2f) - horizontalLineTo(6f) - close() - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(13f, 11f) - verticalLineToRelative(10f) - horizontalLineToRelative(2f) - verticalLineToRelative(-4f) - horizontalLineToRelative(0.8f) - lineToRelative(1.2f, 4f) - horizontalLineToRelative(2f) - lineTo(17.76f, 16.85f) - curveTo(18.5f, 16.55f, 19f, 15.84f, 19f, 15f) - verticalLineToRelative(-2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, -2f) - horizontalLineToRelative(-4f) - moveToRelative(2f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(2f) - horizontalLineToRelative(-2f) - close() - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(24f, 11f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, 2f) - verticalLineToRelative(6f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, 2f) - horizontalLineToRelative(2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, -2f) - verticalLineToRelative(-6f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, -2f) - horizontalLineToRelative(-2f) - moveToRelative(0f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(6f) - horizontalLineToRelative(-2f) - close() - } - path( - fill = SolidColor(Color(0xFF808080)), - stroke = SolidColor(Color.Black), - strokeLineWidth = 2f, - strokeLineCap = StrokeCap.Round, - pathFillType = PathFillType.EvenOdd, - ) { - moveTo(26.664f, 5.353f) - lineTo(5.354f, 26.753f) - } - }.build() - - return _ProModeDisabled!! - } - -@Suppress("ObjectPropertyName") -private var _ProModeDisabled: ImageVector? = null diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeIcon.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeIcon.kt deleted file mode 100644 index ec3368f4da..0000000000 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeIcon.kt +++ /dev/null @@ -1,150 +0,0 @@ -package io.github.sds100.keymapper.base.utils.ui.compose.icons - -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.graphics.vector.PathData -import androidx.compose.ui.graphics.vector.group -import androidx.compose.ui.graphics.vector.path -import androidx.compose.ui.unit.dp - -val KeyMapperIcons.ProModeIcon: ImageVector - get() { - if (_ProMode != null) { - return _ProMode!! - } - _ProMode = ImageVector.Builder( - name = "ProMode", - defaultWidth = 32.dp, - defaultHeight = 32.dp, - viewportWidth = 32f, - viewportHeight = 32f, - ).apply { - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, -0f) - lineTo(32f, -0f) - lineTo(32f, 32f) - lineTo(-0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(4f, 11f) - verticalLineToRelative(10f) - horizontalLineToRelative(2f) - verticalLineToRelative(-4f) - horizontalLineToRelative(2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, -2f) - verticalLineTo(13f) - arcTo(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 8f, 11f) - horizontalLineTo(4f) - moveToRelative(2f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(2f) - horizontalLineTo(6f) - close() - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(13f, 11f) - verticalLineToRelative(10f) - horizontalLineToRelative(2f) - verticalLineToRelative(-4f) - horizontalLineToRelative(0.8f) - lineToRelative(1.2f, 4f) - horizontalLineToRelative(2f) - lineTo(17.76f, 16.85f) - curveTo(18.5f, 16.55f, 19f, 15.84f, 19f, 15f) - verticalLineToRelative(-2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, -2f) - horizontalLineToRelative(-4f) - moveToRelative(2f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(2f) - horizontalLineToRelative(-2f) - close() - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(24f, 11f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, 2f) - verticalLineToRelative(6f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, 2f) - horizontalLineToRelative(2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, -2f) - verticalLineToRelative(-6f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, -2f) - horizontalLineToRelative(-2f) - moveToRelative(0f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(6f) - horizontalLineToRelative(-2f) - close() - } - }.build() - - return _ProMode!! - } - -@Suppress("ObjectPropertyName") -private var _ProMode: ImageVector? = null diff --git a/base/src/main/res/drawable/offline_bolt_24px.xml b/base/src/main/res/drawable/offline_bolt_24px.xml new file mode 100644 index 0000000000..f81aae54c5 --- /dev/null +++ b/base/src/main/res/drawable/offline_bolt_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/base/src/main/res/drawable/pro_mode.xml b/base/src/main/res/drawable/pro_mode.xml deleted file mode 100644 index a0062fc0ed..0000000000 --- a/base/src/main/res/drawable/pro_mode.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/base/src/main/res/values-pt/strings.xml b/base/src/main/res/values-pt/strings.xml index cce432e1a8..929ea6f23a 100644 --- a/base/src/main/res/values-pt/strings.xml +++ b/base/src/main/res/values-pt/strings.xml @@ -532,8 +532,8 @@ Deixe em branco se alguma rede Wi-Fi precisar ser correspondida. Escolha quais dispositivos acionam a troca automática de teclado Registros Isso pode adicionar latência aos seus mapas de teclas, portanto, ative-o somente se estiver tentando depurar o aplicativo ou se o desenvolvedor tiver solicitado. - Use o modo PRO - Detecção avançada de eventos importantes e muito mais + Use o modo PRO + Detecção avançada de eventos importantes e muito mais Luz Escuro Sistema @@ -1220,7 +1220,7 @@ Deixe em branco se alguma rede Wi-Fi precisar ser correspondida. Use o código de chave %d Use o código de digitalização %d Nenhum código de digitalização salvo - Use o modo PRO + Use o modo PRO Adicionar mais Remover @@ -1276,85 +1276,85 @@ Deixe em branco se alguma rede Wi-Fi precisar ser correspondida. +%d restrições herdadas - Modo PRO - Importante! - Remapear botões no modo PRO é perigoso e pode fazer com que eles parem de funcionar se você os remapear incorretamente.\n\nSe você cometer um erro, pode ser necessário reiniciar o dispositivo à força segurando os botões de ligar e volume por 30 segundos — consulte o manual do seu dispositivo ou a internet para saber como fazer isso. - %d… - Eu entendo - Entendido - Configurar - Root detectado - Você pode pular o processo de configuração concedendo permissão de root ao Key Mapper. Isso permitirá que o Key Mapper inicie automaticamente o modo PRO na inicialização. - Iniciar modo PRO - Shizuku detectada - Você pode pular o processo de configuração dando permissão ao Key Mapper Shizuku. - Iniciar Shizuku - Solicitar permissão - Iniciar modo PRO - Configurar com o Key Mapper - Continuar - Continuar (Android 11+) - Opções - Habilitar modo PRO para todos os mapas principais - O Key Mapper usará o ADB Shell para remapeamento - Essas configurações ficarão indisponíveis até que você confirme o aviso. - O serviço do modo PRO está em execução - Parar - Iniciar automaticamente na inicialização - O Modo PRO será iniciado sempre que você ligar ou reiniciar seu dispositivo - Dica de emergência - Se o botão liga/desliga parar de funcionar, mantenha-o pressionado por 10 segundos e solte para desativar o Modo PRO. - Assistente de configuração - Etapa %d de %d - Use o assistente de configuração interativo - Interagir automaticamente com as configurações - Habilitar primeiro o serviço de acessibilidade - Assista ao tutorial - Iniciar serviço - Vá para as configurações - Iniciar serviço - Ativar serviço de acessibilidade - O Key Mapper usa um serviço para ajudar você a configurar o modo PRO. Ele também é útil para mapeamentos de teclas comuns. - Habilitar opções do desenvolvedor - O Key Mapper precisa usar o Android Debug Bridge para iniciar o modo PRO, e você precisa habilitar as opções do desenvolvedor para isso. - Conectar a uma rede WiFi - O Key Mapper precisa de uma rede Wi-Fi para habilitar o ADB. Você não precisa de conexão com a internet.\n\nSem rede Wi-Fi? Use um ponto de acesso do celular de outra pessoa. - Habilitar depuração sem fio - O Key Mapper usa depuração sem fio para iniciar seu serviço de remapeamento e entrada. - Emparelhar depuração sem fio - O Key Mapper precisa ser pareado com a depuração sem fio antes de poder iniciar seu serviço de remapeamento e entrada. - Iniciar serviço - O Key Mapper precisa se conectar ao Android Debug Bridge para iniciar o serviço do modo PRO. - Permitir notificações - O Key Mapper precisa de permissão para notificá-lo caso haja algum problema com o processo de configuração. - Dê permissão - Assistente de configuração - O modo PRO está em execução - Agora você pode remapear botões quando a tela estiver desligada e usar mais ações. - Terminar - Habilitar opções do desenvolvedor - Toque repetidamente no número da compilação - Emparelhamento automático - Procurando código de pareamento e porta… - Não é possível encontrar a porta e o código de emparelhamento - Toque no botão para parear com o código de pareamento e digite o código aqui - Falha ao iniciar o modo PRO - Toque para configurar novamente. Tente o pareamento ADB e reinicie o telefone se o problema persistir. - Modo PRO de inicialização automática - Usando root - Usando shizuku - Usando ADB via WiFi - Modo PRO iniciado - Divirta-se remapeando! ❤️ - Falha no emparelhamento - Mantenha o pop-up do código de emparelhamento na tela ao enviar o código de emparelhamento - Código de pareamento de entrada - O que posso fazer com o modo PRO? - 📲 Você pode remapear mais botões, como o botão de energia. + Modo PRO + Importante! + Remapear botões no modo PRO é perigoso e pode fazer com que eles parem de funcionar se você os remapear incorretamente.\n\nSe você cometer um erro, pode ser necessário reiniciar o dispositivo à força segurando os botões de ligar e volume por 30 segundos — consulte o manual do seu dispositivo ou a internet para saber como fazer isso. + %d… + Eu entendo + Entendido + Configurar + Root detectado + Você pode pular o processo de configuração concedendo permissão de root ao Key Mapper. Isso permitirá que o Key Mapper inicie automaticamente o modo PRO na inicialização. + Iniciar modo PRO + Shizuku detectada + Você pode pular o processo de configuração dando permissão ao Key Mapper Shizuku. + Iniciar Shizuku + Solicitar permissão + Iniciar modo PRO + Configurar com o Key Mapper + Continuar + Continuar (Android 11+) + Opções + Habilitar modo PRO para todos os mapas principais + O Key Mapper usará o ADB Shell para remapeamento + Essas configurações ficarão indisponíveis até que você confirme o aviso. + O serviço do modo PRO está em execução + Parar + Iniciar automaticamente na inicialização + O Modo PRO será iniciado sempre que você ligar ou reiniciar seu dispositivo + Dica de emergência + Se o botão liga/desliga parar de funcionar, mantenha-o pressionado por 10 segundos e solte para desativar o Modo PRO. + Assistente de configuração + Etapa %d de %d + Use o assistente de configuração interativo + Interagir automaticamente com as configurações + Habilitar primeiro o serviço de acessibilidade + Assista ao tutorial + Iniciar serviço + Vá para as configurações + Iniciar serviço + Ativar serviço de acessibilidade + O Key Mapper usa um serviço para ajudar você a configurar o modo PRO. Ele também é útil para mapeamentos de teclas comuns. + Habilitar opções do desenvolvedor + O Key Mapper precisa usar o Android Debug Bridge para iniciar o modo PRO, e você precisa habilitar as opções do desenvolvedor para isso. + Conectar a uma rede WiFi + O Key Mapper precisa de uma rede Wi-Fi para habilitar o ADB. Você não precisa de conexão com a internet.\n\nSem rede Wi-Fi? Use um ponto de acesso do celular de outra pessoa. + Habilitar depuração sem fio + O Key Mapper usa depuração sem fio para iniciar seu serviço de remapeamento e entrada. + Emparelhar depuração sem fio + O Key Mapper precisa ser pareado com a depuração sem fio antes de poder iniciar seu serviço de remapeamento e entrada. + Iniciar serviço + O Key Mapper precisa se conectar ao Android Debug Bridge para iniciar o serviço do modo PRO. + Permitir notificações + O Key Mapper precisa de permissão para notificá-lo caso haja algum problema com o processo de configuração. + Dê permissão + Assistente de configuração + O modo PRO está em execução + Agora você pode remapear botões quando a tela estiver desligada e usar mais ações. + Terminar + Habilitar opções do desenvolvedor + Toque repetidamente no número da compilação + Emparelhamento automático + Procurando código de pareamento e porta… + Não é possível encontrar a porta e o código de emparelhamento + Toque no botão para parear com o código de pareamento e digite o código aqui + Falha ao iniciar o modo PRO + Toque para configurar novamente. Tente o pareamento ADB e reinicie o telefone se o problema persistir. + Modo PRO de inicialização automática + Usando root + Usando shizuku + Usando ADB via WiFi + Modo PRO iniciado + Divirta-se remapeando! ❤️ + Falha no emparelhamento + Mantenha o pop-up do código de emparelhamento na tela ao enviar o código de emparelhamento + Código de pareamento de entrada + O que posso fazer com o modo PRO? + 📲 Você pode remapear mais botões, como o botão de energia. ⌨️ Use qualquer teclado com ações de código de tecla. ⭐️ As seguintes ações estão desbloqueadas: Wi-Fi, Bluetooth, dados móveis, NFC, modo avião, recolher barra de status e ligar/desligar a tela do dispositivo. - Mostrar informações do modo PRO - Descartar + Mostrar informações do modo PRO + Descartar O modo PRO parou inesperadamente Reiniciando automaticamente… Não reinicia automaticamente. Se você não estiver encerrando o serviço, informe o problema ao desenvolvedor. @@ -1413,10 +1413,10 @@ Deixe em branco se alguma rede Wi-Fi precisar ser correspondida. Habilitar Em execução Comprado - Modo PRO - Ativar gratuitamente - Em execução - Não disponível nesta versão do Android + Modo PRO + Ativar gratuitamente + Em execução + Não disponível nesta versão do Android Se o assistente do seu dispositivo for acionado por um botão de hardware, talvez seja possível remapeá-lo gratuitamente com o modo PRO. Tente escolher \"Outro\" na página de acionamento e siga as instruções. Este gatilho requer alguma configuração que varia de acordo com o dispositivo. Por favor, leia as instruções diff --git a/base/src/main/res/values-tr/strings.xml b/base/src/main/res/values-tr/strings.xml index bc9c2d4c71..f53589ee3f 100644 --- a/base/src/main/res/values-tr/strings.xml +++ b/base/src/main/res/values-tr/strings.xml @@ -392,7 +392,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Bunun çalışması için rootlu olmanız veya PRO modunu etkinleştirmiş olmanız gerekir. Sistem bu izni verdiğinde uygulamayı kapatacaktır. Uygulamayı tekrar kendiniz açmanız ve logcat\'i tekrar paylaşmayı denemeniz gerekecek. Bitti Durdur - PRO modunu kullan + PRO modunu kullan Değiştir Kısmen düzelt Tamam @@ -526,8 +526,8 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Hangi cihazların otomatik klavye değiştirmeyi tetikleyeceğini seçin Günlük kaydı Bu, tuş eşlemelerinize gecikme ekleyebilir, bu yüzden yalnızca uygulamayı hata ayıklamaya çalışıyorsanız veya geliştirici tarafından istenmişse açın. - PRO modunu kullan - Gelişmiş tuş olayı algılama ve daha fazlası + PRO modunu kullan + Gelişmiş tuş olayı algılama ve daha fazlası Açık Koyu Sistem @@ -929,8 +929,8 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. ADB Çalıştırma Modu ADB modu akış çıktısını desteklemiyor - PRO Modunu Kur - PRO Modunu Kur (Desteklenmiyor) + PRO Modunu Kur + PRO Modunu Kur (Desteklenmiyor) Yapılandırma Çıktı Henüz çıktı yok. Komutu çalıştırmak için Test\'e tıklayın. @@ -1171,12 +1171,12 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Kayan düğmeler istediğiniz uygulamaların üzerinde görünür. Gerçek düğmeler gibi çalışırlar ve onları istediğiniz gibi yerleştirebilir, stil verebilir ve eşleyebilirsiniz. Kayan düğme %s (%s) Silinen kayan düğme - Daha iyi Caps Lock uyumluluğu - PRO modu, Caps Lock yeniden eşlemesi için daha iyi uyumluluk sağlar. \'PRO modunu kullan\' seçeneğine dokunun ve tekrar kaydedin. - PRO modunu kullan - Ekran kapalıyken yeniden eşleme? - PRO modu ile artık herhangi bir tetikleyici ekran kapalıyken ücretsiz olarak çalışabilir! Tekrar kaydetmeniz gerekecek. - PRO modunu kullan + Daha iyi Caps Lock uyumluluğu + PRO modu, Caps Lock yeniden eşlemesi için daha iyi uyumluluk sağlar. \'PRO modunu kullan\' seçeneğine dokunun ve tekrar kaydedin. + PRO modunu kullan + Ekran kapalıyken yeniden eşleme? + PRO modu ile artık herhangi bir tetikleyici ekran kapalıyken ücretsiz olarak çalışabilir! Tekrar kaydetmeniz gerekecek. + PRO modunu kullan Uygulama sabitleme uyarısı Geri düğmesini tetikleyici olarak kullanmak uygulama sabitleme ile çakışabilir ve kilit açıldığında siyah ekrana neden olabilir. Yeniden başlatma sorunu çözecektir. Klavye simgesi @@ -1321,7 +1321,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Tuş kodu %d kullan Tarama kodu %d kullan Kaydedilmiş tarama kodu yok - PRO modu ile kaydet + PRO modu ile kaydet Daha fazla ekle Kaldır @@ -1381,88 +1381,88 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. +%d devralınan kısıtlamalar - PRO modu - Kurulum - Önemli! - PRO modu ile tuşları yeniden eşlemek tehlikelidir ve yanlış eşlerseniz çalışmalarını durdurabilir.\n\nBir hata yaparsanız, güç ve ses düğmelerini 30 saniye basılı tutarak cihazınızı zorla yeniden başlatmanız gerekebilir — bunun nasıl yapılacağı konusunda cihazınızın kılavuzuna veya internete başvurun. - %d… - Anlıyorum - Anladım - Kurulum - Root algılandı - Key Mapper\'a root izni vererek kurulum sürecini atlayabilirsiniz. Bu, Key Mapper\'ın WiFi bağlantısını beklemeden PRO modunu otomatik olarak başlatmasını da sağlar. - PRO modunu başlat - Shizuku algılandı - Key Mapper\'a Shizuku izni vererek kurulum sürecini atlayabilirsiniz. - Shizuku\'yu başlat - İzin iste - PRO modunu başlat - Key Mapper ile kur - Devam et - Devam et (Android 11+) - Seçenekler - Tüm tuş eşlemeleri için PRO modunu etkinleştir - Key Mapper yeniden eşleme için ADB Shell\'i kullanacak - Bu ayarlar, uyarıyı kabul edene kadar kullanılamaz. - PRO modu hizmeti çalışıyor - Durdur - Otomatik başlat ve çalışır durumda tut - PRO Modu, cihazınızı başlattığınızda, Key Mapper\'ı açtığınızda veya beklenmedik bir şekilde kapandığında kendini başlatır. + PRO modu + Kurulum + Önemli! + PRO modu ile tuşları yeniden eşlemek tehlikelidir ve yanlış eşlerseniz çalışmalarını durdurabilir.\n\nBir hata yaparsanız, güç ve ses düğmelerini 30 saniye basılı tutarak cihazınızı zorla yeniden başlatmanız gerekebilir — bunun nasıl yapılacağı konusunda cihazınızın kılavuzuna veya internete başvurun. + %d… + Anlıyorum + Anladım + Kurulum + Root algılandı + Key Mapper\'a root izni vererek kurulum sürecini atlayabilirsiniz. Bu, Key Mapper\'ın WiFi bağlantısını beklemeden PRO modunu otomatik olarak başlatmasını da sağlar. + PRO modunu başlat + Shizuku algılandı + Key Mapper\'a Shizuku izni vererek kurulum sürecini atlayabilirsiniz. + Shizuku\'yu başlat + İzin iste + PRO modunu başlat + Key Mapper ile kur + Devam et + Devam et (Android 11+) + Seçenekler + Tüm tuş eşlemeleri için PRO modunu etkinleştir + Key Mapper yeniden eşleme için ADB Shell\'i kullanacak + Bu ayarlar, uyarıyı kabul edene kadar kullanılamaz. + PRO modu hizmeti çalışıyor + Durdur + Otomatik başlat ve çalışır durumda tut + PRO Modu, cihazınızı başlattığınızda, Key Mapper\'ı açtığınızda veya beklenmedik bir şekilde kapandığında kendini başlatır. Tuş olayı eylemleri Tuş olayı eylemlerinin nasıl gerçekleştirileceğini seçin - Acil durum ipucu - Güç düğmeniz çalışmazsa, PRO Modu\'nu devre dışı bırakmak için güç düğmesini 10 saniye basılı tutun ve bırakın. - Kurulum sihirbazı - Adım %d / %d - Etkileşimli kurulum asistanını kullan - Ayarlarla otomatik olarak etkileşime gir - Önce erişilebilirlik servisini etkinleştir - Eğitimi izle - Servisi başlat - Ayarlara git - Servisi başlat - Erişilebilirlik servisini etkinleştir - Key Mapper bu servisi kurulumda yardımcı olması için kullanır. Ayrıca sıradan tuş eşlemeleri için de gereklidir. - Geliştirici seçeneklerini etkinleştir - Key Mapper\'ın Android Hata Ayıklama Köprüsü\'nü (ADB) kullanması gerekir, bu nedenle geliştirici seçeneklerini etkinleştirmelisiniz. - Bir WiFi ağına bağlan - Key Mapper\'ın ADB\'yi etkinleştirmek için bir WiFi ağına ihtiyacı var. İnternet bağlantısına ihtiyacınız yok.\n\nWiFi ağı yok mu? Başka birinin telefonundan bir hotspot kullanın. - Kablosuz hata ayıklamayı etkinleştir - Key Mapper, yeniden eşleme ve giriş hizmetini başlatmak için kablosuz hata ayıklamayı kullanır. - Kablosuz hata ayıklamayı eşleştir - Key Mapper\'ın yeniden eşleme ve giriş hizmetini başlatabilmesi için kablosuz hata ayıklama ile eşleşmesi gerekir. - Servisi başlat - Key Mapper\'ın servisi başlatmak için ADB\'ye bağlanması gerekir. - Bildirimlere izin ver - Key Mapper\'ın kurulum sürecinde herhangi bir sorun olması durumunda sizi bilgilendirmek için izne ihtiyacı var. - İzin ver - Uyumsuz USB yapılandırması - PRO Modunun cihazınızı her kilitlediğinizde sonlandırılmaması için varsayılan USB yapılandırmanız olarak \'Veri aktarımı yok\' seçeneğini seçmelisiniz. - Kurulum asistanı - PRO modu çalışıyor - Artık ekran kapalıyken tuşları yeniden eşleyebilir ve daha fazla eylem kullanabilirsiniz. - Bitir - Geliştirici seçeneklerini etkinleştir - Yapı numarasına tekrar tekrar dokun - Otomatik olarak eşleştiriliyor - Eşleştirme kodu ve portu aranıyor… - Eşleştirme portu ve kodu bulunamadı - Eşleştirme koduyla eşleştirmek için düğmeye dokunun ve kodu buraya yazın - PRO Modu başlatılamadı - Tekrar kurmak için dokunun. Tekrar tekrar başarısız olursa ADB eşleştirmeyi ve telefonunuzu yeniden başlatmayı deneyin. - PRO modu otomatik başlatılıyor - Root kullanılıyor - Shizuku kullanılıyor - WiFi üzerinden ADB kullanılıyor - PRO modu başlatıldı - Yeniden eşlemede iyi eğlenceler! ❤️ - Eşleştirme başarısız - Eşleştirme kodunu gönderirken eşleştirme kodu açılır penceresini ekranda tutun - Eşleştirme kodunu gir - PRO modu ile ne yapabilirim? - 📲 Güç düğmeniz gibi daha fazla düğmeyi yeniden eşleyebilirsiniz.\n⌨️ Tuş kodu eylemleriyle herhangi bir ekran klavyesini kullanabilirsiniz.\n⭐️ Ve daha birçok eylemi kullanabilirsiniz. - PRO modu bilgilerini göster - Kapat + Acil durum ipucu + Güç düğmeniz çalışmazsa, PRO Modu\'nu devre dışı bırakmak için güç düğmesini 10 saniye basılı tutun ve bırakın. + Kurulum sihirbazı + Adım %d / %d + Etkileşimli kurulum asistanını kullan + Ayarlarla otomatik olarak etkileşime gir + Önce erişilebilirlik servisini etkinleştir + Eğitimi izle + Servisi başlat + Ayarlara git + Servisi başlat + Erişilebilirlik servisini etkinleştir + Key Mapper bu servisi kurulumda yardımcı olması için kullanır. Ayrıca sıradan tuş eşlemeleri için de gereklidir. + Geliştirici seçeneklerini etkinleştir + Key Mapper\'ın Android Hata Ayıklama Köprüsü\'nü (ADB) kullanması gerekir, bu nedenle geliştirici seçeneklerini etkinleştirmelisiniz. + Bir WiFi ağına bağlan + Key Mapper\'ın ADB\'yi etkinleştirmek için bir WiFi ağına ihtiyacı var. İnternet bağlantısına ihtiyacınız yok.\n\nWiFi ağı yok mu? Başka birinin telefonundan bir hotspot kullanın. + Kablosuz hata ayıklamayı etkinleştir + Key Mapper, yeniden eşleme ve giriş hizmetini başlatmak için kablosuz hata ayıklamayı kullanır. + Kablosuz hata ayıklamayı eşleştir + Key Mapper\'ın yeniden eşleme ve giriş hizmetini başlatabilmesi için kablosuz hata ayıklama ile eşleşmesi gerekir. + Servisi başlat + Key Mapper\'ın servisi başlatmak için ADB\'ye bağlanması gerekir. + Bildirimlere izin ver + Key Mapper\'ın kurulum sürecinde herhangi bir sorun olması durumunda sizi bilgilendirmek için izne ihtiyacı var. + İzin ver + Uyumsuz USB yapılandırması + PRO Modunun cihazınızı her kilitlediğinizde sonlandırılmaması için varsayılan USB yapılandırmanız olarak \'Veri aktarımı yok\' seçeneğini seçmelisiniz. + Kurulum asistanı + PRO modu çalışıyor + Artık ekran kapalıyken tuşları yeniden eşleyebilir ve daha fazla eylem kullanabilirsiniz. + Bitir + Geliştirici seçeneklerini etkinleştir + Yapı numarasına tekrar tekrar dokun + Otomatik olarak eşleştiriliyor + Eşleştirme kodu ve portu aranıyor… + Eşleştirme portu ve kodu bulunamadı + Eşleştirme koduyla eşleştirmek için düğmeye dokunun ve kodu buraya yazın + PRO Modu başlatılamadı + Tekrar kurmak için dokunun. Tekrar tekrar başarısız olursa ADB eşleştirmeyi ve telefonunuzu yeniden başlatmayı deneyin. + PRO modu otomatik başlatılıyor + Root kullanılıyor + Shizuku kullanılıyor + WiFi üzerinden ADB kullanılıyor + PRO modu başlatıldı + Yeniden eşlemede iyi eğlenceler! ❤️ + Eşleştirme başarısız + Eşleştirme kodunu gönderirken eşleştirme kodu açılır penceresini ekranda tutun + Eşleştirme kodunu gir + PRO modu ile ne yapabilirim? + 📲 Güç düğmeniz gibi daha fazla düğmeyi yeniden eşleyebilirsiniz.\n⌨️ Tuş kodu eylemleriyle herhangi bir ekran klavyesini kullanabilirsiniz.\n⭐️ Ve daha birçok eylemi kullanabilirsiniz. + PRO modu bilgilerini göster + Kapat PRO modu beklenmedik şekilde durdu Otomatik olarak yeniden başlatılıyor… Son otomatik başlatma 5 dakikadan daha kısa bir süre önce olduğu için otomatik olarak yeniden başlatılmıyor. Servisi siz sonlandırmıyorsanız, sorunu geliştiriciye bildirin. @@ -1522,10 +1522,10 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Etkinleştir Çalışıyor Satın alındı - PRO modu - Ücretsiz etkinleştir - Çalışıyor - Bu Android sürümünde mevcut değil + PRO modu + Ücretsiz etkinleştir + Çalışıyor + Bu Android sürümünde mevcut değil Cihaz asistanınız bir donanım düğmesiyle tetikleniyorsa, PRO moduyla ücretsiz olarak yeniden eşlemek mümkün olabilir. Tetikleyici sayfasından \'Diğer\'i seçmeyi deneyin ve talimatları izleyin. Bu tetikleyici, cihaza göre değişen bazı kurulumlar gerektirir. Lütfen\ talimatları okuyun @@ -1545,8 +1545,8 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Bu eylemi kullanmak için ek adımlar vardır. Hangi yöntemi kullanmak istediğinizi seçin: Key Mapper giriş yöntemi Ekran klavyesi yok - Normal ekran klavyenizi her zaman kullanabilirsiniz - Uygulamalar ve oyunlarla daha iyi uyumluluk + Normal ekran klavyenizi her zaman kullanabilirsiniz + Uygulamalar ve oyunlarla daha iyi uyumluluk Key Mapper giriş yöntemini etkinleştir Key Mapper giriş yöntemini kullan Yazarken normal ekran klavyesine otomatik olarak geç diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index 26b8a0d5aa..3023a3737a 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -77,8 +77,8 @@ Too many fingers to perform gesture due to android limitations. Gesture duration is too high due to android limitations. You must be using the Key Mapper Input Method for DPAD triggers to work! - PRO mode is unsupported on this Android version - PRO mode not started! + Expert mode is unsupported on this Android version + Expert mode not started! Trigger device not connected! Migrate this screen off trigger @@ -436,9 +436,9 @@ Please grant Key Mapper root permission in your root management app, such as Magisk. Grant WRITE_SECURE_SETTINGS permission - You will need to use PRO mode to grant this permission. + You will need to use Expert mode to grant this permission. - Your device doesn\'t seem to have an accessibility services settings page. You can set up PRO mode or run the ADB command \"adb shell pm grant io.github.sds100.keymapper android.permission.WRITE_SECURE_SETTINGS\". + Your device doesn\'t seem to have an accessibility services settings page. You can set up Expert mode or run the ADB command \"adb shell pm grant io.github.sds100.keymapper android.permission.WRITE_SECURE_SETTINGS\". You must hold down the keys in the order that they are listed. There is a timeout to input this trigger. You can change this timeout in the "Options" tab. How to use this trigger @@ -487,16 +487,16 @@ Some actions and options need this permission to work. You can also get notified when there is important news about the app. Migrate this screen off trigger - We\'re sorry for the disruption. We\'ve introduced a *free* feature called PRO mode that replaces the old screen off remapping option. This feature is much more reliable and unlocks remapping the power button too.\n\nDue to the way it works you will need to record this trigger again with PRO mode. + We\'re sorry for the disruption. We\'ve introduced a *free* feature called Expert mode that replaces the old screen off remapping option. This feature is much more reliable and unlocks remapping the power button too.\n\nDue to the way it works you will need to record this trigger again with Expert mode. Proceed Grant READ_LOGS permission - You will need to be rooted or have enabled PRO mode for this to work. The system will then close the app when it grants this permission. You will need to open it again yourself and try sharing the logcat again. + You will need to be rooted or have enabled Expert mode for this to work. The system will then close the app when it grants this permission. You will need to open it again yourself and try sharing the logcat again. Done Kill - Use PRO mode + Use Expert mode Change Fix partially Ok @@ -682,8 +682,8 @@ Logging This may add latency to your key maps so only turn this on if you are trying to debug the app or have been asked to by the developer. - Use PRO mode - Advanced detection of key events and more + Use Expert mode + Advanced detection of key events and more Light Dark @@ -893,7 +893,7 @@ UI element not found! Command timed out after %1$d seconds - PRO Mode needs starting + Expert mode needs starting Rate limit reached. You can only send once per second. @@ -1150,8 +1150,8 @@ ADB Execution Mode ADB mode does not support streaming output - Setup PRO Mode - Setup PRO Mode (Unsupported) + Setup Expert mode + Setup Expert mode (Unsupported) Configuration Output No output yet. Click Test to run the command. @@ -1430,19 +1430,19 @@ Floating buttons display over the apps you want. They work just like real buttons, and you can place, style, and map them however you like. Floating button %s (%s) Deleted floating button - Better Caps Lock compatibility - PRO mode provides better compatibility for Caps Lock remapping. Tap \'Use PRO mode\' and record it again. - Use PRO mode - Screen off remapping? - Any trigger can now work when the screen is off for free with PRO mode! You will need to record it again. - Use PRO mode + Better Caps Lock compatibility + Expert mode provides better compatibility for Caps Lock remapping. Tap \'Use Expert mode\' and record it again. + Use Expert mode + Screen off remapping? + Any trigger can now work when the screen is off for free with Expert mode! You will need to record it again. + Use Expert mode App pinning warning Using the back button as a trigger may conflict with app pinning, causing a black screen on unlock. A reboot will fix it. Keyboard icon The ⌨ symbol means you must use the Key Mapper input method for this trigger to work due to an Android restriction. Ringer mode actions - Consider using PRO mode for ringer mode actions to avoid conflicts with Do Not Disturb settings. - Use PRO mode + Consider using Expert mode for ringer mode actions to avoid conflicts with Do Not Disturb settings. + Use Expert mode Limit to specific apps? Add constraints in the Constraints tab. Choose a layout @@ -1584,7 +1584,7 @@ Use key code %d Use scan code %d No scan code saved - Use PRO mode + Use Expert mode Add more @@ -1651,114 +1651,114 @@ - - PRO mode - Setup - Important! - Remapping buttons with PRO mode is dangerous and can cause them to stop working if you remap them incorrectly.\n\nIf you make a mistake, you may need to force restart your device by holding down the power and volume buttons for 30 seconds — consult your device\'s manual or the internet for how to do this. - %d… - I understand - Understood - Set up - Root detected - You can skip the set up process by giving Key Mapper root permission. This will let Key Mapper auto start PRO mode without having to wait for a WiFi connection as well. - Start PRO mode - Shizuku detected - You can skip the set up process by giving Key Mapper Shizuku permission. - Start Shizuku - Request permission - Start PRO mode - Set up with Key Mapper - Continue - (Requires Android 11+) - Options - Enable PRO mode for all key maps - Key Mapper will use the ADB Shell for remapping - These settings are unavailable until you acknowledge the warning. - PRO mode service is running - Stop - - Auto start and keep alive - PRO Mode will start itself whenever you boot your device, open Key Mapper, or it dies unexpectedly. + + Expert mode + Setup + Important! + Remapping buttons with Expert mode is dangerous and can cause them to stop working if you remap them incorrectly.\n\nIf you make a mistake, you may need to force restart your device by holding down the power and volume buttons for 30 seconds — consult your device\'s manual or the internet for how to do this. + %d… + I understand + Understood + Set up + Root detected + You can skip the set up process by giving Key Mapper root permission. This will let Key Mapper auto start Expert mode without having to wait for a WiFi connection as well. + Start Expert mode + Shizuku detected + You can skip the set up process by giving Key Mapper Shizuku permission. + Start Shizuku + Request permission + Start Expert mode + Set up with Key Mapper + Continue + (Requires Android 11+) + Options + Enable Expert mode for all key maps + Key Mapper will use the ADB Shell for remapping + These settings are unavailable until you acknowledge the warning. + Expert mode service is running + Stop + + Auto start and keep alive + Expert mode will start itself whenever you boot your device, open Key Mapper, or it dies unexpectedly. Key event actions Select how key event actions are performed - Emergency tip - If your power button stops working, hold down the power button for 10 seconds and release to disable PRO Mode. + Emergency tip + If your power button stops working, hold down the power button for 10 seconds and release to disable Expert mode. - Setup wizard - Step %d of %d - Use interactive setup assistant - Automatically interact with settings - Enable accessibility service first - Watch tutorial - Start service - Go to settings - Start service + Setup wizard + Step %d of %d + Use interactive setup assistant + Automatically interact with settings + Enable accessibility service first + Watch tutorial + Start service + Go to settings + Start service - Enable accessibility service - Key Mapper uses this service to help with setup. It\'s also needed for ordinary key maps. + Enable accessibility service + Key Mapper uses this service to help with setup. It\'s also needed for ordinary key maps. - Enable developer options - Key Mapper needs to use Android Debug Bridge (ADB) so you need to enable developer options. + Enable developer options + Key Mapper needs to use Android Debug Bridge (ADB) so you need to enable developer options. - Connect to a WiFi network - Key Mapper needs a WiFi network to enable ADB. You do not need an internet connection.\n\nNo WiFi network? Use a hotspot from someone else\'s phone. + Connect to a WiFi network + Key Mapper needs a WiFi network to enable ADB. You do not need an internet connection.\n\nNo WiFi network? Use a hotspot from someone else\'s phone. - Enable wireless debugging - Key Mapper uses wireless debugging to launch its remapping and input service. + Enable wireless debugging + Key Mapper uses wireless debugging to launch its remapping and input service. - Pair wireless debugging - Key Mapper needs to pair with wireless debugging before it can launch its remapping and input service. + Pair wireless debugging + Key Mapper needs to pair with wireless debugging before it can launch its remapping and input service. - Start service - Key Mapper needs to connect to ADB to start the service. + Start service + Key Mapper needs to connect to ADB to start the service. - Allow notifications - Key Mapper needs permission to notify you if there are any issues with the set up process. - Give permission + Allow notifications + Key Mapper needs permission to notify you if there are any issues with the set up process. + Give permission - Incompatible USB configuration - You must select \'No data transfer\' as your default USB configuration so that PRO Mode is not killed every time you lock your device. + Incompatible USB configuration + You must select \'No data transfer\' as your default USB configuration so that Expert mode is not killed every time you lock your device. - Setup assistant + Setup assistant - PRO mode is running - You can now remap buttons when the screen is off and use more actions. + Expert mode is running + You can now remap buttons when the screen is off and use more actions. - Finish + Finish - Enable developer options - Tap build number repeatedly + Enable developer options + Tap build number repeatedly - Pairing automatically - Searching for pairing code and port… + Pairing automatically + Searching for pairing code and port… - Unable to find pairing port and code - Tap on the button to pair with pairing code and type the code in here + Unable to find pairing port and code + Tap on the button to pair with pairing code and type the code in here - Starting PRO Mode failed - Tap to set up again. Try ADB pairing and rebooting your phone if it repeatedly fails. + Starting Expert mode failed + Tap to set up again. Try ADB pairing and rebooting your phone if it repeatedly fails. - Auto starting PRO mode - Using root - Using shizuku - Using ADB over WiFi + Auto starting Expert mode + Using root + Using shizuku + Using ADB over WiFi - PRO mode started - Have fun remapping! ❤️ + Expert mode started + Have fun remapping! ❤️ - Pairing failed - Keep the pairing code popup on-screen when submitting the pairing code - Input pairing code + Pairing failed + Keep the pairing code popup on-screen when submitting the pairing code + Input pairing code - What can I do with PRO mode? - 📲 You can remap more buttons, such as your power button.\n⌨️ Use any on-screen keyboard with key code actions.\n⭐️ And use many more actions. - Show PRO mode info - Dismiss + What can I do with Expert mode? + 📲 You can remap more buttons, such as your power button.\n⌨️ Use any on-screen keyboard with key code actions.\n⭐️ And use many more actions. + Show Expert mode info + Dismiss - PRO mode stopped unexpectedly + Expert mode stopped unexpectedly Automatically restarting… Not auto restarting because last auto started less than 5 minutes ago. If you\'re not killing the service report the issue to the developer. @@ -1821,16 +1821,16 @@ Enable Running Purchased - PRO mode - Enable for free - Running - Not available on this Android version - If your device assistant is triggered by a hardware button, it might be possible to remap it for free with PRO mode. Try choosing \'Other\' from the trigger page and follow the instructions. + Expert mode + Enable for free + Running + Not available on this Android version + If your device assistant is triggered by a hardware button, it might be possible to remap it for free with Expert mode. Try choosing \'Other\' from the trigger page and follow the instructions. This trigger requires some set up that varies per device. Please\ read the instructions Read instructions Select trigger type - If your device assistant is triggered by the power button, it might be possible to remap the assistant instead, meaning you don\'t need to use PRO mode. Try choosing \'Assistant\' from the trigger page and follow the instructions. + If your device assistant is triggered by the power button, it might be possible to remap the assistant instead, meaning you don\'t need to use Expert mode. Try choosing \'Assistant\' from the trigger page and follow the instructions. D-Pad button Other simple buttons You will not be able to use your normal on-screen keyboard when remapping DPAD buttons. @@ -1845,8 +1845,8 @@ There are extra steps to use this action. Select which method you want to use: Key Mapper input method No on-screen keyboard - Can use your normal on-screen keyboard at all times - Better compatibility with apps and games + Can use your normal on-screen keyboard at all times + Better compatibility with apps and games Enable Key Mapper input method Use Key Mapper input method Automatically switch to normal on-screen keyboard when typing diff --git a/base/src/test/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarterTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarterTest.kt similarity index 98% rename from base/src/test/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarterTest.kt rename to base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarterTest.kt index 703aa502e7..7161d21886 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarterTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarterTest.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.repositories.FakePreferenceRepository @@ -231,7 +231,7 @@ class SystemBridgeAutoStarterTest { runTest(testDispatcher) { isRootGrantedFlow.value = true fakePreferences.set(Keys.isSystemBridgeUsed, null) - fakePreferences.set(Keys.handledUpgradeToProMode, null) + fakePreferences.set(Keys.handledUpgradeToExpertMode, null) systemBridgeAutoStarter.init() advanceUntilIdle() @@ -245,7 +245,7 @@ class SystemBridgeAutoStarterTest { shizukuIsStartedFlow.value = true shizukuPermissionGrantedFlow.value = true fakePreferences.set(Keys.isSystemBridgeUsed, null) - fakePreferences.set(Keys.handledUpgradeToProMode, null) + fakePreferences.set(Keys.handledUpgradeToExpertMode, null) systemBridgeAutoStarter.init() advanceUntilIdle() @@ -258,7 +258,7 @@ class SystemBridgeAutoStarterTest { runTest(testDispatcher) { isRootGrantedFlow.value = true fakePreferences.set(Keys.isSystemBridgeUsed, null) - fakePreferences.set(Keys.handledUpgradeToProMode, null) + fakePreferences.set(Keys.handledUpgradeToExpertMode, null) systemBridgeAutoStarter.init() advanceUntilIdle() @@ -272,7 +272,7 @@ class SystemBridgeAutoStarterTest { shizukuIsStartedFlow.value = true shizukuPermissionGrantedFlow.value = true fakePreferences.set(Keys.isSystemBridgeUsed, null) - fakePreferences.set(Keys.handledUpgradeToProMode, null) + fakePreferences.set(Keys.handledUpgradeToExpertMode, null) systemBridgeAutoStarter.init() advanceUntilIdle() @@ -324,7 +324,7 @@ class SystemBridgeAutoStarterTest { fun `do not auto start when emergency killed`() = runTest(testDispatcher) { isRootGrantedFlow.value = true fakePreferences.set(Keys.isSystemBridgeEmergencyKilled, true) - fakePreferences.set(Keys.handledUpgradeToProMode, true) + fakePreferences.set(Keys.handledUpgradeToExpertMode, true) systemBridgeAutoStarter.init() advanceUntilIdle() @@ -396,7 +396,7 @@ class SystemBridgeAutoStarterTest { @Test fun `do not auto start when auto start is disabled`() = runTest(testDispatcher) { fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, false) - fakePreferences.set(Keys.handledUpgradeToProMode, true) + fakePreferences.set(Keys.handledUpgradeToExpertMode, true) isRootGrantedFlow.value = true systemBridgeAutoStarter.init() @@ -663,7 +663,7 @@ class SystemBridgeAutoStarterTest { fun `do not auto start with Shizuku on launch if it was never used before`() = runTest(testDispatcher) { fakePreferences.set(Keys.isSystemBridgeUsed, null) - fakePreferences.set(Keys.handledUpgradeToProMode, true) + fakePreferences.set(Keys.handledUpgradeToExpertMode, true) shizukuIsStartedFlow.value = true shizukuPermissionGrantedFlow.value = true diff --git a/base/src/test/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCaseTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCaseTest.kt similarity index 98% rename from base/src/test/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCaseTest.kt rename to base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCaseTest.kt index 8fd7d08346..db43edbbc6 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCaseTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCaseTest.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import io.github.sds100.keymapper.base.repositories.FakePreferenceRepository import io.github.sds100.keymapper.data.Keys diff --git a/base/src/test/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerViewModelTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerViewModelTest.kt index afeac84d16..1ed7f2bc56 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerViewModelTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerViewModelTest.kt @@ -9,9 +9,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is visible when system bridge is connected`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -20,9 +20,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is not visible when system bridge is disconnected`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Disconnected( time = 0L, isStoppedByUser = true, @@ -34,9 +34,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is not visible when system bridge is disconnected unexpectedly`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Disconnected( time = 0L, isStoppedByUser = false, @@ -48,9 +48,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is checked when pro mode recording is enabled`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = true, + isExpertModeRecordingEnabled = true, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -59,9 +59,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is not checked when pro mode recording is disabled`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -70,9 +70,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is enabled when record trigger state is idle`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -81,9 +81,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is enabled when record trigger state is completed`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Completed(emptyList()), - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -92,9 +92,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is disabled when record trigger state is counting down`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.CountingDown(timeLeft = 3), - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -103,9 +103,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is disabled when counting down even if pro mode recording is enabled`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.CountingDown(timeLeft = 5), - isProModeRecordingEnabled = true, + isExpertModeRecordingEnabled = true, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -115,9 +115,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is visible and checked when connected and enabled`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = true, + isExpertModeRecordingEnabled = true, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -128,9 +128,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is not visible when disconnected even if recording is enabled`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = true, + isExpertModeRecordingEnabled = true, systemBridgeState = SystemBridgeConnectionState.Disconnected( time = 0L, isStoppedByUser = true, @@ -144,9 +144,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is visible but disabled when counting down and connected`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.CountingDown(timeLeft = 1), - isProModeRecordingEnabled = true, + isExpertModeRecordingEnabled = true, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -157,9 +157,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is not visible and disabled when counting down and disconnected`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.CountingDown(timeLeft = 2), - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Disconnected( time = 0L, isStoppedByUser = false, diff --git a/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt b/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt index bfafac7250..a51d8d1e9d 100644 --- a/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt +++ b/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt @@ -60,9 +60,9 @@ object Keys { booleanPreferencesKey("key_shown_sequence_trigger_explanation_dialog") val shownTriggerConstraintsTip = booleanPreferencesKey("key_shown_trigger_constraints_tip") - val shownCapsLockProModeTip = + val shownCapsLockExpertModeTip = booleanPreferencesKey("key_shown_caps_lock_pro_mode_compatibility_tip") - val shownVolumeButtonsProModeTip = + val shownVolumeButtonsExpertModeTip = booleanPreferencesKey("key_shown_volume_buttons_pro_mode_tip") val shownScreenPinningTip = booleanPreferencesKey("key_shown_screen_pinning_tip") @@ -122,13 +122,13 @@ object Keys { // val skipTapTargetTutorial = // booleanPreferencesKey("key_skip_tap_target_tutorial") - val isProModeWarningUnderstood = + val isExpertModeWarningUnderstood = booleanPreferencesKey("key_is_pro_mode_warning_understood") - val isProModeInteractiveSetupAssistantEnabled = + val isExpertModeInteractiveSetupAssistantEnabled = booleanPreferencesKey("key_is_pro_mode_setup_assistant_enabled") - val isProModeInfoDismissed = + val isExpertModeInfoDismissed = booleanPreferencesKey("key_is_pro_mode_info_dismissed") val isSystemBridgeKeepAliveEnabled = @@ -160,7 +160,7 @@ object Keys { * This is stored as true when PRO Mode has been auto started after updating * to 4.0 and Key Mapper previously had root or shizuku permission. */ - val handledUpgradeToProMode = booleanPreferencesKey("key_handled_upgrade_to_pro_mode") + val handledUpgradeToExpertMode = booleanPreferencesKey("key_handled_upgrade_to_pro_mode") val handledMigrateScreenOffKeyMapsNotification = booleanPreferencesKey("key_handled_migrate_screen_off_key_maps_notification") diff --git a/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt b/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt index 9d54ca8891..f241647dd6 100644 --- a/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt +++ b/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt @@ -15,9 +15,9 @@ object PreferenceDefaults { const val SEQUENCE_TRIGGER_TIMEOUT = 1000 const val HOLD_DOWN_DURATION = 1000 - const val PRO_MODE_INTERACTIVE_SETUP_ASSISTANT = true + const val EXPERT_MODE_INTERACTIVE_SETUP_ASSISTANT = true - const val PRO_MODE_KEEP_ALIVE = true + const val EXPERT_MODE_KEEP_ALIVE = true // It is false by default and the first time they turn on the system bridge, // the preference will be set to true. From b65470286c0e4e234c3d981e8471f6fc9e903abb Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 18:34:49 +0000 Subject: [PATCH 184/199] style: reformat --- .../sds100/keymapper/base/expertmode/ExpertModeSetupScreen.kt | 2 +- .../keymapper/base/expertmode/SystemBridgeAutoStarter.kt | 2 +- .../sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt | 3 ++- base/src/main/res/values-pt/strings.xml | 4 ++-- base/src/main/res/values-tr/strings.xml | 4 ++-- base/src/main/res/values/strings.xml | 4 ++-- .../sysbridge/manager/SystemBridgeConnectionManager.kt | 2 +- .../sysbridge/service/SystemBridgeSetupController.kt | 4 ++-- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupScreen.kt index ea913fbe5f..aba3e9398d 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupScreen.kt @@ -324,7 +324,7 @@ private fun AssistantCheckBoxRow( stringResource(R.string.expert_mode_setup_wizard_use_assistant_description) } else { stringResource( - R.string.expert_mode_setup_wizard_use_assistant_enable_accessibility_service, + R.string.expert_mode_setup_wizard_use_assistant_enable_service, ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarter.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarter.kt index 1a9bb65c99..cb69b61f41 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarter.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarter.kt @@ -248,7 +248,7 @@ class SystemBridgeAutoStarter @Inject constructor( Timber.i("Auto starting system bridge with Shizuku") showAutoStartNotification( getString( - R.string.expert_mode_setup_notification_auto_start_system_bridge_shizuku_text, + R.string.expert_mode_setup_notification_auto_start_system_bridge_shizuku, ), ) connectionManager.startWithShizuku() diff --git a/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt index 2f4ffe66d3..da7f2078fd 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt @@ -49,7 +49,8 @@ class OnboardingTipDelegateImpl @Inject constructor( private const val PARALLEL_TRIGGER_TIP_ID = "parallel_trigger_tip" private const val SEQUENCE_TRIGGER_TIP_ID = "sequence_trigger_tip" private const val TRIGGER_CONSTRAINTS_TIP_ID = "trigger_constraints_tip" - const val CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID = "caps_lock_expert_mode_compatibility_tip" + const val CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID = + "caps_lock_expert_mode_compatibility_tip" const val VOLUME_BUTTONS_PRO_MODE_TIP_ID = "volume_buttons_expert_mode_tip" const val SCREEN_PINNING_TIP_ID = "screen_pinning_tip" const val IME_DETECTION_TIP_ID = "ime_detection_tip" diff --git a/base/src/main/res/values-pt/strings.xml b/base/src/main/res/values-pt/strings.xml index 929ea6f23a..dd4af2f2a9 100644 --- a/base/src/main/res/values-pt/strings.xml +++ b/base/src/main/res/values-pt/strings.xml @@ -1308,7 +1308,7 @@ Deixe em branco se alguma rede Wi-Fi precisar ser correspondida. Etapa %d de %d Use o assistente de configuração interativo Interagir automaticamente com as configurações - Habilitar primeiro o serviço de acessibilidade + Habilitar primeiro o serviço de acessibilidade Assista ao tutorial Iniciar serviço Vá para as configurações @@ -1342,7 +1342,7 @@ Deixe em branco se alguma rede Wi-Fi precisar ser correspondida. Toque para configurar novamente. Tente o pareamento ADB e reinicie o telefone se o problema persistir. Modo PRO de inicialização automática Usando root - Usando shizuku + Usando shizuku Usando ADB via WiFi Modo PRO iniciado Divirta-se remapeando! ❤️ diff --git a/base/src/main/res/values-tr/strings.xml b/base/src/main/res/values-tr/strings.xml index f53589ee3f..71fb475b1f 100644 --- a/base/src/main/res/values-tr/strings.xml +++ b/base/src/main/res/values-tr/strings.xml @@ -1416,7 +1416,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Adım %d / %d Etkileşimli kurulum asistanını kullan Ayarlarla otomatik olarak etkileşime gir - Önce erişilebilirlik servisini etkinleştir + Önce erişilebilirlik servisini etkinleştir Eğitimi izle Servisi başlat Ayarlara git @@ -1452,7 +1452,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Tekrar kurmak için dokunun. Tekrar tekrar başarısız olursa ADB eşleştirmeyi ve telefonunuzu yeniden başlatmayı deneyin. PRO modu otomatik başlatılıyor Root kullanılıyor - Shizuku kullanılıyor + Shizuku kullanılıyor WiFi üzerinden ADB kullanılıyor PRO modu başlatıldı Yeniden eşlemede iyi eğlenceler! ❤️ diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index 3023a3737a..5a119a42b3 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -1691,7 +1691,7 @@ Step %d of %d Use interactive setup assistant Automatically interact with settings - Enable accessibility service first + Enable accessibility service first Watch tutorial Start service Go to settings @@ -1743,7 +1743,7 @@ Auto starting Expert mode Using root - Using shizuku + Using shizuku Using ADB over WiFi Expert mode started diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt index f9c5941b68..7e1a6dc013 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt @@ -66,7 +66,7 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( time = SystemClock.elapsedRealtime(), // Get whether the user previously stopped the system bridge. isStoppedByUser = - preferences.get(Keys.isSystemBridgeStoppedByUser).firstBlocking() ?: false, + preferences.get(Keys.isSystemBridgeStoppedByUser).firstBlocking() ?: false, ), ) private var isExpectedDeath: Boolean = false diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt index 36af474114..5c46b23560 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt @@ -25,6 +25,8 @@ import io.github.sds100.keymapper.sysbridge.adb.AdbManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState import io.github.sds100.keymapper.sysbridge.manager.awaitConnected +import javax.inject.Inject +import javax.inject.Singleton import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.TimeoutCancellationException @@ -41,8 +43,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeoutOrNull import timber.log.Timber -import javax.inject.Inject -import javax.inject.Singleton @Singleton class SystemBridgeSetupControllerImpl @Inject constructor( From b2d9f2c10c43e1205b79feaf4a82f2a0d17c44ce Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 20:33:38 +0000 Subject: [PATCH 185/199] #1946 feat: change record trigger button text when recording with expert mode --- .../base/trigger/RecordTriggerButtonRow.kt | 13 +++++++++++-- .../base/trigger/TriggerSetupBottomSheet.kt | 9 +++++++++ base/src/main/res/values/strings.xml | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt index 87f4aad736..b58c74cf73 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt @@ -67,6 +67,7 @@ fun RecordTriggerButtonRow( RecordTriggerButton( modifier = Modifier.weight(1f), recordTriggerState, + isExpertModeRecordingEnabled = expertModeRecordSwitchState.isChecked, onClick = onRecordTriggerClick, ) @@ -105,7 +106,12 @@ private fun ExpertModeSwitch( } @Composable -fun RecordTriggerButton(modifier: Modifier, state: RecordTriggerState, onClick: () -> Unit) { +fun RecordTriggerButton( + modifier: Modifier, + state: RecordTriggerState, + isExpertModeRecordingEnabled: Boolean, + onClick: () -> Unit, +) { val colors = ButtonDefaults.filledTonalButtonColors().copy( containerColor = LocalCustomColorsPalette.current.red, contentColor = LocalCustomColorsPalette.current.onRed, @@ -115,8 +121,11 @@ fun RecordTriggerButton(modifier: Modifier, state: RecordTriggerState, onClick: is RecordTriggerState.CountingDown -> stringResource(R.string.button_recording_trigger_countdown, state.timeLeft) - else -> + else -> if (isExpertModeRecordingEnabled) { + stringResource(R.string.button_record_trigger_expert_mode) + } else { stringResource(R.string.button_record_trigger) + } } // Create pulsing animation for the recording dot diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt index 66410b1ca1..c00cb0f19b 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt @@ -183,6 +183,9 @@ private fun GamepadTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = + state is TriggerSetupState.Gamepad.SimpleButtons && + state.isUseExpertModeChecked, onClick = onRecordTriggerClick, ) } else { @@ -298,6 +301,7 @@ private fun MouseTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = true, onClick = onRecordTriggerClick, ) } else { @@ -353,6 +357,7 @@ private fun PowerTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = true, onClick = onRecordTriggerClick, ) } else { @@ -440,6 +445,7 @@ private fun VolumeTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = state.isUseExpertModeChecked, onClick = onRecordTriggerClick, ) } else { @@ -500,6 +506,7 @@ private fun NotDetectedSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = true, onClick = onRecordTriggerClick, ) } else { @@ -586,6 +593,7 @@ private fun OtherTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = state.isUseExpertModeChecked, onClick = onRecordTriggerClick, ) } else { @@ -670,6 +678,7 @@ private fun KeyboardTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = state.isUseExpertModeChecked, onClick = onRecordTriggerClick, ) } else { diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index 5a119a42b3..f6600b491e 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -407,6 +407,7 @@ Add action Tap to record trigger + Record with Expert Mode NEW! Done Fix From 5550234cecfdbd68a438681edfebc9ced4b998fa Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 20:34:48 +0000 Subject: [PATCH 186/199] make Expert Mode title case in all strings --- base/src/main/res/values/strings.xml | 78 ++++++++++++++-------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index f6600b491e..4182a092ed 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -77,8 +77,8 @@ Too many fingers to perform gesture due to android limitations. Gesture duration is too high due to android limitations. You must be using the Key Mapper Input Method for DPAD triggers to work! - Expert mode is unsupported on this Android version - Expert mode not started! + Expert Mode is unsupported on this Android version + Expert Mode not started! Trigger device not connected! Migrate this screen off trigger @@ -437,9 +437,9 @@ Please grant Key Mapper root permission in your root management app, such as Magisk. Grant WRITE_SECURE_SETTINGS permission - You will need to use Expert mode to grant this permission. + You will need to use Expert Mode to grant this permission. - Your device doesn\'t seem to have an accessibility services settings page. You can set up Expert mode or run the ADB command \"adb shell pm grant io.github.sds100.keymapper android.permission.WRITE_SECURE_SETTINGS\". + Your device doesn\'t seem to have an accessibility services settings page. You can set up Expert Mode or run the ADB command \"adb shell pm grant io.github.sds100.keymapper android.permission.WRITE_SECURE_SETTINGS\". You must hold down the keys in the order that they are listed. There is a timeout to input this trigger. You can change this timeout in the "Options" tab. How to use this trigger @@ -488,16 +488,16 @@ Some actions and options need this permission to work. You can also get notified when there is important news about the app. Migrate this screen off trigger - We\'re sorry for the disruption. We\'ve introduced a *free* feature called Expert mode that replaces the old screen off remapping option. This feature is much more reliable and unlocks remapping the power button too.\n\nDue to the way it works you will need to record this trigger again with Expert mode. + We\'re sorry for the disruption. We\'ve introduced a *free* feature called Expert Mode that replaces the old screen off remapping option. This feature is much more reliable and unlocks remapping the power button too.\n\nDue to the way it works you will need to record this trigger again with Expert Mode. Proceed Grant READ_LOGS permission - You will need to be rooted or have enabled Expert mode for this to work. The system will then close the app when it grants this permission. You will need to open it again yourself and try sharing the logcat again. + You will need to be rooted or have enabled Expert Mode for this to work. The system will then close the app when it grants this permission. You will need to open it again yourself and try sharing the logcat again. Done Kill - Use Expert mode + Use Expert Mode Change Fix partially Ok @@ -683,7 +683,7 @@ Logging This may add latency to your key maps so only turn this on if you are trying to debug the app or have been asked to by the developer. - Use Expert mode + Use Expert Mode Advanced detection of key events and more Light @@ -894,7 +894,7 @@ UI element not found! Command timed out after %1$d seconds - Expert mode needs starting + Expert Mode needs starting Rate limit reached. You can only send once per second. @@ -1151,8 +1151,8 @@ ADB Execution Mode ADB mode does not support streaming output - Setup Expert mode - Setup Expert mode (Unsupported) + Setup Expert Mode + Setup Expert Mode (Unsupported) Configuration Output No output yet. Click Test to run the command. @@ -1432,18 +1432,18 @@ Floating button %s (%s) Deleted floating button Better Caps Lock compatibility - Expert mode provides better compatibility for Caps Lock remapping. Tap \'Use Expert mode\' and record it again. - Use Expert mode + Expert Mode provides better compatibility for Caps Lock remapping. Tap \'Use Expert Mode\' and record it again. + Use Expert Mode Screen off remapping? - Any trigger can now work when the screen is off for free with Expert mode! You will need to record it again. - Use Expert mode + Any trigger can now work when the screen is off for free with Expert Mode! You will need to record it again. + Use Expert Mode App pinning warning Using the back button as a trigger may conflict with app pinning, causing a black screen on unlock. A reboot will fix it. Keyboard icon The ⌨ symbol means you must use the Key Mapper input method for this trigger to work due to an Android restriction. Ringer mode actions - Consider using Expert mode for ringer mode actions to avoid conflicts with Do Not Disturb settings. - Use Expert mode + Consider using Expert Mode for ringer mode actions to avoid conflicts with Do Not Disturb settings. + Use Expert Mode Limit to specific apps? Add constraints in the Constraints tab. Choose a layout @@ -1585,7 +1585,7 @@ Use key code %d Use scan code %d No scan code saved - Use Expert mode + Use Expert Mode Add more @@ -1652,41 +1652,41 @@ - - Expert mode + + Expert Mode Setup Important! - Remapping buttons with Expert mode is dangerous and can cause them to stop working if you remap them incorrectly.\n\nIf you make a mistake, you may need to force restart your device by holding down the power and volume buttons for 30 seconds — consult your device\'s manual or the internet for how to do this. + Remapping buttons with Expert Mode is dangerous and can cause them to stop working if you remap them incorrectly.\n\nIf you make a mistake, you may need to force restart your device by holding down the power and volume buttons for 30 seconds — consult your device\'s manual or the internet for how to do this. %d… I understand Understood Set up Root detected - You can skip the set up process by giving Key Mapper root permission. This will let Key Mapper auto start Expert mode without having to wait for a WiFi connection as well. - Start Expert mode + You can skip the set up process by giving Key Mapper root permission. This will let Key Mapper auto start Expert Mode without having to wait for a WiFi connection as well. + Start Expert Mode Shizuku detected You can skip the set up process by giving Key Mapper Shizuku permission. Start Shizuku Request permission - Start Expert mode + Start Expert Mode Set up with Key Mapper Continue (Requires Android 11+) Options - Enable Expert mode for all key maps + Enable Expert Mode for all key maps Key Mapper will use the ADB Shell for remapping These settings are unavailable until you acknowledge the warning. - Expert mode service is running + Expert Mode service is running Stop Auto start and keep alive - Expert mode will start itself whenever you boot your device, open Key Mapper, or it dies unexpectedly. + Expert Mode will start itself whenever you boot your device, open Key Mapper, or it dies unexpectedly. Key event actions Select how key event actions are performed Emergency tip - If your power button stops working, hold down the power button for 10 seconds and release to disable Expert mode. + If your power button stops working, hold down the power button for 10 seconds and release to disable Expert Mode. Setup wizard Step %d of %d @@ -1721,11 +1721,11 @@ Give permission Incompatible USB configuration - You must select \'No data transfer\' as your default USB configuration so that Expert mode is not killed every time you lock your device. + You must select \'No data transfer\' as your default USB configuration so that Expert Mode is not killed every time you lock your device. Setup assistant - Expert mode is running + Expert Mode is running You can now remap buttons when the screen is off and use more actions. Finish @@ -1739,27 +1739,27 @@ Unable to find pairing port and code Tap on the button to pair with pairing code and type the code in here - Starting Expert mode failed + Starting Expert Mode failed Tap to set up again. Try ADB pairing and rebooting your phone if it repeatedly fails. - Auto starting Expert mode + Auto starting Expert Mode Using root Using shizuku Using ADB over WiFi - Expert mode started + Expert Mode started Have fun remapping! ❤️ Pairing failed Keep the pairing code popup on-screen when submitting the pairing code Input pairing code - What can I do with Expert mode? + What can I do with Expert Mode? 📲 You can remap more buttons, such as your power button.\n⌨️ Use any on-screen keyboard with key code actions.\n⭐️ And use many more actions. - Show Expert mode info + Show Expert Mode info Dismiss - Expert mode stopped unexpectedly + Expert Mode stopped unexpectedly Automatically restarting… Not auto restarting because last auto started less than 5 minutes ago. If you\'re not killing the service report the issue to the developer. @@ -1822,16 +1822,16 @@ Enable Running Purchased - Expert mode + Expert Mode Enable for free Running Not available on this Android version - If your device assistant is triggered by a hardware button, it might be possible to remap it for free with Expert mode. Try choosing \'Other\' from the trigger page and follow the instructions. + If your device assistant is triggered by a hardware button, it might be possible to remap it for free with Expert Mode. Try choosing \'Other\' from the trigger page and follow the instructions. This trigger requires some set up that varies per device. Please\ read the instructions Read instructions Select trigger type - If your device assistant is triggered by the power button, it might be possible to remap the assistant instead, meaning you don\'t need to use Expert mode. Try choosing \'Assistant\' from the trigger page and follow the instructions. + If your device assistant is triggered by the power button, it might be possible to remap the assistant instead, meaning you don\'t need to use Expert Mode. Try choosing \'Assistant\' from the trigger page and follow the instructions. D-Pad button Other simple buttons You will not be able to use your normal on-screen keyboard when remapping DPAD buttons. From 065306ee766b03b612edbeed7f1f76c03dfa8fef Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 20:36:03 +0000 Subject: [PATCH 187/199] #1939 simplify text for Expert mode keep alive setting --- base/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index 4182a092ed..40bc2d0c69 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -1680,7 +1680,7 @@ Stop Auto start and keep alive - Expert Mode will start itself whenever you boot your device, open Key Mapper, or it dies unexpectedly. + Expert Mode will start itself whenever you boot your device or it dies unexpectedly. Key event actions Select how key event actions are performed From 9dd7cdaa53c9ca8ad6831602ac998bb24119ef25 Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 20:41:07 +0000 Subject: [PATCH 188/199] chore: bump version code --- app/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/version.properties b/app/version.properties index bd966a4c62..3e5db5110d 100644 --- a/app/version.properties +++ b/app/version.properties @@ -1,2 +1,2 @@ VERSION_NAME=4.0.0-beta.04 -VERSION_CODE=212 +VERSION_CODE=213 From 5d913e409fb82235e99be61ed650a643d9b08de6 Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 20:48:48 +0000 Subject: [PATCH 189/199] fix: do not grab evdev devices with extra key codes if key event actions do not use system bridge --- .../detection/KeyMapDetectionController.kt | 30 ++++++++--- .../BaseAccessibilityServiceController.kt | 1 + .../KeyMapDetectionControllerTest.kt | 52 +++++++++++++++++++ 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt b/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt index 1e83e8a149..61296637b4 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt @@ -14,6 +14,9 @@ import io.github.sds100.keymapper.base.trigger.RecordTriggerController import io.github.sds100.keymapper.base.trigger.RecordTriggerState import io.github.sds100.keymapper.common.models.EvdevDeviceInfo import io.github.sds100.keymapper.common.models.GrabTargetKeyCode +import io.github.sds100.keymapper.data.Keys +import io.github.sds100.keymapper.data.PreferenceDefaults +import io.github.sds100.keymapper.data.repositories.PreferenceRepository import io.github.sds100.keymapper.system.inputevents.KMEvdevEvent import io.github.sds100.keymapper.system.inputevents.KMInputEvent import kotlinx.coroutines.CoroutineScope @@ -21,6 +24,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import timber.log.Timber @@ -33,11 +37,15 @@ class KeyMapDetectionController( private val inputEventHub: InputEventHub, private val pauseKeyMapsUseCase: PauseKeyMapsUseCase, private val recordTriggerController: RecordTriggerController, + private val preferences: PreferenceRepository, ) : InputEventHubCallback { companion object { private const val INPUT_EVENT_HUB_ID = "key_map_controller" - fun getEvdevGrabRequests(algorithm: KeyMapAlgorithm): List { + fun getEvdevGrabRequests( + algorithm: KeyMapAlgorithm, + injectKeyEventActionsWithSystemBridge: Boolean = true, + ): List { val deviceKeyEventMap = mutableMapOf>() for ((index, trigger) in algorithm.triggers.withIndex()) { @@ -54,12 +62,16 @@ class KeyMapDetectionController( .map { actionIndex -> algorithm.actionMap[actionIndex]?.data } .filterNotNull() - val extraKeyCodes = actions - .filterIsInstance() - .map { it.keyCode } + val extraKeyCodes = if (injectKeyEventActionsWithSystemBridge) { + actions + .filterIsInstance() + .map { it.keyCode } + } else { + emptyList() + } for (device in evdevDevices) { - deviceKeyEventMap.getOrPut(device, { mutableSetOf() }).addAll(extraKeyCodes) + deviceKeyEventMap.getOrPut(device) { mutableSetOf() }.addAll(extraKeyCodes) } } @@ -75,6 +87,11 @@ class KeyMapDetectionController( } } + private val injectKeyEventsWithSystemBridge: StateFlow = + preferences.get(Keys.keyEventActionsUseSystemBridge) + .map { it ?: PreferenceDefaults.KEY_EVENT_ACTIONS_USE_SYSTEM_BRIDGE } + .stateIn(coroutineScope, SharingStarted.Eagerly, false) + private val algorithm: KeyMapAlgorithm = KeyMapAlgorithm(coroutineScope, detectUseCase, performActionsUseCase, detectConstraints) @@ -103,7 +120,8 @@ class KeyMapDetectionController( algorithm.loadKeyMaps(keyMapList) // Determine which evdev devices need to be grabbed depending on the state // of the algorithm. - val grabRequests = getEvdevGrabRequests(algorithm) + val grabRequests = + getEvdevGrabRequests(algorithm, injectKeyEventsWithSystemBridge.value) Timber.i( "Grab evdev devices for key map detection: ${grabRequests.joinToString()}", diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt index 2035cf216e..5f36cb46f9 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt @@ -94,6 +94,7 @@ abstract class BaseAccessibilityServiceController( inputEventHub, pauseKeyMapsUseCase, recordTriggerController, + settingsRepository, ) val triggerKeyMapFromOtherAppsController = TriggerKeyMapFromOtherAppsController( diff --git a/base/src/test/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionControllerTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionControllerTest.kt index 4c7227a03a..dccbc31835 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionControllerTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionControllerTest.kt @@ -169,6 +169,58 @@ class KeyMapDetectionControllerTest { ) } + @Test + fun `Do not grab evdev devices with extra key codes if key event actions do not use system bridge`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf( + Action(data = ActionData.OpenCamera), + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X), + ), + ), + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_B, + keyCode = KeyEvent.KEYCODE_BUTTON_B, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + ), + ), + actionList = listOf( + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_Y), + ), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests( + algorithm, + injectKeyEventActionsWithSystemBridge = false, + ) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = intArrayOf(), + ), + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + @Test fun `Grab multiple evdev devices from multiple triggers`() { loadKeyMaps( From 9a00afda14fe070f0773a7defe1b552ae531116e Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 20:52:14 +0000 Subject: [PATCH 190/199] chore: bump version code --- app/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/version.properties b/app/version.properties index 3e5db5110d..273f384054 100644 --- a/app/version.properties +++ b/app/version.properties @@ -1,2 +1,2 @@ VERSION_NAME=4.0.0-beta.04 -VERSION_CODE=213 +VERSION_CODE=214 From ce8782ac7bb3654f3fa67429b0e5fa7aafdade9d Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 21:00:52 +0000 Subject: [PATCH 191/199] fix: show new key map fab text when navigating out of a group to an empty list --- .../github/sds100/keymapper/base/home/KeyMapListViewModel.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/home/KeyMapListViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/home/KeyMapListViewModel.kt index 111593b800..4e85c46321 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/home/KeyMapListViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/home/KeyMapListViewModel.kt @@ -336,9 +336,7 @@ class KeyMapListViewModel( Triple(listState, appBarState, showCreateKeyMapTapTarget) }.collectLatest { (listState, appBarState, showCreateKeyMapTapTarget) -> listState.ifIsData { list -> - if (list.isNotEmpty()) { - showFabText = false - } + showFabText = list.isEmpty() } _state.value = From cdb8f66327194c0a40ee1676dce4337b53a612b3 Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 21:21:47 +0000 Subject: [PATCH 192/199] update string --- base/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index 40bc2d0c69..1d70f24dac 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -594,7 +594,7 @@ Change automatic backup location Turn on automatic backup - Periodically back up your key maps + Back up after modifying key maps Automatically change the on-screen keyboard when a device (e.g a keyboard) connects/disconnects The Key Mapper Input Method will be automatically selected when a chosen device is connected. Your normal keyboard will be automatically selected when the device disconnects. From b6b6111c49c509b1586f4954df7f2723cd10ba41 Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 23:09:27 +0000 Subject: [PATCH 193/199] fix: launching floating buttons lock screen shortcut works again with system bridge --- .../github/sds100/keymapper/base/input/InjectKeyEventModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/input/InjectKeyEventModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/input/InjectKeyEventModel.kt index 4da7fe187f..276c6b4bb5 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/input/InjectKeyEventModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/input/InjectKeyEventModel.kt @@ -13,7 +13,7 @@ data class InjectKeyEventModel( val repeatCount: Int = 0, ) { fun toAndroidKeyEvent(flags: Int = 0): KeyEvent { - val eventTime = SystemClock.elapsedRealtime() + val eventTime = SystemClock.uptimeMillis() return KeyEvent( eventTime, eventTime, From f1d34164e63532b69c44a739c3db484a130a044a Mon Sep 17 00:00:00 2001 From: sds100 Date: Tue, 23 Dec 2025 23:17:04 +0000 Subject: [PATCH 194/199] chore: bump version code --- app/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/version.properties b/app/version.properties index 273f384054..6a17534a00 100644 --- a/app/version.properties +++ b/app/version.properties @@ -1,2 +1,2 @@ VERSION_NAME=4.0.0-beta.04 -VERSION_CODE=214 +VERSION_CODE=217 From e7905085342be48d3ab6a65c4df502482c6c4c4c Mon Sep 17 00:00:00 2001 From: sds100 Date: Wed, 24 Dec 2025 00:09:27 +0000 Subject: [PATCH 195/199] remove unused appiconloader library --- gradle/libs.versions.toml | 2 -- sysbridge/build.gradle.kts | 1 - 2 files changed, 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 89a5687579..f83413ff55 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -44,7 +44,6 @@ introshowcaseview = "2.0.2" conscrypt-android = "2.5.3" boringssl-ndk = "20250114" bouncycastle-bcpkix = "1.70" -appiconloader = "1.5.0" rikkax-core = "1.4.1" junit = "4.13.2" junit-params = "1.1.1" @@ -189,7 +188,6 @@ github-topjohnwu-libsu = { group = "com.github.topjohnwu.libsu", name = "core", conscrypt-android = { group = "org.conscrypt", name = "conscrypt-android", version.ref = "conscrypt-android" } vvb2060-ndk-boringssl = { group = "io.github.vvb2060.ndk", name = "boringssl", version.ref = "boringssl-ndk" } bouncycastle-bcpkix = { group = "org.bouncycastle", name = "bcpkix-jdk15on", version.ref = "bouncycastle-bcpkix" } -zhanghai-appiconloader = { group = "me.zhanghai.android.appiconloader", name = "appiconloader", version.ref = "appiconloader" } rikka-rikkax-core = { group = "dev.rikka.rikkax.core", name = "core-ktx", version.ref = "rikkax-core" } diff --git a/sysbridge/build.gradle.kts b/sysbridge/build.gradle.kts index a84915796d..e72ef3921d 100644 --- a/sysbridge/build.gradle.kts +++ b/sysbridge/build.gradle.kts @@ -105,6 +105,5 @@ dependencies { implementation(libs.vvb2060.ndk.boringssl) implementation(libs.lsposed.hiddenapibypass.updated) implementation(libs.bouncycastle.bcpkix) - implementation(libs.zhanghai.appiconloader) implementation(libs.rikka.rikkax.core) } From cdde1b26e2404018a59607417038aed94dab7956 Mon Sep 17 00:00:00 2001 From: sds100 Date: Wed, 24 Dec 2025 00:13:25 +0000 Subject: [PATCH 196/199] remove unused lsposed hiddenapibypass --- gradle/libs.versions.toml | 2 -- sysbridge/build.gradle.kts | 1 - 2 files changed, 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f83413ff55..81be2676ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,7 +39,6 @@ epoxy = "4.6.2" flexbox = "3.0.0" google-accompanist-drawablepainter = "0.35.0-alpha" hiddenapibypass = "4.3" -hiddenapibypass-lsposed = "6.1" introshowcaseview = "2.0.2" conscrypt-android = "2.5.3" boringssl-ndk = "20250114" @@ -182,7 +181,6 @@ github-mflisar-dragselectrecyclerview = { group = "com.github.MFlisar", name = " jakewharton-timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" } kotson = { group = "com.github.salomonbrys.kotson", name = "kotson", version.ref = "kotson" } lsposed-hiddenapibypass = { group = "org.lsposed.hiddenapibypass", name = "hiddenapibypass", version.ref = "hiddenapibypass" } -lsposed-hiddenapibypass-updated = { group = "org.lsposed.hiddenapibypass", name = "hiddenapibypass", version.ref = "hiddenapibypass-lsposed" } net-lingala-zip4j = { group = "net.lingala.zip4j", name = "zip4j", version.ref = "lingala-zip4j" } github-topjohnwu-libsu = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu-core" } conscrypt-android = { group = "org.conscrypt", name = "conscrypt-android", version.ref = "conscrypt-android" } diff --git a/sysbridge/build.gradle.kts b/sysbridge/build.gradle.kts index e72ef3921d..a08999fc43 100644 --- a/sysbridge/build.gradle.kts +++ b/sysbridge/build.gradle.kts @@ -103,7 +103,6 @@ dependencies { // From Shizuku :manager module build.gradle file. implementation(libs.vvb2060.ndk.boringssl) - implementation(libs.lsposed.hiddenapibypass.updated) implementation(libs.bouncycastle.bcpkix) implementation(libs.rikka.rikkax.core) } From cfc85a581f01c83aed9fc04bf3454b3ec9c60781 Mon Sep 17 00:00:00 2001 From: sds100 Date: Wed, 24 Dec 2025 01:02:39 +0000 Subject: [PATCH 197/199] fix: set MIN_API in Constants to Oreo --- .../java/io/github/sds100/keymapper/common/utils/Constants.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/io/github/sds100/keymapper/common/utils/Constants.kt b/common/src/main/java/io/github/sds100/keymapper/common/utils/Constants.kt index 9ba395cf9c..d06a8fe6fc 100644 --- a/common/src/main/java/io/github/sds100/keymapper/common/utils/Constants.kt +++ b/common/src/main/java/io/github/sds100/keymapper/common/utils/Constants.kt @@ -3,7 +3,7 @@ package io.github.sds100.keymapper.common.utils import android.os.Build object Constants { - const val MIN_API: Int = Build.VERSION_CODES.LOLLIPOP + const val MIN_API: Int = Build.VERSION_CODES.O const val MAX_API: Int = 1000 const val SYSTEM_BRIDGE_MIN_API = Build.VERSION_CODES.Q } From a06944d88603fb232b16b0c703581b98aebd8b1f Mon Sep 17 00:00:00 2001 From: sds100 Date: Wed, 24 Dec 2025 14:24:06 +0000 Subject: [PATCH 198/199] refactor: move SwitchText to correct package --- .../utils/ui/compose/{icons => }/SwitchText.kt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) rename base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/{icons => }/SwitchText.kt (76%) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/SwitchText.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SwitchText.kt similarity index 76% rename from base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/SwitchText.kt rename to base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SwitchText.kt index f05dc715e0..8a362b7c4f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/SwitchText.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SwitchText.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.utils.ui.compose.icons +package io.github.sds100.keymapper.base.utils.ui.compose import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row @@ -16,18 +16,22 @@ import androidx.compose.ui.unit.dp @Composable fun SwitchText( - modifier: Modifier = Modifier, + modifier: Modifier = Modifier.Companion, text: String, isChecked: Boolean, isEnabled: Boolean = true, onCheckedChange: (Boolean) -> Unit, ) { - Surface(modifier = modifier, shape = MaterialTheme.shapes.medium, color = Color.Transparent) { + Surface( + modifier = modifier, + shape = MaterialTheme.shapes.medium, + color = Color.Companion.Transparent, + ) { Row( - modifier = Modifier + modifier = Modifier.Companion .clickable(enabled = isEnabled) { onCheckedChange(!isChecked) } .padding(8.dp), - verticalAlignment = Alignment.CenterVertically, + verticalAlignment = Alignment.Companion.CenterVertically, ) { Switch( enabled = isEnabled, @@ -37,7 +41,7 @@ fun SwitchText( ) Text( - modifier = Modifier.padding(horizontal = 12.dp), + modifier = Modifier.Companion.padding(horizontal = 12.dp), text = text, style = if (isEnabled) { @@ -50,7 +54,7 @@ fun SwitchText( ) }, maxLines = 2, - overflow = TextOverflow.Ellipsis, + overflow = TextOverflow.Companion.Ellipsis, ) } } From 586f640389fb123ac07c6321d027f1a1cf2453d2 Mon Sep 17 00:00:00 2001 From: sds100 Date: Wed, 24 Dec 2025 23:51:15 +0000 Subject: [PATCH 199/199] chore: update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c7879734f..be7db7909c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## [4.0.0 Beta 4](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.04) -#### TO BE RELEASED +#### 25 December 2025 + +Merry Christmas from the Key Mapper team! 🎄 Renamed PRO mode to Expert mode because it sounded like a paid premium feature even though it is free.