diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index a2027703b1..1acb88303f 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -17,9 +17,6 @@ jobs: java-version: 17 cache: 'gradle' - - name: Setup Android SDK - uses: android-actions/setup-android@v2 - - name: Unit tests run: bash ./gradlew testDebugUnitTest @@ -46,12 +43,48 @@ jobs: java-version: 17 cache: 'gradle' - - name: Setup Android SDK - uses: android-actions/setup-android@v2 - - name: Ktlint check run: ./gradlew ktlintCheck + rust: + name: Rust code style and tests + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Install Android Rust targets + run: | + rustup target add armv7-linux-androideabi + rustup target add aarch64-linux-android + rustup target add i686-linux-android + rustup target add x86_64-linux-android + + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + evdev/src/main/rust/evdev_manager/target/ + key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.toml', '**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-rust- + + - name: Check Rust formatting + working-directory: evdev/src/main/rust/evdev_manager + run: cargo fmt --check + + - name: Run Rust tests + working-directory: evdev/src/main/rust/evdev_manager + run: cargo test --package evdev_manager_core + apk: name: Build APK runs-on: ubuntu-latest @@ -77,6 +110,18 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v2 + with: + ndk-version: "27.2.12479018" + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Install Android Rust targets + run: | + rustup target add armv7-linux-androideabi + rustup target add aarch64-linux-android + rustup target add i686-linux-android + rustup target add x86_64-linux-android - name: set up Ruby for fastlane uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 18188155de..5d9ed84b96 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -23,9 +23,6 @@ jobs: java-version: 17 cache: 'gradle' - - name: Setup Android SDK - uses: android-actions/setup-android@v2 - - name: Unit tests run: bash ./gradlew testDebugUnitTest @@ -52,12 +49,48 @@ jobs: java-version: 17 cache: 'gradle' - - name: Setup Android SDK - uses: android-actions/setup-android@v2 - - name: Ktlint check run: ./gradlew ktlintCheck + rust: + name: Rust code style and tests + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Install Android Rust targets + run: | + rustup target add armv7-linux-androideabi + rustup target add aarch64-linux-android + rustup target add i686-linux-android + rustup target add x86_64-linux-android + + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + evdev/src/main/rust/evdev_manager/target/ + key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.toml', '**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-rust- + + - name: Check Rust formatting + working-directory: evdev/src/main/rust/evdev_manager + run: cargo fmt --check + + - name: Run Rust tests + working-directory: evdev/src/main/rust/evdev_manager + run: cargo test --package evdev_manager_core + apk: name: Generate and upload APK to Discord runs-on: ubuntu-latest @@ -89,6 +122,18 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v2 + with: + ndk-version: "27.2.12479018" + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Install Android Rust targets + run: | + rustup target add armv7-linux-androideabi + rustup target add aarch64-linux-android + rustup target add i686-linux-android + rustup target add x86_64-linux-android - name: set up Ruby for fastlane uses: ruby/setup-ruby@v1 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 3ba9020698..f5464edfae 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -116,7 +116,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 { @@ -138,7 +138,8 @@ -keep class io.github.sds100.keymapper.sysbridge.** extends android.content.ContentProvider { *; } # Keep parcelable classes used in AIDL --keep class io.github.sds100.keymapper.common.models.EvdevDeviceHandle { *; } +-keep class io.github.sds100.keymapper.common.models.GrabbedDeviceHandle { *; } +-keep class io.github.sds100.keymapper.common.models.EvdevDeviceInfo { *; } # Keep all rikka.hidden classes and interfaces as they contain AIDL files -keep class rikka.hidden.** { *; } 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 @@ - + ) { + @Suppress("DEPRECATION") + Looper.prepareMainLooper() + SystemBridge() + Looper.loop() + } + } +} diff --git a/app/version.properties b/app/version.properties index 4b2046f38c..f1feecf3c2 100644 --- a/app/version.properties +++ b/app/version.properties @@ -1,2 +1,2 @@ VERSION_NAME=5.0.0-alpha -VERSION_CODE=197 +VERSION_CODE=198 diff --git a/base/src/main/java/io/github/sds100/keymapper/base/detection/DetectKeyMapsUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/detection/DetectKeyMapsUseCase.kt index 97de63d1bd..befa0d8122 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/detection/DetectKeyMapsUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/detection/DetectKeyMapsUseCase.kt @@ -30,7 +30,6 @@ import io.github.sds100.keymapper.data.repositories.PreferenceRepository import io.github.sds100.keymapper.system.popup.ToastAdapter import io.github.sds100.keymapper.system.vibrator.VibratorAdapter import io.github.sds100.keymapper.system.volume.VolumeAdapter -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -49,17 +48,12 @@ class DetectKeyMapsUseCaseImpl @AssistedInject constructor( private val toastAdapter: ToastAdapter, private val resourceProvider: ResourceProvider, private val vibrator: VibratorAdapter, - @Assisted - private val coroutineScope: CoroutineScope, private val inputEventHub: InputEventHub, ) : DetectKeyMapsUseCase { @AssistedFactory interface Factory { - fun create( - accessibilityService: IAccessibilityService, - coroutineScope: CoroutineScope, - ): DetectKeyMapsUseCaseImpl + fun create(accessibilityService: IAccessibilityService): DetectKeyMapsUseCaseImpl } companion object { @@ -228,15 +222,15 @@ class DetectKeyMapsUseCaseImpl @AssistedInject constructor( } } - override fun imitateEvdevEvent(devicePath: String, type: Int, code: Int, value: Int) { + override fun imitateEvdevEvent(deviceId: Int, type: Int, code: Int, value: Int) { if (inputEventHub.isSystemBridgeConnected()) { Timber.d( - "Imitate evdev event, device path: $devicePath, type: $type, code: $code, value: $value", + "Imitate evdev event, device id: $deviceId, type: $type, code: $code, value: $value", ) - inputEventHub.injectEvdevEvent(devicePath, type, code, value) + inputEventHub.injectEvdevEvent(deviceId, type, code, value) } else { Timber.w( - "Cannot imitate evdev event without system bridge connected. Device path: $devicePath, type: $type, code: $code, value: $value", + "Cannot imitate evdev event without system bridge connected.", ) } } @@ -268,5 +262,5 @@ interface DetectKeyMapsUseCase { source: Int = InputDevice.SOURCE_UNKNOWN, ) - fun imitateEvdevEvent(devicePath: String, type: Int, code: Int, value: Int) + fun imitateEvdevEvent(deviceId: Int, type: Int, code: Int, value: Int) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapAlgorithm.kt b/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapAlgorithm.kt index 7429079ace..af9d559dc5 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapAlgorithm.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapAlgorithm.kt @@ -648,13 +648,8 @@ class KeyMapAlgorithm( val event = EvdevEventAlgo( keyCode = inputEvent.androidCode, clickType = null, - devicePath = inputEvent.device.path, - device = EvdevDeviceInfo( - name = inputEvent.device.name, - bus = inputEvent.device.bus, - vendor = inputEvent.device.vendor, - product = inputEvent.device.product, - ), + deviceId = inputEvent.deviceId, + device = inputEvent.deviceInfo, scanCode = inputEvent.code, ) @@ -1490,14 +1485,14 @@ class KeyMapAlgorithm( ) } else if (event is EvdevEventAlgo) { useCase.imitateEvdevEvent( - devicePath = event.devicePath, + deviceId = event.deviceId, KMEvdevEvent.TYPE_KEY_EVENT, event.scanCode, KMEvdevEvent.VALUE_DOWN, ) useCase.imitateEvdevEvent( - devicePath = event.devicePath, + deviceId = event.deviceId, KMEvdevEvent.TYPE_KEY_EVENT, event.scanCode, KMEvdevEvent.VALUE_UP, @@ -1544,20 +1539,20 @@ class KeyMapAlgorithm( } else if (event is EvdevEventAlgo) { if (imitateUpKeyEvent) { useCase.imitateEvdevEvent( - devicePath = event.devicePath, + deviceId = event.deviceId, type = KMEvdevEvent.TYPE_KEY_EVENT, code = event.scanCode, value = KMEvdevEvent.VALUE_UP, ) } else { useCase.imitateEvdevEvent( - devicePath = event.devicePath, + deviceId = event.deviceId, type = KMEvdevEvent.TYPE_KEY_EVENT, code = event.scanCode, value = KMEvdevEvent.VALUE_DOWN, ) useCase.imitateEvdevEvent( - devicePath = event.devicePath, + deviceId = event.deviceId, type = KMEvdevEvent.TYPE_KEY_EVENT, code = event.scanCode, value = KMEvdevEvent.VALUE_UP, @@ -1944,7 +1939,7 @@ class KeyMapAlgorithm( } private data class EvdevEventAlgo( - val devicePath: String, + val deviceId: Int, val device: EvdevDeviceInfo, val scanCode: Int, val keyCode: Int, 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 new file mode 100644 index 0000000000..fd7aba73b2 --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt @@ -0,0 +1,117 @@ +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.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 +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 +import timber.log.Timber + +/** + * Need to use a cache that maps a device id to the other device information. This information + * could be sent in the onEvdevEvent callback instead, but sending non-primitive strings for the + * device name introduces extra overhead across Binder and JNI. + */ +@OptIn(ExperimentalCoroutinesApi::class) +@RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) +@Singleton +class EvdevDevicesDelegate @Inject constructor( + private val coroutineScope: CoroutineScope, + private val devicesAdapter: DevicesAdapter, + private val systemBridgeConnectionManager: SystemBridgeConnectionManager, +) { + private val grabbedDevicesById: MutableStateFlow> = + MutableStateFlow(emptyMap()) + + // 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) + + val allDevices: MutableStateFlow> = MutableStateFlow(emptyList()) + + init { + coroutineScope.launch { + coroutineScope.launch { + // Only listen to changes in the connected input devices if the system bridge + // is connected. + systemBridgeConnectionManager.connectionState.flatMapLatest { connectionState -> + when (connectionState) { + is SystemBridgeConnectionState.Connected -> { + devicesAdapter.connectedInputDevices.onEach { + allDevices.value = fetchAllDevices() + } + } + is SystemBridgeConnectionState.Disconnected -> { + allDevices.value = emptyList() + grabbedDevicesById.value = emptyMap() + emptyFlow() + } + } + }.collect() + } + } + + // Process on another thread because system bridge grabbing calls are blocking + coroutineScope.launch(Dispatchers.IO) { + grabDevicesChannel.receiveAsFlow().collect { devices -> + systemBridgeConnectionManager + .run { bridge -> bridge.setGrabbedDevices(devices.toTypedArray()) } + .onSuccess { grabbedDevices -> + onGrabbedDevicesChanged(grabbedDevices?.filterNotNull() ?: emptyList()) + }.onFailure { error -> + Timber.w( + "Grabbing devices failed in system bridge: $error", + ) + } + } + } + } + + fun setGrabbedDevices(devices: List) { + grabDevicesChannel.trySend(devices) + } + + fun getGrabbedDeviceInfo(id: Int): EvdevDeviceInfo? { + return grabbedDevicesById.value[id] + } + + private fun onGrabbedDevicesChanged(devices: List) { + Timber.i("Grabbed devices changed: [${devices.joinToString { it.name }}]") + + grabbedDevicesById.value = + devices.associate { handle -> + handle.id to + EvdevDeviceInfo(handle.name, handle.bus, handle.vendor, handle.product) + } + } + + private suspend fun fetchAllDevices(): List { + // Do it on a separate thread in case there is deadlock + return withContext(Dispatchers.IO) { + systemBridgeConnectionManager.run { bridge -> + bridge.evdevInputDevices?.filterNotNull() ?: emptyList() + } + }.onFailure { error -> + Timber.e("Failed to get evdev input devices from system bridge: $error") + }.valueIfFailure { emptyList() } + } +} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevHandleCache.kt b/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevHandleCache.kt deleted file mode 100644 index aa592e491e..0000000000 --- a/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevHandleCache.kt +++ /dev/null @@ -1,97 +0,0 @@ -package io.github.sds100.keymapper.base.input - -import androidx.annotation.RequiresApi -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle -import io.github.sds100.keymapper.common.models.EvdevDeviceInfo -import io.github.sds100.keymapper.common.utils.Constants -import io.github.sds100.keymapper.common.utils.onFailure -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.sysbridge.manager.isConnected -import io.github.sds100.keymapper.system.devices.DevicesAdapter -import javax.inject.Inject -import javax.inject.Singleton -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import timber.log.Timber - -@RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) -@Singleton -class EvdevHandleCache @Inject constructor( - private val coroutineScope: CoroutineScope, - private val devicesAdapter: DevicesAdapter, - private val systemBridgeConnectionManager: SystemBridgeConnectionManager, -) { - private val devicesByPath: MutableStateFlow> = - MutableStateFlow(emptyMap()) - - val devices: StateFlow> = - devicesByPath - .map { pathMap -> - pathMap.values.map { device -> - EvdevDeviceInfo( - name = device.name, - bus = device.bus, - vendor = device.vendor, - product = device.product, - ) - } - } - .stateIn(coroutineScope, SharingStarted.Eagerly, emptyList()) - - init { - coroutineScope.launch { - combine( - devicesAdapter.connectedInputDevices, - systemBridgeConnectionManager.connectionState, - ) { _, connectionState -> - if (connectionState is SystemBridgeConnectionState.Connected) { - invalidate() - } else { - devicesByPath.value = emptyMap() - } - }.collect() - } - } - - fun getByPath(path: String): EvdevDeviceHandle? { - return devicesByPath.value[path] - } - - fun getByInfo(deviceInfo: EvdevDeviceInfo): EvdevDeviceHandle? { - return devicesByPath.value.values.firstOrNull { - it.name == deviceInfo.name && - it.bus == deviceInfo.bus && - it.vendor == deviceInfo.vendor && - it.product == deviceInfo.product - } - } - - suspend fun invalidate() { - if (!systemBridgeConnectionManager.isConnected()) { - devicesByPath.value = emptyMap() - return - } - - // Do it on a separate thread in case there is deadlock - val newDevices = withContext(Dispatchers.IO) { - systemBridgeConnectionManager.run { bridge -> - bridge.evdevInputDevices.associateBy { it.path } - } - }.onFailure { error -> - Timber.e("Failed to get evdev input devices from system bridge $error") - }.valueIfFailure { emptyMap() } - - devicesByPath.value = newDevices - } -} 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 e8d73beb13..01d1796d12 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 @@ -11,17 +11,14 @@ 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.onFailure import io.github.sds100.keymapper.common.utils.onSuccess import io.github.sds100.keymapper.common.utils.then -import io.github.sds100.keymapper.common.utils.valueOrNull import io.github.sds100.keymapper.data.Keys import io.github.sds100.keymapper.data.repositories.PreferenceRepository -import io.github.sds100.keymapper.sysbridge.IEvdevCallback +import io.github.sds100.keymapper.evdev.IEvdevCallback import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState import io.github.sds100.keymapper.sysbridge.manager.isConnected -import io.github.sds100.keymapper.sysbridge.utils.SystemBridgeError import io.github.sds100.keymapper.system.inputevents.KMEvdevEvent import io.github.sds100.keymapper.system.inputevents.KMGamePadEvent import io.github.sds100.keymapper.system.inputevents.KMInputEvent @@ -34,11 +31,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -51,7 +45,7 @@ class InputEventHubImpl @Inject constructor( private val systemBridgeConnManager: SystemBridgeConnectionManager, private val imeInputEventInjector: ImeInputEventInjector, private val preferenceRepository: PreferenceRepository, - private val evdevHandlesCache: EvdevHandleCache, + private val evdevDevicesDelegate: EvdevDevicesDelegate, ) : IEvdevCallback.Stub(), InputEventHub { @@ -73,8 +67,6 @@ class InputEventHubImpl @Inject constructor( } }.stateIn(coroutineScope, SharingStarted.Eagerly, false) - private val invalidateGrabbedDevicesChannel: Channel = Channel(capacity = 10) - init { startKeyEventProcessingLoop() @@ -83,21 +75,12 @@ class InputEventHubImpl @Inject constructor( systemBridgeConnManager.connectionState .filterIsInstance() .collect { + // Whenever the system bridge is connected systemBridgeConnManager.run { bridge -> bridge.registerEvdevCallback(this@InputEventHubImpl) } - } - } - coroutineScope.launch { - invalidateGrabbedDevicesChannel - .consumeAsFlow() - .map { - clients.values.flatMap { it.grabbedEvdevDevices }.toSet().toList() - } - .flowOn(Dispatchers.Default) - .collectLatest { devices -> - invalidateGrabbedEvdevDevices(devices) + invalidateGrabbedDevices() } } } @@ -123,17 +106,9 @@ class InputEventHubImpl @Inject constructor( return systemBridgeConnManager.isConnected() } - override fun onEvdevEventLoopStarted() { - Timber.i("On evdev event loop started") - - coroutineScope.launch { - invalidateGrabbedDevicesChannel.send(Unit) - } - } - @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) override fun onEvdevEvent( - devicePath: String?, + deviceId: Int, timeSec: Long, timeUsec: Long, type: Int, @@ -141,10 +116,9 @@ class InputEventHubImpl @Inject constructor( value: Int, androidCode: Int, ): Boolean { - devicePath ?: return false - - val handle = evdevHandlesCache.getByPath(devicePath) ?: return false - val evdevEvent = KMEvdevEvent(handle, type, code, value, androidCode, timeSec, timeUsec) + val info = evdevDevicesDelegate.getGrabbedDeviceInfo(deviceId) ?: return false + val evdevEvent = + KMEvdevEvent(deviceId, info, type, code, value, androidCode, timeSec, timeUsec) return onInputEvent(evdevEvent, InputEventDetectionSource.EVDEV) } @@ -162,20 +136,13 @@ class InputEventHubImpl @Inject constructor( for (clientContext in clients.values) { if (event is KMEvdevEvent) { if (!clientContext.evdevEventTypes.contains(event.type) || - clientContext.grabbedEvdevDevices.isEmpty() + clientContext.devicesToGrab.isEmpty() ) { continue } - val deviceInfo = EvdevDeviceInfo( - event.device.name, - event.device.bus, - event.device.vendor, - event.device.product, - ) - // Only send events from evdev devices to the client if they grabbed it - if (!clientContext.grabbedEvdevDevices.contains(deviceInfo)) { + if (!clientContext.devicesToGrab.contains(event.deviceInfo)) { continue } @@ -200,7 +167,7 @@ class InputEventHubImpl @Inject constructor( when (event) { is KMEvdevEvent -> { Timber.d( - "Evdev event: devicePath=${event.device.path}, deviceName=${event.device.name}, type=${event.type}, code=${event.code}, value=${event.value}", + "Evdev event: deviceId=${event.deviceId}, deviceName=${event.deviceInfo.name}, type=${event.type}, code=${event.code}, value=${event.value}", ) } @@ -247,14 +214,20 @@ class InputEventHubImpl @Inject constructor( } clients[clientId] = ClientContext(callback, emptySet(), evdevEventTypes.toSet()) + if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { + invalidateGrabbedDevices() + } } override fun unregisterClient(clientId: String) { Timber.d("InputEventHub: Unregistering client $clientId") clients.remove(clientId) - invalidateGrabbedDevicesChannel.trySend(Unit) + if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { + invalidateGrabbedDevices() + } } + @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) override fun setGrabbedEvdevDevices(clientId: String, devices: List) { if (!clients.containsKey(clientId)) { throw IllegalArgumentException( @@ -262,9 +235,8 @@ class InputEventHubImpl @Inject constructor( ) } - clients[clientId] = clients[clientId]!!.copy(grabbedEvdevDevices = devices.toSet()) - - invalidateGrabbedDevicesChannel.trySend(Unit) + clients[clientId] = clients[clientId]!!.copy(devicesToGrab = devices.toSet()) + invalidateGrabbedDevices() } @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) @@ -275,54 +247,21 @@ class InputEventHubImpl @Inject constructor( ) } - val devices = evdevHandlesCache.devices.value.toSet() - clients[clientId] = clients[clientId]!!.copy(grabbedEvdevDevices = devices) - - invalidateGrabbedDevicesChannel.trySend(Unit) - } - - @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) - private suspend fun invalidateGrabbedEvdevDevices(evdevDevices: List) { - // Invalidate the cache first to make sure it is up to date. - evdevHandlesCache.invalidate() - - // Grabbing can block if there are other grabbing or event loop start/stop operations happening. - systemBridgeConnManager.run { bridge -> bridge.ungrabAllEvdevDevices() } - .onSuccess { Timber.i("Ungrabbed all evdev devices: $it") } - .then { - val handles: Array = - evdevDevices.mapNotNull { evdevHandlesCache.getByInfo(it)?.path }.toTypedArray() - - val result = - systemBridgeConnManager.run { bridge -> bridge.grabEvdevDeviceArray(handles) } - - if (result.valueOrNull() == true) { - Success(Unit) - } else { - KMError.Exception(Exception("Failed to grab")) - } - } - .onSuccess { result -> - Timber.i("Grabbed evdev devices [${evdevDevices.joinToString { it.name }}]") - } - .onFailure { error -> - // Do not log if it is expected to prevent log spam. - if (error !is SystemBridgeError.Disconnected) { - Timber.e("Failed to grab evdev devices.") - } - } + val devices = evdevDevicesDelegate.allDevices.value.toSet() + clients[clientId] = clients[clientId]!!.copy(devicesToGrab = devices) + invalidateGrabbedDevices() } @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) override fun injectEvdevEvent( - devicePath: String, + deviceId: Int, type: Int, code: Int, value: Int, ): KMResult { return systemBridgeConnManager.run { bridge -> bridge.writeEvdevEvent( - devicePath, + deviceId, type, code, value, @@ -383,12 +322,18 @@ class InputEventHubImpl @Inject constructor( .firstBlocking() } + @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) + private fun invalidateGrabbedDevices() { + val devicesToGrab = clients.values.flatMap { it.devicesToGrab }.toSet() + evdevDevicesDelegate.setGrabbedDevices(devicesToGrab.toList()) + } + private data class ClientContext( val callback: InputEventHubCallback, /** * The evdev devices that this client wants to grab. */ - val grabbedEvdevDevices: Set, + val devicesToGrab: Set, val evdevEventTypes: Set, ) } @@ -429,7 +374,7 @@ interface InputEventHub { */ fun injectKeyEventAsync(event: InjectKeyEventModel): KMResult - fun injectEvdevEvent(devicePath: String, type: Int, code: Int, value: Int): KMResult + fun injectEvdevEvent(deviceId: Int, type: Int, code: Int, value: Int): KMResult /** * Send an input event to the connected clients. 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 74f7a31df3..35be4f212d 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 @@ -7,7 +7,7 @@ import io.github.sds100.keymapper.base.actions.DisplayActionUseCase import io.github.sds100.keymapper.base.actions.GetActionErrorUseCase import io.github.sds100.keymapper.base.constraints.DisplayConstraintUseCase import io.github.sds100.keymapper.base.constraints.GetConstraintErrorUseCase -import io.github.sds100.keymapper.base.input.EvdevHandleCache +import io.github.sds100.keymapper.base.input.EvdevDevicesDelegate import io.github.sds100.keymapper.base.purchasing.ProductId import io.github.sds100.keymapper.base.purchasing.PurchasingError.ProductNotPurchased import io.github.sds100.keymapper.base.purchasing.PurchasingManager @@ -70,7 +70,7 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( private val buildConfigProvider: BuildConfigProvider, private val navigationProvider: NavigationProvider, private val systemBridgeConnectionManager: SystemBridgeConnectionManager, - private val evdevHandleCache: EvdevHandleCache, + private val grabbedEvdevDeviceCache: EvdevDevicesDelegate, ) : DisplayKeyMapUseCase, GetActionErrorUseCase by getActionErrorUseCase, GetConstraintErrorUseCase by getConstraintErrorUseCase { @@ -113,7 +113,7 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( private val evdevDevices: Flow?> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - evdevHandleCache.devices + grabbedEvdevDeviceCache.allDevices } else { flowOf(null) } 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 065224ad53..2a9d5c9b2d 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 @@ -82,7 +82,6 @@ abstract class BaseAccessibilityServiceController( private val detectKeyMapsUseCase = detectKeyMapsUseCaseFactory.create( accessibilityService = service, - coroutineScope = service.lifecycleScope, ) val detectConstraintsUseCase = detectConstraintsUseCaseFactory.create(service) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerController.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerController.kt index 725ae003f7..b8e2b97424 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerController.kt @@ -223,7 +223,7 @@ class RecordTriggerControllerImpl @Inject constructor( return RecordedKey.EvdevEvent( keyCode = evdevEvent.androidCode, scanCode = evdevEvent.code, - device = evdevEvent.device, + device = evdevEvent.deviceInfo, ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordedKey.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordedKey.kt index 468b64a329..b6e611b9c1 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordedKey.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordedKey.kt @@ -1,7 +1,7 @@ package io.github.sds100.keymapper.base.trigger import io.github.sds100.keymapper.base.input.InputEventDetectionSource -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo sealed class RecordedKey { data class KeyEvent( @@ -13,6 +13,6 @@ sealed class RecordedKey { val detectionSource: InputEventDetectionSource, ) : RecordedKey() - data class EvdevEvent(val keyCode: Int, val scanCode: Int, val device: EvdevDeviceHandle) : + data class EvdevEvent(val keyCode: Int, val scanCode: Int, val device: EvdevDeviceInfo) : RecordedKey() } diff --git a/base/src/test/java/io/github/sds100/keymapper/base/keymaps/KeyMapAlgorithmTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/keymaps/KeyMapAlgorithmTest.kt index 11a00e269a..3f331fdec5 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/keymaps/KeyMapAlgorithmTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/keymaps/KeyMapAlgorithmTest.kt @@ -30,7 +30,6 @@ import io.github.sds100.keymapper.base.utils.parallelTrigger import io.github.sds100.keymapper.base.utils.sequenceTrigger import io.github.sds100.keymapper.base.utils.singleKeyTrigger import io.github.sds100.keymapper.base.utils.triggerKey -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle import io.github.sds100.keymapper.common.models.EvdevDeviceInfo import io.github.sds100.keymapper.common.utils.InputDeviceInfo import io.github.sds100.keymapper.common.utils.InputEventAction @@ -4750,8 +4749,8 @@ class KeyMapAlgorithmTest { controller.onInputEvent( KMEvdevEvent( type = KMEvdevEvent.TYPE_KEY_EVENT, - device = EvdevDeviceHandle( - path = "/dev/input${device.name}", + deviceId = 0, + deviceInfo = EvdevDeviceInfo( name = device.name, bus = device.bus, vendor = device.vendor, @@ -4769,8 +4768,8 @@ class KeyMapAlgorithmTest { controller.onInputEvent( KMEvdevEvent( type = KMEvdevEvent.TYPE_KEY_EVENT, - device = EvdevDeviceHandle( - path = "/dev/input${device.name}", + deviceId = 0, + deviceInfo = EvdevDeviceInfo( name = device.name, bus = device.bus, vendor = device.vendor, 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/common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.aidl b/common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceInfo.aidl similarity index 63% rename from common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.aidl rename to common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceInfo.aidl index 8ea6d7f489..daeafbd464 100644 --- a/common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.aidl +++ b/common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceInfo.aidl @@ -1,3 +1,3 @@ package io.github.sds100.keymapper.common.models; -parcelable EvdevDeviceHandle; \ No newline at end of file +parcelable EvdevDeviceInfo; \ No newline at end of file diff --git a/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.aidl b/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.aidl new file mode 100644 index 0000000000..2ad7314522 --- /dev/null +++ b/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.aidl @@ -0,0 +1,3 @@ +package io.github.sds100.keymapper.common.models; + +parcelable GrabbedDeviceHandle; \ No newline at end of file diff --git a/common/src/main/java/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.kt b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.kt similarity index 64% rename from common/src/main/java/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.kt rename to common/src/main/java/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.kt index 1b14c03b86..a9d1855a2d 100644 --- a/common/src/main/java/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.kt +++ b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.kt @@ -4,11 +4,8 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class EvdevDeviceHandle( - /** - * The path to the device. E.g /dev/input/event1 - */ - val path: String, +data class GrabbedDeviceHandle( + val id: Int, val name: String, val bus: Int, val vendor: Int, diff --git a/evdev/.gitignore b/evdev/.gitignore new file mode 100644 index 0000000000..48b4c6e25e --- /dev/null +++ b/evdev/.gitignore @@ -0,0 +1,5 @@ +/build +.cxx +/src/main/cpp/libevdev/event-names.h +/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/dictionaries/project.xml b/evdev/.idea/dictionaries/project.xml new file mode 100644 index 0000000000..77c05f2c6b --- /dev/null +++ b/evdev/.idea/dictionaries/project.xml @@ -0,0 +1,7 @@ + + + + uinput + + + \ No newline at end of file diff --git a/evdev/.idea/gradle.xml b/evdev/.idea/gradle.xml new file mode 100644 index 0000000000..e77e5a3224 --- /dev/null +++ b/evdev/.idea/gradle.xml @@ -0,0 +1,12 @@ + + + + + + + \ 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/rust.xml b/evdev/.idea/rust.xml new file mode 100644 index 0000000000..7bc91eafa3 --- /dev/null +++ b/evdev/.idea/rust.xml @@ -0,0 +1,6 @@ + + + + + \ 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 new file mode 100644 index 0000000000..f18aaa306c --- /dev/null +++ b/evdev/build.gradle.kts @@ -0,0 +1,138 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.mozilla.rust.android) +} + +android { + namespace = "io.github.sds100.keymapper.evdev" + compileSdk = libs.versions.compile.sdk.get().toInt() + + // IMPORTANT! If you change this, also change it in build.rs for evdev crate and any CI workflows. This must + // match one of the NDK versions installed in the GitHub actions runner. E.g look here for Ubuntu 24.04 + // https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md + ndkVersion = "27.3.13750724" + + defaultConfig { + // System bridge originally only supported Android 10+ because that was the min sdk + // for binder-ndk library. Even though that is no longer used, it is extra effort to support + // the changes to the internal Android APIs on Android 8 and 9. + minSdk = 29 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + + buildFeatures { + // Disable because a Java implementation of IEvdevCallback is not required in this module + aidl = false + } + + packaging { + jniLibs { + // This replaces extractNativeLibs option in the manifest. This is needed so the + // libraries are extracted to a location on disk where the system bridge process + // can access them. Start in Android 6.0, they are no longer extracted by default. + useLegacyPackaging = true + + // This is required on Android 15. Otherwise a java.lang.UnsatisfiedLinkError: dlopen failed: empty/missing DT_HASH/DT_GNU_HASH error is thrown. + keepDebugSymbols.add("**/*.so") + } + } +} + +cargo { + val isDebug = gradle.startParameter.taskNames.any { it.lowercase().contains("debug") } + + module = "src/main/rust/evdev_manager" + libname = "evdev_manager" + targets = if (isDebug) { + // Only building for one target saves some time when developing + listOf("arm64") + } else { + 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 (isDebug) { + "debug" + } else { + "release" + } +} + +dependencies { +} + +// 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) { + 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) +} + +// Note: NDK AIDL compilation is no longer needed since we're using pure JNI +// instead of C++ Binder layer. The Kotlin side still uses IEvdevCallback AIDL, +// but that's handled by Android's standard AIDL processing. + +tasks.named("preBuild") { + dependsOn(generateLibEvDevEventNames) +} + +// Ensure event names are generated before cargo build runs +afterEvaluate { + tasks.matching { it.name.contains("cargoBuild") }.configureEach { + dependsOn(generateLibEvDevEventNames) + } +} + +// 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/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/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 99% rename from sysbridge/src/main/cpp/libevdev/make-event-names.py rename to evdev/src/main/cpp/libevdev/make-event-names.py index 743b4b58b1..16a984d960 --- a/sysbridge/src/main/cpp/libevdev/make-event-names.py +++ b/evdev/src/main/cpp/libevdev/make-event-names.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/_env python3 # # Parses linux/input.h scanning for #define KEY_FOO 134 # Prints C header files or Python files that can be used as diff --git a/evdev/src/main/rust/evdev/Cargo.lock b/evdev/src/main/rust/evdev/Cargo.lock new file mode 100644 index 0000000000..e5c3b3271b --- /dev/null +++ b/evdev/src/main/rust/evdev/Cargo.lock @@ -0,0 +1,53 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "cc" +version = "1.2.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "evdev" +version = "0.1.0" +dependencies = [ + "bitflags", + "cc", + "libc", + "log", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "libc" +version = "1.0.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7222002e5385b4d9327755661e3847c970e8fbf9dea6da8c57f16e8cfbff53a8" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" diff --git a/evdev/src/main/rust/evdev/Cargo.toml b/evdev/src/main/rust/evdev/Cargo.toml new file mode 100644 index 0000000000..489211c191 --- /dev/null +++ b/evdev/src/main/rust/evdev/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "evdev" +version = "0.1.0" +edition = "2021" + +[lib] +name = "evdev" +# "cdylib" is necessary to produce a C-style dynamic library that can be loaded by Android's JNI. +# "rlib" is necessary to allow this crate to be used as a dependency by other Rust crates. +crate-type = ["cdylib", "rlib"] + +[profile.dev] +debug = true +strip = false # Don't strip symbols, keeps them in the .so file + +[dependencies] +libc = "1.0.0-alpha.1" +log = "0.4.28" +bitflags = "2.10.0" + +[build-dependencies] +cc = "1.2.46" diff --git a/evdev/src/main/rust/evdev/README.md b/evdev/src/main/rust/evdev/README.md new file mode 100644 index 0000000000..f96a1eb7e9 --- /dev/null +++ b/evdev/src/main/rust/evdev/README.md @@ -0,0 +1 @@ +Most of the code in this crate is taken from https://github.com/ndesh26/evdev-rs. \ No newline at end of file diff --git a/evdev/src/main/rust/evdev/build.rs b/evdev/src/main/rust/evdev/build.rs new file mode 100644 index 0000000000..c4813c4800 --- /dev/null +++ b/evdev/src/main/rust/evdev/build.rs @@ -0,0 +1,133 @@ +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"); + + // 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(&libevdev_dir) + .include(sysroot_include.join("linux/input-event-codes.h")) + .flag("-Werror=format") + .flag("-Wno-unused-parameter") + .flag("-fdata-sections") + .flag("-ffunction-sections"); + + if env::var("PROFILE").unwrap() == "release" { + c_builder.flag("-O2").flag("-fvisibility=hidden"); + } + + c_builder.compile("evdev_c"); +} + +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.3.13750724"; + + 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 +} diff --git a/evdev/src/main/rust/evdev/src/device.rs b/evdev/src/main/rust/evdev/src/device.rs new file mode 100644 index 0000000000..52897f6bb5 --- /dev/null +++ b/evdev/src/main/rust/evdev/src/device.rs @@ -0,0 +1,898 @@ +use crate::{AbsInfo, GrabMode, InputEvent, LedState, ReadFlag, ReadStatus, TimeVal}; +use libc::{c_int, c_uint, c_void}; +use std::ffi::CString; +use std::fs::File; +use std::fs::OpenOptions; +use std::io::Read; +use std::mem::ManuallyDrop; +use std::os::unix::fs::OpenOptionsExt; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::path::Path; +use std::{io, ptr}; + +use crate::enums::*; +use crate::libevdev; +use crate::util::*; + +/// Types that can be enabled on a DeviceWrapper (i.e. buttons, keys, relative motion) +/// +/// Generally this method will not be called directly, but will insted be called through [Device::enable()](crate::Device::enable) +/// +/// ```rust +/// # use evdev_rs::{UninitDevice, DeviceWrapper, Enable, enums::{EventCode, EV_REL::REL_X}}; +/// let dev = UninitDevice::new().expect("Device creation failed"); +/// dev.enable(EventCode::EV_REL(REL_X)).expect("Enable failed"); +/// ``` +/// +/// If you need to enable a EV_ABS or EV_REP event code, use +/// [enable_event_code](crate::Device::enable_event_code), as this +/// implementation doesn't pass EV_ABS data. +pub trait Enable { + fn enable(&self, device: &D) -> io::Result<()>; + fn disable(&self, device: &D) -> io::Result<()>; + fn has(&self, device: &D) -> bool; +} + +impl Enable for InputProp { + fn enable(&self, device: &D) -> io::Result<()> { + device.enable_property(self) + } + fn disable(&self, device: &D) -> io::Result<()> { + device.disable_property(self) + } + fn has(&self, device: &D) -> bool { + device.has_property(self) + } +} + +impl Enable for EventType { + fn enable(&self, device: &D) -> io::Result<()> { + device.enable_event_type(self) + } + fn disable(&self, device: &D) -> io::Result<()> { + device.disable_event_type(self) + } + fn has(&self, device: &D) -> bool { + device.has_event_type(self) + } +} + +impl Enable for EventCode { + fn enable(&self, device: &D) -> io::Result<()> { + device.enable_event_code(self, None) + } + fn disable(&self, device: &D) -> io::Result<()> { + device.disable_event_code(self) + } + fn has(&self, device: &D) -> bool { + device.has_event_code(self) + } +} + +/// Extra data for use with enable_event_code +#[derive(Clone, Copy, Debug)] +pub enum EnableCodeData { + AbsInfo(AbsInfo), + RepInfo(i32), +} + +/// Abstraction over structs which contain an inner `*mut libevdev` +pub trait DeviceWrapper: Sized { + fn raw(&self) -> *mut libevdev::libevdev; + + /// Forcibly enable an EventType/InputProp 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 has(). + /// + /// This is a local modification only affecting only this representation of + /// this device. + fn enable(&self, e: E) -> io::Result<()> { + e.enable(self) + } + + /// Enables this property, a call to `set_file` will overwrite any previously set values + /// + /// Note: Please use the `enable` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn enable_property(&self, prop: &InputProp) -> io::Result<()> { + let result = + unsafe { libevdev::libevdev_enable_property(self.raw(), *prop as c_uint) as i32 }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// 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. + /// + /// Note: Please use the `enable` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn enable_event_type(&self, ev_type: &EventType) -> io::Result<()> { + let result = + unsafe { libevdev::libevdev_enable_event_type(self.raw(), *ev_type as c_uint) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// 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_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 `None`. + /// + /// Note: Please use the `enable` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn enable_event_code( + &self, + ev_code: &EventCode, + data: Option, + ) -> io::Result<()> { + let data = match ev_code { + EventCode::EV_ABS(_) => match data { + Some(EnableCodeData::AbsInfo(info)) => &info.as_raw() as *const _ as *const c_void, + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "EventCode::EV_ABS must be paired with EnableCodeData::AbsInfo", + )) + } + }, + EventCode::EV_REP(_) => match data { + Some(EnableCodeData::RepInfo(info)) => { + &libc::c_int::from(info) as *const _ as *const c_void + } + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "EventCode::EV_REP must be paired with EnableCodeData::RepInfo", + )) + } + }, + _ => ptr::null(), + }; + + let (ev_type, ev_code) = event_code_to_int(ev_code); + + let result = + unsafe { libevdev::libevdev_enable_event_code(self.raw(), ev_type, ev_code, data) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Forcibly disable an EventType/EventCode on this device, even if the + /// underlying device provides it. This effectively mutes the respective set of + /// events. has() will return false for this EventType/EventCode + /// + /// In most cases, a caller likely only wants to disable a single code, not + /// the whole type. + /// + /// Disabling EV_SYN will not work. In Peter's Words "Don't shoot yourself + /// in the foot. It hurts". + /// + /// This is a local modification only affecting only this representation of + /// this device. + fn disable(&self, d: E) -> io::Result<()> { + d.disable(self) + } + + /// 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 `disable_event_code` for that. + /// + /// Disabling EV_SYN will not work. In Peter's Words "Don't shoot yourself + /// in the foot. It hurts". + /// + /// This is a local modification only affecting only this representation of + /// this device. + /// + /// Note: Please use the `disable` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn disable_event_type(&self, ev_type: &EventType) -> io::Result<()> { + let result = + unsafe { libevdev::libevdev_disable_event_type(self.raw(), *ev_type as c_uint) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + /// 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. `has_event_code` will return false for + /// this code. + /// + /// Disabling all event codes for a given type will not disable the event + /// type. Use `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. + /// + /// Note: Please use the `disable` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn disable_event_code(&self, code: &EventCode) -> io::Result<()> { + let (ev_type, ev_code) = event_code_to_int(code); + let result = unsafe { libevdev::libevdev_disable_event_code(self.raw(), ev_type, ev_code) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + fn disable_property(&self, prop: &InputProp) -> io::Result<()> { + let result = unsafe { libevdev::libevdev_disable_property(self.raw(), (*prop) as c_uint) }; + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Returns `true` if device support the InputProp/EventType/EventCode and false otherwise + fn has(&self, e: E) -> bool { + e.has(self) + } + + /// Returns `true` if device support the property and false otherwise + /// + /// Note: Please use the `has` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn has_property(&self, prop: &InputProp) -> bool { + unsafe { libevdev::libevdev_has_property(self.raw(), *prop as c_uint) != 0 } + } + + /// Returns `true` is the device support this event type and `false` otherwise + /// + /// Note: Please use the `has` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn has_event_type(&self, ev_type: &EventType) -> bool { + unsafe { libevdev::libevdev_has_event_type(self.raw(), *ev_type as c_uint) != 0 } + } + + /// Return `true` is the device support this event type and code and `false` otherwise + /// + /// Note: Please use the `has` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn has_event_code(&self, code: &EventCode) -> bool { + unsafe { + let (ev_type, ev_code) = event_code_to_int(code); + libevdev::libevdev_has_event_code(self.raw(), ev_type, ev_code) != 0 + } + } + + string_getter!( + #[doc = "Get device's name, as set by the kernel, or overridden by a call to `set_name`"], + name, libevdev_get_name, + #[doc = "Get device's physical location, as set by the kernel, or overridden by a call to `set_phys`"], + phys, libevdev_get_phys, + #[doc = "Get device's unique identifier, as set by the kernel, or overridden by a call to `set_uniq`"], + uniq, libevdev_get_uniq + ); + + string_setter!( + set_name, + libevdev_set_name, + set_phys, + libevdev_set_phys, + set_uniq, + libevdev_set_uniq + ); + + product_getter!( + product_id, + libevdev_get_id_product, + vendor_id, + libevdev_get_id_vendor, + bustype, + libevdev_get_id_bustype, + version, + libevdev_get_id_version + ); + + product_setter!( + set_product_id, + libevdev_set_id_product, + set_vendor_id, + libevdev_set_id_vendor, + set_bustype, + libevdev_set_id_bustype, + set_version, + libevdev_set_id_version + ); + + /// Get the axis info for the given axis, as advertised by the kernel. + /// + /// Returns the `AbsInfo` for the given the code or None if the device + /// doesn't support this code + fn abs_info(&self, code: &EventCode) -> Option { + let (_, ev_code) = event_code_to_int(code); + let a = unsafe { libevdev::libevdev_get_abs_info(self.raw(), ev_code).as_ref()? }; + + Some(AbsInfo { + value: a.value, + minimum: a.minimum, + maximum: a.maximum, + fuzz: a.fuzz, + flat: a.flat, + resolution: a.resolution, + }) + } + + /// Change the abs info for the given EV_ABS event code, if the code exists. + /// + /// This function has no effect if `has_event_code` returns false for + /// this code. + fn set_abs_info(&self, code: &EventCode, absinfo: &AbsInfo) { + let (_, ev_code) = event_code_to_int(code); + + unsafe { + libevdev::libevdev_set_abs_info(self.raw(), ev_code, &absinfo.as_raw() as *const _); + } + } + + /// Returns the current value of the event type. + /// + /// If the device supports this event type and code, the return value is + /// set to the current value of this axis. Otherwise, `None` is returned. + fn event_value(&self, code: &EventCode) -> Option { + let mut value: i32 = 0; + let (ev_type, ev_code) = event_code_to_int(code); + let valid = unsafe { + libevdev::libevdev_fetch_event_value(self.raw(), ev_type, ev_code, &mut value) + }; + + match valid { + 0 => None, + _ => Some(value), + } + } + + /// 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 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 + /// `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, `set_event_value` returns Err. + fn set_event_value(&self, code: &EventCode, val: i32) -> io::Result<()> { + let (ev_type, ev_code) = event_code_to_int(code); + let result = unsafe { + libevdev::libevdev_set_event_value(self.raw(), ev_type, ev_code, val as c_int) + }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + abs_getter!( + abs_minimum, + libevdev_get_abs_minimum, + abs_maximum, + libevdev_get_abs_maximum, + abs_fuzz, + libevdev_get_abs_fuzz, + abs_flat, + libevdev_get_abs_flat, + abs_resolution, + libevdev_get_abs_resolution + ); + + abs_setter!( + set_abs_minimum, + libevdev_set_abs_minimum, + set_abs_maximum, + libevdev_set_abs_maximum, + set_abs_fuzz, + libevdev_set_abs_fuzz, + set_abs_flat, + libevdev_set_abs_flat, + set_abs_resolution, + libevdev_set_abs_resolution + ); + + /// Return the current value of the code for the given slot. + /// + /// If the device supports this event code, the return value is + /// is set to the current value of this axis. Otherwise, or + /// if the event code is not an ABS_MT_* event code, `None` is returned + fn slot_value(&self, slot: u32, code: &EventCode) -> Option { + let (_, ev_code) = event_code_to_int(code); + let mut value: i32 = 0; + let valid = unsafe { + libevdev::libevdev_fetch_slot_value(self.raw(), slot as c_uint, ev_code, &mut value) + }; + + match valid { + 0 => None, + _ => Some(value), + } + } + + /// 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 `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 `set_event_value` instead. + fn set_slot_value(&self, slot: u32, code: &EventCode, val: i32) -> io::Result<()> { + let (_, ev_code) = event_code_to_int(code); + let result = unsafe { + libevdev::libevdev_set_slot_value(self.raw(), slot as c_uint, ev_code, val as c_int) + }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Get the number of slots supported by this device. + /// + /// The number of slots supported, or `None` if the device does not provide + /// any slots + /// + /// A device may provide ABS_MT_SLOT but a total number of 0 slots. Hence + /// the return value of `None` for "device does not provide slots at all" + fn num_slots(&self) -> Option { + let result = unsafe { libevdev::libevdev_get_num_slots(self.raw()) }; + + match result { + -1 => None, + slots => Some(slots), + } + } + + /// 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 file 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. + fn current_slot(&self) -> Option { + let result = unsafe { libevdev::libevdev_get_current_slot(self.raw()) }; + + match result { + -1 => None, + slots => Some(slots), + } + } +} + +/// Opaque struct representing an evdev device with no backing file +pub struct UninitDevice { + raw: *mut libevdev::libevdev, +} + +unsafe impl Send for UninitDevice {} + +impl DeviceWrapper for UninitDevice { + fn raw(&self) -> *mut libevdev::libevdev { + self.raw + } +} + +impl UninitDevice { + /// Initialize a new libevdev device. + /// + /// Generally you should use Device::new_from_file instead of this method + /// This function only initializes the struct to sane default values. + /// To actually hook up the device to a kernel device, use `set_file`. + pub fn new() -> Option { + let libevdev = unsafe { libevdev::libevdev_new() }; + + if libevdev.is_null() { + None + } else { + Some(UninitDevice { raw: libevdev }) + } + } + + /// Set the file for this struct and initialize internal data. + /// + /// If the device changed and you need to re-read a device, use `Device::new_from_file` method. + /// If you need to change the file after + /// closing and re-opening the same device, use `change_file`. + pub fn set_file(self, file: File) -> io::Result { + // Don't call UninitDevice's destructor so we can reuse the inner libevdev + let leak = ManuallyDrop::new(self); + let result = unsafe { libevdev::libevdev_set_fd(leak.raw, file.as_raw_fd()) }; + match result { + 0 => Ok(Device { + file, + raw: leak.raw, + }), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + #[deprecated( + since = "0.5.0", + note = "Prefer `set_file`. Some function names were changed so they + more closely match their type signature. See issue 42 for discussion + https://github.com/ndesh26/evdev-rs/issues/42" + )] + pub fn set_fd(self, file: File) -> io::Result { + self.set_file(file) + } +} + +impl Drop for UninitDevice { + fn drop(&mut self) { + unsafe { + libevdev::libevdev_free(self.raw); + } + } +} + +impl std::fmt::Debug for UninitDevice { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("UninitDevice") + .field("name", &self.name()) + .field("phys", &self.phys()) + .field("uniq", &self.uniq()) + .finish() + } +} + +/// Opaque struct representing an evdev device +/// +/// Unlike libevdev, this `Device` maintains an associated file as an invariant +pub struct Device { + file: File, + raw: *mut libevdev::libevdev, +} + +unsafe impl Send for Device {} + +impl DeviceWrapper for Device { + fn raw(&self) -> *mut libevdev::libevdev { + self.raw + } +} + +impl Device { + /// Initialize a new libevdev device from the given file. + /// + /// This is a shortcut for + /// + /// ```rust,no_run + /// use evdev_rs::{Device, UninitDevice}; + /// # use std::fs::File; + /// + /// let uninit_device = UninitDevice::new().unwrap(); + /// # let file = File::open("/dev/input/event0").unwrap(); + /// let device = uninit_device.set_file(file); + /// ``` + /// + /// The caller is responsible for opening the file and setting + /// the `O_NONBLOCK` flag and handling permissions. + /// If the file is opened without O_NONBLOCK flag then next_event + /// should be called with ReadFlag::BLOCKING. Due to the caching + /// nature of next_event we might block while trying to buffer + /// new events even though some events are already present. + pub fn new_from_file(file: File) -> io::Result { + let mut libevdev = std::ptr::null_mut(); + let result = unsafe { libevdev::libevdev_new_from_fd(file.as_raw_fd(), &mut libevdev) }; + + match result { + 0 => Ok(Device { + file, + raw: libevdev, + }), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + #[deprecated( + since = "0.5.0", + note = "Prefer `new_from_file`. Some function names were changed so they + more closely match their type signature. See issue 42 for discussion + https://github.com/ndesh26/evdev-rs/issues/42" + )] + pub fn new_from_fd(file: File) -> io::Result { + Self::new_from_file(file) + } + + /// Opens a device with the given path as the location of devnode + /// + /// The devnode file is opened with `O_NONBLOCK` and all the pending + /// events are first read from the file before creating the device. + pub fn new_from_path>(path: P) -> io::Result { + let mut file = OpenOptions::new() + .read(true) + .write(false) // Writing is only allowed when rooted so must be false + .custom_flags(libc::O_NONBLOCK) + .open(path)?; + let mut buffer = [0u8; 20 * std::mem::size_of::()]; + + let last_result = loop { + let result = file.read(&mut buffer); + if result.is_err() { + break result; + } + }; + let _error_code = io::Error::from(io::ErrorKind::WouldBlock); + match last_result { + Err(_error_code) => Self::new_from_file(file), + _ => Err(io::Error::new( + io::ErrorKind::WouldBlock, + "Unable to open file with O_NONBLOCK", + )), + } + } + + /// Returns the file associated with the device + pub fn file(&self) -> &File { + &self.file + } + + #[deprecated( + since = "0.5.0", + note = "Prefer `file`. This function can easily be misused. Calling + this method, then dropping the returned file will lead to failures + e.g. next_event will return an Err()" + )] + pub fn fd(&self) -> Option { + let result = unsafe { libevdev::libevdev_get_fd(self.raw) }; + match result { + 0 => None, + _ => unsafe { Some(File::from_raw_fd(result)) }, + } + } + + /// Change the file for this device, without re-reading the actual device. + /// + /// On success, returns the file that was previously associated with this + /// device. + /// + /// If the file changes after initializing the device, for example after a + /// VT-switch in the X.org X server, this function updates the internal + /// file to the newly opened. No check is made that new file points to the + /// same device. If the device has changed, evdev's behavior is undefined. + /// + /// evdev device does not sync itself after changing the file and keeps the + /// current device state. Use next_event with the FORCE_SYNC flag to force + /// a re-sync. + /// + /// # Example + /// + /// ```rust,no_run + /// use evdev_rs::{Device, UninitDevice, ReadFlag, ReadStatus}; + /// # use std::fs::File; + /// # fn hidden() -> std::io::Result<()> { + /// let mut dev = Device::new_from_file(File::open("/dev/input/input0")?)?; + /// dev.change_file(File::open("/dev/input/input1")?)?; + /// dev.next_event(ReadFlag::FORCE_SYNC); + /// while dev.next_event(ReadFlag::SYNC).ok().unwrap().0 == ReadStatus::Sync + /// {} // noop + /// # Ok(()) + /// # } + /// ``` + /// After changing the file, the device is assumed ungrabbed and a caller must + /// call libevdev_grab() again. + pub fn change_file(&mut self, file: File) -> io::Result { + let result = unsafe { libevdev::libevdev_change_fd(self.raw, file.as_raw_fd()) }; + + match result { + 0 => { + let mut file = file; + std::mem::swap(&mut file, &mut self.file); + Ok(file) + } + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + #[deprecated( + since = "0.5.0", + note = "Prefer new_from_file. Some function names were changed so they + more closely match their type signature. See issue 42 for discussion + https://github.com/ndesh26/evdev-rs/issues/42" + )] + pub fn change_fd(&mut self, file: File) -> io::Result<()> { + self.change_file(file)?; + Ok(()) + } + + /// 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 Device::change_file(), it must + /// also re-issue a grab with libevdev_grab(). + pub fn grab(&mut self, grab: GrabMode) -> io::Result<()> { + let result = unsafe { libevdev::libevdev_grab(self.raw, grab as c_int) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Check if there are events waiting for us. + /// + /// This function does not consume an event and may not access the device + /// file at all. If there are events queued internally this function will + /// return true. 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 `next_event` when select(2) or + /// poll(2) return is safe. You do not need `has_event_pending` if + /// you're using select(2) or poll(2). + pub fn has_event_pending(&self) -> bool { + unsafe { libevdev::libevdev_has_event_pending(self.raw) > 0 } + } + + /// Return the driver version of a device already intialize with `set_file` + pub fn driver_version(&self) -> i32 { + unsafe { libevdev::libevdev_get_driver_version(self.raw) as i32 } + } + + /// Set the device's EV_ABS axis to the value defined in the abs + /// parameter. This will be written to the kernel. + pub fn set_kernel_abs_info(&self, code: &EventCode, absinfo: &AbsInfo) { + let (_, ev_code) = event_code_to_int(code); + + unsafe { + libevdev::libevdev_kernel_set_abs_info( + self.raw, + ev_code, + &absinfo.as_raw() as *const _, + ); + } + } + + /// Turn an LED on or off. + /// + /// enabling an LED requires write permissions on the device's file descriptor. + pub fn kernel_set_led_value(&self, code: &EventCode, value: LedState) -> io::Result<()> { + let (_, ev_code) = event_code_to_int(code); + let result = + unsafe { libevdev::libevdev_kernel_set_led_value(self.raw, ev_code, value as c_int) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// 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. + pub fn set_clock_id(&self, clockid: i32) -> io::Result<()> { + let result = unsafe { libevdev::libevdev_set_clock_id(self.raw, clockid) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// 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 `evdev::NORMAL` set), this function returns + /// `ReadStatus::Success` and returns the event. If no events are available at + /// this time, it returns `-EAGAIN` as `Err`. + /// + /// If the current event is an `EV_SYN::SYN_DROPPED` event, this function returns + /// `ReadStatus::Sync` and is set to the `EV_SYN` event.The caller should now call + /// this function with the `evdev::SYNC` flag set, to get the set of events that + /// make up the device state delta. This function returns ReadStatus::Sync for + /// each event part of that delta, until it returns `-EAGAIN` once all events + /// have been synced. + /// + /// If a device needs to be synced by the caller but the caller does not call + /// with the `evdev::SYNC` flag set, all events from the diff are dropped after + /// evdev 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 `current_slot` and checks the + /// `ABS_MT_TRACKING_ID` values for all slots. + /// + /// If a device has changed state without events being enqueued in evdev, + /// e.g. after changing the file descriptor, use the `evdev::FORCE_SYNC` flag. + /// This triggers an internal sync of the device and `next_event` returns + /// `ReadStatus::Sync`. + pub fn next_event(&self, flags: ReadFlag) -> io::Result<(ReadStatus, InputEvent)> { + let mut ev = libevdev::input_event { + time: libevdev::timeval { + tv_sec: 0, + tv_usec: 0, + }, + type_: 0, + code: 0, + value: 0, + }; + + let result = + unsafe { libevdev::libevdev_next_event(self.raw, flags.bits() as c_uint, &mut ev) }; + + let event = InputEvent { + time: TimeVal { + tv_sec: ev.time.tv_sec, + tv_usec: ev.time.tv_usec, + }, + event_code: int_to_event_code(ev.type_ as u32, ev.code as u32), + value: ev.value, + }; + + match result { + libevdev::LIBEVDEV_READ_STATUS_SUCCESS => Ok((ReadStatus::Success, event)), + libevdev::LIBEVDEV_READ_STATUS_SYNC => Ok((ReadStatus::Sync, event)), + error => Err(io::Error::from_raw_os_error(-error)), + } + } +} + +impl Drop for Device { + fn drop(&mut self) { + unsafe { + libevdev::libevdev_free(self.raw); + } + } +} + +impl std::fmt::Debug for Device { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("Device") + .field("name", &self.name()) + .field("phys", &self.phys()) + .field("uniq", &self.uniq()) + .finish() + } +} + +impl AsRawFd for Device { + fn as_raw_fd(&self) -> RawFd { + self.file.as_raw_fd() + } +} diff --git a/evdev/src/main/rust/evdev/src/enums.rs b/evdev/src/main/rust/evdev/src/enums.rs new file mode 100644 index 0000000000..02831b9335 --- /dev/null +++ b/evdev/src/main/rust/evdev/src/enums.rs @@ -0,0 +1,2713 @@ +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EventType { + EV_SYN = 0, + EV_KEY = 1, + EV_REL = 2, + EV_ABS = 3, + EV_MSC = 4, + EV_SW = 5, + EV_LED = 17, + EV_SND = 18, + EV_REP = 20, + EV_FF = 21, + EV_PWR = 22, + EV_FF_STATUS = 23, + EV_UNK, + EV_MAX = 31, +} + +pub const fn int_to_event_type(code: u32) -> Option { + match code { + 0 => Some(EventType::EV_SYN), + 1 => Some(EventType::EV_KEY), + 2 => Some(EventType::EV_REL), + 3 => Some(EventType::EV_ABS), + 4 => Some(EventType::EV_MSC), + 5 => Some(EventType::EV_SW), + 17 => Some(EventType::EV_LED), + 18 => Some(EventType::EV_SND), + 20 => Some(EventType::EV_REP), + 21 => Some(EventType::EV_FF), + 22 => Some(EventType::EV_PWR), + 23 => Some(EventType::EV_FF_STATUS), + c if c < 31 => Some(EventType::EV_UNK), + 31 => Some(EventType::EV_MAX), + _ => None, + } +} + +impl std::str::FromStr for EventType { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "EV_SYN" => Ok(EventType::EV_SYN), + "EV_KEY" => Ok(EventType::EV_KEY), + "EV_REL" => Ok(EventType::EV_REL), + "EV_ABS" => Ok(EventType::EV_ABS), + "EV_MSC" => Ok(EventType::EV_MSC), + "EV_SW" => Ok(EventType::EV_SW), + "EV_LED" => Ok(EventType::EV_LED), + "EV_SND" => Ok(EventType::EV_SND), + "EV_REP" => Ok(EventType::EV_REP), + "EV_FF" => Ok(EventType::EV_FF), + "EV_PWR" => Ok(EventType::EV_PWR), + "EV_FF_STATUS" => Ok(EventType::EV_FF_STATUS), + "EV_MAX" => Ok(EventType::EV_MAX), + _ => Err(()), + } + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum EventCode { + EV_SYN(EV_SYN), + EV_KEY(EV_KEY), + EV_REL(EV_REL), + EV_ABS(EV_ABS), + EV_MSC(EV_MSC), + EV_SW(EV_SW), + EV_LED(EV_LED), + EV_SND(EV_SND), + EV_REP(EV_REP), + EV_FF(EV_FF), + EV_PWR, + EV_FF_STATUS(EV_FF), + EV_UNK { event_type: u32, event_code: u32 }, + EV_MAX, +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_REL { + REL_X = 0, + REL_Y = 1, + REL_Z = 2, + REL_RX = 3, + REL_RY = 4, + REL_RZ = 5, + REL_HWHEEL = 6, + REL_DIAL = 7, + REL_WHEEL = 8, + REL_MISC = 9, + REL_RESERVED = 10, + REL_WHEEL_HI_RES = 11, + REL_HWHEEL_HI_RES = 12, + REL_MAX = 15, +} + +pub const fn int_to_ev_rel(code: u32) -> Option { + match code { + 0 => Some(EV_REL::REL_X), + 1 => Some(EV_REL::REL_Y), + 2 => Some(EV_REL::REL_Z), + 3 => Some(EV_REL::REL_RX), + 4 => Some(EV_REL::REL_RY), + 5 => Some(EV_REL::REL_RZ), + 6 => Some(EV_REL::REL_HWHEEL), + 7 => Some(EV_REL::REL_DIAL), + 8 => Some(EV_REL::REL_WHEEL), + 9 => Some(EV_REL::REL_MISC), + 10 => Some(EV_REL::REL_RESERVED), + 11 => Some(EV_REL::REL_WHEEL_HI_RES), + 12 => Some(EV_REL::REL_HWHEEL_HI_RES), + 15 => Some(EV_REL::REL_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_REL { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "REL_X" => Ok(EV_REL::REL_X), + "REL_Y" => Ok(EV_REL::REL_Y), + "REL_Z" => Ok(EV_REL::REL_Z), + "REL_RX" => Ok(EV_REL::REL_RX), + "REL_RY" => Ok(EV_REL::REL_RY), + "REL_RZ" => Ok(EV_REL::REL_RZ), + "REL_HWHEEL" => Ok(EV_REL::REL_HWHEEL), + "REL_DIAL" => Ok(EV_REL::REL_DIAL), + "REL_WHEEL" => Ok(EV_REL::REL_WHEEL), + "REL_MISC" => Ok(EV_REL::REL_MISC), + "REL_RESERVED" => Ok(EV_REL::REL_RESERVED), + "REL_WHEEL_HI_RES" => Ok(EV_REL::REL_WHEEL_HI_RES), + "REL_HWHEEL_HI_RES" => Ok(EV_REL::REL_HWHEEL_HI_RES), + "REL_MAX" => Ok(EV_REL::REL_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_REL { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_ABS { + ABS_X = 0, + ABS_Y = 1, + ABS_Z = 2, + ABS_RX = 3, + ABS_RY = 4, + ABS_RZ = 5, + ABS_THROTTLE = 6, + ABS_RUDDER = 7, + ABS_WHEEL = 8, + ABS_GAS = 9, + ABS_BRAKE = 10, + ABS_HAT0X = 16, + ABS_HAT0Y = 17, + ABS_HAT1X = 18, + ABS_HAT1Y = 19, + ABS_HAT2X = 20, + ABS_HAT2Y = 21, + ABS_HAT3X = 22, + ABS_HAT3Y = 23, + ABS_PRESSURE = 24, + ABS_DISTANCE = 25, + ABS_TILT_X = 26, + ABS_TILT_Y = 27, + ABS_TOOL_WIDTH = 28, + ABS_VOLUME = 32, + ABS_PROFILE = 33, + ABS_MISC = 40, + ABS_RESERVED = 46, + ABS_MT_SLOT = 47, + ABS_MT_TOUCH_MAJOR = 48, + ABS_MT_TOUCH_MINOR = 49, + ABS_MT_WIDTH_MAJOR = 50, + ABS_MT_WIDTH_MINOR = 51, + ABS_MT_ORIENTATION = 52, + ABS_MT_POSITION_X = 53, + ABS_MT_POSITION_Y = 54, + ABS_MT_TOOL_TYPE = 55, + ABS_MT_BLOB_ID = 56, + ABS_MT_TRACKING_ID = 57, + ABS_MT_PRESSURE = 58, + ABS_MT_DISTANCE = 59, + ABS_MT_TOOL_X = 60, + ABS_MT_TOOL_Y = 61, + ABS_MAX = 63, +} + +pub const fn int_to_ev_abs(code: u32) -> Option { + match code { + 0 => Some(EV_ABS::ABS_X), + 1 => Some(EV_ABS::ABS_Y), + 2 => Some(EV_ABS::ABS_Z), + 3 => Some(EV_ABS::ABS_RX), + 4 => Some(EV_ABS::ABS_RY), + 5 => Some(EV_ABS::ABS_RZ), + 6 => Some(EV_ABS::ABS_THROTTLE), + 7 => Some(EV_ABS::ABS_RUDDER), + 8 => Some(EV_ABS::ABS_WHEEL), + 9 => Some(EV_ABS::ABS_GAS), + 10 => Some(EV_ABS::ABS_BRAKE), + 16 => Some(EV_ABS::ABS_HAT0X), + 17 => Some(EV_ABS::ABS_HAT0Y), + 18 => Some(EV_ABS::ABS_HAT1X), + 19 => Some(EV_ABS::ABS_HAT1Y), + 20 => Some(EV_ABS::ABS_HAT2X), + 21 => Some(EV_ABS::ABS_HAT2Y), + 22 => Some(EV_ABS::ABS_HAT3X), + 23 => Some(EV_ABS::ABS_HAT3Y), + 24 => Some(EV_ABS::ABS_PRESSURE), + 25 => Some(EV_ABS::ABS_DISTANCE), + 26 => Some(EV_ABS::ABS_TILT_X), + 27 => Some(EV_ABS::ABS_TILT_Y), + 28 => Some(EV_ABS::ABS_TOOL_WIDTH), + 32 => Some(EV_ABS::ABS_VOLUME), + 33 => Some(EV_ABS::ABS_PROFILE), + 40 => Some(EV_ABS::ABS_MISC), + 46 => Some(EV_ABS::ABS_RESERVED), + 47 => Some(EV_ABS::ABS_MT_SLOT), + 48 => Some(EV_ABS::ABS_MT_TOUCH_MAJOR), + 49 => Some(EV_ABS::ABS_MT_TOUCH_MINOR), + 50 => Some(EV_ABS::ABS_MT_WIDTH_MAJOR), + 51 => Some(EV_ABS::ABS_MT_WIDTH_MINOR), + 52 => Some(EV_ABS::ABS_MT_ORIENTATION), + 53 => Some(EV_ABS::ABS_MT_POSITION_X), + 54 => Some(EV_ABS::ABS_MT_POSITION_Y), + 55 => Some(EV_ABS::ABS_MT_TOOL_TYPE), + 56 => Some(EV_ABS::ABS_MT_BLOB_ID), + 57 => Some(EV_ABS::ABS_MT_TRACKING_ID), + 58 => Some(EV_ABS::ABS_MT_PRESSURE), + 59 => Some(EV_ABS::ABS_MT_DISTANCE), + 60 => Some(EV_ABS::ABS_MT_TOOL_X), + 61 => Some(EV_ABS::ABS_MT_TOOL_Y), + 63 => Some(EV_ABS::ABS_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_ABS { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "ABS_X" => Ok(EV_ABS::ABS_X), + "ABS_Y" => Ok(EV_ABS::ABS_Y), + "ABS_Z" => Ok(EV_ABS::ABS_Z), + "ABS_RX" => Ok(EV_ABS::ABS_RX), + "ABS_RY" => Ok(EV_ABS::ABS_RY), + "ABS_RZ" => Ok(EV_ABS::ABS_RZ), + "ABS_THROTTLE" => Ok(EV_ABS::ABS_THROTTLE), + "ABS_RUDDER" => Ok(EV_ABS::ABS_RUDDER), + "ABS_WHEEL" => Ok(EV_ABS::ABS_WHEEL), + "ABS_GAS" => Ok(EV_ABS::ABS_GAS), + "ABS_BRAKE" => Ok(EV_ABS::ABS_BRAKE), + "ABS_HAT0X" => Ok(EV_ABS::ABS_HAT0X), + "ABS_HAT0Y" => Ok(EV_ABS::ABS_HAT0Y), + "ABS_HAT1X" => Ok(EV_ABS::ABS_HAT1X), + "ABS_HAT1Y" => Ok(EV_ABS::ABS_HAT1Y), + "ABS_HAT2X" => Ok(EV_ABS::ABS_HAT2X), + "ABS_HAT2Y" => Ok(EV_ABS::ABS_HAT2Y), + "ABS_HAT3X" => Ok(EV_ABS::ABS_HAT3X), + "ABS_HAT3Y" => Ok(EV_ABS::ABS_HAT3Y), + "ABS_PRESSURE" => Ok(EV_ABS::ABS_PRESSURE), + "ABS_DISTANCE" => Ok(EV_ABS::ABS_DISTANCE), + "ABS_TILT_X" => Ok(EV_ABS::ABS_TILT_X), + "ABS_TILT_Y" => Ok(EV_ABS::ABS_TILT_Y), + "ABS_TOOL_WIDTH" => Ok(EV_ABS::ABS_TOOL_WIDTH), + "ABS_VOLUME" => Ok(EV_ABS::ABS_VOLUME), + "ABS_PROFILE" => Ok(EV_ABS::ABS_PROFILE), + "ABS_MISC" => Ok(EV_ABS::ABS_MISC), + "ABS_RESERVED" => Ok(EV_ABS::ABS_RESERVED), + "ABS_MT_SLOT" => Ok(EV_ABS::ABS_MT_SLOT), + "ABS_MT_TOUCH_MAJOR" => Ok(EV_ABS::ABS_MT_TOUCH_MAJOR), + "ABS_MT_TOUCH_MINOR" => Ok(EV_ABS::ABS_MT_TOUCH_MINOR), + "ABS_MT_WIDTH_MAJOR" => Ok(EV_ABS::ABS_MT_WIDTH_MAJOR), + "ABS_MT_WIDTH_MINOR" => Ok(EV_ABS::ABS_MT_WIDTH_MINOR), + "ABS_MT_ORIENTATION" => Ok(EV_ABS::ABS_MT_ORIENTATION), + "ABS_MT_POSITION_X" => Ok(EV_ABS::ABS_MT_POSITION_X), + "ABS_MT_POSITION_Y" => Ok(EV_ABS::ABS_MT_POSITION_Y), + "ABS_MT_TOOL_TYPE" => Ok(EV_ABS::ABS_MT_TOOL_TYPE), + "ABS_MT_BLOB_ID" => Ok(EV_ABS::ABS_MT_BLOB_ID), + "ABS_MT_TRACKING_ID" => Ok(EV_ABS::ABS_MT_TRACKING_ID), + "ABS_MT_PRESSURE" => Ok(EV_ABS::ABS_MT_PRESSURE), + "ABS_MT_DISTANCE" => Ok(EV_ABS::ABS_MT_DISTANCE), + "ABS_MT_TOOL_X" => Ok(EV_ABS::ABS_MT_TOOL_X), + "ABS_MT_TOOL_Y" => Ok(EV_ABS::ABS_MT_TOOL_Y), + "ABS_MAX" => Ok(EV_ABS::ABS_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_ABS { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_KEY { + KEY_RESERVED = 0, + KEY_ESC = 1, + KEY_1 = 2, + KEY_2 = 3, + KEY_3 = 4, + KEY_4 = 5, + KEY_5 = 6, + KEY_6 = 7, + KEY_7 = 8, + KEY_8 = 9, + KEY_9 = 10, + KEY_0 = 11, + KEY_MINUS = 12, + KEY_EQUAL = 13, + KEY_BACKSPACE = 14, + KEY_TAB = 15, + KEY_Q = 16, + KEY_W = 17, + KEY_E = 18, + KEY_R = 19, + KEY_T = 20, + KEY_Y = 21, + KEY_U = 22, + KEY_I = 23, + KEY_O = 24, + KEY_P = 25, + KEY_LEFTBRACE = 26, + KEY_RIGHTBRACE = 27, + KEY_ENTER = 28, + KEY_LEFTCTRL = 29, + KEY_A = 30, + KEY_S = 31, + KEY_D = 32, + KEY_F = 33, + KEY_G = 34, + KEY_H = 35, + KEY_J = 36, + KEY_K = 37, + KEY_L = 38, + KEY_SEMICOLON = 39, + KEY_APOSTROPHE = 40, + KEY_GRAVE = 41, + KEY_LEFTSHIFT = 42, + KEY_BACKSLASH = 43, + KEY_Z = 44, + KEY_X = 45, + KEY_C = 46, + KEY_V = 47, + KEY_B = 48, + KEY_N = 49, + KEY_M = 50, + KEY_COMMA = 51, + KEY_DOT = 52, + KEY_SLASH = 53, + KEY_RIGHTSHIFT = 54, + KEY_KPASTERISK = 55, + KEY_LEFTALT = 56, + KEY_SPACE = 57, + KEY_CAPSLOCK = 58, + KEY_F1 = 59, + KEY_F2 = 60, + KEY_F3 = 61, + KEY_F4 = 62, + KEY_F5 = 63, + KEY_F6 = 64, + KEY_F7 = 65, + KEY_F8 = 66, + KEY_F9 = 67, + KEY_F10 = 68, + KEY_NUMLOCK = 69, + KEY_SCROLLLOCK = 70, + KEY_KP7 = 71, + KEY_KP8 = 72, + KEY_KP9 = 73, + KEY_KPMINUS = 74, + KEY_KP4 = 75, + KEY_KP5 = 76, + KEY_KP6 = 77, + KEY_KPPLUS = 78, + KEY_KP1 = 79, + KEY_KP2 = 80, + KEY_KP3 = 81, + KEY_KP0 = 82, + KEY_KPDOT = 83, + KEY_ZENKAKUHANKAKU = 85, + KEY_102ND = 86, + KEY_F11 = 87, + KEY_F12 = 88, + KEY_RO = 89, + KEY_KATAKANA = 90, + KEY_HIRAGANA = 91, + KEY_HENKAN = 92, + KEY_KATAKANAHIRAGANA = 93, + KEY_MUHENKAN = 94, + KEY_KPJPCOMMA = 95, + KEY_KPENTER = 96, + KEY_RIGHTCTRL = 97, + KEY_KPSLASH = 98, + KEY_SYSRQ = 99, + KEY_RIGHTALT = 100, + KEY_LINEFEED = 101, + KEY_HOME = 102, + KEY_UP = 103, + KEY_PAGEUP = 104, + KEY_LEFT = 105, + KEY_RIGHT = 106, + KEY_END = 107, + KEY_DOWN = 108, + KEY_PAGEDOWN = 109, + KEY_INSERT = 110, + KEY_DELETE = 111, + KEY_MACRO = 112, + KEY_MUTE = 113, + KEY_VOLUMEDOWN = 114, + KEY_VOLUMEUP = 115, + KEY_POWER = 116, + KEY_KPEQUAL = 117, + KEY_KPPLUSMINUS = 118, + KEY_PAUSE = 119, + KEY_SCALE = 120, + KEY_KPCOMMA = 121, + KEY_HANGEUL = 122, + KEY_HANJA = 123, + KEY_YEN = 124, + KEY_LEFTMETA = 125, + KEY_RIGHTMETA = 126, + KEY_COMPOSE = 127, + KEY_STOP = 128, + KEY_AGAIN = 129, + KEY_PROPS = 130, + KEY_UNDO = 131, + KEY_FRONT = 132, + KEY_COPY = 133, + KEY_OPEN = 134, + KEY_PASTE = 135, + KEY_FIND = 136, + KEY_CUT = 137, + KEY_HELP = 138, + KEY_MENU = 139, + KEY_CALC = 140, + KEY_SETUP = 141, + KEY_SLEEP = 142, + KEY_WAKEUP = 143, + KEY_FILE = 144, + KEY_SENDFILE = 145, + KEY_DELETEFILE = 146, + KEY_XFER = 147, + KEY_PROG1 = 148, + KEY_PROG2 = 149, + KEY_WWW = 150, + KEY_MSDOS = 151, + KEY_COFFEE = 152, + KEY_ROTATE_DISPLAY = 153, + KEY_CYCLEWINDOWS = 154, + KEY_MAIL = 155, + KEY_BOOKMARKS = 156, + KEY_COMPUTER = 157, + KEY_BACK = 158, + KEY_FORWARD = 159, + KEY_CLOSECD = 160, + KEY_EJECTCD = 161, + KEY_EJECTCLOSECD = 162, + KEY_NEXTSONG = 163, + KEY_PLAYPAUSE = 164, + KEY_PREVIOUSSONG = 165, + KEY_STOPCD = 166, + KEY_RECORD = 167, + KEY_REWIND = 168, + KEY_PHONE = 169, + KEY_ISO = 170, + KEY_CONFIG = 171, + KEY_HOMEPAGE = 172, + KEY_REFRESH = 173, + KEY_EXIT = 174, + KEY_MOVE = 175, + KEY_EDIT = 176, + KEY_SCROLLUP = 177, + KEY_SCROLLDOWN = 178, + KEY_KPLEFTPAREN = 179, + KEY_KPRIGHTPAREN = 180, + KEY_NEW = 181, + KEY_REDO = 182, + KEY_F13 = 183, + KEY_F14 = 184, + KEY_F15 = 185, + KEY_F16 = 186, + KEY_F17 = 187, + KEY_F18 = 188, + KEY_F19 = 189, + KEY_F20 = 190, + KEY_F21 = 191, + KEY_F22 = 192, + KEY_F23 = 193, + KEY_F24 = 194, + KEY_PLAYCD = 200, + KEY_PAUSECD = 201, + KEY_PROG3 = 202, + KEY_PROG4 = 203, + KEY_ALL_APPLICATIONS = 204, + KEY_SUSPEND = 205, + KEY_CLOSE = 206, + KEY_PLAY = 207, + KEY_FASTFORWARD = 208, + KEY_BASSBOOST = 209, + KEY_PRINT = 210, + KEY_HP = 211, + KEY_CAMERA = 212, + KEY_SOUND = 213, + KEY_QUESTION = 214, + KEY_EMAIL = 215, + KEY_CHAT = 216, + KEY_SEARCH = 217, + KEY_CONNECT = 218, + KEY_FINANCE = 219, + KEY_SPORT = 220, + KEY_SHOP = 221, + KEY_ALTERASE = 222, + KEY_CANCEL = 223, + KEY_BRIGHTNESSDOWN = 224, + KEY_BRIGHTNESSUP = 225, + KEY_MEDIA = 226, + KEY_SWITCHVIDEOMODE = 227, + KEY_KBDILLUMTOGGLE = 228, + KEY_KBDILLUMDOWN = 229, + KEY_KBDILLUMUP = 230, + KEY_SEND = 231, + KEY_REPLY = 232, + KEY_FORWARDMAIL = 233, + KEY_SAVE = 234, + KEY_DOCUMENTS = 235, + KEY_BATTERY = 236, + KEY_BLUETOOTH = 237, + KEY_WLAN = 238, + KEY_UWB = 239, + KEY_UNKNOWN = 240, + KEY_VIDEO_NEXT = 241, + KEY_VIDEO_PREV = 242, + KEY_BRIGHTNESS_CYCLE = 243, + KEY_BRIGHTNESS_AUTO = 244, + KEY_DISPLAY_OFF = 245, + KEY_WWAN = 246, + KEY_RFKILL = 247, + KEY_MICMUTE = 248, + KEY_OK = 352, + KEY_SELECT = 353, + KEY_GOTO = 354, + KEY_CLEAR = 355, + KEY_POWER2 = 356, + KEY_OPTION = 357, + KEY_INFO = 358, + KEY_TIME = 359, + KEY_VENDOR = 360, + KEY_ARCHIVE = 361, + KEY_PROGRAM = 362, + KEY_CHANNEL = 363, + KEY_FAVORITES = 364, + KEY_EPG = 365, + KEY_PVR = 366, + KEY_MHP = 367, + KEY_LANGUAGE = 368, + KEY_TITLE = 369, + KEY_SUBTITLE = 370, + KEY_ANGLE = 371, + KEY_FULL_SCREEN = 372, + KEY_MODE = 373, + KEY_KEYBOARD = 374, + KEY_ASPECT_RATIO = 375, + KEY_PC = 376, + KEY_TV = 377, + KEY_TV2 = 378, + KEY_VCR = 379, + KEY_VCR2 = 380, + KEY_SAT = 381, + KEY_SAT2 = 382, + KEY_CD = 383, + KEY_TAPE = 384, + KEY_RADIO = 385, + KEY_TUNER = 386, + KEY_PLAYER = 387, + KEY_TEXT = 388, + KEY_DVD = 389, + KEY_AUX = 390, + KEY_MP3 = 391, + KEY_AUDIO = 392, + KEY_VIDEO = 393, + KEY_DIRECTORY = 394, + KEY_LIST = 395, + KEY_MEMO = 396, + KEY_CALENDAR = 397, + KEY_RED = 398, + KEY_GREEN = 399, + KEY_YELLOW = 400, + KEY_BLUE = 401, + KEY_CHANNELUP = 402, + KEY_CHANNELDOWN = 403, + KEY_FIRST = 404, + KEY_LAST = 405, + KEY_AB = 406, + KEY_NEXT = 407, + KEY_RESTART = 408, + KEY_SLOW = 409, + KEY_SHUFFLE = 410, + KEY_BREAK = 411, + KEY_PREVIOUS = 412, + KEY_DIGITS = 413, + KEY_TEEN = 414, + KEY_TWEN = 415, + KEY_VIDEOPHONE = 416, + KEY_GAMES = 417, + KEY_ZOOMIN = 418, + KEY_ZOOMOUT = 419, + KEY_ZOOMRESET = 420, + KEY_WORDPROCESSOR = 421, + KEY_EDITOR = 422, + KEY_SPREADSHEET = 423, + KEY_GRAPHICSEDITOR = 424, + KEY_PRESENTATION = 425, + KEY_DATABASE = 426, + KEY_NEWS = 427, + KEY_VOICEMAIL = 428, + KEY_ADDRESSBOOK = 429, + KEY_MESSENGER = 430, + KEY_DISPLAYTOGGLE = 431, + KEY_SPELLCHECK = 432, + KEY_LOGOFF = 433, + KEY_DOLLAR = 434, + KEY_EURO = 435, + KEY_FRAMEBACK = 436, + KEY_FRAMEFORWARD = 437, + KEY_CONTEXT_MENU = 438, + KEY_MEDIA_REPEAT = 439, + KEY_10CHANNELSUP = 440, + KEY_10CHANNELSDOWN = 441, + KEY_IMAGES = 442, + KEY_NOTIFICATION_CENTER = 444, + KEY_PICKUP_PHONE = 445, + KEY_HANGUP_PHONE = 446, + KEY_DEL_EOL = 448, + KEY_DEL_EOS = 449, + KEY_INS_LINE = 450, + KEY_DEL_LINE = 451, + KEY_FN = 464, + KEY_FN_ESC = 465, + KEY_FN_F1 = 466, + KEY_FN_F2 = 467, + KEY_FN_F3 = 468, + KEY_FN_F4 = 469, + KEY_FN_F5 = 470, + KEY_FN_F6 = 471, + KEY_FN_F7 = 472, + KEY_FN_F8 = 473, + KEY_FN_F9 = 474, + KEY_FN_F10 = 475, + KEY_FN_F11 = 476, + KEY_FN_F12 = 477, + KEY_FN_1 = 478, + KEY_FN_2 = 479, + KEY_FN_D = 480, + KEY_FN_E = 481, + KEY_FN_F = 482, + KEY_FN_S = 483, + KEY_FN_B = 484, + KEY_FN_RIGHT_SHIFT = 485, + KEY_BRL_DOT1 = 497, + KEY_BRL_DOT2 = 498, + KEY_BRL_DOT3 = 499, + KEY_BRL_DOT4 = 500, + KEY_BRL_DOT5 = 501, + KEY_BRL_DOT6 = 502, + KEY_BRL_DOT7 = 503, + KEY_BRL_DOT8 = 504, + KEY_BRL_DOT9 = 505, + KEY_BRL_DOT10 = 506, + KEY_NUMERIC_0 = 512, + KEY_NUMERIC_1 = 513, + KEY_NUMERIC_2 = 514, + KEY_NUMERIC_3 = 515, + KEY_NUMERIC_4 = 516, + KEY_NUMERIC_5 = 517, + KEY_NUMERIC_6 = 518, + KEY_NUMERIC_7 = 519, + KEY_NUMERIC_8 = 520, + KEY_NUMERIC_9 = 521, + KEY_NUMERIC_STAR = 522, + KEY_NUMERIC_POUND = 523, + KEY_NUMERIC_A = 524, + KEY_NUMERIC_B = 525, + KEY_NUMERIC_C = 526, + KEY_NUMERIC_D = 527, + KEY_CAMERA_FOCUS = 528, + KEY_WPS_BUTTON = 529, + KEY_TOUCHPAD_TOGGLE = 530, + KEY_TOUCHPAD_ON = 531, + KEY_TOUCHPAD_OFF = 532, + KEY_CAMERA_ZOOMIN = 533, + KEY_CAMERA_ZOOMOUT = 534, + KEY_CAMERA_UP = 535, + KEY_CAMERA_DOWN = 536, + KEY_CAMERA_LEFT = 537, + KEY_CAMERA_RIGHT = 538, + KEY_ATTENDANT_ON = 539, + KEY_ATTENDANT_OFF = 540, + KEY_ATTENDANT_TOGGLE = 541, + KEY_LIGHTS_TOGGLE = 542, + KEY_ALS_TOGGLE = 560, + KEY_ROTATE_LOCK_TOGGLE = 561, + KEY_REFRESH_RATE_TOGGLE = 562, + KEY_BUTTONCONFIG = 576, + KEY_TASKMANAGER = 577, + KEY_JOURNAL = 578, + KEY_CONTROLPANEL = 579, + KEY_APPSELECT = 580, + KEY_SCREENSAVER = 581, + KEY_VOICECOMMAND = 582, + KEY_ASSISTANT = 583, + KEY_KBD_LAYOUT_NEXT = 584, + KEY_EMOJI_PICKER = 585, + KEY_DICTATE = 586, + KEY_CAMERA_ACCESS_ENABLE = 587, + KEY_CAMERA_ACCESS_DISABLE = 588, + KEY_CAMERA_ACCESS_TOGGLE = 589, + KEY_ACCESSIBILITY = 590, + KEY_DO_NOT_DISTURB = 591, + KEY_BRIGHTNESS_MIN = 592, + KEY_BRIGHTNESS_MAX = 593, + KEY_KBDINPUTASSIST_PREV = 608, + KEY_KBDINPUTASSIST_NEXT = 609, + KEY_KBDINPUTASSIST_PREVGROUP = 610, + KEY_KBDINPUTASSIST_NEXTGROUP = 611, + KEY_KBDINPUTASSIST_ACCEPT = 612, + KEY_KBDINPUTASSIST_CANCEL = 613, + KEY_RIGHT_UP = 614, + KEY_RIGHT_DOWN = 615, + KEY_LEFT_UP = 616, + KEY_LEFT_DOWN = 617, + KEY_ROOT_MENU = 618, + KEY_MEDIA_TOP_MENU = 619, + KEY_NUMERIC_11 = 620, + KEY_NUMERIC_12 = 621, + KEY_AUDIO_DESC = 622, + KEY_3D_MODE = 623, + KEY_NEXT_FAVORITE = 624, + KEY_STOP_RECORD = 625, + KEY_PAUSE_RECORD = 626, + KEY_VOD = 627, + KEY_UNMUTE = 628, + KEY_FASTREVERSE = 629, + KEY_SLOWREVERSE = 630, + KEY_DATA = 631, + KEY_ONSCREEN_KEYBOARD = 632, + KEY_PRIVACY_SCREEN_TOGGLE = 633, + KEY_SELECTIVE_SCREENSHOT = 634, + KEY_NEXT_ELEMENT = 635, + KEY_PREVIOUS_ELEMENT = 636, + KEY_AUTOPILOT_ENGAGE_TOGGLE = 637, + KEY_MARK_WAYPOINT = 638, + KEY_SOS = 639, + KEY_NAV_CHART = 640, + KEY_FISHING_CHART = 641, + KEY_SINGLE_RANGE_RADAR = 642, + KEY_DUAL_RANGE_RADAR = 643, + KEY_RADAR_OVERLAY = 644, + KEY_TRADITIONAL_SONAR = 645, + KEY_CLEARVU_SONAR = 646, + KEY_SIDEVU_SONAR = 647, + KEY_NAV_INFO = 648, + KEY_BRIGHTNESS_MENU = 649, + KEY_MACRO1 = 656, + KEY_MACRO2 = 657, + KEY_MACRO3 = 658, + KEY_MACRO4 = 659, + KEY_MACRO5 = 660, + KEY_MACRO6 = 661, + KEY_MACRO7 = 662, + KEY_MACRO8 = 663, + KEY_MACRO9 = 664, + KEY_MACRO10 = 665, + KEY_MACRO11 = 666, + KEY_MACRO12 = 667, + KEY_MACRO13 = 668, + KEY_MACRO14 = 669, + KEY_MACRO15 = 670, + KEY_MACRO16 = 671, + KEY_MACRO17 = 672, + KEY_MACRO18 = 673, + KEY_MACRO19 = 674, + KEY_MACRO20 = 675, + KEY_MACRO21 = 676, + KEY_MACRO22 = 677, + KEY_MACRO23 = 678, + KEY_MACRO24 = 679, + KEY_MACRO25 = 680, + KEY_MACRO26 = 681, + KEY_MACRO27 = 682, + KEY_MACRO28 = 683, + KEY_MACRO29 = 684, + KEY_MACRO30 = 685, + KEY_MACRO_RECORD_START = 688, + KEY_MACRO_RECORD_STOP = 689, + KEY_MACRO_PRESET_CYCLE = 690, + KEY_MACRO_PRESET1 = 691, + KEY_MACRO_PRESET2 = 692, + KEY_MACRO_PRESET3 = 693, + KEY_KBD_LCD_MENU1 = 696, + KEY_KBD_LCD_MENU2 = 697, + KEY_KBD_LCD_MENU3 = 698, + KEY_KBD_LCD_MENU4 = 699, + KEY_KBD_LCD_MENU5 = 700, + KEY_MAX = 767, + BTN_0 = 256, + BTN_1 = 257, + BTN_2 = 258, + BTN_3 = 259, + BTN_4 = 260, + BTN_5 = 261, + BTN_6 = 262, + BTN_7 = 263, + BTN_8 = 264, + BTN_9 = 265, + BTN_LEFT = 272, + BTN_RIGHT = 273, + BTN_MIDDLE = 274, + BTN_SIDE = 275, + BTN_EXTRA = 276, + BTN_FORWARD = 277, + BTN_BACK = 278, + BTN_TASK = 279, + BTN_TRIGGER = 288, + BTN_THUMB = 289, + BTN_THUMB2 = 290, + BTN_TOP = 291, + BTN_TOP2 = 292, + BTN_PINKIE = 293, + BTN_BASE = 294, + BTN_BASE2 = 295, + BTN_BASE3 = 296, + BTN_BASE4 = 297, + BTN_BASE5 = 298, + BTN_BASE6 = 299, + BTN_DEAD = 303, + BTN_SOUTH = 304, + BTN_EAST = 305, + BTN_C = 306, + BTN_NORTH = 307, + BTN_WEST = 308, + BTN_Z = 309, + BTN_TL = 310, + BTN_TR = 311, + BTN_TL2 = 312, + BTN_TR2 = 313, + BTN_SELECT = 314, + BTN_START = 315, + BTN_MODE = 316, + BTN_THUMBL = 317, + BTN_THUMBR = 318, + BTN_TOOL_PEN = 320, + BTN_TOOL_RUBBER = 321, + BTN_TOOL_BRUSH = 322, + BTN_TOOL_PENCIL = 323, + BTN_TOOL_AIRBRUSH = 324, + BTN_TOOL_FINGER = 325, + BTN_TOOL_MOUSE = 326, + BTN_TOOL_LENS = 327, + BTN_TOOL_QUINTTAP = 328, + BTN_STYLUS3 = 329, + BTN_TOUCH = 330, + BTN_STYLUS = 331, + BTN_STYLUS2 = 332, + BTN_TOOL_DOUBLETAP = 333, + BTN_TOOL_TRIPLETAP = 334, + BTN_TOOL_QUADTAP = 335, + BTN_GEAR_DOWN = 336, + BTN_GEAR_UP = 337, + BTN_DPAD_UP = 544, + BTN_DPAD_DOWN = 545, + BTN_DPAD_LEFT = 546, + BTN_DPAD_RIGHT = 547, + BTN_TRIGGER_HAPPY1 = 704, + BTN_TRIGGER_HAPPY2 = 705, + BTN_TRIGGER_HAPPY3 = 706, + BTN_TRIGGER_HAPPY4 = 707, + BTN_TRIGGER_HAPPY5 = 708, + BTN_TRIGGER_HAPPY6 = 709, + BTN_TRIGGER_HAPPY7 = 710, + BTN_TRIGGER_HAPPY8 = 711, + BTN_TRIGGER_HAPPY9 = 712, + BTN_TRIGGER_HAPPY10 = 713, + BTN_TRIGGER_HAPPY11 = 714, + BTN_TRIGGER_HAPPY12 = 715, + BTN_TRIGGER_HAPPY13 = 716, + BTN_TRIGGER_HAPPY14 = 717, + BTN_TRIGGER_HAPPY15 = 718, + BTN_TRIGGER_HAPPY16 = 719, + BTN_TRIGGER_HAPPY17 = 720, + BTN_TRIGGER_HAPPY18 = 721, + BTN_TRIGGER_HAPPY19 = 722, + BTN_TRIGGER_HAPPY20 = 723, + BTN_TRIGGER_HAPPY21 = 724, + BTN_TRIGGER_HAPPY22 = 725, + BTN_TRIGGER_HAPPY23 = 726, + BTN_TRIGGER_HAPPY24 = 727, + BTN_TRIGGER_HAPPY25 = 728, + BTN_TRIGGER_HAPPY26 = 729, + BTN_TRIGGER_HAPPY27 = 730, + BTN_TRIGGER_HAPPY28 = 731, + BTN_TRIGGER_HAPPY29 = 732, + BTN_TRIGGER_HAPPY30 = 733, + BTN_TRIGGER_HAPPY31 = 734, + BTN_TRIGGER_HAPPY32 = 735, + BTN_TRIGGER_HAPPY33 = 736, + BTN_TRIGGER_HAPPY34 = 737, + BTN_TRIGGER_HAPPY35 = 738, + BTN_TRIGGER_HAPPY36 = 739, + BTN_TRIGGER_HAPPY37 = 740, + BTN_TRIGGER_HAPPY38 = 741, + BTN_TRIGGER_HAPPY39 = 742, + BTN_TRIGGER_HAPPY40 = 743, +} + +pub const fn int_to_ev_key(code: u32) -> Option { + match code { + 0 => Some(EV_KEY::KEY_RESERVED), + 1 => Some(EV_KEY::KEY_ESC), + 2 => Some(EV_KEY::KEY_1), + 3 => Some(EV_KEY::KEY_2), + 4 => Some(EV_KEY::KEY_3), + 5 => Some(EV_KEY::KEY_4), + 6 => Some(EV_KEY::KEY_5), + 7 => Some(EV_KEY::KEY_6), + 8 => Some(EV_KEY::KEY_7), + 9 => Some(EV_KEY::KEY_8), + 10 => Some(EV_KEY::KEY_9), + 11 => Some(EV_KEY::KEY_0), + 12 => Some(EV_KEY::KEY_MINUS), + 13 => Some(EV_KEY::KEY_EQUAL), + 14 => Some(EV_KEY::KEY_BACKSPACE), + 15 => Some(EV_KEY::KEY_TAB), + 16 => Some(EV_KEY::KEY_Q), + 17 => Some(EV_KEY::KEY_W), + 18 => Some(EV_KEY::KEY_E), + 19 => Some(EV_KEY::KEY_R), + 20 => Some(EV_KEY::KEY_T), + 21 => Some(EV_KEY::KEY_Y), + 22 => Some(EV_KEY::KEY_U), + 23 => Some(EV_KEY::KEY_I), + 24 => Some(EV_KEY::KEY_O), + 25 => Some(EV_KEY::KEY_P), + 26 => Some(EV_KEY::KEY_LEFTBRACE), + 27 => Some(EV_KEY::KEY_RIGHTBRACE), + 28 => Some(EV_KEY::KEY_ENTER), + 29 => Some(EV_KEY::KEY_LEFTCTRL), + 30 => Some(EV_KEY::KEY_A), + 31 => Some(EV_KEY::KEY_S), + 32 => Some(EV_KEY::KEY_D), + 33 => Some(EV_KEY::KEY_F), + 34 => Some(EV_KEY::KEY_G), + 35 => Some(EV_KEY::KEY_H), + 36 => Some(EV_KEY::KEY_J), + 37 => Some(EV_KEY::KEY_K), + 38 => Some(EV_KEY::KEY_L), + 39 => Some(EV_KEY::KEY_SEMICOLON), + 40 => Some(EV_KEY::KEY_APOSTROPHE), + 41 => Some(EV_KEY::KEY_GRAVE), + 42 => Some(EV_KEY::KEY_LEFTSHIFT), + 43 => Some(EV_KEY::KEY_BACKSLASH), + 44 => Some(EV_KEY::KEY_Z), + 45 => Some(EV_KEY::KEY_X), + 46 => Some(EV_KEY::KEY_C), + 47 => Some(EV_KEY::KEY_V), + 48 => Some(EV_KEY::KEY_B), + 49 => Some(EV_KEY::KEY_N), + 50 => Some(EV_KEY::KEY_M), + 51 => Some(EV_KEY::KEY_COMMA), + 52 => Some(EV_KEY::KEY_DOT), + 53 => Some(EV_KEY::KEY_SLASH), + 54 => Some(EV_KEY::KEY_RIGHTSHIFT), + 55 => Some(EV_KEY::KEY_KPASTERISK), + 56 => Some(EV_KEY::KEY_LEFTALT), + 57 => Some(EV_KEY::KEY_SPACE), + 58 => Some(EV_KEY::KEY_CAPSLOCK), + 59 => Some(EV_KEY::KEY_F1), + 60 => Some(EV_KEY::KEY_F2), + 61 => Some(EV_KEY::KEY_F3), + 62 => Some(EV_KEY::KEY_F4), + 63 => Some(EV_KEY::KEY_F5), + 64 => Some(EV_KEY::KEY_F6), + 65 => Some(EV_KEY::KEY_F7), + 66 => Some(EV_KEY::KEY_F8), + 67 => Some(EV_KEY::KEY_F9), + 68 => Some(EV_KEY::KEY_F10), + 69 => Some(EV_KEY::KEY_NUMLOCK), + 70 => Some(EV_KEY::KEY_SCROLLLOCK), + 71 => Some(EV_KEY::KEY_KP7), + 72 => Some(EV_KEY::KEY_KP8), + 73 => Some(EV_KEY::KEY_KP9), + 74 => Some(EV_KEY::KEY_KPMINUS), + 75 => Some(EV_KEY::KEY_KP4), + 76 => Some(EV_KEY::KEY_KP5), + 77 => Some(EV_KEY::KEY_KP6), + 78 => Some(EV_KEY::KEY_KPPLUS), + 79 => Some(EV_KEY::KEY_KP1), + 80 => Some(EV_KEY::KEY_KP2), + 81 => Some(EV_KEY::KEY_KP3), + 82 => Some(EV_KEY::KEY_KP0), + 83 => Some(EV_KEY::KEY_KPDOT), + 85 => Some(EV_KEY::KEY_ZENKAKUHANKAKU), + 86 => Some(EV_KEY::KEY_102ND), + 87 => Some(EV_KEY::KEY_F11), + 88 => Some(EV_KEY::KEY_F12), + 89 => Some(EV_KEY::KEY_RO), + 90 => Some(EV_KEY::KEY_KATAKANA), + 91 => Some(EV_KEY::KEY_HIRAGANA), + 92 => Some(EV_KEY::KEY_HENKAN), + 93 => Some(EV_KEY::KEY_KATAKANAHIRAGANA), + 94 => Some(EV_KEY::KEY_MUHENKAN), + 95 => Some(EV_KEY::KEY_KPJPCOMMA), + 96 => Some(EV_KEY::KEY_KPENTER), + 97 => Some(EV_KEY::KEY_RIGHTCTRL), + 98 => Some(EV_KEY::KEY_KPSLASH), + 99 => Some(EV_KEY::KEY_SYSRQ), + 100 => Some(EV_KEY::KEY_RIGHTALT), + 101 => Some(EV_KEY::KEY_LINEFEED), + 102 => Some(EV_KEY::KEY_HOME), + 103 => Some(EV_KEY::KEY_UP), + 104 => Some(EV_KEY::KEY_PAGEUP), + 105 => Some(EV_KEY::KEY_LEFT), + 106 => Some(EV_KEY::KEY_RIGHT), + 107 => Some(EV_KEY::KEY_END), + 108 => Some(EV_KEY::KEY_DOWN), + 109 => Some(EV_KEY::KEY_PAGEDOWN), + 110 => Some(EV_KEY::KEY_INSERT), + 111 => Some(EV_KEY::KEY_DELETE), + 112 => Some(EV_KEY::KEY_MACRO), + 113 => Some(EV_KEY::KEY_MUTE), + 114 => Some(EV_KEY::KEY_VOLUMEDOWN), + 115 => Some(EV_KEY::KEY_VOLUMEUP), + 116 => Some(EV_KEY::KEY_POWER), + 117 => Some(EV_KEY::KEY_KPEQUAL), + 118 => Some(EV_KEY::KEY_KPPLUSMINUS), + 119 => Some(EV_KEY::KEY_PAUSE), + 120 => Some(EV_KEY::KEY_SCALE), + 121 => Some(EV_KEY::KEY_KPCOMMA), + 122 => Some(EV_KEY::KEY_HANGEUL), + 123 => Some(EV_KEY::KEY_HANJA), + 124 => Some(EV_KEY::KEY_YEN), + 125 => Some(EV_KEY::KEY_LEFTMETA), + 126 => Some(EV_KEY::KEY_RIGHTMETA), + 127 => Some(EV_KEY::KEY_COMPOSE), + 128 => Some(EV_KEY::KEY_STOP), + 129 => Some(EV_KEY::KEY_AGAIN), + 130 => Some(EV_KEY::KEY_PROPS), + 131 => Some(EV_KEY::KEY_UNDO), + 132 => Some(EV_KEY::KEY_FRONT), + 133 => Some(EV_KEY::KEY_COPY), + 134 => Some(EV_KEY::KEY_OPEN), + 135 => Some(EV_KEY::KEY_PASTE), + 136 => Some(EV_KEY::KEY_FIND), + 137 => Some(EV_KEY::KEY_CUT), + 138 => Some(EV_KEY::KEY_HELP), + 139 => Some(EV_KEY::KEY_MENU), + 140 => Some(EV_KEY::KEY_CALC), + 141 => Some(EV_KEY::KEY_SETUP), + 142 => Some(EV_KEY::KEY_SLEEP), + 143 => Some(EV_KEY::KEY_WAKEUP), + 144 => Some(EV_KEY::KEY_FILE), + 145 => Some(EV_KEY::KEY_SENDFILE), + 146 => Some(EV_KEY::KEY_DELETEFILE), + 147 => Some(EV_KEY::KEY_XFER), + 148 => Some(EV_KEY::KEY_PROG1), + 149 => Some(EV_KEY::KEY_PROG2), + 150 => Some(EV_KEY::KEY_WWW), + 151 => Some(EV_KEY::KEY_MSDOS), + 152 => Some(EV_KEY::KEY_COFFEE), + 153 => Some(EV_KEY::KEY_ROTATE_DISPLAY), + 154 => Some(EV_KEY::KEY_CYCLEWINDOWS), + 155 => Some(EV_KEY::KEY_MAIL), + 156 => Some(EV_KEY::KEY_BOOKMARKS), + 157 => Some(EV_KEY::KEY_COMPUTER), + 158 => Some(EV_KEY::KEY_BACK), + 159 => Some(EV_KEY::KEY_FORWARD), + 160 => Some(EV_KEY::KEY_CLOSECD), + 161 => Some(EV_KEY::KEY_EJECTCD), + 162 => Some(EV_KEY::KEY_EJECTCLOSECD), + 163 => Some(EV_KEY::KEY_NEXTSONG), + 164 => Some(EV_KEY::KEY_PLAYPAUSE), + 165 => Some(EV_KEY::KEY_PREVIOUSSONG), + 166 => Some(EV_KEY::KEY_STOPCD), + 167 => Some(EV_KEY::KEY_RECORD), + 168 => Some(EV_KEY::KEY_REWIND), + 169 => Some(EV_KEY::KEY_PHONE), + 170 => Some(EV_KEY::KEY_ISO), + 171 => Some(EV_KEY::KEY_CONFIG), + 172 => Some(EV_KEY::KEY_HOMEPAGE), + 173 => Some(EV_KEY::KEY_REFRESH), + 174 => Some(EV_KEY::KEY_EXIT), + 175 => Some(EV_KEY::KEY_MOVE), + 176 => Some(EV_KEY::KEY_EDIT), + 177 => Some(EV_KEY::KEY_SCROLLUP), + 178 => Some(EV_KEY::KEY_SCROLLDOWN), + 179 => Some(EV_KEY::KEY_KPLEFTPAREN), + 180 => Some(EV_KEY::KEY_KPRIGHTPAREN), + 181 => Some(EV_KEY::KEY_NEW), + 182 => Some(EV_KEY::KEY_REDO), + 183 => Some(EV_KEY::KEY_F13), + 184 => Some(EV_KEY::KEY_F14), + 185 => Some(EV_KEY::KEY_F15), + 186 => Some(EV_KEY::KEY_F16), + 187 => Some(EV_KEY::KEY_F17), + 188 => Some(EV_KEY::KEY_F18), + 189 => Some(EV_KEY::KEY_F19), + 190 => Some(EV_KEY::KEY_F20), + 191 => Some(EV_KEY::KEY_F21), + 192 => Some(EV_KEY::KEY_F22), + 193 => Some(EV_KEY::KEY_F23), + 194 => Some(EV_KEY::KEY_F24), + 200 => Some(EV_KEY::KEY_PLAYCD), + 201 => Some(EV_KEY::KEY_PAUSECD), + 202 => Some(EV_KEY::KEY_PROG3), + 203 => Some(EV_KEY::KEY_PROG4), + 204 => Some(EV_KEY::KEY_ALL_APPLICATIONS), + 205 => Some(EV_KEY::KEY_SUSPEND), + 206 => Some(EV_KEY::KEY_CLOSE), + 207 => Some(EV_KEY::KEY_PLAY), + 208 => Some(EV_KEY::KEY_FASTFORWARD), + 209 => Some(EV_KEY::KEY_BASSBOOST), + 210 => Some(EV_KEY::KEY_PRINT), + 211 => Some(EV_KEY::KEY_HP), + 212 => Some(EV_KEY::KEY_CAMERA), + 213 => Some(EV_KEY::KEY_SOUND), + 214 => Some(EV_KEY::KEY_QUESTION), + 215 => Some(EV_KEY::KEY_EMAIL), + 216 => Some(EV_KEY::KEY_CHAT), + 217 => Some(EV_KEY::KEY_SEARCH), + 218 => Some(EV_KEY::KEY_CONNECT), + 219 => Some(EV_KEY::KEY_FINANCE), + 220 => Some(EV_KEY::KEY_SPORT), + 221 => Some(EV_KEY::KEY_SHOP), + 222 => Some(EV_KEY::KEY_ALTERASE), + 223 => Some(EV_KEY::KEY_CANCEL), + 224 => Some(EV_KEY::KEY_BRIGHTNESSDOWN), + 225 => Some(EV_KEY::KEY_BRIGHTNESSUP), + 226 => Some(EV_KEY::KEY_MEDIA), + 227 => Some(EV_KEY::KEY_SWITCHVIDEOMODE), + 228 => Some(EV_KEY::KEY_KBDILLUMTOGGLE), + 229 => Some(EV_KEY::KEY_KBDILLUMDOWN), + 230 => Some(EV_KEY::KEY_KBDILLUMUP), + 231 => Some(EV_KEY::KEY_SEND), + 232 => Some(EV_KEY::KEY_REPLY), + 233 => Some(EV_KEY::KEY_FORWARDMAIL), + 234 => Some(EV_KEY::KEY_SAVE), + 235 => Some(EV_KEY::KEY_DOCUMENTS), + 236 => Some(EV_KEY::KEY_BATTERY), + 237 => Some(EV_KEY::KEY_BLUETOOTH), + 238 => Some(EV_KEY::KEY_WLAN), + 239 => Some(EV_KEY::KEY_UWB), + 240 => Some(EV_KEY::KEY_UNKNOWN), + 241 => Some(EV_KEY::KEY_VIDEO_NEXT), + 242 => Some(EV_KEY::KEY_VIDEO_PREV), + 243 => Some(EV_KEY::KEY_BRIGHTNESS_CYCLE), + 244 => Some(EV_KEY::KEY_BRIGHTNESS_AUTO), + 245 => Some(EV_KEY::KEY_DISPLAY_OFF), + 246 => Some(EV_KEY::KEY_WWAN), + 247 => Some(EV_KEY::KEY_RFKILL), + 248 => Some(EV_KEY::KEY_MICMUTE), + 352 => Some(EV_KEY::KEY_OK), + 353 => Some(EV_KEY::KEY_SELECT), + 354 => Some(EV_KEY::KEY_GOTO), + 355 => Some(EV_KEY::KEY_CLEAR), + 356 => Some(EV_KEY::KEY_POWER2), + 357 => Some(EV_KEY::KEY_OPTION), + 358 => Some(EV_KEY::KEY_INFO), + 359 => Some(EV_KEY::KEY_TIME), + 360 => Some(EV_KEY::KEY_VENDOR), + 361 => Some(EV_KEY::KEY_ARCHIVE), + 362 => Some(EV_KEY::KEY_PROGRAM), + 363 => Some(EV_KEY::KEY_CHANNEL), + 364 => Some(EV_KEY::KEY_FAVORITES), + 365 => Some(EV_KEY::KEY_EPG), + 366 => Some(EV_KEY::KEY_PVR), + 367 => Some(EV_KEY::KEY_MHP), + 368 => Some(EV_KEY::KEY_LANGUAGE), + 369 => Some(EV_KEY::KEY_TITLE), + 370 => Some(EV_KEY::KEY_SUBTITLE), + 371 => Some(EV_KEY::KEY_ANGLE), + 372 => Some(EV_KEY::KEY_FULL_SCREEN), + 373 => Some(EV_KEY::KEY_MODE), + 374 => Some(EV_KEY::KEY_KEYBOARD), + 375 => Some(EV_KEY::KEY_ASPECT_RATIO), + 376 => Some(EV_KEY::KEY_PC), + 377 => Some(EV_KEY::KEY_TV), + 378 => Some(EV_KEY::KEY_TV2), + 379 => Some(EV_KEY::KEY_VCR), + 380 => Some(EV_KEY::KEY_VCR2), + 381 => Some(EV_KEY::KEY_SAT), + 382 => Some(EV_KEY::KEY_SAT2), + 383 => Some(EV_KEY::KEY_CD), + 384 => Some(EV_KEY::KEY_TAPE), + 385 => Some(EV_KEY::KEY_RADIO), + 386 => Some(EV_KEY::KEY_TUNER), + 387 => Some(EV_KEY::KEY_PLAYER), + 388 => Some(EV_KEY::KEY_TEXT), + 389 => Some(EV_KEY::KEY_DVD), + 390 => Some(EV_KEY::KEY_AUX), + 391 => Some(EV_KEY::KEY_MP3), + 392 => Some(EV_KEY::KEY_AUDIO), + 393 => Some(EV_KEY::KEY_VIDEO), + 394 => Some(EV_KEY::KEY_DIRECTORY), + 395 => Some(EV_KEY::KEY_LIST), + 396 => Some(EV_KEY::KEY_MEMO), + 397 => Some(EV_KEY::KEY_CALENDAR), + 398 => Some(EV_KEY::KEY_RED), + 399 => Some(EV_KEY::KEY_GREEN), + 400 => Some(EV_KEY::KEY_YELLOW), + 401 => Some(EV_KEY::KEY_BLUE), + 402 => Some(EV_KEY::KEY_CHANNELUP), + 403 => Some(EV_KEY::KEY_CHANNELDOWN), + 404 => Some(EV_KEY::KEY_FIRST), + 405 => Some(EV_KEY::KEY_LAST), + 406 => Some(EV_KEY::KEY_AB), + 407 => Some(EV_KEY::KEY_NEXT), + 408 => Some(EV_KEY::KEY_RESTART), + 409 => Some(EV_KEY::KEY_SLOW), + 410 => Some(EV_KEY::KEY_SHUFFLE), + 411 => Some(EV_KEY::KEY_BREAK), + 412 => Some(EV_KEY::KEY_PREVIOUS), + 413 => Some(EV_KEY::KEY_DIGITS), + 414 => Some(EV_KEY::KEY_TEEN), + 415 => Some(EV_KEY::KEY_TWEN), + 416 => Some(EV_KEY::KEY_VIDEOPHONE), + 417 => Some(EV_KEY::KEY_GAMES), + 418 => Some(EV_KEY::KEY_ZOOMIN), + 419 => Some(EV_KEY::KEY_ZOOMOUT), + 420 => Some(EV_KEY::KEY_ZOOMRESET), + 421 => Some(EV_KEY::KEY_WORDPROCESSOR), + 422 => Some(EV_KEY::KEY_EDITOR), + 423 => Some(EV_KEY::KEY_SPREADSHEET), + 424 => Some(EV_KEY::KEY_GRAPHICSEDITOR), + 425 => Some(EV_KEY::KEY_PRESENTATION), + 426 => Some(EV_KEY::KEY_DATABASE), + 427 => Some(EV_KEY::KEY_NEWS), + 428 => Some(EV_KEY::KEY_VOICEMAIL), + 429 => Some(EV_KEY::KEY_ADDRESSBOOK), + 430 => Some(EV_KEY::KEY_MESSENGER), + 431 => Some(EV_KEY::KEY_DISPLAYTOGGLE), + 432 => Some(EV_KEY::KEY_SPELLCHECK), + 433 => Some(EV_KEY::KEY_LOGOFF), + 434 => Some(EV_KEY::KEY_DOLLAR), + 435 => Some(EV_KEY::KEY_EURO), + 436 => Some(EV_KEY::KEY_FRAMEBACK), + 437 => Some(EV_KEY::KEY_FRAMEFORWARD), + 438 => Some(EV_KEY::KEY_CONTEXT_MENU), + 439 => Some(EV_KEY::KEY_MEDIA_REPEAT), + 440 => Some(EV_KEY::KEY_10CHANNELSUP), + 441 => Some(EV_KEY::KEY_10CHANNELSDOWN), + 442 => Some(EV_KEY::KEY_IMAGES), + 444 => Some(EV_KEY::KEY_NOTIFICATION_CENTER), + 445 => Some(EV_KEY::KEY_PICKUP_PHONE), + 446 => Some(EV_KEY::KEY_HANGUP_PHONE), + 448 => Some(EV_KEY::KEY_DEL_EOL), + 449 => Some(EV_KEY::KEY_DEL_EOS), + 450 => Some(EV_KEY::KEY_INS_LINE), + 451 => Some(EV_KEY::KEY_DEL_LINE), + 464 => Some(EV_KEY::KEY_FN), + 465 => Some(EV_KEY::KEY_FN_ESC), + 466 => Some(EV_KEY::KEY_FN_F1), + 467 => Some(EV_KEY::KEY_FN_F2), + 468 => Some(EV_KEY::KEY_FN_F3), + 469 => Some(EV_KEY::KEY_FN_F4), + 470 => Some(EV_KEY::KEY_FN_F5), + 471 => Some(EV_KEY::KEY_FN_F6), + 472 => Some(EV_KEY::KEY_FN_F7), + 473 => Some(EV_KEY::KEY_FN_F8), + 474 => Some(EV_KEY::KEY_FN_F9), + 475 => Some(EV_KEY::KEY_FN_F10), + 476 => Some(EV_KEY::KEY_FN_F11), + 477 => Some(EV_KEY::KEY_FN_F12), + 478 => Some(EV_KEY::KEY_FN_1), + 479 => Some(EV_KEY::KEY_FN_2), + 480 => Some(EV_KEY::KEY_FN_D), + 481 => Some(EV_KEY::KEY_FN_E), + 482 => Some(EV_KEY::KEY_FN_F), + 483 => Some(EV_KEY::KEY_FN_S), + 484 => Some(EV_KEY::KEY_FN_B), + 485 => Some(EV_KEY::KEY_FN_RIGHT_SHIFT), + 497 => Some(EV_KEY::KEY_BRL_DOT1), + 498 => Some(EV_KEY::KEY_BRL_DOT2), + 499 => Some(EV_KEY::KEY_BRL_DOT3), + 500 => Some(EV_KEY::KEY_BRL_DOT4), + 501 => Some(EV_KEY::KEY_BRL_DOT5), + 502 => Some(EV_KEY::KEY_BRL_DOT6), + 503 => Some(EV_KEY::KEY_BRL_DOT7), + 504 => Some(EV_KEY::KEY_BRL_DOT8), + 505 => Some(EV_KEY::KEY_BRL_DOT9), + 506 => Some(EV_KEY::KEY_BRL_DOT10), + 512 => Some(EV_KEY::KEY_NUMERIC_0), + 513 => Some(EV_KEY::KEY_NUMERIC_1), + 514 => Some(EV_KEY::KEY_NUMERIC_2), + 515 => Some(EV_KEY::KEY_NUMERIC_3), + 516 => Some(EV_KEY::KEY_NUMERIC_4), + 517 => Some(EV_KEY::KEY_NUMERIC_5), + 518 => Some(EV_KEY::KEY_NUMERIC_6), + 519 => Some(EV_KEY::KEY_NUMERIC_7), + 520 => Some(EV_KEY::KEY_NUMERIC_8), + 521 => Some(EV_KEY::KEY_NUMERIC_9), + 522 => Some(EV_KEY::KEY_NUMERIC_STAR), + 523 => Some(EV_KEY::KEY_NUMERIC_POUND), + 524 => Some(EV_KEY::KEY_NUMERIC_A), + 525 => Some(EV_KEY::KEY_NUMERIC_B), + 526 => Some(EV_KEY::KEY_NUMERIC_C), + 527 => Some(EV_KEY::KEY_NUMERIC_D), + 528 => Some(EV_KEY::KEY_CAMERA_FOCUS), + 529 => Some(EV_KEY::KEY_WPS_BUTTON), + 530 => Some(EV_KEY::KEY_TOUCHPAD_TOGGLE), + 531 => Some(EV_KEY::KEY_TOUCHPAD_ON), + 532 => Some(EV_KEY::KEY_TOUCHPAD_OFF), + 533 => Some(EV_KEY::KEY_CAMERA_ZOOMIN), + 534 => Some(EV_KEY::KEY_CAMERA_ZOOMOUT), + 535 => Some(EV_KEY::KEY_CAMERA_UP), + 536 => Some(EV_KEY::KEY_CAMERA_DOWN), + 537 => Some(EV_KEY::KEY_CAMERA_LEFT), + 538 => Some(EV_KEY::KEY_CAMERA_RIGHT), + 539 => Some(EV_KEY::KEY_ATTENDANT_ON), + 540 => Some(EV_KEY::KEY_ATTENDANT_OFF), + 541 => Some(EV_KEY::KEY_ATTENDANT_TOGGLE), + 542 => Some(EV_KEY::KEY_LIGHTS_TOGGLE), + 560 => Some(EV_KEY::KEY_ALS_TOGGLE), + 561 => Some(EV_KEY::KEY_ROTATE_LOCK_TOGGLE), + 562 => Some(EV_KEY::KEY_REFRESH_RATE_TOGGLE), + 576 => Some(EV_KEY::KEY_BUTTONCONFIG), + 577 => Some(EV_KEY::KEY_TASKMANAGER), + 578 => Some(EV_KEY::KEY_JOURNAL), + 579 => Some(EV_KEY::KEY_CONTROLPANEL), + 580 => Some(EV_KEY::KEY_APPSELECT), + 581 => Some(EV_KEY::KEY_SCREENSAVER), + 582 => Some(EV_KEY::KEY_VOICECOMMAND), + 583 => Some(EV_KEY::KEY_ASSISTANT), + 584 => Some(EV_KEY::KEY_KBD_LAYOUT_NEXT), + 585 => Some(EV_KEY::KEY_EMOJI_PICKER), + 586 => Some(EV_KEY::KEY_DICTATE), + 587 => Some(EV_KEY::KEY_CAMERA_ACCESS_ENABLE), + 588 => Some(EV_KEY::KEY_CAMERA_ACCESS_DISABLE), + 589 => Some(EV_KEY::KEY_CAMERA_ACCESS_TOGGLE), + 590 => Some(EV_KEY::KEY_ACCESSIBILITY), + 591 => Some(EV_KEY::KEY_DO_NOT_DISTURB), + 592 => Some(EV_KEY::KEY_BRIGHTNESS_MIN), + 593 => Some(EV_KEY::KEY_BRIGHTNESS_MAX), + 608 => Some(EV_KEY::KEY_KBDINPUTASSIST_PREV), + 609 => Some(EV_KEY::KEY_KBDINPUTASSIST_NEXT), + 610 => Some(EV_KEY::KEY_KBDINPUTASSIST_PREVGROUP), + 611 => Some(EV_KEY::KEY_KBDINPUTASSIST_NEXTGROUP), + 612 => Some(EV_KEY::KEY_KBDINPUTASSIST_ACCEPT), + 613 => Some(EV_KEY::KEY_KBDINPUTASSIST_CANCEL), + 614 => Some(EV_KEY::KEY_RIGHT_UP), + 615 => Some(EV_KEY::KEY_RIGHT_DOWN), + 616 => Some(EV_KEY::KEY_LEFT_UP), + 617 => Some(EV_KEY::KEY_LEFT_DOWN), + 618 => Some(EV_KEY::KEY_ROOT_MENU), + 619 => Some(EV_KEY::KEY_MEDIA_TOP_MENU), + 620 => Some(EV_KEY::KEY_NUMERIC_11), + 621 => Some(EV_KEY::KEY_NUMERIC_12), + 622 => Some(EV_KEY::KEY_AUDIO_DESC), + 623 => Some(EV_KEY::KEY_3D_MODE), + 624 => Some(EV_KEY::KEY_NEXT_FAVORITE), + 625 => Some(EV_KEY::KEY_STOP_RECORD), + 626 => Some(EV_KEY::KEY_PAUSE_RECORD), + 627 => Some(EV_KEY::KEY_VOD), + 628 => Some(EV_KEY::KEY_UNMUTE), + 629 => Some(EV_KEY::KEY_FASTREVERSE), + 630 => Some(EV_KEY::KEY_SLOWREVERSE), + 631 => Some(EV_KEY::KEY_DATA), + 632 => Some(EV_KEY::KEY_ONSCREEN_KEYBOARD), + 633 => Some(EV_KEY::KEY_PRIVACY_SCREEN_TOGGLE), + 634 => Some(EV_KEY::KEY_SELECTIVE_SCREENSHOT), + 635 => Some(EV_KEY::KEY_NEXT_ELEMENT), + 636 => Some(EV_KEY::KEY_PREVIOUS_ELEMENT), + 637 => Some(EV_KEY::KEY_AUTOPILOT_ENGAGE_TOGGLE), + 638 => Some(EV_KEY::KEY_MARK_WAYPOINT), + 639 => Some(EV_KEY::KEY_SOS), + 640 => Some(EV_KEY::KEY_NAV_CHART), + 641 => Some(EV_KEY::KEY_FISHING_CHART), + 642 => Some(EV_KEY::KEY_SINGLE_RANGE_RADAR), + 643 => Some(EV_KEY::KEY_DUAL_RANGE_RADAR), + 644 => Some(EV_KEY::KEY_RADAR_OVERLAY), + 645 => Some(EV_KEY::KEY_TRADITIONAL_SONAR), + 646 => Some(EV_KEY::KEY_CLEARVU_SONAR), + 647 => Some(EV_KEY::KEY_SIDEVU_SONAR), + 648 => Some(EV_KEY::KEY_NAV_INFO), + 649 => Some(EV_KEY::KEY_BRIGHTNESS_MENU), + 656 => Some(EV_KEY::KEY_MACRO1), + 657 => Some(EV_KEY::KEY_MACRO2), + 658 => Some(EV_KEY::KEY_MACRO3), + 659 => Some(EV_KEY::KEY_MACRO4), + 660 => Some(EV_KEY::KEY_MACRO5), + 661 => Some(EV_KEY::KEY_MACRO6), + 662 => Some(EV_KEY::KEY_MACRO7), + 663 => Some(EV_KEY::KEY_MACRO8), + 664 => Some(EV_KEY::KEY_MACRO9), + 665 => Some(EV_KEY::KEY_MACRO10), + 666 => Some(EV_KEY::KEY_MACRO11), + 667 => Some(EV_KEY::KEY_MACRO12), + 668 => Some(EV_KEY::KEY_MACRO13), + 669 => Some(EV_KEY::KEY_MACRO14), + 670 => Some(EV_KEY::KEY_MACRO15), + 671 => Some(EV_KEY::KEY_MACRO16), + 672 => Some(EV_KEY::KEY_MACRO17), + 673 => Some(EV_KEY::KEY_MACRO18), + 674 => Some(EV_KEY::KEY_MACRO19), + 675 => Some(EV_KEY::KEY_MACRO20), + 676 => Some(EV_KEY::KEY_MACRO21), + 677 => Some(EV_KEY::KEY_MACRO22), + 678 => Some(EV_KEY::KEY_MACRO23), + 679 => Some(EV_KEY::KEY_MACRO24), + 680 => Some(EV_KEY::KEY_MACRO25), + 681 => Some(EV_KEY::KEY_MACRO26), + 682 => Some(EV_KEY::KEY_MACRO27), + 683 => Some(EV_KEY::KEY_MACRO28), + 684 => Some(EV_KEY::KEY_MACRO29), + 685 => Some(EV_KEY::KEY_MACRO30), + 688 => Some(EV_KEY::KEY_MACRO_RECORD_START), + 689 => Some(EV_KEY::KEY_MACRO_RECORD_STOP), + 690 => Some(EV_KEY::KEY_MACRO_PRESET_CYCLE), + 691 => Some(EV_KEY::KEY_MACRO_PRESET1), + 692 => Some(EV_KEY::KEY_MACRO_PRESET2), + 693 => Some(EV_KEY::KEY_MACRO_PRESET3), + 696 => Some(EV_KEY::KEY_KBD_LCD_MENU1), + 697 => Some(EV_KEY::KEY_KBD_LCD_MENU2), + 698 => Some(EV_KEY::KEY_KBD_LCD_MENU3), + 699 => Some(EV_KEY::KEY_KBD_LCD_MENU4), + 700 => Some(EV_KEY::KEY_KBD_LCD_MENU5), + 767 => Some(EV_KEY::KEY_MAX), + 256 => Some(EV_KEY::BTN_0), + 257 => Some(EV_KEY::BTN_1), + 258 => Some(EV_KEY::BTN_2), + 259 => Some(EV_KEY::BTN_3), + 260 => Some(EV_KEY::BTN_4), + 261 => Some(EV_KEY::BTN_5), + 262 => Some(EV_KEY::BTN_6), + 263 => Some(EV_KEY::BTN_7), + 264 => Some(EV_KEY::BTN_8), + 265 => Some(EV_KEY::BTN_9), + 272 => Some(EV_KEY::BTN_LEFT), + 273 => Some(EV_KEY::BTN_RIGHT), + 274 => Some(EV_KEY::BTN_MIDDLE), + 275 => Some(EV_KEY::BTN_SIDE), + 276 => Some(EV_KEY::BTN_EXTRA), + 277 => Some(EV_KEY::BTN_FORWARD), + 278 => Some(EV_KEY::BTN_BACK), + 279 => Some(EV_KEY::BTN_TASK), + 288 => Some(EV_KEY::BTN_TRIGGER), + 289 => Some(EV_KEY::BTN_THUMB), + 290 => Some(EV_KEY::BTN_THUMB2), + 291 => Some(EV_KEY::BTN_TOP), + 292 => Some(EV_KEY::BTN_TOP2), + 293 => Some(EV_KEY::BTN_PINKIE), + 294 => Some(EV_KEY::BTN_BASE), + 295 => Some(EV_KEY::BTN_BASE2), + 296 => Some(EV_KEY::BTN_BASE3), + 297 => Some(EV_KEY::BTN_BASE4), + 298 => Some(EV_KEY::BTN_BASE5), + 299 => Some(EV_KEY::BTN_BASE6), + 303 => Some(EV_KEY::BTN_DEAD), + 304 => Some(EV_KEY::BTN_SOUTH), + 305 => Some(EV_KEY::BTN_EAST), + 306 => Some(EV_KEY::BTN_C), + 307 => Some(EV_KEY::BTN_NORTH), + 308 => Some(EV_KEY::BTN_WEST), + 309 => Some(EV_KEY::BTN_Z), + 310 => Some(EV_KEY::BTN_TL), + 311 => Some(EV_KEY::BTN_TR), + 312 => Some(EV_KEY::BTN_TL2), + 313 => Some(EV_KEY::BTN_TR2), + 314 => Some(EV_KEY::BTN_SELECT), + 315 => Some(EV_KEY::BTN_START), + 316 => Some(EV_KEY::BTN_MODE), + 317 => Some(EV_KEY::BTN_THUMBL), + 318 => Some(EV_KEY::BTN_THUMBR), + 320 => Some(EV_KEY::BTN_TOOL_PEN), + 321 => Some(EV_KEY::BTN_TOOL_RUBBER), + 322 => Some(EV_KEY::BTN_TOOL_BRUSH), + 323 => Some(EV_KEY::BTN_TOOL_PENCIL), + 324 => Some(EV_KEY::BTN_TOOL_AIRBRUSH), + 325 => Some(EV_KEY::BTN_TOOL_FINGER), + 326 => Some(EV_KEY::BTN_TOOL_MOUSE), + 327 => Some(EV_KEY::BTN_TOOL_LENS), + 328 => Some(EV_KEY::BTN_TOOL_QUINTTAP), + 329 => Some(EV_KEY::BTN_STYLUS3), + 330 => Some(EV_KEY::BTN_TOUCH), + 331 => Some(EV_KEY::BTN_STYLUS), + 332 => Some(EV_KEY::BTN_STYLUS2), + 333 => Some(EV_KEY::BTN_TOOL_DOUBLETAP), + 334 => Some(EV_KEY::BTN_TOOL_TRIPLETAP), + 335 => Some(EV_KEY::BTN_TOOL_QUADTAP), + 336 => Some(EV_KEY::BTN_GEAR_DOWN), + 337 => Some(EV_KEY::BTN_GEAR_UP), + 544 => Some(EV_KEY::BTN_DPAD_UP), + 545 => Some(EV_KEY::BTN_DPAD_DOWN), + 546 => Some(EV_KEY::BTN_DPAD_LEFT), + 547 => Some(EV_KEY::BTN_DPAD_RIGHT), + 704 => Some(EV_KEY::BTN_TRIGGER_HAPPY1), + 705 => Some(EV_KEY::BTN_TRIGGER_HAPPY2), + 706 => Some(EV_KEY::BTN_TRIGGER_HAPPY3), + 707 => Some(EV_KEY::BTN_TRIGGER_HAPPY4), + 708 => Some(EV_KEY::BTN_TRIGGER_HAPPY5), + 709 => Some(EV_KEY::BTN_TRIGGER_HAPPY6), + 710 => Some(EV_KEY::BTN_TRIGGER_HAPPY7), + 711 => Some(EV_KEY::BTN_TRIGGER_HAPPY8), + 712 => Some(EV_KEY::BTN_TRIGGER_HAPPY9), + 713 => Some(EV_KEY::BTN_TRIGGER_HAPPY10), + 714 => Some(EV_KEY::BTN_TRIGGER_HAPPY11), + 715 => Some(EV_KEY::BTN_TRIGGER_HAPPY12), + 716 => Some(EV_KEY::BTN_TRIGGER_HAPPY13), + 717 => Some(EV_KEY::BTN_TRIGGER_HAPPY14), + 718 => Some(EV_KEY::BTN_TRIGGER_HAPPY15), + 719 => Some(EV_KEY::BTN_TRIGGER_HAPPY16), + 720 => Some(EV_KEY::BTN_TRIGGER_HAPPY17), + 721 => Some(EV_KEY::BTN_TRIGGER_HAPPY18), + 722 => Some(EV_KEY::BTN_TRIGGER_HAPPY19), + 723 => Some(EV_KEY::BTN_TRIGGER_HAPPY20), + 724 => Some(EV_KEY::BTN_TRIGGER_HAPPY21), + 725 => Some(EV_KEY::BTN_TRIGGER_HAPPY22), + 726 => Some(EV_KEY::BTN_TRIGGER_HAPPY23), + 727 => Some(EV_KEY::BTN_TRIGGER_HAPPY24), + 728 => Some(EV_KEY::BTN_TRIGGER_HAPPY25), + 729 => Some(EV_KEY::BTN_TRIGGER_HAPPY26), + 730 => Some(EV_KEY::BTN_TRIGGER_HAPPY27), + 731 => Some(EV_KEY::BTN_TRIGGER_HAPPY28), + 732 => Some(EV_KEY::BTN_TRIGGER_HAPPY29), + 733 => Some(EV_KEY::BTN_TRIGGER_HAPPY30), + 734 => Some(EV_KEY::BTN_TRIGGER_HAPPY31), + 735 => Some(EV_KEY::BTN_TRIGGER_HAPPY32), + 736 => Some(EV_KEY::BTN_TRIGGER_HAPPY33), + 737 => Some(EV_KEY::BTN_TRIGGER_HAPPY34), + 738 => Some(EV_KEY::BTN_TRIGGER_HAPPY35), + 739 => Some(EV_KEY::BTN_TRIGGER_HAPPY36), + 740 => Some(EV_KEY::BTN_TRIGGER_HAPPY37), + 741 => Some(EV_KEY::BTN_TRIGGER_HAPPY38), + 742 => Some(EV_KEY::BTN_TRIGGER_HAPPY39), + 743 => Some(EV_KEY::BTN_TRIGGER_HAPPY40), + _ => None, + } +} + +impl std::str::FromStr for EV_KEY { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "KEY_RESERVED" => Ok(EV_KEY::KEY_RESERVED), + "KEY_ESC" => Ok(EV_KEY::KEY_ESC), + "KEY_1" => Ok(EV_KEY::KEY_1), + "KEY_2" => Ok(EV_KEY::KEY_2), + "KEY_3" => Ok(EV_KEY::KEY_3), + "KEY_4" => Ok(EV_KEY::KEY_4), + "KEY_5" => Ok(EV_KEY::KEY_5), + "KEY_6" => Ok(EV_KEY::KEY_6), + "KEY_7" => Ok(EV_KEY::KEY_7), + "KEY_8" => Ok(EV_KEY::KEY_8), + "KEY_9" => Ok(EV_KEY::KEY_9), + "KEY_0" => Ok(EV_KEY::KEY_0), + "KEY_MINUS" => Ok(EV_KEY::KEY_MINUS), + "KEY_EQUAL" => Ok(EV_KEY::KEY_EQUAL), + "KEY_BACKSPACE" => Ok(EV_KEY::KEY_BACKSPACE), + "KEY_TAB" => Ok(EV_KEY::KEY_TAB), + "KEY_Q" => Ok(EV_KEY::KEY_Q), + "KEY_W" => Ok(EV_KEY::KEY_W), + "KEY_E" => Ok(EV_KEY::KEY_E), + "KEY_R" => Ok(EV_KEY::KEY_R), + "KEY_T" => Ok(EV_KEY::KEY_T), + "KEY_Y" => Ok(EV_KEY::KEY_Y), + "KEY_U" => Ok(EV_KEY::KEY_U), + "KEY_I" => Ok(EV_KEY::KEY_I), + "KEY_O" => Ok(EV_KEY::KEY_O), + "KEY_P" => Ok(EV_KEY::KEY_P), + "KEY_LEFTBRACE" => Ok(EV_KEY::KEY_LEFTBRACE), + "KEY_RIGHTBRACE" => Ok(EV_KEY::KEY_RIGHTBRACE), + "KEY_ENTER" => Ok(EV_KEY::KEY_ENTER), + "KEY_LEFTCTRL" => Ok(EV_KEY::KEY_LEFTCTRL), + "KEY_A" => Ok(EV_KEY::KEY_A), + "KEY_S" => Ok(EV_KEY::KEY_S), + "KEY_D" => Ok(EV_KEY::KEY_D), + "KEY_F" => Ok(EV_KEY::KEY_F), + "KEY_G" => Ok(EV_KEY::KEY_G), + "KEY_H" => Ok(EV_KEY::KEY_H), + "KEY_J" => Ok(EV_KEY::KEY_J), + "KEY_K" => Ok(EV_KEY::KEY_K), + "KEY_L" => Ok(EV_KEY::KEY_L), + "KEY_SEMICOLON" => Ok(EV_KEY::KEY_SEMICOLON), + "KEY_APOSTROPHE" => Ok(EV_KEY::KEY_APOSTROPHE), + "KEY_GRAVE" => Ok(EV_KEY::KEY_GRAVE), + "KEY_LEFTSHIFT" => Ok(EV_KEY::KEY_LEFTSHIFT), + "KEY_BACKSLASH" => Ok(EV_KEY::KEY_BACKSLASH), + "KEY_Z" => Ok(EV_KEY::KEY_Z), + "KEY_X" => Ok(EV_KEY::KEY_X), + "KEY_C" => Ok(EV_KEY::KEY_C), + "KEY_V" => Ok(EV_KEY::KEY_V), + "KEY_B" => Ok(EV_KEY::KEY_B), + "KEY_N" => Ok(EV_KEY::KEY_N), + "KEY_M" => Ok(EV_KEY::KEY_M), + "KEY_COMMA" => Ok(EV_KEY::KEY_COMMA), + "KEY_DOT" => Ok(EV_KEY::KEY_DOT), + "KEY_SLASH" => Ok(EV_KEY::KEY_SLASH), + "KEY_RIGHTSHIFT" => Ok(EV_KEY::KEY_RIGHTSHIFT), + "KEY_KPASTERISK" => Ok(EV_KEY::KEY_KPASTERISK), + "KEY_LEFTALT" => Ok(EV_KEY::KEY_LEFTALT), + "KEY_SPACE" => Ok(EV_KEY::KEY_SPACE), + "KEY_CAPSLOCK" => Ok(EV_KEY::KEY_CAPSLOCK), + "KEY_F1" => Ok(EV_KEY::KEY_F1), + "KEY_F2" => Ok(EV_KEY::KEY_F2), + "KEY_F3" => Ok(EV_KEY::KEY_F3), + "KEY_F4" => Ok(EV_KEY::KEY_F4), + "KEY_F5" => Ok(EV_KEY::KEY_F5), + "KEY_F6" => Ok(EV_KEY::KEY_F6), + "KEY_F7" => Ok(EV_KEY::KEY_F7), + "KEY_F8" => Ok(EV_KEY::KEY_F8), + "KEY_F9" => Ok(EV_KEY::KEY_F9), + "KEY_F10" => Ok(EV_KEY::KEY_F10), + "KEY_NUMLOCK" => Ok(EV_KEY::KEY_NUMLOCK), + "KEY_SCROLLLOCK" => Ok(EV_KEY::KEY_SCROLLLOCK), + "KEY_KP7" => Ok(EV_KEY::KEY_KP7), + "KEY_KP8" => Ok(EV_KEY::KEY_KP8), + "KEY_KP9" => Ok(EV_KEY::KEY_KP9), + "KEY_KPMINUS" => Ok(EV_KEY::KEY_KPMINUS), + "KEY_KP4" => Ok(EV_KEY::KEY_KP4), + "KEY_KP5" => Ok(EV_KEY::KEY_KP5), + "KEY_KP6" => Ok(EV_KEY::KEY_KP6), + "KEY_KPPLUS" => Ok(EV_KEY::KEY_KPPLUS), + "KEY_KP1" => Ok(EV_KEY::KEY_KP1), + "KEY_KP2" => Ok(EV_KEY::KEY_KP2), + "KEY_KP3" => Ok(EV_KEY::KEY_KP3), + "KEY_KP0" => Ok(EV_KEY::KEY_KP0), + "KEY_KPDOT" => Ok(EV_KEY::KEY_KPDOT), + "KEY_ZENKAKUHANKAKU" => Ok(EV_KEY::KEY_ZENKAKUHANKAKU), + "KEY_102ND" => Ok(EV_KEY::KEY_102ND), + "KEY_F11" => Ok(EV_KEY::KEY_F11), + "KEY_F12" => Ok(EV_KEY::KEY_F12), + "KEY_RO" => Ok(EV_KEY::KEY_RO), + "KEY_KATAKANA" => Ok(EV_KEY::KEY_KATAKANA), + "KEY_HIRAGANA" => Ok(EV_KEY::KEY_HIRAGANA), + "KEY_HENKAN" => Ok(EV_KEY::KEY_HENKAN), + "KEY_KATAKANAHIRAGANA" => Ok(EV_KEY::KEY_KATAKANAHIRAGANA), + "KEY_MUHENKAN" => Ok(EV_KEY::KEY_MUHENKAN), + "KEY_KPJPCOMMA" => Ok(EV_KEY::KEY_KPJPCOMMA), + "KEY_KPENTER" => Ok(EV_KEY::KEY_KPENTER), + "KEY_RIGHTCTRL" => Ok(EV_KEY::KEY_RIGHTCTRL), + "KEY_KPSLASH" => Ok(EV_KEY::KEY_KPSLASH), + "KEY_SYSRQ" => Ok(EV_KEY::KEY_SYSRQ), + "KEY_RIGHTALT" => Ok(EV_KEY::KEY_RIGHTALT), + "KEY_LINEFEED" => Ok(EV_KEY::KEY_LINEFEED), + "KEY_HOME" => Ok(EV_KEY::KEY_HOME), + "KEY_UP" => Ok(EV_KEY::KEY_UP), + "KEY_PAGEUP" => Ok(EV_KEY::KEY_PAGEUP), + "KEY_LEFT" => Ok(EV_KEY::KEY_LEFT), + "KEY_RIGHT" => Ok(EV_KEY::KEY_RIGHT), + "KEY_END" => Ok(EV_KEY::KEY_END), + "KEY_DOWN" => Ok(EV_KEY::KEY_DOWN), + "KEY_PAGEDOWN" => Ok(EV_KEY::KEY_PAGEDOWN), + "KEY_INSERT" => Ok(EV_KEY::KEY_INSERT), + "KEY_DELETE" => Ok(EV_KEY::KEY_DELETE), + "KEY_MACRO" => Ok(EV_KEY::KEY_MACRO), + "KEY_MUTE" => Ok(EV_KEY::KEY_MUTE), + "KEY_VOLUMEDOWN" => Ok(EV_KEY::KEY_VOLUMEDOWN), + "KEY_VOLUMEUP" => Ok(EV_KEY::KEY_VOLUMEUP), + "KEY_POWER" => Ok(EV_KEY::KEY_POWER), + "KEY_KPEQUAL" => Ok(EV_KEY::KEY_KPEQUAL), + "KEY_KPPLUSMINUS" => Ok(EV_KEY::KEY_KPPLUSMINUS), + "KEY_PAUSE" => Ok(EV_KEY::KEY_PAUSE), + "KEY_SCALE" => Ok(EV_KEY::KEY_SCALE), + "KEY_KPCOMMA" => Ok(EV_KEY::KEY_KPCOMMA), + "KEY_HANGEUL" => Ok(EV_KEY::KEY_HANGEUL), + "KEY_HANJA" => Ok(EV_KEY::KEY_HANJA), + "KEY_YEN" => Ok(EV_KEY::KEY_YEN), + "KEY_LEFTMETA" => Ok(EV_KEY::KEY_LEFTMETA), + "KEY_RIGHTMETA" => Ok(EV_KEY::KEY_RIGHTMETA), + "KEY_COMPOSE" => Ok(EV_KEY::KEY_COMPOSE), + "KEY_STOP" => Ok(EV_KEY::KEY_STOP), + "KEY_AGAIN" => Ok(EV_KEY::KEY_AGAIN), + "KEY_PROPS" => Ok(EV_KEY::KEY_PROPS), + "KEY_UNDO" => Ok(EV_KEY::KEY_UNDO), + "KEY_FRONT" => Ok(EV_KEY::KEY_FRONT), + "KEY_COPY" => Ok(EV_KEY::KEY_COPY), + "KEY_OPEN" => Ok(EV_KEY::KEY_OPEN), + "KEY_PASTE" => Ok(EV_KEY::KEY_PASTE), + "KEY_FIND" => Ok(EV_KEY::KEY_FIND), + "KEY_CUT" => Ok(EV_KEY::KEY_CUT), + "KEY_HELP" => Ok(EV_KEY::KEY_HELP), + "KEY_MENU" => Ok(EV_KEY::KEY_MENU), + "KEY_CALC" => Ok(EV_KEY::KEY_CALC), + "KEY_SETUP" => Ok(EV_KEY::KEY_SETUP), + "KEY_SLEEP" => Ok(EV_KEY::KEY_SLEEP), + "KEY_WAKEUP" => Ok(EV_KEY::KEY_WAKEUP), + "KEY_FILE" => Ok(EV_KEY::KEY_FILE), + "KEY_SENDFILE" => Ok(EV_KEY::KEY_SENDFILE), + "KEY_DELETEFILE" => Ok(EV_KEY::KEY_DELETEFILE), + "KEY_XFER" => Ok(EV_KEY::KEY_XFER), + "KEY_PROG1" => Ok(EV_KEY::KEY_PROG1), + "KEY_PROG2" => Ok(EV_KEY::KEY_PROG2), + "KEY_WWW" => Ok(EV_KEY::KEY_WWW), + "KEY_MSDOS" => Ok(EV_KEY::KEY_MSDOS), + "KEY_COFFEE" => Ok(EV_KEY::KEY_COFFEE), + "KEY_ROTATE_DISPLAY" => Ok(EV_KEY::KEY_ROTATE_DISPLAY), + "KEY_CYCLEWINDOWS" => Ok(EV_KEY::KEY_CYCLEWINDOWS), + "KEY_MAIL" => Ok(EV_KEY::KEY_MAIL), + "KEY_BOOKMARKS" => Ok(EV_KEY::KEY_BOOKMARKS), + "KEY_COMPUTER" => Ok(EV_KEY::KEY_COMPUTER), + "KEY_BACK" => Ok(EV_KEY::KEY_BACK), + "KEY_FORWARD" => Ok(EV_KEY::KEY_FORWARD), + "KEY_CLOSECD" => Ok(EV_KEY::KEY_CLOSECD), + "KEY_EJECTCD" => Ok(EV_KEY::KEY_EJECTCD), + "KEY_EJECTCLOSECD" => Ok(EV_KEY::KEY_EJECTCLOSECD), + "KEY_NEXTSONG" => Ok(EV_KEY::KEY_NEXTSONG), + "KEY_PLAYPAUSE" => Ok(EV_KEY::KEY_PLAYPAUSE), + "KEY_PREVIOUSSONG" => Ok(EV_KEY::KEY_PREVIOUSSONG), + "KEY_STOPCD" => Ok(EV_KEY::KEY_STOPCD), + "KEY_RECORD" => Ok(EV_KEY::KEY_RECORD), + "KEY_REWIND" => Ok(EV_KEY::KEY_REWIND), + "KEY_PHONE" => Ok(EV_KEY::KEY_PHONE), + "KEY_ISO" => Ok(EV_KEY::KEY_ISO), + "KEY_CONFIG" => Ok(EV_KEY::KEY_CONFIG), + "KEY_HOMEPAGE" => Ok(EV_KEY::KEY_HOMEPAGE), + "KEY_REFRESH" => Ok(EV_KEY::KEY_REFRESH), + "KEY_EXIT" => Ok(EV_KEY::KEY_EXIT), + "KEY_MOVE" => Ok(EV_KEY::KEY_MOVE), + "KEY_EDIT" => Ok(EV_KEY::KEY_EDIT), + "KEY_SCROLLUP" => Ok(EV_KEY::KEY_SCROLLUP), + "KEY_SCROLLDOWN" => Ok(EV_KEY::KEY_SCROLLDOWN), + "KEY_KPLEFTPAREN" => Ok(EV_KEY::KEY_KPLEFTPAREN), + "KEY_KPRIGHTPAREN" => Ok(EV_KEY::KEY_KPRIGHTPAREN), + "KEY_NEW" => Ok(EV_KEY::KEY_NEW), + "KEY_REDO" => Ok(EV_KEY::KEY_REDO), + "KEY_F13" => Ok(EV_KEY::KEY_F13), + "KEY_F14" => Ok(EV_KEY::KEY_F14), + "KEY_F15" => Ok(EV_KEY::KEY_F15), + "KEY_F16" => Ok(EV_KEY::KEY_F16), + "KEY_F17" => Ok(EV_KEY::KEY_F17), + "KEY_F18" => Ok(EV_KEY::KEY_F18), + "KEY_F19" => Ok(EV_KEY::KEY_F19), + "KEY_F20" => Ok(EV_KEY::KEY_F20), + "KEY_F21" => Ok(EV_KEY::KEY_F21), + "KEY_F22" => Ok(EV_KEY::KEY_F22), + "KEY_F23" => Ok(EV_KEY::KEY_F23), + "KEY_F24" => Ok(EV_KEY::KEY_F24), + "KEY_PLAYCD" => Ok(EV_KEY::KEY_PLAYCD), + "KEY_PAUSECD" => Ok(EV_KEY::KEY_PAUSECD), + "KEY_PROG3" => Ok(EV_KEY::KEY_PROG3), + "KEY_PROG4" => Ok(EV_KEY::KEY_PROG4), + "KEY_ALL_APPLICATIONS" => Ok(EV_KEY::KEY_ALL_APPLICATIONS), + "KEY_SUSPEND" => Ok(EV_KEY::KEY_SUSPEND), + "KEY_CLOSE" => Ok(EV_KEY::KEY_CLOSE), + "KEY_PLAY" => Ok(EV_KEY::KEY_PLAY), + "KEY_FASTFORWARD" => Ok(EV_KEY::KEY_FASTFORWARD), + "KEY_BASSBOOST" => Ok(EV_KEY::KEY_BASSBOOST), + "KEY_PRINT" => Ok(EV_KEY::KEY_PRINT), + "KEY_HP" => Ok(EV_KEY::KEY_HP), + "KEY_CAMERA" => Ok(EV_KEY::KEY_CAMERA), + "KEY_SOUND" => Ok(EV_KEY::KEY_SOUND), + "KEY_QUESTION" => Ok(EV_KEY::KEY_QUESTION), + "KEY_EMAIL" => Ok(EV_KEY::KEY_EMAIL), + "KEY_CHAT" => Ok(EV_KEY::KEY_CHAT), + "KEY_SEARCH" => Ok(EV_KEY::KEY_SEARCH), + "KEY_CONNECT" => Ok(EV_KEY::KEY_CONNECT), + "KEY_FINANCE" => Ok(EV_KEY::KEY_FINANCE), + "KEY_SPORT" => Ok(EV_KEY::KEY_SPORT), + "KEY_SHOP" => Ok(EV_KEY::KEY_SHOP), + "KEY_ALTERASE" => Ok(EV_KEY::KEY_ALTERASE), + "KEY_CANCEL" => Ok(EV_KEY::KEY_CANCEL), + "KEY_BRIGHTNESSDOWN" => Ok(EV_KEY::KEY_BRIGHTNESSDOWN), + "KEY_BRIGHTNESSUP" => Ok(EV_KEY::KEY_BRIGHTNESSUP), + "KEY_MEDIA" => Ok(EV_KEY::KEY_MEDIA), + "KEY_SWITCHVIDEOMODE" => Ok(EV_KEY::KEY_SWITCHVIDEOMODE), + "KEY_KBDILLUMTOGGLE" => Ok(EV_KEY::KEY_KBDILLUMTOGGLE), + "KEY_KBDILLUMDOWN" => Ok(EV_KEY::KEY_KBDILLUMDOWN), + "KEY_KBDILLUMUP" => Ok(EV_KEY::KEY_KBDILLUMUP), + "KEY_SEND" => Ok(EV_KEY::KEY_SEND), + "KEY_REPLY" => Ok(EV_KEY::KEY_REPLY), + "KEY_FORWARDMAIL" => Ok(EV_KEY::KEY_FORWARDMAIL), + "KEY_SAVE" => Ok(EV_KEY::KEY_SAVE), + "KEY_DOCUMENTS" => Ok(EV_KEY::KEY_DOCUMENTS), + "KEY_BATTERY" => Ok(EV_KEY::KEY_BATTERY), + "KEY_BLUETOOTH" => Ok(EV_KEY::KEY_BLUETOOTH), + "KEY_WLAN" => Ok(EV_KEY::KEY_WLAN), + "KEY_UWB" => Ok(EV_KEY::KEY_UWB), + "KEY_UNKNOWN" => Ok(EV_KEY::KEY_UNKNOWN), + "KEY_VIDEO_NEXT" => Ok(EV_KEY::KEY_VIDEO_NEXT), + "KEY_VIDEO_PREV" => Ok(EV_KEY::KEY_VIDEO_PREV), + "KEY_BRIGHTNESS_CYCLE" => Ok(EV_KEY::KEY_BRIGHTNESS_CYCLE), + "KEY_BRIGHTNESS_AUTO" => Ok(EV_KEY::KEY_BRIGHTNESS_AUTO), + "KEY_DISPLAY_OFF" => Ok(EV_KEY::KEY_DISPLAY_OFF), + "KEY_WWAN" => Ok(EV_KEY::KEY_WWAN), + "KEY_RFKILL" => Ok(EV_KEY::KEY_RFKILL), + "KEY_MICMUTE" => Ok(EV_KEY::KEY_MICMUTE), + "KEY_OK" => Ok(EV_KEY::KEY_OK), + "KEY_SELECT" => Ok(EV_KEY::KEY_SELECT), + "KEY_GOTO" => Ok(EV_KEY::KEY_GOTO), + "KEY_CLEAR" => Ok(EV_KEY::KEY_CLEAR), + "KEY_POWER2" => Ok(EV_KEY::KEY_POWER2), + "KEY_OPTION" => Ok(EV_KEY::KEY_OPTION), + "KEY_INFO" => Ok(EV_KEY::KEY_INFO), + "KEY_TIME" => Ok(EV_KEY::KEY_TIME), + "KEY_VENDOR" => Ok(EV_KEY::KEY_VENDOR), + "KEY_ARCHIVE" => Ok(EV_KEY::KEY_ARCHIVE), + "KEY_PROGRAM" => Ok(EV_KEY::KEY_PROGRAM), + "KEY_CHANNEL" => Ok(EV_KEY::KEY_CHANNEL), + "KEY_FAVORITES" => Ok(EV_KEY::KEY_FAVORITES), + "KEY_EPG" => Ok(EV_KEY::KEY_EPG), + "KEY_PVR" => Ok(EV_KEY::KEY_PVR), + "KEY_MHP" => Ok(EV_KEY::KEY_MHP), + "KEY_LANGUAGE" => Ok(EV_KEY::KEY_LANGUAGE), + "KEY_TITLE" => Ok(EV_KEY::KEY_TITLE), + "KEY_SUBTITLE" => Ok(EV_KEY::KEY_SUBTITLE), + "KEY_ANGLE" => Ok(EV_KEY::KEY_ANGLE), + "KEY_FULL_SCREEN" => Ok(EV_KEY::KEY_FULL_SCREEN), + "KEY_MODE" => Ok(EV_KEY::KEY_MODE), + "KEY_KEYBOARD" => Ok(EV_KEY::KEY_KEYBOARD), + "KEY_ASPECT_RATIO" => Ok(EV_KEY::KEY_ASPECT_RATIO), + "KEY_PC" => Ok(EV_KEY::KEY_PC), + "KEY_TV" => Ok(EV_KEY::KEY_TV), + "KEY_TV2" => Ok(EV_KEY::KEY_TV2), + "KEY_VCR" => Ok(EV_KEY::KEY_VCR), + "KEY_VCR2" => Ok(EV_KEY::KEY_VCR2), + "KEY_SAT" => Ok(EV_KEY::KEY_SAT), + "KEY_SAT2" => Ok(EV_KEY::KEY_SAT2), + "KEY_CD" => Ok(EV_KEY::KEY_CD), + "KEY_TAPE" => Ok(EV_KEY::KEY_TAPE), + "KEY_RADIO" => Ok(EV_KEY::KEY_RADIO), + "KEY_TUNER" => Ok(EV_KEY::KEY_TUNER), + "KEY_PLAYER" => Ok(EV_KEY::KEY_PLAYER), + "KEY_TEXT" => Ok(EV_KEY::KEY_TEXT), + "KEY_DVD" => Ok(EV_KEY::KEY_DVD), + "KEY_AUX" => Ok(EV_KEY::KEY_AUX), + "KEY_MP3" => Ok(EV_KEY::KEY_MP3), + "KEY_AUDIO" => Ok(EV_KEY::KEY_AUDIO), + "KEY_VIDEO" => Ok(EV_KEY::KEY_VIDEO), + "KEY_DIRECTORY" => Ok(EV_KEY::KEY_DIRECTORY), + "KEY_LIST" => Ok(EV_KEY::KEY_LIST), + "KEY_MEMO" => Ok(EV_KEY::KEY_MEMO), + "KEY_CALENDAR" => Ok(EV_KEY::KEY_CALENDAR), + "KEY_RED" => Ok(EV_KEY::KEY_RED), + "KEY_GREEN" => Ok(EV_KEY::KEY_GREEN), + "KEY_YELLOW" => Ok(EV_KEY::KEY_YELLOW), + "KEY_BLUE" => Ok(EV_KEY::KEY_BLUE), + "KEY_CHANNELUP" => Ok(EV_KEY::KEY_CHANNELUP), + "KEY_CHANNELDOWN" => Ok(EV_KEY::KEY_CHANNELDOWN), + "KEY_FIRST" => Ok(EV_KEY::KEY_FIRST), + "KEY_LAST" => Ok(EV_KEY::KEY_LAST), + "KEY_AB" => Ok(EV_KEY::KEY_AB), + "KEY_NEXT" => Ok(EV_KEY::KEY_NEXT), + "KEY_RESTART" => Ok(EV_KEY::KEY_RESTART), + "KEY_SLOW" => Ok(EV_KEY::KEY_SLOW), + "KEY_SHUFFLE" => Ok(EV_KEY::KEY_SHUFFLE), + "KEY_BREAK" => Ok(EV_KEY::KEY_BREAK), + "KEY_PREVIOUS" => Ok(EV_KEY::KEY_PREVIOUS), + "KEY_DIGITS" => Ok(EV_KEY::KEY_DIGITS), + "KEY_TEEN" => Ok(EV_KEY::KEY_TEEN), + "KEY_TWEN" => Ok(EV_KEY::KEY_TWEN), + "KEY_VIDEOPHONE" => Ok(EV_KEY::KEY_VIDEOPHONE), + "KEY_GAMES" => Ok(EV_KEY::KEY_GAMES), + "KEY_ZOOMIN" => Ok(EV_KEY::KEY_ZOOMIN), + "KEY_ZOOMOUT" => Ok(EV_KEY::KEY_ZOOMOUT), + "KEY_ZOOMRESET" => Ok(EV_KEY::KEY_ZOOMRESET), + "KEY_WORDPROCESSOR" => Ok(EV_KEY::KEY_WORDPROCESSOR), + "KEY_EDITOR" => Ok(EV_KEY::KEY_EDITOR), + "KEY_SPREADSHEET" => Ok(EV_KEY::KEY_SPREADSHEET), + "KEY_GRAPHICSEDITOR" => Ok(EV_KEY::KEY_GRAPHICSEDITOR), + "KEY_PRESENTATION" => Ok(EV_KEY::KEY_PRESENTATION), + "KEY_DATABASE" => Ok(EV_KEY::KEY_DATABASE), + "KEY_NEWS" => Ok(EV_KEY::KEY_NEWS), + "KEY_VOICEMAIL" => Ok(EV_KEY::KEY_VOICEMAIL), + "KEY_ADDRESSBOOK" => Ok(EV_KEY::KEY_ADDRESSBOOK), + "KEY_MESSENGER" => Ok(EV_KEY::KEY_MESSENGER), + "KEY_DISPLAYTOGGLE" => Ok(EV_KEY::KEY_DISPLAYTOGGLE), + "KEY_SPELLCHECK" => Ok(EV_KEY::KEY_SPELLCHECK), + "KEY_LOGOFF" => Ok(EV_KEY::KEY_LOGOFF), + "KEY_DOLLAR" => Ok(EV_KEY::KEY_DOLLAR), + "KEY_EURO" => Ok(EV_KEY::KEY_EURO), + "KEY_FRAMEBACK" => Ok(EV_KEY::KEY_FRAMEBACK), + "KEY_FRAMEFORWARD" => Ok(EV_KEY::KEY_FRAMEFORWARD), + "KEY_CONTEXT_MENU" => Ok(EV_KEY::KEY_CONTEXT_MENU), + "KEY_MEDIA_REPEAT" => Ok(EV_KEY::KEY_MEDIA_REPEAT), + "KEY_10CHANNELSUP" => Ok(EV_KEY::KEY_10CHANNELSUP), + "KEY_10CHANNELSDOWN" => Ok(EV_KEY::KEY_10CHANNELSDOWN), + "KEY_IMAGES" => Ok(EV_KEY::KEY_IMAGES), + "KEY_NOTIFICATION_CENTER" => Ok(EV_KEY::KEY_NOTIFICATION_CENTER), + "KEY_PICKUP_PHONE" => Ok(EV_KEY::KEY_PICKUP_PHONE), + "KEY_HANGUP_PHONE" => Ok(EV_KEY::KEY_HANGUP_PHONE), + "KEY_DEL_EOL" => Ok(EV_KEY::KEY_DEL_EOL), + "KEY_DEL_EOS" => Ok(EV_KEY::KEY_DEL_EOS), + "KEY_INS_LINE" => Ok(EV_KEY::KEY_INS_LINE), + "KEY_DEL_LINE" => Ok(EV_KEY::KEY_DEL_LINE), + "KEY_FN" => Ok(EV_KEY::KEY_FN), + "KEY_FN_ESC" => Ok(EV_KEY::KEY_FN_ESC), + "KEY_FN_F1" => Ok(EV_KEY::KEY_FN_F1), + "KEY_FN_F2" => Ok(EV_KEY::KEY_FN_F2), + "KEY_FN_F3" => Ok(EV_KEY::KEY_FN_F3), + "KEY_FN_F4" => Ok(EV_KEY::KEY_FN_F4), + "KEY_FN_F5" => Ok(EV_KEY::KEY_FN_F5), + "KEY_FN_F6" => Ok(EV_KEY::KEY_FN_F6), + "KEY_FN_F7" => Ok(EV_KEY::KEY_FN_F7), + "KEY_FN_F8" => Ok(EV_KEY::KEY_FN_F8), + "KEY_FN_F9" => Ok(EV_KEY::KEY_FN_F9), + "KEY_FN_F10" => Ok(EV_KEY::KEY_FN_F10), + "KEY_FN_F11" => Ok(EV_KEY::KEY_FN_F11), + "KEY_FN_F12" => Ok(EV_KEY::KEY_FN_F12), + "KEY_FN_1" => Ok(EV_KEY::KEY_FN_1), + "KEY_FN_2" => Ok(EV_KEY::KEY_FN_2), + "KEY_FN_D" => Ok(EV_KEY::KEY_FN_D), + "KEY_FN_E" => Ok(EV_KEY::KEY_FN_E), + "KEY_FN_F" => Ok(EV_KEY::KEY_FN_F), + "KEY_FN_S" => Ok(EV_KEY::KEY_FN_S), + "KEY_FN_B" => Ok(EV_KEY::KEY_FN_B), + "KEY_FN_RIGHT_SHIFT" => Ok(EV_KEY::KEY_FN_RIGHT_SHIFT), + "KEY_BRL_DOT1" => Ok(EV_KEY::KEY_BRL_DOT1), + "KEY_BRL_DOT2" => Ok(EV_KEY::KEY_BRL_DOT2), + "KEY_BRL_DOT3" => Ok(EV_KEY::KEY_BRL_DOT3), + "KEY_BRL_DOT4" => Ok(EV_KEY::KEY_BRL_DOT4), + "KEY_BRL_DOT5" => Ok(EV_KEY::KEY_BRL_DOT5), + "KEY_BRL_DOT6" => Ok(EV_KEY::KEY_BRL_DOT6), + "KEY_BRL_DOT7" => Ok(EV_KEY::KEY_BRL_DOT7), + "KEY_BRL_DOT8" => Ok(EV_KEY::KEY_BRL_DOT8), + "KEY_BRL_DOT9" => Ok(EV_KEY::KEY_BRL_DOT9), + "KEY_BRL_DOT10" => Ok(EV_KEY::KEY_BRL_DOT10), + "KEY_NUMERIC_0" => Ok(EV_KEY::KEY_NUMERIC_0), + "KEY_NUMERIC_1" => Ok(EV_KEY::KEY_NUMERIC_1), + "KEY_NUMERIC_2" => Ok(EV_KEY::KEY_NUMERIC_2), + "KEY_NUMERIC_3" => Ok(EV_KEY::KEY_NUMERIC_3), + "KEY_NUMERIC_4" => Ok(EV_KEY::KEY_NUMERIC_4), + "KEY_NUMERIC_5" => Ok(EV_KEY::KEY_NUMERIC_5), + "KEY_NUMERIC_6" => Ok(EV_KEY::KEY_NUMERIC_6), + "KEY_NUMERIC_7" => Ok(EV_KEY::KEY_NUMERIC_7), + "KEY_NUMERIC_8" => Ok(EV_KEY::KEY_NUMERIC_8), + "KEY_NUMERIC_9" => Ok(EV_KEY::KEY_NUMERIC_9), + "KEY_NUMERIC_STAR" => Ok(EV_KEY::KEY_NUMERIC_STAR), + "KEY_NUMERIC_POUND" => Ok(EV_KEY::KEY_NUMERIC_POUND), + "KEY_NUMERIC_A" => Ok(EV_KEY::KEY_NUMERIC_A), + "KEY_NUMERIC_B" => Ok(EV_KEY::KEY_NUMERIC_B), + "KEY_NUMERIC_C" => Ok(EV_KEY::KEY_NUMERIC_C), + "KEY_NUMERIC_D" => Ok(EV_KEY::KEY_NUMERIC_D), + "KEY_CAMERA_FOCUS" => Ok(EV_KEY::KEY_CAMERA_FOCUS), + "KEY_WPS_BUTTON" => Ok(EV_KEY::KEY_WPS_BUTTON), + "KEY_TOUCHPAD_TOGGLE" => Ok(EV_KEY::KEY_TOUCHPAD_TOGGLE), + "KEY_TOUCHPAD_ON" => Ok(EV_KEY::KEY_TOUCHPAD_ON), + "KEY_TOUCHPAD_OFF" => Ok(EV_KEY::KEY_TOUCHPAD_OFF), + "KEY_CAMERA_ZOOMIN" => Ok(EV_KEY::KEY_CAMERA_ZOOMIN), + "KEY_CAMERA_ZOOMOUT" => Ok(EV_KEY::KEY_CAMERA_ZOOMOUT), + "KEY_CAMERA_UP" => Ok(EV_KEY::KEY_CAMERA_UP), + "KEY_CAMERA_DOWN" => Ok(EV_KEY::KEY_CAMERA_DOWN), + "KEY_CAMERA_LEFT" => Ok(EV_KEY::KEY_CAMERA_LEFT), + "KEY_CAMERA_RIGHT" => Ok(EV_KEY::KEY_CAMERA_RIGHT), + "KEY_ATTENDANT_ON" => Ok(EV_KEY::KEY_ATTENDANT_ON), + "KEY_ATTENDANT_OFF" => Ok(EV_KEY::KEY_ATTENDANT_OFF), + "KEY_ATTENDANT_TOGGLE" => Ok(EV_KEY::KEY_ATTENDANT_TOGGLE), + "KEY_LIGHTS_TOGGLE" => Ok(EV_KEY::KEY_LIGHTS_TOGGLE), + "KEY_ALS_TOGGLE" => Ok(EV_KEY::KEY_ALS_TOGGLE), + "KEY_ROTATE_LOCK_TOGGLE" => Ok(EV_KEY::KEY_ROTATE_LOCK_TOGGLE), + "KEY_REFRESH_RATE_TOGGLE" => Ok(EV_KEY::KEY_REFRESH_RATE_TOGGLE), + "KEY_BUTTONCONFIG" => Ok(EV_KEY::KEY_BUTTONCONFIG), + "KEY_TASKMANAGER" => Ok(EV_KEY::KEY_TASKMANAGER), + "KEY_JOURNAL" => Ok(EV_KEY::KEY_JOURNAL), + "KEY_CONTROLPANEL" => Ok(EV_KEY::KEY_CONTROLPANEL), + "KEY_APPSELECT" => Ok(EV_KEY::KEY_APPSELECT), + "KEY_SCREENSAVER" => Ok(EV_KEY::KEY_SCREENSAVER), + "KEY_VOICECOMMAND" => Ok(EV_KEY::KEY_VOICECOMMAND), + "KEY_ASSISTANT" => Ok(EV_KEY::KEY_ASSISTANT), + "KEY_KBD_LAYOUT_NEXT" => Ok(EV_KEY::KEY_KBD_LAYOUT_NEXT), + "KEY_EMOJI_PICKER" => Ok(EV_KEY::KEY_EMOJI_PICKER), + "KEY_DICTATE" => Ok(EV_KEY::KEY_DICTATE), + "KEY_CAMERA_ACCESS_ENABLE" => Ok(EV_KEY::KEY_CAMERA_ACCESS_ENABLE), + "KEY_CAMERA_ACCESS_DISABLE" => Ok(EV_KEY::KEY_CAMERA_ACCESS_DISABLE), + "KEY_CAMERA_ACCESS_TOGGLE" => Ok(EV_KEY::KEY_CAMERA_ACCESS_TOGGLE), + "KEY_ACCESSIBILITY" => Ok(EV_KEY::KEY_ACCESSIBILITY), + "KEY_DO_NOT_DISTURB" => Ok(EV_KEY::KEY_DO_NOT_DISTURB), + "KEY_BRIGHTNESS_MIN" => Ok(EV_KEY::KEY_BRIGHTNESS_MIN), + "KEY_BRIGHTNESS_MAX" => Ok(EV_KEY::KEY_BRIGHTNESS_MAX), + "KEY_KBDINPUTASSIST_PREV" => Ok(EV_KEY::KEY_KBDINPUTASSIST_PREV), + "KEY_KBDINPUTASSIST_NEXT" => Ok(EV_KEY::KEY_KBDINPUTASSIST_NEXT), + "KEY_KBDINPUTASSIST_PREVGROUP" => Ok(EV_KEY::KEY_KBDINPUTASSIST_PREVGROUP), + "KEY_KBDINPUTASSIST_NEXTGROUP" => Ok(EV_KEY::KEY_KBDINPUTASSIST_NEXTGROUP), + "KEY_KBDINPUTASSIST_ACCEPT" => Ok(EV_KEY::KEY_KBDINPUTASSIST_ACCEPT), + "KEY_KBDINPUTASSIST_CANCEL" => Ok(EV_KEY::KEY_KBDINPUTASSIST_CANCEL), + "KEY_RIGHT_UP" => Ok(EV_KEY::KEY_RIGHT_UP), + "KEY_RIGHT_DOWN" => Ok(EV_KEY::KEY_RIGHT_DOWN), + "KEY_LEFT_UP" => Ok(EV_KEY::KEY_LEFT_UP), + "KEY_LEFT_DOWN" => Ok(EV_KEY::KEY_LEFT_DOWN), + "KEY_ROOT_MENU" => Ok(EV_KEY::KEY_ROOT_MENU), + "KEY_MEDIA_TOP_MENU" => Ok(EV_KEY::KEY_MEDIA_TOP_MENU), + "KEY_NUMERIC_11" => Ok(EV_KEY::KEY_NUMERIC_11), + "KEY_NUMERIC_12" => Ok(EV_KEY::KEY_NUMERIC_12), + "KEY_AUDIO_DESC" => Ok(EV_KEY::KEY_AUDIO_DESC), + "KEY_3D_MODE" => Ok(EV_KEY::KEY_3D_MODE), + "KEY_NEXT_FAVORITE" => Ok(EV_KEY::KEY_NEXT_FAVORITE), + "KEY_STOP_RECORD" => Ok(EV_KEY::KEY_STOP_RECORD), + "KEY_PAUSE_RECORD" => Ok(EV_KEY::KEY_PAUSE_RECORD), + "KEY_VOD" => Ok(EV_KEY::KEY_VOD), + "KEY_UNMUTE" => Ok(EV_KEY::KEY_UNMUTE), + "KEY_FASTREVERSE" => Ok(EV_KEY::KEY_FASTREVERSE), + "KEY_SLOWREVERSE" => Ok(EV_KEY::KEY_SLOWREVERSE), + "KEY_DATA" => Ok(EV_KEY::KEY_DATA), + "KEY_ONSCREEN_KEYBOARD" => Ok(EV_KEY::KEY_ONSCREEN_KEYBOARD), + "KEY_PRIVACY_SCREEN_TOGGLE" => Ok(EV_KEY::KEY_PRIVACY_SCREEN_TOGGLE), + "KEY_SELECTIVE_SCREENSHOT" => Ok(EV_KEY::KEY_SELECTIVE_SCREENSHOT), + "KEY_NEXT_ELEMENT" => Ok(EV_KEY::KEY_NEXT_ELEMENT), + "KEY_PREVIOUS_ELEMENT" => Ok(EV_KEY::KEY_PREVIOUS_ELEMENT), + "KEY_AUTOPILOT_ENGAGE_TOGGLE" => Ok(EV_KEY::KEY_AUTOPILOT_ENGAGE_TOGGLE), + "KEY_MARK_WAYPOINT" => Ok(EV_KEY::KEY_MARK_WAYPOINT), + "KEY_SOS" => Ok(EV_KEY::KEY_SOS), + "KEY_NAV_CHART" => Ok(EV_KEY::KEY_NAV_CHART), + "KEY_FISHING_CHART" => Ok(EV_KEY::KEY_FISHING_CHART), + "KEY_SINGLE_RANGE_RADAR" => Ok(EV_KEY::KEY_SINGLE_RANGE_RADAR), + "KEY_DUAL_RANGE_RADAR" => Ok(EV_KEY::KEY_DUAL_RANGE_RADAR), + "KEY_RADAR_OVERLAY" => Ok(EV_KEY::KEY_RADAR_OVERLAY), + "KEY_TRADITIONAL_SONAR" => Ok(EV_KEY::KEY_TRADITIONAL_SONAR), + "KEY_CLEARVU_SONAR" => Ok(EV_KEY::KEY_CLEARVU_SONAR), + "KEY_SIDEVU_SONAR" => Ok(EV_KEY::KEY_SIDEVU_SONAR), + "KEY_NAV_INFO" => Ok(EV_KEY::KEY_NAV_INFO), + "KEY_BRIGHTNESS_MENU" => Ok(EV_KEY::KEY_BRIGHTNESS_MENU), + "KEY_MACRO1" => Ok(EV_KEY::KEY_MACRO1), + "KEY_MACRO2" => Ok(EV_KEY::KEY_MACRO2), + "KEY_MACRO3" => Ok(EV_KEY::KEY_MACRO3), + "KEY_MACRO4" => Ok(EV_KEY::KEY_MACRO4), + "KEY_MACRO5" => Ok(EV_KEY::KEY_MACRO5), + "KEY_MACRO6" => Ok(EV_KEY::KEY_MACRO6), + "KEY_MACRO7" => Ok(EV_KEY::KEY_MACRO7), + "KEY_MACRO8" => Ok(EV_KEY::KEY_MACRO8), + "KEY_MACRO9" => Ok(EV_KEY::KEY_MACRO9), + "KEY_MACRO10" => Ok(EV_KEY::KEY_MACRO10), + "KEY_MACRO11" => Ok(EV_KEY::KEY_MACRO11), + "KEY_MACRO12" => Ok(EV_KEY::KEY_MACRO12), + "KEY_MACRO13" => Ok(EV_KEY::KEY_MACRO13), + "KEY_MACRO14" => Ok(EV_KEY::KEY_MACRO14), + "KEY_MACRO15" => Ok(EV_KEY::KEY_MACRO15), + "KEY_MACRO16" => Ok(EV_KEY::KEY_MACRO16), + "KEY_MACRO17" => Ok(EV_KEY::KEY_MACRO17), + "KEY_MACRO18" => Ok(EV_KEY::KEY_MACRO18), + "KEY_MACRO19" => Ok(EV_KEY::KEY_MACRO19), + "KEY_MACRO20" => Ok(EV_KEY::KEY_MACRO20), + "KEY_MACRO21" => Ok(EV_KEY::KEY_MACRO21), + "KEY_MACRO22" => Ok(EV_KEY::KEY_MACRO22), + "KEY_MACRO23" => Ok(EV_KEY::KEY_MACRO23), + "KEY_MACRO24" => Ok(EV_KEY::KEY_MACRO24), + "KEY_MACRO25" => Ok(EV_KEY::KEY_MACRO25), + "KEY_MACRO26" => Ok(EV_KEY::KEY_MACRO26), + "KEY_MACRO27" => Ok(EV_KEY::KEY_MACRO27), + "KEY_MACRO28" => Ok(EV_KEY::KEY_MACRO28), + "KEY_MACRO29" => Ok(EV_KEY::KEY_MACRO29), + "KEY_MACRO30" => Ok(EV_KEY::KEY_MACRO30), + "KEY_MACRO_RECORD_START" => Ok(EV_KEY::KEY_MACRO_RECORD_START), + "KEY_MACRO_RECORD_STOP" => Ok(EV_KEY::KEY_MACRO_RECORD_STOP), + "KEY_MACRO_PRESET_CYCLE" => Ok(EV_KEY::KEY_MACRO_PRESET_CYCLE), + "KEY_MACRO_PRESET1" => Ok(EV_KEY::KEY_MACRO_PRESET1), + "KEY_MACRO_PRESET2" => Ok(EV_KEY::KEY_MACRO_PRESET2), + "KEY_MACRO_PRESET3" => Ok(EV_KEY::KEY_MACRO_PRESET3), + "KEY_KBD_LCD_MENU1" => Ok(EV_KEY::KEY_KBD_LCD_MENU1), + "KEY_KBD_LCD_MENU2" => Ok(EV_KEY::KEY_KBD_LCD_MENU2), + "KEY_KBD_LCD_MENU3" => Ok(EV_KEY::KEY_KBD_LCD_MENU3), + "KEY_KBD_LCD_MENU4" => Ok(EV_KEY::KEY_KBD_LCD_MENU4), + "KEY_KBD_LCD_MENU5" => Ok(EV_KEY::KEY_KBD_LCD_MENU5), + "KEY_MAX" => Ok(EV_KEY::KEY_MAX), + "BTN_0" => Ok(EV_KEY::BTN_0), + "BTN_1" => Ok(EV_KEY::BTN_1), + "BTN_2" => Ok(EV_KEY::BTN_2), + "BTN_3" => Ok(EV_KEY::BTN_3), + "BTN_4" => Ok(EV_KEY::BTN_4), + "BTN_5" => Ok(EV_KEY::BTN_5), + "BTN_6" => Ok(EV_KEY::BTN_6), + "BTN_7" => Ok(EV_KEY::BTN_7), + "BTN_8" => Ok(EV_KEY::BTN_8), + "BTN_9" => Ok(EV_KEY::BTN_9), + "BTN_LEFT" => Ok(EV_KEY::BTN_LEFT), + "BTN_RIGHT" => Ok(EV_KEY::BTN_RIGHT), + "BTN_MIDDLE" => Ok(EV_KEY::BTN_MIDDLE), + "BTN_SIDE" => Ok(EV_KEY::BTN_SIDE), + "BTN_EXTRA" => Ok(EV_KEY::BTN_EXTRA), + "BTN_FORWARD" => Ok(EV_KEY::BTN_FORWARD), + "BTN_BACK" => Ok(EV_KEY::BTN_BACK), + "BTN_TASK" => Ok(EV_KEY::BTN_TASK), + "BTN_TRIGGER" => Ok(EV_KEY::BTN_TRIGGER), + "BTN_THUMB" => Ok(EV_KEY::BTN_THUMB), + "BTN_THUMB2" => Ok(EV_KEY::BTN_THUMB2), + "BTN_TOP" => Ok(EV_KEY::BTN_TOP), + "BTN_TOP2" => Ok(EV_KEY::BTN_TOP2), + "BTN_PINKIE" => Ok(EV_KEY::BTN_PINKIE), + "BTN_BASE" => Ok(EV_KEY::BTN_BASE), + "BTN_BASE2" => Ok(EV_KEY::BTN_BASE2), + "BTN_BASE3" => Ok(EV_KEY::BTN_BASE3), + "BTN_BASE4" => Ok(EV_KEY::BTN_BASE4), + "BTN_BASE5" => Ok(EV_KEY::BTN_BASE5), + "BTN_BASE6" => Ok(EV_KEY::BTN_BASE6), + "BTN_DEAD" => Ok(EV_KEY::BTN_DEAD), + "BTN_SOUTH" => Ok(EV_KEY::BTN_SOUTH), + "BTN_EAST" => Ok(EV_KEY::BTN_EAST), + "BTN_C" => Ok(EV_KEY::BTN_C), + "BTN_NORTH" => Ok(EV_KEY::BTN_NORTH), + "BTN_WEST" => Ok(EV_KEY::BTN_WEST), + "BTN_Z" => Ok(EV_KEY::BTN_Z), + "BTN_TL" => Ok(EV_KEY::BTN_TL), + "BTN_TR" => Ok(EV_KEY::BTN_TR), + "BTN_TL2" => Ok(EV_KEY::BTN_TL2), + "BTN_TR2" => Ok(EV_KEY::BTN_TR2), + "BTN_SELECT" => Ok(EV_KEY::BTN_SELECT), + "BTN_START" => Ok(EV_KEY::BTN_START), + "BTN_MODE" => Ok(EV_KEY::BTN_MODE), + "BTN_THUMBL" => Ok(EV_KEY::BTN_THUMBL), + "BTN_THUMBR" => Ok(EV_KEY::BTN_THUMBR), + "BTN_TOOL_PEN" => Ok(EV_KEY::BTN_TOOL_PEN), + "BTN_TOOL_RUBBER" => Ok(EV_KEY::BTN_TOOL_RUBBER), + "BTN_TOOL_BRUSH" => Ok(EV_KEY::BTN_TOOL_BRUSH), + "BTN_TOOL_PENCIL" => Ok(EV_KEY::BTN_TOOL_PENCIL), + "BTN_TOOL_AIRBRUSH" => Ok(EV_KEY::BTN_TOOL_AIRBRUSH), + "BTN_TOOL_FINGER" => Ok(EV_KEY::BTN_TOOL_FINGER), + "BTN_TOOL_MOUSE" => Ok(EV_KEY::BTN_TOOL_MOUSE), + "BTN_TOOL_LENS" => Ok(EV_KEY::BTN_TOOL_LENS), + "BTN_TOOL_QUINTTAP" => Ok(EV_KEY::BTN_TOOL_QUINTTAP), + "BTN_STYLUS3" => Ok(EV_KEY::BTN_STYLUS3), + "BTN_TOUCH" => Ok(EV_KEY::BTN_TOUCH), + "BTN_STYLUS" => Ok(EV_KEY::BTN_STYLUS), + "BTN_STYLUS2" => Ok(EV_KEY::BTN_STYLUS2), + "BTN_TOOL_DOUBLETAP" => Ok(EV_KEY::BTN_TOOL_DOUBLETAP), + "BTN_TOOL_TRIPLETAP" => Ok(EV_KEY::BTN_TOOL_TRIPLETAP), + "BTN_TOOL_QUADTAP" => Ok(EV_KEY::BTN_TOOL_QUADTAP), + "BTN_GEAR_DOWN" => Ok(EV_KEY::BTN_GEAR_DOWN), + "BTN_GEAR_UP" => Ok(EV_KEY::BTN_GEAR_UP), + "BTN_DPAD_UP" => Ok(EV_KEY::BTN_DPAD_UP), + "BTN_DPAD_DOWN" => Ok(EV_KEY::BTN_DPAD_DOWN), + "BTN_DPAD_LEFT" => Ok(EV_KEY::BTN_DPAD_LEFT), + "BTN_DPAD_RIGHT" => Ok(EV_KEY::BTN_DPAD_RIGHT), + "BTN_TRIGGER_HAPPY1" => Ok(EV_KEY::BTN_TRIGGER_HAPPY1), + "BTN_TRIGGER_HAPPY2" => Ok(EV_KEY::BTN_TRIGGER_HAPPY2), + "BTN_TRIGGER_HAPPY3" => Ok(EV_KEY::BTN_TRIGGER_HAPPY3), + "BTN_TRIGGER_HAPPY4" => Ok(EV_KEY::BTN_TRIGGER_HAPPY4), + "BTN_TRIGGER_HAPPY5" => Ok(EV_KEY::BTN_TRIGGER_HAPPY5), + "BTN_TRIGGER_HAPPY6" => Ok(EV_KEY::BTN_TRIGGER_HAPPY6), + "BTN_TRIGGER_HAPPY7" => Ok(EV_KEY::BTN_TRIGGER_HAPPY7), + "BTN_TRIGGER_HAPPY8" => Ok(EV_KEY::BTN_TRIGGER_HAPPY8), + "BTN_TRIGGER_HAPPY9" => Ok(EV_KEY::BTN_TRIGGER_HAPPY9), + "BTN_TRIGGER_HAPPY10" => Ok(EV_KEY::BTN_TRIGGER_HAPPY10), + "BTN_TRIGGER_HAPPY11" => Ok(EV_KEY::BTN_TRIGGER_HAPPY11), + "BTN_TRIGGER_HAPPY12" => Ok(EV_KEY::BTN_TRIGGER_HAPPY12), + "BTN_TRIGGER_HAPPY13" => Ok(EV_KEY::BTN_TRIGGER_HAPPY13), + "BTN_TRIGGER_HAPPY14" => Ok(EV_KEY::BTN_TRIGGER_HAPPY14), + "BTN_TRIGGER_HAPPY15" => Ok(EV_KEY::BTN_TRIGGER_HAPPY15), + "BTN_TRIGGER_HAPPY16" => Ok(EV_KEY::BTN_TRIGGER_HAPPY16), + "BTN_TRIGGER_HAPPY17" => Ok(EV_KEY::BTN_TRIGGER_HAPPY17), + "BTN_TRIGGER_HAPPY18" => Ok(EV_KEY::BTN_TRIGGER_HAPPY18), + "BTN_TRIGGER_HAPPY19" => Ok(EV_KEY::BTN_TRIGGER_HAPPY19), + "BTN_TRIGGER_HAPPY20" => Ok(EV_KEY::BTN_TRIGGER_HAPPY20), + "BTN_TRIGGER_HAPPY21" => Ok(EV_KEY::BTN_TRIGGER_HAPPY21), + "BTN_TRIGGER_HAPPY22" => Ok(EV_KEY::BTN_TRIGGER_HAPPY22), + "BTN_TRIGGER_HAPPY23" => Ok(EV_KEY::BTN_TRIGGER_HAPPY23), + "BTN_TRIGGER_HAPPY24" => Ok(EV_KEY::BTN_TRIGGER_HAPPY24), + "BTN_TRIGGER_HAPPY25" => Ok(EV_KEY::BTN_TRIGGER_HAPPY25), + "BTN_TRIGGER_HAPPY26" => Ok(EV_KEY::BTN_TRIGGER_HAPPY26), + "BTN_TRIGGER_HAPPY27" => Ok(EV_KEY::BTN_TRIGGER_HAPPY27), + "BTN_TRIGGER_HAPPY28" => Ok(EV_KEY::BTN_TRIGGER_HAPPY28), + "BTN_TRIGGER_HAPPY29" => Ok(EV_KEY::BTN_TRIGGER_HAPPY29), + "BTN_TRIGGER_HAPPY30" => Ok(EV_KEY::BTN_TRIGGER_HAPPY30), + "BTN_TRIGGER_HAPPY31" => Ok(EV_KEY::BTN_TRIGGER_HAPPY31), + "BTN_TRIGGER_HAPPY32" => Ok(EV_KEY::BTN_TRIGGER_HAPPY32), + "BTN_TRIGGER_HAPPY33" => Ok(EV_KEY::BTN_TRIGGER_HAPPY33), + "BTN_TRIGGER_HAPPY34" => Ok(EV_KEY::BTN_TRIGGER_HAPPY34), + "BTN_TRIGGER_HAPPY35" => Ok(EV_KEY::BTN_TRIGGER_HAPPY35), + "BTN_TRIGGER_HAPPY36" => Ok(EV_KEY::BTN_TRIGGER_HAPPY36), + "BTN_TRIGGER_HAPPY37" => Ok(EV_KEY::BTN_TRIGGER_HAPPY37), + "BTN_TRIGGER_HAPPY38" => Ok(EV_KEY::BTN_TRIGGER_HAPPY38), + "BTN_TRIGGER_HAPPY39" => Ok(EV_KEY::BTN_TRIGGER_HAPPY39), + "BTN_TRIGGER_HAPPY40" => Ok(EV_KEY::BTN_TRIGGER_HAPPY40), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_KEY { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_LED { + LED_NUML = 0, + LED_CAPSL = 1, + LED_SCROLLL = 2, + LED_COMPOSE = 3, + LED_KANA = 4, + LED_SLEEP = 5, + LED_SUSPEND = 6, + LED_MUTE = 7, + LED_MISC = 8, + LED_MAIL = 9, + LED_CHARGING = 10, + LED_MAX = 15, +} + +pub const fn int_to_ev_led(code: u32) -> Option { + match code { + 0 => Some(EV_LED::LED_NUML), + 1 => Some(EV_LED::LED_CAPSL), + 2 => Some(EV_LED::LED_SCROLLL), + 3 => Some(EV_LED::LED_COMPOSE), + 4 => Some(EV_LED::LED_KANA), + 5 => Some(EV_LED::LED_SLEEP), + 6 => Some(EV_LED::LED_SUSPEND), + 7 => Some(EV_LED::LED_MUTE), + 8 => Some(EV_LED::LED_MISC), + 9 => Some(EV_LED::LED_MAIL), + 10 => Some(EV_LED::LED_CHARGING), + 15 => Some(EV_LED::LED_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_LED { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "LED_NUML" => Ok(EV_LED::LED_NUML), + "LED_CAPSL" => Ok(EV_LED::LED_CAPSL), + "LED_SCROLLL" => Ok(EV_LED::LED_SCROLLL), + "LED_COMPOSE" => Ok(EV_LED::LED_COMPOSE), + "LED_KANA" => Ok(EV_LED::LED_KANA), + "LED_SLEEP" => Ok(EV_LED::LED_SLEEP), + "LED_SUSPEND" => Ok(EV_LED::LED_SUSPEND), + "LED_MUTE" => Ok(EV_LED::LED_MUTE), + "LED_MISC" => Ok(EV_LED::LED_MISC), + "LED_MAIL" => Ok(EV_LED::LED_MAIL), + "LED_CHARGING" => Ok(EV_LED::LED_CHARGING), + "LED_MAX" => Ok(EV_LED::LED_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_LED { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_SND { + SND_CLICK = 0, + SND_BELL = 1, + SND_TONE = 2, + SND_MAX = 7, +} + +pub const fn int_to_ev_snd(code: u32) -> Option { + match code { + 0 => Some(EV_SND::SND_CLICK), + 1 => Some(EV_SND::SND_BELL), + 2 => Some(EV_SND::SND_TONE), + 7 => Some(EV_SND::SND_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_SND { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "SND_CLICK" => Ok(EV_SND::SND_CLICK), + "SND_BELL" => Ok(EV_SND::SND_BELL), + "SND_TONE" => Ok(EV_SND::SND_TONE), + "SND_MAX" => Ok(EV_SND::SND_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_SND { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_MSC { + MSC_SERIAL = 0, + MSC_PULSELED = 1, + MSC_GESTURE = 2, + MSC_RAW = 3, + MSC_SCAN = 4, + MSC_TIMESTAMP = 5, + MSC_MAX = 7, +} + +pub const fn int_to_ev_msc(code: u32) -> Option { + match code { + 0 => Some(EV_MSC::MSC_SERIAL), + 1 => Some(EV_MSC::MSC_PULSELED), + 2 => Some(EV_MSC::MSC_GESTURE), + 3 => Some(EV_MSC::MSC_RAW), + 4 => Some(EV_MSC::MSC_SCAN), + 5 => Some(EV_MSC::MSC_TIMESTAMP), + 7 => Some(EV_MSC::MSC_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_MSC { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "MSC_SERIAL" => Ok(EV_MSC::MSC_SERIAL), + "MSC_PULSELED" => Ok(EV_MSC::MSC_PULSELED), + "MSC_GESTURE" => Ok(EV_MSC::MSC_GESTURE), + "MSC_RAW" => Ok(EV_MSC::MSC_RAW), + "MSC_SCAN" => Ok(EV_MSC::MSC_SCAN), + "MSC_TIMESTAMP" => Ok(EV_MSC::MSC_TIMESTAMP), + "MSC_MAX" => Ok(EV_MSC::MSC_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_MSC { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_SW { + SW_LID = 0, + SW_TABLET_MODE = 1, + SW_HEADPHONE_INSERT = 2, + SW_RFKILL_ALL = 3, + SW_MICROPHONE_INSERT = 4, + SW_DOCK = 5, + SW_LINEOUT_INSERT = 6, + SW_JACK_PHYSICAL_INSERT = 7, + SW_VIDEOOUT_INSERT = 8, + SW_CAMERA_LENS_COVER = 9, + SW_KEYPAD_SLIDE = 10, + SW_FRONT_PROXIMITY = 11, + SW_ROTATE_LOCK = 12, + SW_LINEIN_INSERT = 13, + SW_MUTE_DEVICE = 14, + SW_PEN_INSERTED = 15, + SW_MACHINE_COVER = 16, +} + +impl EV_SW { + pub const SW_MAX: EV_SW = EV_SW::SW_MACHINE_COVER; +} + +pub const fn int_to_ev_sw(code: u32) -> Option { + match code { + 0 => Some(EV_SW::SW_LID), + 1 => Some(EV_SW::SW_TABLET_MODE), + 2 => Some(EV_SW::SW_HEADPHONE_INSERT), + 3 => Some(EV_SW::SW_RFKILL_ALL), + 4 => Some(EV_SW::SW_MICROPHONE_INSERT), + 5 => Some(EV_SW::SW_DOCK), + 6 => Some(EV_SW::SW_LINEOUT_INSERT), + 7 => Some(EV_SW::SW_JACK_PHYSICAL_INSERT), + 8 => Some(EV_SW::SW_VIDEOOUT_INSERT), + 9 => Some(EV_SW::SW_CAMERA_LENS_COVER), + 10 => Some(EV_SW::SW_KEYPAD_SLIDE), + 11 => Some(EV_SW::SW_FRONT_PROXIMITY), + 12 => Some(EV_SW::SW_ROTATE_LOCK), + 13 => Some(EV_SW::SW_LINEIN_INSERT), + 14 => Some(EV_SW::SW_MUTE_DEVICE), + 15 => Some(EV_SW::SW_PEN_INSERTED), + 16 => Some(EV_SW::SW_MACHINE_COVER), + _ => None, + } +} + +impl std::str::FromStr for EV_SW { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "SW_LID" => Ok(EV_SW::SW_LID), + "SW_TABLET_MODE" => Ok(EV_SW::SW_TABLET_MODE), + "SW_HEADPHONE_INSERT" => Ok(EV_SW::SW_HEADPHONE_INSERT), + "SW_RFKILL_ALL" => Ok(EV_SW::SW_RFKILL_ALL), + "SW_MICROPHONE_INSERT" => Ok(EV_SW::SW_MICROPHONE_INSERT), + "SW_DOCK" => Ok(EV_SW::SW_DOCK), + "SW_LINEOUT_INSERT" => Ok(EV_SW::SW_LINEOUT_INSERT), + "SW_JACK_PHYSICAL_INSERT" => Ok(EV_SW::SW_JACK_PHYSICAL_INSERT), + "SW_VIDEOOUT_INSERT" => Ok(EV_SW::SW_VIDEOOUT_INSERT), + "SW_CAMERA_LENS_COVER" => Ok(EV_SW::SW_CAMERA_LENS_COVER), + "SW_KEYPAD_SLIDE" => Ok(EV_SW::SW_KEYPAD_SLIDE), + "SW_FRONT_PROXIMITY" => Ok(EV_SW::SW_FRONT_PROXIMITY), + "SW_ROTATE_LOCK" => Ok(EV_SW::SW_ROTATE_LOCK), + "SW_LINEIN_INSERT" => Ok(EV_SW::SW_LINEIN_INSERT), + "SW_MUTE_DEVICE" => Ok(EV_SW::SW_MUTE_DEVICE), + "SW_PEN_INSERTED" => Ok(EV_SW::SW_PEN_INSERTED), + "SW_MACHINE_COVER" => Ok(EV_SW::SW_MACHINE_COVER), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_SW { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_SYN { + SYN_REPORT = 0, + SYN_CONFIG = 1, + SYN_MT_REPORT = 2, + SYN_DROPPED = 3, + SYN_MAX = 15, +} + +pub const fn int_to_ev_syn(code: u32) -> Option { + match code { + 0 => Some(EV_SYN::SYN_REPORT), + 1 => Some(EV_SYN::SYN_CONFIG), + 2 => Some(EV_SYN::SYN_MT_REPORT), + 3 => Some(EV_SYN::SYN_DROPPED), + 15 => Some(EV_SYN::SYN_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_SYN { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "SYN_REPORT" => Ok(EV_SYN::SYN_REPORT), + "SYN_CONFIG" => Ok(EV_SYN::SYN_CONFIG), + "SYN_MT_REPORT" => Ok(EV_SYN::SYN_MT_REPORT), + "SYN_DROPPED" => Ok(EV_SYN::SYN_DROPPED), + "SYN_MAX" => Ok(EV_SYN::SYN_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_SYN { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_REP { + REP_DELAY = 0, + REP_PERIOD = 1, +} + +impl EV_REP { + pub const REP_MAX: EV_REP = EV_REP::REP_PERIOD; +} + +pub const fn int_to_ev_rep(code: u32) -> Option { + match code { + 0 => Some(EV_REP::REP_DELAY), + 1 => Some(EV_REP::REP_PERIOD), + _ => None, + } +} + +impl std::str::FromStr for EV_REP { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "REP_DELAY" => Ok(EV_REP::REP_DELAY), + "REP_PERIOD" => Ok(EV_REP::REP_PERIOD), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_REP { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum InputProp { + INPUT_PROP_POINTER = 0, + INPUT_PROP_DIRECT = 1, + INPUT_PROP_BUTTONPAD = 2, + INPUT_PROP_SEMI_MT = 3, + INPUT_PROP_TOPBUTTONPAD = 4, + INPUT_PROP_POINTING_STICK = 5, + INPUT_PROP_ACCELEROMETER = 6, + INPUT_PROP_MAX = 31, +} + +pub const fn int_to_input_prop(code: u32) -> Option { + match code { + 0 => Some(InputProp::INPUT_PROP_POINTER), + 1 => Some(InputProp::INPUT_PROP_DIRECT), + 2 => Some(InputProp::INPUT_PROP_BUTTONPAD), + 3 => Some(InputProp::INPUT_PROP_SEMI_MT), + 4 => Some(InputProp::INPUT_PROP_TOPBUTTONPAD), + 5 => Some(InputProp::INPUT_PROP_POINTING_STICK), + 6 => Some(InputProp::INPUT_PROP_ACCELEROMETER), + 31 => Some(InputProp::INPUT_PROP_MAX), + _ => None, + } +} + +impl std::str::FromStr for InputProp { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "INPUT_PROP_POINTER" => Ok(InputProp::INPUT_PROP_POINTER), + "INPUT_PROP_DIRECT" => Ok(InputProp::INPUT_PROP_DIRECT), + "INPUT_PROP_BUTTONPAD" => Ok(InputProp::INPUT_PROP_BUTTONPAD), + "INPUT_PROP_SEMI_MT" => Ok(InputProp::INPUT_PROP_SEMI_MT), + "INPUT_PROP_TOPBUTTONPAD" => Ok(InputProp::INPUT_PROP_TOPBUTTONPAD), + "INPUT_PROP_POINTING_STICK" => Ok(InputProp::INPUT_PROP_POINTING_STICK), + "INPUT_PROP_ACCELEROMETER" => Ok(InputProp::INPUT_PROP_ACCELEROMETER), + "INPUT_PROP_MAX" => Ok(InputProp::INPUT_PROP_MAX), + _ => Err(()), + } + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_FF { + FF_STATUS_STOPPED = 0, + FF_STATUS_PLAYING = 1, + FF_RUMBLE = 80, + FF_PERIODIC = 81, + FF_CONSTANT = 82, + FF_SPRING = 83, + FF_FRICTION = 84, + FF_DAMPER = 85, + FF_INERTIA = 86, + FF_RAMP = 87, + FF_SQUARE = 88, + FF_TRIANGLE = 89, + FF_SINE = 90, + FF_SAW_UP = 91, + FF_SAW_DOWN = 92, + FF_CUSTOM = 93, + FF_GAIN = 96, + FF_AUTOCENTER = 97, + FF_MAX = 127, +} + +impl EV_FF { + pub const FF_STATUS_MAX: EV_FF = EV_FF::FF_STATUS_PLAYING; +} + +pub const fn int_to_ev_ff(code: u32) -> Option { + match code { + 0 => Some(EV_FF::FF_STATUS_STOPPED), + 1 => Some(EV_FF::FF_STATUS_PLAYING), + 80 => Some(EV_FF::FF_RUMBLE), + 81 => Some(EV_FF::FF_PERIODIC), + 82 => Some(EV_FF::FF_CONSTANT), + 83 => Some(EV_FF::FF_SPRING), + 84 => Some(EV_FF::FF_FRICTION), + 85 => Some(EV_FF::FF_DAMPER), + 86 => Some(EV_FF::FF_INERTIA), + 87 => Some(EV_FF::FF_RAMP), + 88 => Some(EV_FF::FF_SQUARE), + 89 => Some(EV_FF::FF_TRIANGLE), + 90 => Some(EV_FF::FF_SINE), + 91 => Some(EV_FF::FF_SAW_UP), + 92 => Some(EV_FF::FF_SAW_DOWN), + 93 => Some(EV_FF::FF_CUSTOM), + 96 => Some(EV_FF::FF_GAIN), + 97 => Some(EV_FF::FF_AUTOCENTER), + 127 => Some(EV_FF::FF_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_FF { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "FF_STATUS_STOPPED" => Ok(EV_FF::FF_STATUS_STOPPED), + "FF_STATUS_PLAYING" => Ok(EV_FF::FF_STATUS_PLAYING), + "FF_RUMBLE" => Ok(EV_FF::FF_RUMBLE), + "FF_PERIODIC" => Ok(EV_FF::FF_PERIODIC), + "FF_CONSTANT" => Ok(EV_FF::FF_CONSTANT), + "FF_SPRING" => Ok(EV_FF::FF_SPRING), + "FF_FRICTION" => Ok(EV_FF::FF_FRICTION), + "FF_DAMPER" => Ok(EV_FF::FF_DAMPER), + "FF_INERTIA" => Ok(EV_FF::FF_INERTIA), + "FF_RAMP" => Ok(EV_FF::FF_RAMP), + "FF_SQUARE" => Ok(EV_FF::FF_SQUARE), + "FF_TRIANGLE" => Ok(EV_FF::FF_TRIANGLE), + "FF_SINE" => Ok(EV_FF::FF_SINE), + "FF_SAW_UP" => Ok(EV_FF::FF_SAW_UP), + "FF_SAW_DOWN" => Ok(EV_FF::FF_SAW_DOWN), + "FF_CUSTOM" => Ok(EV_FF::FF_CUSTOM), + "FF_GAIN" => Ok(EV_FF::FF_GAIN), + "FF_AUTOCENTER" => Ok(EV_FF::FF_AUTOCENTER), + "FF_MAX" => Ok(EV_FF::FF_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_FF { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum BusType { + BUS_PCI = 1, + BUS_ISAPNP = 2, + BUS_USB = 3, + BUS_HIL = 4, + BUS_BLUETOOTH = 5, + BUS_VIRTUAL = 6, + BUS_ISA = 16, + BUS_I8042 = 17, + BUS_XTKBD = 18, + BUS_RS232 = 19, + BUS_GAMEPORT = 20, + BUS_PARPORT = 21, + BUS_AMIGA = 22, + BUS_ADB = 23, + BUS_I2C = 24, + BUS_HOST = 25, + BUS_GSC = 26, + BUS_ATARI = 27, + BUS_SPI = 28, + BUS_RMI = 29, + BUS_CEC = 30, + BUS_INTEL_ISHTP = 31, + BUS_AMD_SFH = 32, +} + +pub const fn int_to_bus_type(code: u32) -> Option { + match code { + 1 => Some(BusType::BUS_PCI), + 2 => Some(BusType::BUS_ISAPNP), + 3 => Some(BusType::BUS_USB), + 4 => Some(BusType::BUS_HIL), + 5 => Some(BusType::BUS_BLUETOOTH), + 6 => Some(BusType::BUS_VIRTUAL), + 16 => Some(BusType::BUS_ISA), + 17 => Some(BusType::BUS_I8042), + 18 => Some(BusType::BUS_XTKBD), + 19 => Some(BusType::BUS_RS232), + 20 => Some(BusType::BUS_GAMEPORT), + 21 => Some(BusType::BUS_PARPORT), + 22 => Some(BusType::BUS_AMIGA), + 23 => Some(BusType::BUS_ADB), + 24 => Some(BusType::BUS_I2C), + 25 => Some(BusType::BUS_HOST), + 26 => Some(BusType::BUS_GSC), + 27 => Some(BusType::BUS_ATARI), + 28 => Some(BusType::BUS_SPI), + 29 => Some(BusType::BUS_RMI), + 30 => Some(BusType::BUS_CEC), + 31 => Some(BusType::BUS_INTEL_ISHTP), + 32 => Some(BusType::BUS_AMD_SFH), + _ => None, + } +} + +impl std::str::FromStr for BusType { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "BUS_PCI" => Ok(BusType::BUS_PCI), + "BUS_ISAPNP" => Ok(BusType::BUS_ISAPNP), + "BUS_USB" => Ok(BusType::BUS_USB), + "BUS_HIL" => Ok(BusType::BUS_HIL), + "BUS_BLUETOOTH" => Ok(BusType::BUS_BLUETOOTH), + "BUS_VIRTUAL" => Ok(BusType::BUS_VIRTUAL), + "BUS_ISA" => Ok(BusType::BUS_ISA), + "BUS_I8042" => Ok(BusType::BUS_I8042), + "BUS_XTKBD" => Ok(BusType::BUS_XTKBD), + "BUS_RS232" => Ok(BusType::BUS_RS232), + "BUS_GAMEPORT" => Ok(BusType::BUS_GAMEPORT), + "BUS_PARPORT" => Ok(BusType::BUS_PARPORT), + "BUS_AMIGA" => Ok(BusType::BUS_AMIGA), + "BUS_ADB" => Ok(BusType::BUS_ADB), + "BUS_I2C" => Ok(BusType::BUS_I2C), + "BUS_HOST" => Ok(BusType::BUS_HOST), + "BUS_GSC" => Ok(BusType::BUS_GSC), + "BUS_ATARI" => Ok(BusType::BUS_ATARI), + "BUS_SPI" => Ok(BusType::BUS_SPI), + "BUS_RMI" => Ok(BusType::BUS_RMI), + "BUS_CEC" => Ok(BusType::BUS_CEC), + "BUS_INTEL_ISHTP" => Ok(BusType::BUS_INTEL_ISHTP), + "BUS_AMD_SFH" => Ok(BusType::BUS_AMD_SFH), + _ => Err(()), + } + } +} + +impl std::fmt::Display for BusType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/evdev/src/main/rust/evdev/src/lib.rs b/evdev/src/main/rust/evdev/src/lib.rs new file mode 100644 index 0000000000..d2ed7a9196 --- /dev/null +++ b/evdev/src/main/rust/evdev/src/lib.rs @@ -0,0 +1,238 @@ +//! This crate is from evdev_rs https://github.com/ndesh26/evdev-rs. +//! It is copied so I can compile libevdev against the Android NDK and so that +//! a libevdev submodule isn't required. Also so more complex build steps can be removed. +//! Rust bindings to libevdev, a wrapper for evdev devices. + +#![allow(unused_imports, dead_code, unused_assignments)] + +#[macro_use] +mod macros; + +mod device; +pub mod enums; +mod libevdev; +mod uinput; +pub mod util; + +use bitflags::bitflags; +use libc::{c_uint, suseconds_t, time_t}; +use std::convert::{TryFrom, TryInto}; +use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH}; + +use enums::*; +use util::*; + +pub use util::EventCodeIterator; +pub use util::EventTypeIterator; +pub use util::InputPropIterator; + +#[doc(inline)] +pub use device::Device; +#[doc(inline)] +pub use device::DeviceWrapper; +#[doc(inline)] +pub use device::Enable; +#[doc(inline)] +pub use device::EnableCodeData; +#[doc(inline)] +pub use device::UninitDevice; +#[doc(inline)] +pub use uinput::UInputDevice; + +pub enum GrabMode { + /// Grab the device if not currently grabbed + Grab = libevdev::LIBEVDEV_GRAB as isize, + /// Ungrab the device if currently grabbed + Ungrab = libevdev::LIBEVDEV_UNGRAB as isize, +} + +bitflags! { +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] + pub struct ReadFlag: u32 { + /// Process data in sync mode + const SYNC = 1; + /// Process data in normal mode + const NORMAL = 2; + /// Pretend the next event is a SYN_DROPPED and require the + /// caller to sync + const FORCE_SYNC = 4; + /// The fd is not in O_NONBLOCK and a read may block + const BLOCKING = 8; + } +} + +#[derive(PartialEq)] +pub enum ReadStatus { + /// `next_event` has finished without an error and an event is available + /// for processing. + Success = libevdev::LIBEVDEV_READ_STATUS_SUCCESS as isize, + /// Depending on the `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. + Sync = libevdev::LIBEVDEV_READ_STATUS_SYNC as isize, +} + +pub enum LedState { + /// Turn the LED on + On = libevdev::LIBEVDEV_LED_ON as isize, + /// Turn the LED off + Off = libevdev::LIBEVDEV_LED_OFF as isize, +} + +#[derive(Debug)] +pub struct DeviceId { + pub bustype: BusType, + pub vendor: u16, + pub product: u16, + pub version: u16, +} + +#[derive(Clone, Copy, Debug)] +/// used by EVIOCGABS/EVIOCSABS ioctls +pub struct AbsInfo { + /// latest reported value for the axis + pub value: i32, + /// specifies minimum value for the axis + pub minimum: i32, + /// specifies maximum value for the axis + pub maximum: i32, + /// specifies fuzz value that is used to filter noise from + /// the event stream + pub fuzz: i32, + /// values that are within this value will be discarded by + /// joydev interface and reported as 0 instead + pub flat: i32, + /// specifies resolution for the values reported for + /// the axis + pub resolution: i32, +} + +impl AbsInfo { + pub const fn from_raw(absinfo: libevdev::input_absinfo) -> AbsInfo { + AbsInfo { + value: absinfo.value, + minimum: absinfo.minimum, + maximum: absinfo.maximum, + fuzz: absinfo.fuzz, + flat: absinfo.flat, + resolution: absinfo.resolution, + } + } + + pub const fn as_raw(&self) -> libevdev::input_absinfo { + libevdev::input_absinfo { + value: self.value, + minimum: self.minimum, + maximum: self.maximum, + fuzz: self.fuzz, + flat: self.flat, + resolution: self.resolution, + } + } +} + +#[derive(Copy, Clone, Eq, Hash, PartialOrd, Ord, Debug, PartialEq)] +pub struct TimeVal { + pub tv_sec: time_t, + pub tv_usec: suseconds_t, +} + +impl TryFrom for TimeVal { + type Error = SystemTimeError; + fn try_from(system_time: SystemTime) -> Result { + let d = system_time.duration_since(UNIX_EPOCH)?; + Ok(TimeVal { + tv_sec: d.as_secs() as time_t, + tv_usec: d.subsec_micros() as suseconds_t, + }) + } +} + +impl TryInto for TimeVal { + type Error = (); + /// Fails if TimeVal.tv_usec is >= 10^6 or if the TimeVal is outside + /// the range of SystemTime + fn try_into(self) -> Result { + let secs = self.tv_sec.try_into().map_err(|_| ())?; + let nanos = (self.tv_usec * 1000).try_into().map_err(|_| ())?; + let duration = Duration::new(secs, nanos); + UNIX_EPOCH.checked_add(duration).ok_or(()) + } +} + +impl TimeVal { + pub const fn new(tv_sec: time_t, tv_usec: suseconds_t) -> TimeVal { + const MICROS_PER_SEC: suseconds_t = 1_000_000; + TimeVal { + tv_sec: tv_sec + (tv_usec / MICROS_PER_SEC) as time_t, + tv_usec: tv_usec % MICROS_PER_SEC, + } + } + + pub const fn from_raw(timeval: &libc::timeval) -> TimeVal { + TimeVal { + tv_sec: timeval.tv_sec, + tv_usec: timeval.tv_usec, + } + } + + pub const fn as_raw(&self) -> libc::timeval { + libc::timeval { + tv_sec: self.tv_sec, + tv_usec: self.tv_usec, + } + } +} + +/// The event structure itself +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct InputEvent { + /// The time at which event occurred + pub time: TimeVal, + pub event_code: EventCode, + pub value: i32, +} + +impl InputEvent { + pub const fn new(timeval: &TimeVal, code: &EventCode, value: i32) -> InputEvent { + InputEvent { + time: *timeval, + event_code: *code, + value, + } + } + + pub fn event_type(&self) -> Option { + int_to_event_type(event_code_to_int(&self.event_code).0) + } + + pub fn from_raw(event: &libevdev::input_event) -> InputEvent { + let ev_type = event.type_ as u32; + let event_code = int_to_event_code(ev_type, event.code as u32); + InputEvent { + time: TimeVal::from_raw(&event.time), + event_code, + value: event.value, + } + } + + pub fn as_raw(&self) -> libevdev::input_event { + let (ev_type, ev_code) = event_code_to_int(&self.event_code); + libevdev::input_event { + time: self.time.as_raw(), + type_: ev_type as u16, + code: ev_code as u16, + value: self.value, + } + } + + pub fn is_type(&self, ev_type: &EventType) -> bool { + unsafe { libevdev::libevdev_event_is_type(&self.as_raw(), *ev_type as c_uint) == 1 } + } + + pub fn is_code(&self, code: &EventCode) -> bool { + let (ev_type, ev_code) = event_code_to_int(code); + + unsafe { libevdev::libevdev_event_is_code(&self.as_raw(), ev_type, ev_code) == 1 } + } +} diff --git a/evdev/src/main/rust/evdev/src/libevdev.rs b/evdev/src/main/rust/evdev/src/libevdev.rs new file mode 100644 index 0000000000..caa6a7ab79 --- /dev/null +++ b/evdev/src/main/rust/evdev/src/libevdev.rs @@ -0,0 +1,187 @@ +#![allow(bad_style)] +#![allow(dead_code)] +#![allow(improper_ctypes)] + +pub use libc::timeval; +use libc::{c_char, c_int, c_uint, c_ushort, c_void, size_t}; + +pub type __enum_ty = c_int; +pub type libevdev_read_flag = __enum_ty; +pub type libevdev_log_priority = __enum_ty; +pub type libevdev_grab_mode = __enum_ty; +pub type libevdev_read_status = __enum_ty; +pub type libevdev_led_value = __enum_ty; +pub type libevdev_uinput_open_mode = __enum_ty; + +pub const LIBEVDEV_READ_FLAG_SYNC: libevdev_read_flag = 1; +pub const LIBEVDEV_READ_FLAG_NORMAL: libevdev_read_flag = 2; +pub const LIBEVDEV_READ_FLAG_FORCE_SYNC: libevdev_read_flag = 4; +pub const LIBEVDEV_READ_FLAG_BLOCKING: libevdev_read_flag = 8; + +pub const LIBEVDEV_LOG_ERROR: libevdev_log_priority = 10; +pub const LIBEVDEV_LOG_INFO: libevdev_log_priority = 20; +pub const LIBEVDEV_LOG_DEBUG: libevdev_log_priority = 30; + +pub const LIBEVDEV_GRAB: libevdev_grab_mode = 3; +pub const LIBEVDEV_UNGRAB: libevdev_grab_mode = 4; + +pub const LIBEVDEV_READ_STATUS_SUCCESS: libevdev_read_status = 0; +pub const LIBEVDEV_READ_STATUS_SYNC: libevdev_read_status = 1; + +pub const LIBEVDEV_LED_ON: libevdev_led_value = 3; +pub const LIBEVDEV_LED_OFF: libevdev_led_value = 4; + +pub const LIBEVDEV_UINPUT_OPEN_MANAGED: libevdev_uinput_open_mode = -2; + +pub enum libevdev {} + +pub enum libevdev_uinput {} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct input_event { + pub time: timeval, + pub type_: c_ushort, + pub code: c_ushort, + pub value: c_int, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct input_absinfo { + pub value: c_int, + pub minimum: c_int, + pub maximum: c_int, + pub fuzz: c_int, + pub flat: c_int, + pub resolution: c_int, +} + +extern "C" { + pub fn libevdev_new() -> *mut libevdev; + pub fn libevdev_new_from_fd(fd: c_int, ctx: *mut *mut libevdev) -> c_int; + pub fn libevdev_free(ctx: *mut libevdev); + pub fn libevdev_grab(ctx: *mut libevdev, grab: libevdev_grab_mode) -> c_int; + pub fn libevdev_set_fd(ctx: *mut libevdev, fd: c_int) -> c_int; + pub fn libevdev_change_fd(ctx: *mut libevdev, fd: c_int) -> c_int; + pub fn libevdev_get_fd(ctx: *mut libevdev) -> c_int; + pub fn libevdev_next_event(ctx: *mut libevdev, flags: c_uint, ev: *mut input_event) -> c_int; + pub fn libevdev_has_event_pending(ctx: *mut libevdev) -> c_int; + pub fn libevdev_get_name(ctx: *const libevdev) -> *const c_char; + pub fn libevdev_set_name(ctx: *mut libevdev, name: *const c_char); + pub fn libevdev_get_phys(ctx: *const libevdev) -> *const c_char; + pub fn libevdev_set_phys(ctx: *mut libevdev, phys: *const c_char); + pub fn libevdev_get_uniq(ctx: *const libevdev) -> *const c_char; + pub fn libevdev_set_uniq(ctx: *mut libevdev, uniq: *const c_char); + pub fn libevdev_get_id_product(ctx: *const libevdev) -> c_int; + pub fn libevdev_set_id_product(ctx: *mut libevdev, product_id: c_int); + pub fn libevdev_get_id_vendor(ctx: *const libevdev) -> c_int; + pub fn libevdev_set_id_vendor(ctx: *mut libevdev, vendor_id: c_int); + pub fn libevdev_get_id_bustype(ctx: *const libevdev) -> c_int; + pub fn libevdev_set_id_bustype(ctx: *mut libevdev, bustype: c_int); + pub fn libevdev_get_id_version(ctx: *const libevdev) -> c_int; + pub fn libevdev_set_id_version(ctx: *mut libevdev, version: c_int); + pub fn libevdev_get_driver_version(ctx: *const libevdev) -> c_int; + pub fn libevdev_has_property(ctx: *const libevdev, prop: c_uint) -> c_int; + pub fn libevdev_enable_property(ctx: *mut libevdev, prop: c_uint) -> c_int; + pub fn libevdev_disable_property(ctx: *mut libevdev, prop: c_uint) -> c_int; + pub fn libevdev_has_event_type(ctx: *const libevdev, type_: c_uint) -> c_int; + pub fn libevdev_has_event_code(ctx: *const libevdev, type_: c_uint, code: c_uint) -> c_int; + pub fn libevdev_get_abs_minimum(ctx: *const libevdev, code: c_uint) -> c_int; + pub fn libevdev_get_abs_maximum(ctx: *const libevdev, code: c_uint) -> c_int; + pub fn libevdev_get_abs_fuzz(ctx: *const libevdev, code: c_uint) -> c_int; + pub fn libevdev_get_abs_flat(ctx: *const libevdev, code: c_uint) -> c_int; + pub fn libevdev_get_abs_resolution(ctx: *const libevdev, code: c_uint) -> c_int; + pub fn libevdev_get_abs_info(ctx: *const libevdev, code: c_uint) -> *const input_absinfo; + pub fn libevdev_get_event_value(ctx: *const libevdev, type_: c_uint, code: c_uint) -> c_int; + pub fn libevdev_set_event_value( + ctx: *mut libevdev, + type_: c_uint, + code: c_uint, + value: c_int, + ) -> c_int; + pub fn libevdev_fetch_event_value( + ctx: *const libevdev, + type_: c_uint, + code: c_uint, + value: *mut c_int, + ) -> c_int; + pub fn libevdev_get_slot_value(ctx: *const libevdev, slot: c_uint, code: c_uint) -> c_int; + pub fn libevdev_set_slot_value( + ctx: *mut libevdev, + slot: c_uint, + code: c_uint, + value: c_int, + ) -> c_int; + pub fn libevdev_fetch_slot_value( + ctx: *const libevdev, + slot: c_uint, + code: c_uint, + value: *mut c_int, + ) -> c_int; + pub fn libevdev_get_num_slots(ctx: *const libevdev) -> c_int; + pub fn libevdev_get_current_slot(ctx: *const libevdev) -> c_int; + pub fn libevdev_set_abs_minimum(ctx: *mut libevdev, code: c_uint, min: c_int); + pub fn libevdev_set_abs_maximum(ctx: *mut libevdev, code: c_uint, max: c_int); + pub fn libevdev_set_abs_fuzz(ctx: *mut libevdev, code: c_uint, fuzz: c_int); + pub fn libevdev_set_abs_flat(ctx: *mut libevdev, code: c_uint, flat: c_int); + pub fn libevdev_set_abs_resolution(ctx: *mut libevdev, code: c_uint, resolution: c_int); + pub fn libevdev_set_abs_info(ctx: *mut libevdev, code: c_uint, abs: *const input_absinfo); + pub fn libevdev_enable_event_type(ctx: *mut libevdev, type_: c_uint) -> c_int; + pub fn libevdev_disable_event_type(ctx: *mut libevdev, type_: c_uint) -> c_int; + pub fn libevdev_enable_event_code( + ctx: *mut libevdev, + type_: c_uint, + code: c_uint, + data: *const c_void, + ) -> c_int; + pub fn libevdev_disable_event_code(ctx: *mut libevdev, type_: c_uint, code: c_uint) -> c_int; + pub fn libevdev_kernel_set_abs_info( + ctx: *mut libevdev, + code: c_uint, + abs: *const input_absinfo, + ) -> c_int; + pub fn libevdev_kernel_set_led_value( + ctx: *mut libevdev, + code: c_uint, + value: libevdev_led_value, + ) -> c_int; + pub fn libevdev_kernel_set_led_values(ctx: *mut libevdev, ...) -> c_int; + pub fn libevdev_set_clock_id(ctx: *mut libevdev, clockid: c_int) -> c_int; + pub fn libevdev_event_is_type(ev: *const input_event, type_: c_uint) -> c_int; + pub fn libevdev_event_is_code(ev: *const input_event, type_: c_uint, code: c_uint) -> c_int; + pub fn libevdev_event_type_get_name(type_: c_uint) -> *const c_char; + pub fn libevdev_event_code_get_name(type_: c_uint, code: c_uint) -> *const c_char; + pub fn libevdev_property_get_name(prop: c_uint) -> *const c_char; + pub fn libevdev_event_type_get_max(type_: c_uint) -> c_int; + pub fn libevdev_event_type_from_name(name: *const c_char) -> c_int; + pub fn libevdev_event_type_from_name_n(name: *const c_char, len: size_t) -> c_int; + pub fn libevdev_event_code_from_name(type_: c_uint, name: *const c_char) -> c_int; + pub fn libevdev_event_code_from_name_n( + type_: c_uint, + name: *const c_char, + len: size_t, + ) -> c_int; + pub fn libevdev_property_from_name(name: *const c_char) -> c_int; + pub fn libevdev_property_from_name_n(name: *const c_char, len: size_t) -> c_int; + pub fn libevdev_get_repeat( + ctx: *const libevdev, + delay: *mut c_int, + period: *mut c_int, + ) -> c_int; + pub fn libevdev_uinput_create_from_device( + ctx: *const libevdev, + uinput_fd: c_int, + uinput_dev: *mut *mut libevdev_uinput, + ) -> c_int; + pub fn libevdev_uinput_destroy(uinput_dev: *mut libevdev_uinput); + pub fn libevdev_uinput_get_devnode(uinput_dev: *mut libevdev_uinput) -> *const c_char; + pub fn libevdev_uinput_get_fd(uinput_dev: *const libevdev_uinput) -> c_int; + pub fn libevdev_uinput_get_syspath(uinput_dev: *mut libevdev_uinput) -> *const c_char; + pub fn libevdev_uinput_write_event( + uinput_dev: *const libevdev_uinput, + type_: c_uint, + code: c_uint, + value: c_int, + ) -> c_int; +} diff --git a/evdev/src/main/rust/evdev/src/macros.rs b/evdev/src/main/rust/evdev/src/macros.rs new file mode 100644 index 0000000000..27bc575fed --- /dev/null +++ b/evdev/src/main/rust/evdev/src/macros.rs @@ -0,0 +1,81 @@ +macro_rules! string_getter { + ( $( #[$doc:meta], $func_name:ident, $c_func: ident ),* ) => { + $( + #[$doc] + fn $func_name (&self) -> Option<&str> { + unsafe { + ptr_to_str(libevdev::$c_func(self.raw())) + } + } + )* + }; +} + +macro_rules! string_setter { + ( $( $func_name:ident, $c_func: ident ),* ) => { + $( + fn $func_name (&self, field: &str) { + let field = CString::new(field).unwrap(); + unsafe { + libevdev::$c_func(self.raw(), field.as_ptr()) + } + } + )* + }; +} + +macro_rules! product_getter { + ( $( $func_name:ident, $c_func: ident ),* ) => { + $( + fn $func_name (&self) -> u16 { + unsafe { + libevdev::$c_func(self.raw()) as u16 + } + } + )* + }; +} + +macro_rules! product_setter { + ( $( $func_name:ident, $c_func: ident ),* ) => { + $( + fn $func_name (&self, field: u16) { + unsafe { + libevdev::$c_func(self.raw(), field as c_int); + } + } + )* + }; +} + +macro_rules! abs_getter { + ( $( $func_name:ident, $c_func: ident ),* ) => { + $( + fn $func_name (&self, + code: u32) -> std::io::Result { + let result = unsafe { + libevdev::$c_func(self.raw(), code as c_uint) as i32 + }; + + match result { + 0 => Err(std::io::Error::from_raw_os_error(0)), + k => Ok(k) + } + } + )* + }; +} + +macro_rules! abs_setter { + ( $( $func_name:ident, $c_func: ident ),* ) => { + $( + fn $func_name (&self, + code: u32, + val: i32) { + unsafe { + libevdev::$c_func(self.raw(), code as c_uint, val as c_int); + } + } + )* + }; +} \ No newline at end of file diff --git a/evdev/src/main/rust/evdev/src/uinput.rs b/evdev/src/main/rust/evdev/src/uinput.rs new file mode 100644 index 0000000000..d140ddf084 --- /dev/null +++ b/evdev/src/main/rust/evdev/src/uinput.rs @@ -0,0 +1,122 @@ +use crate::enums::{EventCode, EventType, EV_SYN}; +use crate::util::*; +use crate::{device::DeviceWrapper, InputEvent}; +use libc::{c_int, c_uint}; +use std::io; +use std::os::unix::io::RawFd; + +use crate::libevdev; + +/// Opaque struct representing an evdev uinput device +pub struct UInputDevice { + raw: *mut libevdev::libevdev_uinput, +} + +unsafe impl Sync for UInputDevice {} +unsafe impl Send for UInputDevice {} + +impl UInputDevice { + fn raw(&self) -> *mut libevdev::libevdev_uinput { + self.raw + } + + /// 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. + pub fn create_from_device(device: &T) -> io::Result { + let mut libevdev_uinput = std::ptr::null_mut(); + let result = unsafe { + libevdev::libevdev_uinput_create_from_device( + device.raw(), + libevdev::LIBEVDEV_UINPUT_OPEN_MANAGED, + &mut libevdev_uinput, + ) + }; + + match result { + 0 => Ok(UInputDevice { + raw: libevdev_uinput, + }), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + ///Return the device node representing this uinput device. + /// + /// This relies on `libevdev_uinput_get_syspath()` to provide a valid syspath. + pub fn devnode(&self) -> Option<&str> { + unsafe { ptr_to_str(libevdev::libevdev_uinput_get_devnode(self.raw())) } + } + + ///Return the syspath representing this uinput device. + /// + /// If the UI_GET_SYSNAME ioctl 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(). + pub fn syspath(&self) -> Option<&str> { + unsafe { ptr_to_str(libevdev::libevdev_uinput_get_syspath(self.raw())) } + } + + /// 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. + pub fn as_fd(&self) -> Option { + match unsafe { libevdev::libevdev_uinput_get_fd(self.raw()) } { + 0 => None, + result => Some(result), + } + } + + #[deprecated( + since = "0.5.0", + note = "Prefer `as_fd`. Some function names were changed so they + more closely match their type signature. See issue 42 for discussion + https://github.com/ndesh26/evdev-rs/issues/42" + )] + pub fn fd(&self) -> Option { + self.as_fd() + } + + /// 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. + pub fn write_event(&self, event_type: u32, code: u32, value: i32) -> io::Result<()> { + let result = + unsafe { libevdev::libevdev_uinput_write_event(self.raw(), event_type, code, value) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + pub fn write_syn_event(&self, code: EV_SYN) -> io::Result<()> { + let (event_type, event_code) = event_code_to_int(&EventCode::EV_SYN(code)); + + self.write_event(event_type, event_code, 0) + } +} + +impl Drop for UInputDevice { + fn drop(&mut self) { + unsafe { + libevdev::libevdev_uinput_destroy(self.raw()); + } + } +} + +impl std::fmt::Debug for UInputDevice { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("UInputDevice") + .field("devnode", &self.devnode()) + .finish() + } +} diff --git a/evdev/src/main/rust/evdev/src/util.rs b/evdev/src/main/rust/evdev/src/util.rs new file mode 100644 index 0000000000..f74fb7c84c --- /dev/null +++ b/evdev/src/main/rust/evdev/src/util.rs @@ -0,0 +1,435 @@ +use crate::enums::*; +use crate::libevdev as raw; +use libc::{c_char, c_uint}; +use log; +use log::warn; +use std::ffi::{CStr, CString}; +use std::fmt; + +pub(crate) unsafe fn ptr_to_str(ptr: *const c_char) -> Option<&'static str> { + let slice = CStr::from_ptr(ptr.as_ref()?); + let buf = slice.to_bytes(); + std::str::from_utf8(buf).ok() +} + +pub struct EventTypeIterator { + current: EventType, +} + +pub struct EventCodeIterator { + current: EventCode, +} + +pub struct InputPropIterator { + current: InputProp, +} + +impl EventTypeIterator { + pub fn new() -> Self { + EventTypeIterator { + current: EventType::EV_SYN, + } + } +} + +impl EventCodeIterator { + pub fn new(event_type: &EventType) -> Self { + let event_code = match *event_type { + EventType::EV_SYN => EventCode::EV_SYN(EV_SYN::SYN_REPORT), + EventType::EV_KEY => EventCode::EV_KEY(EV_KEY::KEY_RESERVED), + EventType::EV_REL => EventCode::EV_REL(EV_REL::REL_X), + EventType::EV_ABS => EventCode::EV_ABS(EV_ABS::ABS_X), + EventType::EV_MSC => EventCode::EV_MSC(EV_MSC::MSC_SERIAL), + EventType::EV_SW => EventCode::EV_SW(EV_SW::SW_LID), + EventType::EV_LED => EventCode::EV_LED(EV_LED::LED_NUML), + EventType::EV_SND => EventCode::EV_SND(EV_SND::SND_CLICK), + EventType::EV_REP => EventCode::EV_REP(EV_REP::REP_DELAY), + EventType::EV_FF => EventCode::EV_FF(EV_FF::FF_STATUS_STOPPED), + EventType::EV_FF_STATUS => EventCode::EV_FF_STATUS(EV_FF::FF_STATUS_STOPPED), + _ => EventCode::EV_MAX, + }; + + EventCodeIterator { + current: event_code, + } + } +} + +impl InputPropIterator { + pub fn new() -> Self { + InputPropIterator { + current: InputProp::INPUT_PROP_POINTER, + } + } +} + +pub fn event_code_to_int(event_code: &EventCode) -> (c_uint, c_uint) { + match *event_code { + EventCode::EV_SYN(code) => (EventType::EV_SYN as c_uint, code as c_uint), + EventCode::EV_KEY(code) => (EventType::EV_KEY as c_uint, code as c_uint), + EventCode::EV_REL(code) => (EventType::EV_REL as c_uint, code as c_uint), + EventCode::EV_ABS(code) => (EventType::EV_ABS as c_uint, code as c_uint), + EventCode::EV_MSC(code) => (EventType::EV_MSC as c_uint, code as c_uint), + EventCode::EV_SW(code) => (EventType::EV_SW as c_uint, code as c_uint), + EventCode::EV_LED(code) => (EventType::EV_LED as c_uint, code as c_uint), + EventCode::EV_SND(code) => (EventType::EV_SND as c_uint, code as c_uint), + EventCode::EV_REP(code) => (EventType::EV_REP as c_uint, code as c_uint), + EventCode::EV_FF(code) => (EventType::EV_FF as c_uint, code as c_uint), + EventCode::EV_FF_STATUS(code) => (EventType::EV_FF_STATUS as c_uint, code as c_uint), + EventCode::EV_UNK { + event_type, + event_code, + } => (event_type as c_uint, event_code as c_uint), + _ => { + warn!("Event code not found"); + (0, 0) + } + } +} + +pub fn int_to_event_code(event_type: c_uint, event_code: c_uint) -> EventCode { + let ev_type: EventType = int_to_event_type(event_type as u32).unwrap(); + let code = event_code as u32; + + let ev_code = match ev_type { + EventType::EV_SYN => int_to_ev_syn(code).map(EventCode::EV_SYN), + EventType::EV_KEY => int_to_ev_key(code).map(EventCode::EV_KEY), + EventType::EV_ABS => int_to_ev_abs(code).map(EventCode::EV_ABS), + EventType::EV_REL => int_to_ev_rel(code).map(EventCode::EV_REL), + EventType::EV_MSC => int_to_ev_msc(code).map(EventCode::EV_MSC), + EventType::EV_SW => int_to_ev_sw(code).map(EventCode::EV_SW), + EventType::EV_LED => int_to_ev_led(code).map(EventCode::EV_LED), + EventType::EV_SND => int_to_ev_snd(code).map(EventCode::EV_SND), + EventType::EV_REP => int_to_ev_rep(code).map(EventCode::EV_REP), + EventType::EV_FF => int_to_ev_ff(code).map(EventCode::EV_FF), + EventType::EV_PWR => Some(EventCode::EV_PWR), + EventType::EV_FF_STATUS => int_to_ev_ff(code).map(EventCode::EV_FF_STATUS), + EventType::EV_UNK => None, + EventType::EV_MAX => Some(EventCode::EV_MAX), + }; + + ev_code.unwrap_or(EventCode::EV_UNK { + event_type, + event_code, + }) +} + +impl fmt::Display for EventType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + unsafe { ptr_to_str(raw::libevdev_event_type_get_name(*self as c_uint)) }.unwrap_or("") + ) + } +} + +impl fmt::Display for EventCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (ev_type, ev_code) = event_code_to_int(self); + write!( + f, + "{}", + unsafe { ptr_to_str(raw::libevdev_event_code_get_name(ev_type, ev_code)) } + .unwrap_or("") + ) + } +} + +impl fmt::Display for InputProp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + unsafe { ptr_to_str(raw::libevdev_property_get_name(*self as c_uint)) }.unwrap_or("") + ) + } +} + +impl EventType { + pub fn iter(&self) -> EventTypeIterator { + EventTypeIterator { current: *self } + } + + /// The given type constant for the passed name or Errno if not found. + pub fn from_str(name: &str) -> Option { + let name = CString::new(name).unwrap(); + let result = unsafe { raw::libevdev_event_type_from_name(name.as_ptr()) }; + + match result { + -1 => None, + k => int_to_event_type(k as u32), + } + } + + /// The max value defined for the given event type, e.g. ABS_MAX for a type + /// of EV_ABS, or Errno for an invalid type. + pub fn get_max(ev_type: &EventType) -> Option { + let result = unsafe { raw::libevdev_event_type_get_max(*ev_type as c_uint) }; + + match result { + k if k < 0 => None, + k => Some(k as u32), + } + } +} + +impl EventCode { + pub fn iter(&self) -> EventCodeIterator { + EventCodeIterator { current: *self } + } + + /// 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 Errno if not + /// found. + pub fn from_str(ev_type: &EventType, name: &str) -> Option { + let name = CString::new(name).unwrap(); + let result = + unsafe { raw::libevdev_event_code_from_name(*ev_type as c_uint, name.as_ptr()) }; + + match result { + -1 => None, + k => Some(int_to_event_code(*ev_type as u32, k as u32)), + } + } +} + +impl InputProp { + pub fn iter(&self) -> InputPropIterator { + InputPropIterator { current: *self } + } + + /// 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 Errno if not found. + pub fn from_str(name: &str) -> Option { + let name = CString::new(name).unwrap(); + let result = unsafe { raw::libevdev_property_from_name(name.as_ptr()) }; + + match result { + -1 => None, + k => int_to_input_prop(k as u32), + } + } +} + +// Iterator trait for the enum iterators +impl Iterator for EventTypeIterator { + type Item = EventType; + + fn next(&mut self) -> Option { + match self.current { + EventType::EV_MAX => None, + _ => { + let mut raw_code = (self.current as u32) + 1; + loop { + match int_to_event_type(raw_code) { + // TODO: Find a way to iterate over Unknown types + Some(EventType::EV_UNK) => raw_code += 1, + Some(x) => { + let code = self.current; + self.current = x; + return Some(code); + } + None => raw_code += 1, + } + } + } + } + } +} + +impl Iterator for EventCodeIterator { + type Item = EventCode; + + fn next(&mut self) -> Option { + match self.current { + EventCode::EV_SYN(code) => match code { + EV_SYN::SYN_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_syn(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_SYN(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_KEY(code) => match code { + EV_KEY::KEY_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_key(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_KEY(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_REL(code) => match code { + EV_REL::REL_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_rel(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_REL(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_ABS(code) => match code { + EV_ABS::ABS_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_abs(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_ABS(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_MSC(code) => match code { + EV_MSC::MSC_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_msc(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_MSC(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_SW(code) => match code { + EV_SW::SW_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_sw(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_SW(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_LED(code) => match code { + EV_LED::LED_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_led(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_LED(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_SND(code) => match code { + EV_SND::SND_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_snd(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_SND(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_REP(code) => match code { + EV_REP::REP_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_rep(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_REP(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_FF(code) => match code { + EV_FF::FF_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_ff(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_FF(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + _ => None, + } + } +} + +impl Iterator for InputPropIterator { + type Item = InputProp; + + fn next(&mut self) -> Option { + match self.current { + InputProp::INPUT_PROP_MAX => None, + _ => { + let mut raw_enum = (self.current as u32) + 1; + loop { + match int_to_input_prop(raw_enum) { + Some(x) => { + let prop = self.current; + self.current = x; + return Some(prop); + } + None => raw_enum += 1, + } + } + } + } + } +} 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..081b15aa28 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/Cargo.lock @@ -0,0 +1,382 @@ +# 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 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 = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cc" +version = "1.2.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +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 = "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" +version = "0.1.0" +dependencies = [ + "bitflags", + "cc", + "libc 1.0.0-alpha.1", + "log 0.4.28", +] + +[[package]] +name = "evdev_manager" +version = "0.1.0" +dependencies = [ + "android_log", + "evdev", + "evdev_manager_core", + "jni", + "libc 0.2.177", + "log 0.4.28", +] + +[[package]] +name = "evdev_manager_core" +version = "0.1.0" +dependencies = [ + "evdev", + "libc 0.2.177", + "log 0.4.28", + "mio", + "slab", + "tokio", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[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 = "libc" +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" +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 = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc 0.2.177", + "log 0.4.28", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[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.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +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 = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +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 = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +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 = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[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..a959b55cbd --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = [ + "core", + "jni", +] +resolver = "2" diff --git a/evdev/src/main/rust/evdev_manager/core/Cargo.toml b/evdev/src/main/rust/evdev_manager/core/Cargo.toml new file mode 100644 index 0000000000..bf2f855846 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "evdev_manager_core" +version = "0.1.0" +edition = "2021" + +[lib] +name = "evdev_manager_core" +crate-type = ["rlib"] + +[dependencies] +log = "0.4.28" +evdev = { path = "../../evdev" } +libc = "0.2.177" +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"] } + diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/android_codes.rs b/evdev/src/main/rust/evdev_manager/core/src/android/android_codes.rs new file mode 100644 index 0000000000..c88417091d --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/android_codes.rs @@ -0,0 +1,659 @@ +use std::os::raw::c_uint; + +pub const AKEYCODE_UNKNOWN: c_uint = 0; +pub const AKEYCODE_SOFT_LEFT: c_uint = 1; +pub const AKEYCODE_SOFT_RIGHT: c_uint = 2; +pub const AKEYCODE_HOME: c_uint = 3; +pub const AKEYCODE_BACK: c_uint = 4; +pub const AKEYCODE_CALL: c_uint = 5; +pub const AKEYCODE_ENDCALL: c_uint = 6; +pub const AKEYCODE_0: c_uint = 7; +pub const AKEYCODE_1: c_uint = 8; +pub const AKEYCODE_2: c_uint = 9; +pub const AKEYCODE_3: c_uint = 10; +pub const AKEYCODE_4: c_uint = 11; +pub const AKEYCODE_5: c_uint = 12; +pub const AKEYCODE_6: c_uint = 13; +pub const AKEYCODE_7: c_uint = 14; +pub const AKEYCODE_8: c_uint = 15; +pub const AKEYCODE_9: c_uint = 16; +pub const AKEYCODE_STAR: c_uint = 17; +pub const AKEYCODE_POUND: c_uint = 18; +pub const AKEYCODE_DPAD_UP: c_uint = 19; +pub const AKEYCODE_DPAD_DOWN: c_uint = 20; +pub const AKEYCODE_DPAD_LEFT: c_uint = 21; +pub const AKEYCODE_DPAD_RIGHT: c_uint = 22; +pub const AKEYCODE_DPAD_CENTER: c_uint = 23; +pub const AKEYCODE_VOLUME_UP: c_uint = 24; +pub const AKEYCODE_VOLUME_DOWN: c_uint = 25; +pub const AKEYCODE_POWER: c_uint = 26; +pub const AKEYCODE_CAMERA: c_uint = 27; +pub const AKEYCODE_CLEAR: c_uint = 28; +pub const AKEYCODE_A: c_uint = 29; +pub const AKEYCODE_B: c_uint = 30; +pub const AKEYCODE_C: c_uint = 31; +pub const AKEYCODE_D: c_uint = 32; +pub const AKEYCODE_E: c_uint = 33; +pub const AKEYCODE_F: c_uint = 34; +pub const AKEYCODE_G: c_uint = 35; +pub const AKEYCODE_H: c_uint = 36; +pub const AKEYCODE_I: c_uint = 37; +pub const AKEYCODE_J: c_uint = 38; +pub const AKEYCODE_K: c_uint = 39; +pub const AKEYCODE_L: c_uint = 40; +pub const AKEYCODE_M: c_uint = 41; +pub const AKEYCODE_N: c_uint = 42; +pub const AKEYCODE_O: c_uint = 43; +pub const AKEYCODE_P: c_uint = 44; +pub const AKEYCODE_Q: c_uint = 45; +pub const AKEYCODE_R: c_uint = 46; +pub const AKEYCODE_S: c_uint = 47; +pub const AKEYCODE_T: c_uint = 48; +pub const AKEYCODE_U: c_uint = 49; +pub const AKEYCODE_V: c_uint = 50; +pub const AKEYCODE_W: c_uint = 51; +pub const AKEYCODE_X: c_uint = 52; +pub const AKEYCODE_Y: c_uint = 53; +pub const AKEYCODE_Z: c_uint = 54; +pub const AKEYCODE_COMMA: c_uint = 55; +pub const AKEYCODE_PERIOD: c_uint = 56; +pub const AKEYCODE_ALT_LEFT: c_uint = 57; +pub const AKEYCODE_ALT_RIGHT: c_uint = 58; +pub const AKEYCODE_SHIFT_LEFT: c_uint = 59; +pub const AKEYCODE_SHIFT_RIGHT: c_uint = 60; +pub const AKEYCODE_TAB: c_uint = 61; +pub const AKEYCODE_SPACE: c_uint = 62; +pub const AKEYCODE_SYM: c_uint = 63; +pub const AKEYCODE_EXPLORER: c_uint = 64; +pub const AKEYCODE_ENVELOPE: c_uint = 65; +pub const AKEYCODE_ENTER: c_uint = 66; +pub const AKEYCODE_DEL: c_uint = 67; +pub const AKEYCODE_GRAVE: c_uint = 68; +pub const AKEYCODE_MINUS: c_uint = 69; +pub const AKEYCODE_EQUALS: c_uint = 70; +pub const AKEYCODE_LEFT_BRACKET: c_uint = 71; +pub const AKEYCODE_RIGHT_BRACKET: c_uint = 72; +pub const AKEYCODE_BACKSLASH: c_uint = 73; +pub const AKEYCODE_SEMICOLON: c_uint = 74; +pub const AKEYCODE_APOSTROPHE: c_uint = 75; +pub const AKEYCODE_SLASH: c_uint = 76; +pub const AKEYCODE_AT: c_uint = 77; +pub const AKEYCODE_NUM: c_uint = 78; +pub const AKEYCODE_HEADSETHOOK: c_uint = 79; +pub const AKEYCODE_FOCUS: c_uint = 80; +pub const AKEYCODE_PLUS: c_uint = 81; +pub const AKEYCODE_MENU: c_uint = 82; +pub const AKEYCODE_NOTIFICATION: c_uint = 83; +pub const AKEYCODE_SEARCH: c_uint = 84; +pub const AKEYCODE_MEDIA_PLAY_PAUSE: c_uint = 85; +pub const AKEYCODE_MEDIA_STOP: c_uint = 86; +pub const AKEYCODE_MEDIA_NEXT: c_uint = 87; +pub const AKEYCODE_MEDIA_PREVIOUS: c_uint = 88; +pub const AKEYCODE_MEDIA_REWIND: c_uint = 89; +pub const AKEYCODE_MEDIA_FAST_FORWARD: c_uint = 90; +pub const AKEYCODE_MUTE: c_uint = 91; +pub const AKEYCODE_PAGE_UP: c_uint = 92; +pub const AKEYCODE_PAGE_DOWN: c_uint = 93; +pub const AKEYCODE_PICTSYMBOLS: c_uint = 94; +pub const AKEYCODE_SWITCH_CHARSET: c_uint = 95; +pub const AKEYCODE_BUTTON_A: c_uint = 96; +pub const AKEYCODE_BUTTON_B: c_uint = 97; +pub const AKEYCODE_BUTTON_C: c_uint = 98; +pub const AKEYCODE_BUTTON_X: c_uint = 99; +pub const AKEYCODE_BUTTON_Y: c_uint = 100; +pub const AKEYCODE_BUTTON_Z: c_uint = 101; +pub const AKEYCODE_BUTTON_L1: c_uint = 102; +pub const AKEYCODE_BUTTON_R1: c_uint = 103; +pub const AKEYCODE_BUTTON_L2: c_uint = 104; +pub const AKEYCODE_BUTTON_R2: c_uint = 105; +pub const AKEYCODE_BUTTON_THUMBL: c_uint = 106; +pub const AKEYCODE_BUTTON_THUMBR: c_uint = 107; +pub const AKEYCODE_BUTTON_START: c_uint = 108; +pub const AKEYCODE_BUTTON_SELECT: c_uint = 109; +pub const AKEYCODE_BUTTON_MODE: c_uint = 110; +pub const AKEYCODE_ESCAPE: c_uint = 111; +pub const AKEYCODE_FORWARD_DEL: c_uint = 112; +pub const AKEYCODE_CTRL_LEFT: c_uint = 113; +pub const AKEYCODE_CTRL_RIGHT: c_uint = 114; +pub const AKEYCODE_CAPS_LOCK: c_uint = 115; +pub const AKEYCODE_SCROLL_LOCK: c_uint = 116; +pub const AKEYCODE_META_LEFT: c_uint = 117; +pub const AKEYCODE_META_RIGHT: c_uint = 118; +pub const AKEYCODE_FUNCTION: c_uint = 119; +pub const AKEYCODE_SYSRQ: c_uint = 120; +pub const AKEYCODE_BREAK: c_uint = 121; +pub const AKEYCODE_MOVE_HOME: c_uint = 122; +pub const AKEYCODE_MOVE_END: c_uint = 123; +pub const AKEYCODE_INSERT: c_uint = 124; +pub const AKEYCODE_FORWARD: c_uint = 125; +pub const AKEYCODE_MEDIA_PLAY: c_uint = 126; +pub const AKEYCODE_MEDIA_PAUSE: c_uint = 127; +pub const AKEYCODE_MEDIA_CLOSE: c_uint = 128; +pub const AKEYCODE_MEDIA_EJECT: c_uint = 129; +pub const AKEYCODE_MEDIA_RECORD: c_uint = 130; +pub const AKEYCODE_F1: c_uint = 131; +pub const AKEYCODE_F2: c_uint = 132; +pub const AKEYCODE_F3: c_uint = 133; +pub const AKEYCODE_F4: c_uint = 134; +pub const AKEYCODE_F5: c_uint = 135; +pub const AKEYCODE_F6: c_uint = 136; +pub const AKEYCODE_F7: c_uint = 137; +pub const AKEYCODE_F8: c_uint = 138; +pub const AKEYCODE_F9: c_uint = 139; +pub const AKEYCODE_F10: c_uint = 140; +pub const AKEYCODE_F11: c_uint = 141; +pub const AKEYCODE_F12: c_uint = 142; +pub const AKEYCODE_NUM_LOCK: c_uint = 143; +pub const AKEYCODE_NUMPAD_0: c_uint = 144; +pub const AKEYCODE_NUMPAD_1: c_uint = 145; +pub const AKEYCODE_NUMPAD_2: c_uint = 146; +pub const AKEYCODE_NUMPAD_3: c_uint = 147; +pub const AKEYCODE_NUMPAD_4: c_uint = 148; +pub const AKEYCODE_NUMPAD_5: c_uint = 149; +pub const AKEYCODE_NUMPAD_6: c_uint = 150; +pub const AKEYCODE_NUMPAD_7: c_uint = 151; +pub const AKEYCODE_NUMPAD_8: c_uint = 152; +pub const AKEYCODE_NUMPAD_9: c_uint = 153; +pub const AKEYCODE_NUMPAD_DIVIDE: c_uint = 154; +pub const AKEYCODE_NUMPAD_MULTIPLY: c_uint = 155; +pub const AKEYCODE_NUMPAD_SUBTRACT: c_uint = 156; +pub const AKEYCODE_NUMPAD_ADD: c_uint = 157; +pub const AKEYCODE_NUMPAD_DOT: c_uint = 158; +pub const AKEYCODE_NUMPAD_COMMA: c_uint = 159; +pub const AKEYCODE_NUMPAD_ENTER: c_uint = 160; +pub const AKEYCODE_NUMPAD_EQUALS: c_uint = 161; +pub const AKEYCODE_NUMPAD_LEFT_PAREN: c_uint = 162; +pub const AKEYCODE_NUMPAD_RIGHT_PAREN: c_uint = 163; +pub const AKEYCODE_VOLUME_MUTE: c_uint = 164; +pub const AKEYCODE_INFO: c_uint = 165; +pub const AKEYCODE_CHANNEL_UP: c_uint = 166; +pub const AKEYCODE_CHANNEL_DOWN: c_uint = 167; +pub const AKEYCODE_ZOOM_IN: c_uint = 168; +pub const AKEYCODE_ZOOM_OUT: c_uint = 169; +pub const AKEYCODE_TV: c_uint = 170; +pub const AKEYCODE_WINDOW: c_uint = 171; +pub const AKEYCODE_GUIDE: c_uint = 172; +pub const AKEYCODE_DVR: c_uint = 173; +pub const AKEYCODE_BOOKMARK: c_uint = 174; +pub const AKEYCODE_CAPTIONS: c_uint = 175; +pub const AKEYCODE_SETTINGS: c_uint = 176; +pub const AKEYCODE_TV_POWER: c_uint = 177; +pub const AKEYCODE_TV_INPUT: c_uint = 178; +pub const AKEYCODE_STB_POWER: c_uint = 179; +pub const AKEYCODE_STB_INPUT: c_uint = 180; +pub const AKEYCODE_AVR_POWER: c_uint = 181; +pub const AKEYCODE_AVR_INPUT: c_uint = 182; +pub const AKEYCODE_PROG_RED: c_uint = 183; +pub const AKEYCODE_PROG_GREEN: c_uint = 184; +pub const AKEYCODE_PROG_YELLOW: c_uint = 185; +pub const AKEYCODE_PROG_BLUE: c_uint = 186; +pub const AKEYCODE_APP_SWITCH: c_uint = 187; +pub const AKEYCODE_BUTTON_1: c_uint = 188; +pub const AKEYCODE_BUTTON_2: c_uint = 189; +pub const AKEYCODE_BUTTON_3: c_uint = 190; +pub const AKEYCODE_BUTTON_4: c_uint = 191; +pub const AKEYCODE_BUTTON_5: c_uint = 192; +pub const AKEYCODE_BUTTON_6: c_uint = 193; +pub const AKEYCODE_BUTTON_7: c_uint = 194; +pub const AKEYCODE_BUTTON_8: c_uint = 195; +pub const AKEYCODE_BUTTON_9: c_uint = 196; +pub const AKEYCODE_BUTTON_10: c_uint = 197; +pub const AKEYCODE_BUTTON_11: c_uint = 198; +pub const AKEYCODE_BUTTON_12: c_uint = 199; +pub const AKEYCODE_BUTTON_13: c_uint = 200; +pub const AKEYCODE_BUTTON_14: c_uint = 201; +pub const AKEYCODE_BUTTON_15: c_uint = 202; +pub const AKEYCODE_BUTTON_16: c_uint = 203; +pub const AKEYCODE_LANGUAGE_SWITCH: c_uint = 204; +pub const AKEYCODE_MANNER_MODE: c_uint = 205; +pub const AKEYCODE_3D_MODE: c_uint = 206; +pub const AKEYCODE_CONTACTS: c_uint = 207; +pub const AKEYCODE_CALENDAR: c_uint = 208; +pub const AKEYCODE_MUSIC: c_uint = 209; +pub const AKEYCODE_CALCULATOR: c_uint = 210; +pub const AKEYCODE_ZENKAKU_HANKAKU: c_uint = 211; +pub const AKEYCODE_EISU: c_uint = 212; +pub const AKEYCODE_MUHENKAN: c_uint = 213; +pub const AKEYCODE_HENKAN: c_uint = 214; +pub const AKEYCODE_KATAKANA_HIRAGANA: c_uint = 215; +pub const AKEYCODE_YEN: c_uint = 216; +pub const AKEYCODE_RO: c_uint = 217; +pub const AKEYCODE_KANA: c_uint = 218; +pub const AKEYCODE_ASSIST: c_uint = 219; +pub const AKEYCODE_BRIGHTNESS_DOWN: c_uint = 220; +pub const AKEYCODE_BRIGHTNESS_UP: c_uint = 221; +pub const AKEYCODE_MEDIA_AUDIO_TRACK: c_uint = 222; +pub const AKEYCODE_SLEEP: c_uint = 223; +pub const AKEYCODE_WAKEUP: c_uint = 224; +pub const AKEYCODE_PAIRING: c_uint = 225; +pub const AKEYCODE_MEDIA_TOP_MENU: c_uint = 226; +pub const AKEYCODE_11: c_uint = 227; +pub const AKEYCODE_12: c_uint = 228; +pub const AKEYCODE_LAST_CHANNEL: c_uint = 229; +pub const AKEYCODE_TV_DATA_SERVICE: c_uint = 230; +pub const AKEYCODE_VOICE_ASSIST: c_uint = 231; +pub const AKEYCODE_TV_RADIO_SERVICE: c_uint = 232; +pub const AKEYCODE_TV_TELETEXT: c_uint = 233; +pub const AKEYCODE_TV_NUMBER_ENTRY: c_uint = 234; +pub const AKEYCODE_TV_TERRESTRIAL_ANALOG: c_uint = 235; +pub const AKEYCODE_TV_TERRESTRIAL_DIGITAL: c_uint = 236; +pub const AKEYCODE_TV_SATELLITE: c_uint = 237; +pub const AKEYCODE_TV_SATELLITE_BS: c_uint = 238; +pub const AKEYCODE_TV_SATELLITE_CS: c_uint = 239; +pub const AKEYCODE_TV_SATELLITE_SERVICE: c_uint = 240; +pub const AKEYCODE_TV_NETWORK: c_uint = 241; +pub const AKEYCODE_TV_ANTENNA_CABLE: c_uint = 242; +pub const AKEYCODE_TV_INPUT_HDMI_1: c_uint = 243; +pub const AKEYCODE_TV_INPUT_HDMI_2: c_uint = 244; +pub const AKEYCODE_TV_INPUT_HDMI_3: c_uint = 245; +pub const AKEYCODE_TV_INPUT_HDMI_4: c_uint = 246; +pub const AKEYCODE_TV_INPUT_COMPOSITE_1: c_uint = 247; +pub const AKEYCODE_TV_INPUT_COMPOSITE_2: c_uint = 248; +pub const AKEYCODE_TV_INPUT_COMPONENT_1: c_uint = 249; +pub const AKEYCODE_TV_INPUT_COMPONENT_2: c_uint = 250; +pub const AKEYCODE_TV_INPUT_VGA_1: c_uint = 251; +pub const AKEYCODE_TV_AUDIO_DESCRIPTION: c_uint = 252; +pub const AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: c_uint = 253; +pub const AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: c_uint = 254; +pub const AKEYCODE_TV_ZOOM_MODE: c_uint = 255; +pub const AKEYCODE_TV_CONTENTS_MENU: c_uint = 256; +pub const AKEYCODE_TV_MEDIA_CONTEXT_MENU: c_uint = 257; +pub const AKEYCODE_TV_TIMER_PROGRAMMING: c_uint = 258; +pub const AKEYCODE_HELP: c_uint = 259; +pub const AKEYCODE_NAVIGATE_PREVIOUS: c_uint = 260; +pub const AKEYCODE_NAVIGATE_NEXT: c_uint = 261; +pub const AKEYCODE_NAVIGATE_IN: c_uint = 262; +pub const AKEYCODE_NAVIGATE_OUT: c_uint = 263; +pub const AKEYCODE_STEM_PRIMARY: c_uint = 264; +pub const AKEYCODE_STEM_1: c_uint = 265; +pub const AKEYCODE_STEM_2: c_uint = 266; +pub const AKEYCODE_STEM_3: c_uint = 267; +pub const AKEYCODE_DPAD_UP_LEFT: c_uint = 268; +pub const AKEYCODE_DPAD_DOWN_LEFT: c_uint = 269; +pub const AKEYCODE_DPAD_UP_RIGHT: c_uint = 270; +pub const AKEYCODE_DPAD_DOWN_RIGHT: c_uint = 271; +pub const AKEYCODE_MEDIA_SKIP_FORWARD: c_uint = 272; +pub const AKEYCODE_MEDIA_SKIP_BACKWARD: c_uint = 273; +pub const AKEYCODE_MEDIA_STEP_FORWARD: c_uint = 274; +pub const AKEYCODE_MEDIA_STEP_BACKWARD: c_uint = 275; +pub const AKEYCODE_SOFT_SLEEP: c_uint = 276; +pub const AKEYCODE_CUT: c_uint = 277; +pub const AKEYCODE_COPY: c_uint = 278; +pub const AKEYCODE_PASTE: c_uint = 279; +pub const AKEYCODE_SYSTEM_NAVIGATION_UP: c_uint = 280; +pub const AKEYCODE_SYSTEM_NAVIGATION_DOWN: c_uint = 281; +pub const AKEYCODE_SYSTEM_NAVIGATION_LEFT: c_uint = 282; +pub const AKEYCODE_SYSTEM_NAVIGATION_RIGHT: c_uint = 283; +pub const AKEYCODE_ALL_APPS: c_uint = 284; +pub const AKEYCODE_REFRESH: c_uint = 285; +pub const AKEYCODE_THUMBS_UP: c_uint = 286; +pub const AKEYCODE_THUMBS_DOWN: c_uint = 287; +pub const AKEYCODE_PROFILE_SWITCH: c_uint = 288; +pub const AKEYCODE_VIDEO_APP_1: c_uint = 289; +pub const AKEYCODE_VIDEO_APP_2: c_uint = 290; +pub const AKEYCODE_VIDEO_APP_3: c_uint = 291; +pub const AKEYCODE_VIDEO_APP_4: c_uint = 292; +pub const AKEYCODE_VIDEO_APP_5: c_uint = 293; +pub const AKEYCODE_VIDEO_APP_6: c_uint = 294; +pub const AKEYCODE_VIDEO_APP_7: c_uint = 295; +pub const AKEYCODE_VIDEO_APP_8: c_uint = 296; +pub const AKEYCODE_FEATURED_APP_1: c_uint = 297; +pub const AKEYCODE_FEATURED_APP_2: c_uint = 298; +pub const AKEYCODE_FEATURED_APP_3: c_uint = 299; +pub const AKEYCODE_FEATURED_APP_4: c_uint = 300; +pub const AKEYCODE_DEMO_APP_1: c_uint = 301; +pub const AKEYCODE_DEMO_APP_2: c_uint = 302; +pub const AKEYCODE_DEMO_APP_3: c_uint = 303; +pub const AKEYCODE_DEMO_APP_4: c_uint = 304; +pub const AKEYCODE_KEYBOARD_BACKLIGHT_DOWN: c_uint = 305; +pub const AKEYCODE_KEYBOARD_BACKLIGHT_UP: c_uint = 306; +pub const AKEYCODE_KEYBOARD_BACKLIGHT_TOGGLE: c_uint = 307; +pub const AKEYCODE_STYLUS_BUTTON_PRIMARY: c_uint = 308; +pub const AKEYCODE_STYLUS_BUTTON_SECONDARY: c_uint = 309; +pub const AKEYCODE_STYLUS_BUTTON_TERTIARY: c_uint = 310; +pub const AKEYCODE_STYLUS_BUTTON_TAIL: c_uint = 311; +pub const AKEYCODE_RECENT_APPS: c_uint = 312; +pub const AKEYCODE_MACRO_1: c_uint = 313; +pub const AKEYCODE_MACRO_2: c_uint = 314; +pub const AKEYCODE_MACRO_3: c_uint = 315; +pub const AKEYCODE_MACRO_4: c_uint = 316; + +pub const AMOTION_EVENT_AXIS_X: c_uint = 0; +/** Axis constant: Y axis of a motion event. + +- For a touch screen, reports the absolute Y screen position of the center of +the touch contact area. The units are display pixels. +- For a touch pad, reports the absolute Y surface position of the center of the touch +contact area. The units are device-dependent. +- For a mouse, reports the absolute Y screen position of the mouse pointer. +The units are display pixels. +- For a trackball, reports the relative vertical displacement of the trackball. +The value is normalized to a range from -1.0 (up) to 1.0 (down). +- For a joystick, reports the absolute Y position of the joystick. +The value is normalized to a range from -1.0 (up or far) to 1.0 (down or near).*/ +pub const AMOTION_EVENT_AXIS_Y: c_uint = 1; +/** Axis constant: Pressure axis of a motion event. + +- For a touch screen or touch pad, reports the approximate pressure applied to the surface +by a finger or other tool. The value is normalized to a range from +0 (no pressure at all) to 1 (normal pressure), although values higher than 1 +may be generated depending on the calibration of the input device. +- For a trackball, the value is set to 1 if the trackball button is pressed +or 0 otherwise. +- For a mouse, the value is set to 1 if the primary mouse button is pressed +or 0 otherwise.*/ +pub const AMOTION_EVENT_AXIS_PRESSURE: c_uint = 2; +/** Axis constant: Size axis of a motion event. + +- For a touch screen or touch pad, reports the approximate size of the contact area in +relation to the maximum detectable size for the device. The value is normalized +to a range from 0 (smallest detectable size) to 1 (largest detectable size), +although it is not a linear scale. This value is of limited use. +To obtain calibrated size information, see +{@link AMOTION_EVENT_AXIS_TOUCH_MAJOR} or {@link AMOTION_EVENT_AXIS_TOOL_MAJOR}.*/ +pub const AMOTION_EVENT_AXIS_SIZE: c_uint = 3; +/** Axis constant: TouchMajor axis of a motion event. + +- For a touch screen, reports the length of the major axis of an ellipse that +represents the touch area at the point of contact. +The units are display pixels. +- For a touch pad, reports the length of the major axis of an ellipse that +represents the touch area at the point of contact. +The units are device-dependent.*/ +pub const AMOTION_EVENT_AXIS_TOUCH_MAJOR: c_uint = 4; +/** Axis constant: TouchMinor axis of a motion event. + +- For a touch screen, reports the length of the minor axis of an ellipse that +represents the touch area at the point of contact. +The units are display pixels. +- For a touch pad, reports the length of the minor axis of an ellipse that +represents the touch area at the point of contact. +The units are device-dependent. + +When the touch is circular, the major and minor axis lengths will be equal to one another.*/ +pub const AMOTION_EVENT_AXIS_TOUCH_MINOR: c_uint = 5; +/** Axis constant: ToolMajor axis of a motion event. + +- For a touch screen, reports the length of the major axis of an ellipse that +represents the size of the approaching finger or tool used to make contact. +- For a touch pad, reports the length of the major axis of an ellipse that +represents the size of the approaching finger or tool used to make contact. +The units are device-dependent. + +When the touch is circular, the major and minor axis lengths will be equal to one another. + +The tool size may be larger than the touch size since the tool may not be fully +in contact with the touch sensor.*/ +pub const AMOTION_EVENT_AXIS_TOOL_MAJOR: c_uint = 6; +/** Axis constant: ToolMinor axis of a motion event. + +- For a touch screen, reports the length of the minor axis of an ellipse that +represents the size of the approaching finger or tool used to make contact. +- For a touch pad, reports the length of the minor axis of an ellipse that +represents the size of the approaching finger or tool used to make contact. +The units are device-dependent. + +When the touch is circular, the major and minor axis lengths will be equal to one another. + +The tool size may be larger than the touch size since the tool may not be fully +in contact with the touch sensor.*/ +pub const AMOTION_EVENT_AXIS_TOOL_MINOR: c_uint = 7; +/** Axis constant: Orientation axis of a motion event. + +- For a touch screen or touch pad, reports the orientation of the finger +or tool in radians relative to the vertical plane of the device. +An angle of 0 radians indicates that the major axis of contact is oriented +upwards, is perfectly circular or is of unknown orientation. A positive angle +indicates that the major axis of contact is oriented to the right. A negative angle +indicates that the major axis of contact is oriented to the left. +The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians +(finger pointing fully right). +- For a stylus, the orientation indicates the direction in which the stylus +is pointing in relation to the vertical axis of the current orientation of the screen. +The range is from -PI radians to PI radians, where 0 is pointing up, +-PI/2 radians is pointing left, -PI or PI radians is pointing down, and PI/2 radians +is pointing right. See also #AMOTION_EVENT_AXIS_TILT.*/ +pub const AMOTION_EVENT_AXIS_ORIENTATION: c_uint = 8; +/** Axis constant: Vertical Scroll axis of a motion event. + +- For a mouse, reports the relative movement of the vertical scroll wheel. +The value is normalized to a range from -1.0 (down) to 1.0 (up). + +This axis should be used to scroll views vertically.*/ +pub const AMOTION_EVENT_AXIS_VSCROLL: c_uint = 9; +/** Axis constant: Horizontal Scroll axis of a motion event. + +- For a mouse, reports the relative movement of the horizontal scroll wheel. +The value is normalized to a range from -1.0 (left) to 1.0 (right). + +This axis should be used to scroll views horizontally.*/ +pub const AMOTION_EVENT_AXIS_HSCROLL: c_uint = 10; +/** Axis constant: Z axis of a motion event. + +- For a joystick, reports the absolute Z position of the joystick. +The value is normalized to a range from -1.0 (high) to 1.0 (low). +On game pads with two analog joysticks, this axis is often reinterpreted +to report the absolute X position of the second joystick instead.*/ +pub const AMOTION_EVENT_AXIS_Z: c_uint = 11; +/** Axis constant: X Rotation axis of a motion event. + +- For a joystick, reports the absolute rotation angle about the X axis. +The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise).*/ +pub const AMOTION_EVENT_AXIS_RX: c_uint = 12; +/** Axis constant: Y Rotation axis of a motion event. + +- For a joystick, reports the absolute rotation angle about the Y axis. +The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise).*/ +pub const AMOTION_EVENT_AXIS_RY: c_uint = 13; +/** Axis constant: Z Rotation axis of a motion event. + +- For a joystick, reports the absolute rotation angle about the Z axis. +The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise). +On game pads with two analog joysticks, this axis is often reinterpreted +to report the absolute Y position of the second joystick instead.*/ +pub const AMOTION_EVENT_AXIS_RZ: c_uint = 14; +/** Axis constant: Hat X axis of a motion event. + +- For a joystick, reports the absolute X position of the directional hat control. +The value is normalized to a range from -1.0 (left) to 1.0 (right).*/ +pub const AMOTION_EVENT_AXIS_HAT_X: c_uint = 15; +/** Axis constant: Hat Y axis of a motion event. + +- For a joystick, reports the absolute Y position of the directional hat control. +The value is normalized to a range from -1.0 (up) to 1.0 (down).*/ +pub const AMOTION_EVENT_AXIS_HAT_Y: c_uint = 16; +/** Axis constant: Left Trigger axis of a motion event. + +- For a joystick, reports the absolute position of the left trigger control. +The value is normalized to a range from 0.0 (released) to 1.0 (fully pressed).*/ +pub const AMOTION_EVENT_AXIS_LTRIGGER: c_uint = 17; +/** Axis constant: Right Trigger axis of a motion event. + +- For a joystick, reports the absolute position of the right trigger control. +The value is normalized to a range from 0.0 (released) to 1.0 (fully pressed).*/ +pub const AMOTION_EVENT_AXIS_RTRIGGER: c_uint = 18; +/** Axis constant: Throttle axis of a motion event. + +- For a joystick, reports the absolute position of the throttle control. +The value is normalized to a range from 0.0 (fully open) to 1.0 (fully closed).*/ +pub const AMOTION_EVENT_AXIS_THROTTLE: c_uint = 19; +/** Axis constant: Rudder axis of a motion event. + +- For a joystick, reports the absolute position of the rudder control. +The value is normalized to a range from -1.0 (turn left) to 1.0 (turn right).*/ +pub const AMOTION_EVENT_AXIS_RUDDER: c_uint = 20; +/** Axis constant: Wheel axis of a motion event. + +- For a joystick, reports the absolute position of the steering wheel control. +The value is normalized to a range from -1.0 (turn left) to 1.0 (turn right).*/ +pub const AMOTION_EVENT_AXIS_WHEEL: c_uint = 21; +/** Axis constant: Gas axis of a motion event. + +- For a joystick, reports the absolute position of the gas (accelerator) control. +The value is normalized to a range from 0.0 (no acceleration) +to 1.0 (maximum acceleration).*/ +pub const AMOTION_EVENT_AXIS_GAS: c_uint = 22; +/** Axis constant: Brake axis of a motion event. + +- For a joystick, reports the absolute position of the brake control. +The value is normalized to a range from 0.0 (no braking) to 1.0 (maximum braking).*/ +pub const AMOTION_EVENT_AXIS_BRAKE: c_uint = 23; +/** Axis constant: Distance axis of a motion event. + +- For a stylus, reports the distance of the stylus from the screen. +A value of 0.0 indicates direct contact and larger values indicate increasing +distance from the surface.*/ +pub const AMOTION_EVENT_AXIS_DISTANCE: c_uint = 24; +/** Axis constant: Tilt axis of a motion event. + +- For a stylus, reports the tilt angle of the stylus in radians where +0 radians indicates that the stylus is being held perpendicular to the +surface, and PI/2 radians indicates that the stylus is being held flat +against the surface.*/ +pub const AMOTION_EVENT_AXIS_TILT: c_uint = 25; +/** Axis constant: Generic scroll axis of a motion event. + +- This is used for scroll axis motion events that can't be classified as strictly + vertical or horizontal. The movement of a rotating scroller is an example of this.*/ +pub const AMOTION_EVENT_AXIS_SCROLL: c_uint = 26; +/** Axis constant: The movement of x position of a motion event. + +- For a mouse, reports a difference of x position between the previous position. +This is useful when pointer is captured, in that case the mouse pointer doesn't +change the location but this axis reports the difference which allows the app +to see how the mouse is moved.*/ +pub const AMOTION_EVENT_AXIS_RELATIVE_X: c_uint = 27; +/** Axis constant: The movement of y position of a motion event. + +Same as #AMOTION_EVENT_AXIS_RELATIVE_X, but for y position.*/ +pub const AMOTION_EVENT_AXIS_RELATIVE_Y: c_uint = 28; +/** Axis constant: Generic 1 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_1: c_uint = 32; +/** Axis constant: Generic 2 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_2: c_uint = 33; +/** Axis constant: Generic 3 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_3: c_uint = 34; +/** Axis constant: Generic 4 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_4: c_uint = 35; +/** Axis constant: Generic 5 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_5: c_uint = 36; +/** Axis constant: Generic 6 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_6: c_uint = 37; +/** Axis constant: Generic 7 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_7: c_uint = 38; +/** Axis constant: Generic 8 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_8: c_uint = 39; +/** Axis constant: Generic 9 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_9: c_uint = 40; +/** Axis constant: Generic 10 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_10: c_uint = 41; +/** Axis constant: Generic 11 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_11: c_uint = 42; +/** Axis constant: Generic 12 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_12: c_uint = 43; +/** Axis constant: Generic 13 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_13: c_uint = 44; +/** Axis constant: Generic 14 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_14: c_uint = 45; +/** Axis constant: Generic 15 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_15: c_uint = 46; +/** Axis constant: Generic 16 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_16: c_uint = 47; +/** Axis constant: X gesture offset axis of a motion event. + +- For a touch pad, reports the distance that a swipe gesture has moved in the X axis, as a + proportion of the touch pad's size. For example, if a touch pad is 1000 units wide, and a + swipe gesture starts at X = 500 then moves to X = 400, this axis would have a value of + -0.1. + +These values are relative to the state from the last event, not accumulated, so developers +should make sure to process this axis value for all batched historical events. + +This axis is only set on the first pointer in a motion event.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_X_OFFSET: c_uint = 48; +/** Axis constant: Y gesture offset axis of a motion event. + +The same as {@link AMOTION_EVENT_AXIS_GESTURE_X_OFFSET}, but for the Y axis.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET: c_uint = 49; +/** Axis constant: X scroll distance axis of a motion event. + +- For a touch pad, reports the distance that should be scrolled in the X axis as a result of + the user's two-finger scroll gesture, in display pixels. + +These values are relative to the state from the last event, not accumulated, so developers +should make sure to process this axis value for all batched historical events. + +This axis is only set on the first pointer in a motion event.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE: c_uint = 50; +/** Axis constant: Y scroll distance axis of a motion event. + +The same as {@link AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE}, but for the Y axis.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE: c_uint = 51; +/** Axis constant: pinch scale factor of a motion event. + +- For a touch pad, reports the change in distance between the fingers when the user is making + a pinch gesture, as a proportion of that distance when the gesture was last reported. For + example, if the fingers were 50 units apart and are now 52 units apart, the scale factor + would be 1.04. + +These values are relative to the state from the last event, not accumulated, so developers +should make sure to process this axis value for all batched historical events. + +This axis is only set on the first pointer in a motion event.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR: c_uint = 52; +/** Axis constant: the number of fingers being used in a multi-finger swipe gesture. + +- For a touch pad, reports the number of fingers being used in a multi-finger swipe gesture + (with CLASSIFICATION_MULTI_FINGER_SWIPE). + +Since CLASSIFICATION_MULTI_FINGER_SWIPE is a hidden API, so is this axis. It is only set on +the first pointer in a motion event.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT: c_uint = 53; + +/* These flags originate in RawEvents and are generally set in the key map. + * NOTE: If you want a flag to be able to set in a keylayout file, then you must add it to + * InputEventLabels.h as well. */ + +// Indicates that the event should wake the device. +pub const POLICY_FLAG_WAKE: c_uint = 0x00000001; + +// Indicates that the key is virtual, such as a capacitive button, and should +// generate haptic feedback. Virtual keys may be suppressed for some time +// after a recent touch to prevent accidental activation of virtual keys adjacent +// to the touch screen during an edge swipe. + +pub const POLICY_FLAG_VIRTUAL: c_uint = 0x00000002; + +// Indicates that the key is the special function modifier. +pub const POLICY_FLAG_FUNCTION: c_uint = 0x00000004; + +// Indicates that the key represents a special gesture that has been detected by +// the touch firmware or driver. Causes touch events from the same device to be canceled. +// This policy flag prevents key events from changing touch mode state. +pub const POLICY_FLAG_GESTURE: c_uint = 0x00000008; + +// Indicates that key usage mapping represents a fallback mapping. +// Fallback mappings cannot be used to definitively determine whether a device +// supports a key code. For example, a HID device can report a key press +// as a HID usage code if it is not mapped to any linux key code in the kernel. +// However, we cannot know which HID usage codes that device supports from +// userspace through the evdev. We can use fallback mappings to convert HID +// usage codes to Android key codes without needing to know if a device can +// actually report the usage code. +pub const POLICY_FLAG_FALLBACK_USAGE_MAPPING: c_uint = 0x00000010; diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/input_event_lookup.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/input_event_lookup.rs new file mode 100644 index 0000000000..c7db755926 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/input_event_lookup.rs @@ -0,0 +1,476 @@ +//! Input event label lookup functionality. +//! +//! Provides lookup functions for converting between string labels and numeric values +//! for Android key codes, flags, and axes. +//! +//! Uses Android NDK constants (AKEYCODE_*, AMOTION_EVENT_AXIS_*) from the generated bindings. +//! POLICY_FLAG_* constants are defined locally as they come from the local Input.h header. + +use crate::android::android_codes; +use std::collections::HashMap; +use std::sync::OnceLock; + +// Macro to define keycode entry using AKEYCODE_* constant +macro_rules! define_keycode { + ($name:ident) => { + (stringify!($name).to_string(), android_codes::$name as u32) + }; +} + +// Macro to define axis entry using AMOTION_EVENT_AXIS_* constant +macro_rules! define_axis { + ($name:ident) => { + (stringify!($name).to_string(), android_codes::$name as u32) + }; +} + +// Macro to define policy flag entry using POLICY_FLAG_* constant +macro_rules! define_flag { + ($name:ident) => { + (stringify!($name).to_string(), android_codes::$name as u32) + }; +} + +// Build the keycodes map +fn build_keycodes_map() -> HashMap { + let mut map = HashMap::new(); + + // Helper macro to insert keycode + macro_rules! insert_keycode { + ($name:ident) => { + let (name, value) = define_keycode!($name); + map.insert(name, value); + }; + } + + insert_keycode!(AKEYCODE_0); + insert_keycode!(AKEYCODE_1); + insert_keycode!(AKEYCODE_2); + insert_keycode!(AKEYCODE_3); + insert_keycode!(AKEYCODE_4); + insert_keycode!(AKEYCODE_5); + insert_keycode!(AKEYCODE_6); + insert_keycode!(AKEYCODE_7); + insert_keycode!(AKEYCODE_8); + insert_keycode!(AKEYCODE_9); + insert_keycode!(AKEYCODE_UNKNOWN); + insert_keycode!(AKEYCODE_SOFT_LEFT); + insert_keycode!(AKEYCODE_SOFT_RIGHT); + insert_keycode!(AKEYCODE_HOME); + insert_keycode!(AKEYCODE_BACK); + insert_keycode!(AKEYCODE_CALL); + insert_keycode!(AKEYCODE_ENDCALL); + insert_keycode!(AKEYCODE_STAR); + insert_keycode!(AKEYCODE_POUND); + insert_keycode!(AKEYCODE_DPAD_UP); + insert_keycode!(AKEYCODE_DPAD_DOWN); + insert_keycode!(AKEYCODE_DPAD_LEFT); + insert_keycode!(AKEYCODE_DPAD_RIGHT); + insert_keycode!(AKEYCODE_DPAD_CENTER); + insert_keycode!(AKEYCODE_VOLUME_UP); + insert_keycode!(AKEYCODE_VOLUME_DOWN); + insert_keycode!(AKEYCODE_POWER); + insert_keycode!(AKEYCODE_CAMERA); + insert_keycode!(AKEYCODE_CLEAR); + insert_keycode!(AKEYCODE_A); + insert_keycode!(AKEYCODE_B); + insert_keycode!(AKEYCODE_C); + insert_keycode!(AKEYCODE_D); + insert_keycode!(AKEYCODE_E); + insert_keycode!(AKEYCODE_F); + insert_keycode!(AKEYCODE_G); + insert_keycode!(AKEYCODE_H); + insert_keycode!(AKEYCODE_I); + insert_keycode!(AKEYCODE_J); + insert_keycode!(AKEYCODE_K); + insert_keycode!(AKEYCODE_L); + insert_keycode!(AKEYCODE_M); + insert_keycode!(AKEYCODE_N); + insert_keycode!(AKEYCODE_O); + insert_keycode!(AKEYCODE_P); + insert_keycode!(AKEYCODE_Q); + insert_keycode!(AKEYCODE_R); + insert_keycode!(AKEYCODE_S); + insert_keycode!(AKEYCODE_T); + insert_keycode!(AKEYCODE_U); + insert_keycode!(AKEYCODE_V); + insert_keycode!(AKEYCODE_W); + insert_keycode!(AKEYCODE_X); + insert_keycode!(AKEYCODE_Y); + insert_keycode!(AKEYCODE_Z); + insert_keycode!(AKEYCODE_COMMA); + insert_keycode!(AKEYCODE_PERIOD); + insert_keycode!(AKEYCODE_ALT_LEFT); + insert_keycode!(AKEYCODE_ALT_RIGHT); + insert_keycode!(AKEYCODE_SHIFT_LEFT); + insert_keycode!(AKEYCODE_SHIFT_RIGHT); + insert_keycode!(AKEYCODE_TAB); + insert_keycode!(AKEYCODE_SPACE); + insert_keycode!(AKEYCODE_SYM); + insert_keycode!(AKEYCODE_EXPLORER); + insert_keycode!(AKEYCODE_ENVELOPE); + insert_keycode!(AKEYCODE_ENTER); + insert_keycode!(AKEYCODE_DEL); + insert_keycode!(AKEYCODE_GRAVE); + insert_keycode!(AKEYCODE_MINUS); + insert_keycode!(AKEYCODE_EQUALS); + insert_keycode!(AKEYCODE_LEFT_BRACKET); + insert_keycode!(AKEYCODE_RIGHT_BRACKET); + insert_keycode!(AKEYCODE_BACKSLASH); + insert_keycode!(AKEYCODE_SEMICOLON); + insert_keycode!(AKEYCODE_APOSTROPHE); + insert_keycode!(AKEYCODE_SLASH); + insert_keycode!(AKEYCODE_AT); + insert_keycode!(AKEYCODE_NUM); + insert_keycode!(AKEYCODE_HEADSETHOOK); + insert_keycode!(AKEYCODE_FOCUS); + insert_keycode!(AKEYCODE_PLUS); + insert_keycode!(AKEYCODE_MENU); + insert_keycode!(AKEYCODE_NOTIFICATION); + insert_keycode!(AKEYCODE_SEARCH); + insert_keycode!(AKEYCODE_MEDIA_PLAY_PAUSE); + insert_keycode!(AKEYCODE_MEDIA_STOP); + insert_keycode!(AKEYCODE_MEDIA_NEXT); + insert_keycode!(AKEYCODE_MEDIA_PREVIOUS); + insert_keycode!(AKEYCODE_MEDIA_REWIND); + insert_keycode!(AKEYCODE_MEDIA_FAST_FORWARD); + insert_keycode!(AKEYCODE_MUTE); + insert_keycode!(AKEYCODE_PAGE_UP); + insert_keycode!(AKEYCODE_PAGE_DOWN); + insert_keycode!(AKEYCODE_PICTSYMBOLS); + insert_keycode!(AKEYCODE_SWITCH_CHARSET); + insert_keycode!(AKEYCODE_BUTTON_A); + insert_keycode!(AKEYCODE_BUTTON_B); + insert_keycode!(AKEYCODE_BUTTON_C); + insert_keycode!(AKEYCODE_BUTTON_X); + insert_keycode!(AKEYCODE_BUTTON_Y); + insert_keycode!(AKEYCODE_BUTTON_Z); + insert_keycode!(AKEYCODE_BUTTON_L1); + insert_keycode!(AKEYCODE_BUTTON_R1); + insert_keycode!(AKEYCODE_BUTTON_L2); + insert_keycode!(AKEYCODE_BUTTON_R2); + insert_keycode!(AKEYCODE_BUTTON_THUMBL); + insert_keycode!(AKEYCODE_BUTTON_THUMBR); + insert_keycode!(AKEYCODE_BUTTON_START); + insert_keycode!(AKEYCODE_BUTTON_SELECT); + insert_keycode!(AKEYCODE_BUTTON_MODE); + insert_keycode!(AKEYCODE_ESCAPE); + insert_keycode!(AKEYCODE_FORWARD_DEL); + insert_keycode!(AKEYCODE_CTRL_LEFT); + insert_keycode!(AKEYCODE_CTRL_RIGHT); + insert_keycode!(AKEYCODE_CAPS_LOCK); + insert_keycode!(AKEYCODE_SCROLL_LOCK); + insert_keycode!(AKEYCODE_META_LEFT); + insert_keycode!(AKEYCODE_META_RIGHT); + insert_keycode!(AKEYCODE_FUNCTION); + insert_keycode!(AKEYCODE_SYSRQ); + insert_keycode!(AKEYCODE_BREAK); + insert_keycode!(AKEYCODE_MOVE_HOME); + insert_keycode!(AKEYCODE_MOVE_END); + insert_keycode!(AKEYCODE_INSERT); + insert_keycode!(AKEYCODE_FORWARD); + insert_keycode!(AKEYCODE_MEDIA_PLAY); + insert_keycode!(AKEYCODE_MEDIA_PAUSE); + insert_keycode!(AKEYCODE_MEDIA_CLOSE); + insert_keycode!(AKEYCODE_MEDIA_EJECT); + insert_keycode!(AKEYCODE_MEDIA_RECORD); + insert_keycode!(AKEYCODE_F1); + insert_keycode!(AKEYCODE_F2); + insert_keycode!(AKEYCODE_F3); + insert_keycode!(AKEYCODE_F4); + insert_keycode!(AKEYCODE_F5); + insert_keycode!(AKEYCODE_F6); + insert_keycode!(AKEYCODE_F7); + insert_keycode!(AKEYCODE_F8); + insert_keycode!(AKEYCODE_F9); + insert_keycode!(AKEYCODE_F10); + insert_keycode!(AKEYCODE_F11); + insert_keycode!(AKEYCODE_F12); + insert_keycode!(AKEYCODE_NUM_LOCK); + insert_keycode!(AKEYCODE_NUMPAD_0); + insert_keycode!(AKEYCODE_NUMPAD_1); + insert_keycode!(AKEYCODE_NUMPAD_2); + insert_keycode!(AKEYCODE_NUMPAD_3); + insert_keycode!(AKEYCODE_NUMPAD_4); + insert_keycode!(AKEYCODE_NUMPAD_5); + insert_keycode!(AKEYCODE_NUMPAD_6); + insert_keycode!(AKEYCODE_NUMPAD_7); + insert_keycode!(AKEYCODE_NUMPAD_8); + insert_keycode!(AKEYCODE_NUMPAD_9); + insert_keycode!(AKEYCODE_NUMPAD_DIVIDE); + insert_keycode!(AKEYCODE_NUMPAD_MULTIPLY); + insert_keycode!(AKEYCODE_NUMPAD_SUBTRACT); + insert_keycode!(AKEYCODE_NUMPAD_ADD); + insert_keycode!(AKEYCODE_NUMPAD_DOT); + insert_keycode!(AKEYCODE_NUMPAD_COMMA); + insert_keycode!(AKEYCODE_NUMPAD_ENTER); + insert_keycode!(AKEYCODE_NUMPAD_EQUALS); + insert_keycode!(AKEYCODE_NUMPAD_LEFT_PAREN); + insert_keycode!(AKEYCODE_NUMPAD_RIGHT_PAREN); + insert_keycode!(AKEYCODE_VOLUME_MUTE); + insert_keycode!(AKEYCODE_INFO); + insert_keycode!(AKEYCODE_CHANNEL_UP); + insert_keycode!(AKEYCODE_CHANNEL_DOWN); + insert_keycode!(AKEYCODE_ZOOM_IN); + insert_keycode!(AKEYCODE_ZOOM_OUT); + insert_keycode!(AKEYCODE_TV); + insert_keycode!(AKEYCODE_WINDOW); + insert_keycode!(AKEYCODE_GUIDE); + insert_keycode!(AKEYCODE_DVR); + insert_keycode!(AKEYCODE_BOOKMARK); + insert_keycode!(AKEYCODE_CAPTIONS); + insert_keycode!(AKEYCODE_SETTINGS); + insert_keycode!(AKEYCODE_TV_POWER); + insert_keycode!(AKEYCODE_TV_INPUT); + insert_keycode!(AKEYCODE_STB_POWER); + insert_keycode!(AKEYCODE_STB_INPUT); + insert_keycode!(AKEYCODE_AVR_POWER); + insert_keycode!(AKEYCODE_AVR_INPUT); + insert_keycode!(AKEYCODE_PROG_RED); + insert_keycode!(AKEYCODE_PROG_GREEN); + insert_keycode!(AKEYCODE_PROG_YELLOW); + insert_keycode!(AKEYCODE_PROG_BLUE); + insert_keycode!(AKEYCODE_APP_SWITCH); + insert_keycode!(AKEYCODE_BUTTON_1); + insert_keycode!(AKEYCODE_BUTTON_2); + insert_keycode!(AKEYCODE_BUTTON_3); + insert_keycode!(AKEYCODE_BUTTON_4); + insert_keycode!(AKEYCODE_BUTTON_5); + insert_keycode!(AKEYCODE_BUTTON_6); + insert_keycode!(AKEYCODE_BUTTON_7); + insert_keycode!(AKEYCODE_BUTTON_8); + insert_keycode!(AKEYCODE_BUTTON_9); + insert_keycode!(AKEYCODE_BUTTON_10); + insert_keycode!(AKEYCODE_BUTTON_11); + insert_keycode!(AKEYCODE_BUTTON_12); + insert_keycode!(AKEYCODE_BUTTON_13); + insert_keycode!(AKEYCODE_BUTTON_14); + insert_keycode!(AKEYCODE_BUTTON_15); + insert_keycode!(AKEYCODE_BUTTON_16); + insert_keycode!(AKEYCODE_LANGUAGE_SWITCH); + insert_keycode!(AKEYCODE_MANNER_MODE); + insert_keycode!(AKEYCODE_3D_MODE); + insert_keycode!(AKEYCODE_CONTACTS); + insert_keycode!(AKEYCODE_CALENDAR); + insert_keycode!(AKEYCODE_MUSIC); + insert_keycode!(AKEYCODE_CALCULATOR); + insert_keycode!(AKEYCODE_ZENKAKU_HANKAKU); + insert_keycode!(AKEYCODE_EISU); + insert_keycode!(AKEYCODE_MUHENKAN); + insert_keycode!(AKEYCODE_HENKAN); + insert_keycode!(AKEYCODE_KATAKANA_HIRAGANA); + insert_keycode!(AKEYCODE_YEN); + insert_keycode!(AKEYCODE_RO); + insert_keycode!(AKEYCODE_KANA); + insert_keycode!(AKEYCODE_ASSIST); + insert_keycode!(AKEYCODE_BRIGHTNESS_DOWN); + insert_keycode!(AKEYCODE_BRIGHTNESS_UP); + insert_keycode!(AKEYCODE_MEDIA_AUDIO_TRACK); + insert_keycode!(AKEYCODE_SLEEP); + insert_keycode!(AKEYCODE_WAKEUP); + insert_keycode!(AKEYCODE_PAIRING); + insert_keycode!(AKEYCODE_MEDIA_TOP_MENU); + insert_keycode!(AKEYCODE_LAST_CHANNEL); + insert_keycode!(AKEYCODE_TV_DATA_SERVICE); + insert_keycode!(AKEYCODE_VOICE_ASSIST); + insert_keycode!(AKEYCODE_TV_RADIO_SERVICE); + insert_keycode!(AKEYCODE_TV_TELETEXT); + insert_keycode!(AKEYCODE_TV_NUMBER_ENTRY); + insert_keycode!(AKEYCODE_TV_TERRESTRIAL_ANALOG); + insert_keycode!(AKEYCODE_TV_TERRESTRIAL_DIGITAL); + insert_keycode!(AKEYCODE_TV_SATELLITE); + insert_keycode!(AKEYCODE_TV_SATELLITE_BS); + insert_keycode!(AKEYCODE_TV_SATELLITE_CS); + insert_keycode!(AKEYCODE_TV_SATELLITE_SERVICE); + insert_keycode!(AKEYCODE_TV_NETWORK); + insert_keycode!(AKEYCODE_TV_ANTENNA_CABLE); + insert_keycode!(AKEYCODE_TV_INPUT_HDMI_1); + insert_keycode!(AKEYCODE_TV_INPUT_HDMI_2); + insert_keycode!(AKEYCODE_TV_INPUT_HDMI_3); + insert_keycode!(AKEYCODE_TV_INPUT_HDMI_4); + insert_keycode!(AKEYCODE_TV_INPUT_COMPOSITE_1); + insert_keycode!(AKEYCODE_TV_INPUT_COMPOSITE_2); + insert_keycode!(AKEYCODE_TV_INPUT_COMPONENT_1); + insert_keycode!(AKEYCODE_TV_INPUT_COMPONENT_2); + insert_keycode!(AKEYCODE_TV_INPUT_VGA_1); + insert_keycode!(AKEYCODE_TV_AUDIO_DESCRIPTION); + insert_keycode!(AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP); + insert_keycode!(AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN); + insert_keycode!(AKEYCODE_TV_ZOOM_MODE); + insert_keycode!(AKEYCODE_TV_CONTENTS_MENU); + insert_keycode!(AKEYCODE_TV_MEDIA_CONTEXT_MENU); + insert_keycode!(AKEYCODE_TV_TIMER_PROGRAMMING); + insert_keycode!(AKEYCODE_HELP); + insert_keycode!(AKEYCODE_NAVIGATE_PREVIOUS); + insert_keycode!(AKEYCODE_NAVIGATE_NEXT); + insert_keycode!(AKEYCODE_NAVIGATE_IN); + insert_keycode!(AKEYCODE_NAVIGATE_OUT); + insert_keycode!(AKEYCODE_STEM_PRIMARY); + insert_keycode!(AKEYCODE_STEM_1); + insert_keycode!(AKEYCODE_STEM_2); + insert_keycode!(AKEYCODE_STEM_3); + insert_keycode!(AKEYCODE_DPAD_UP_LEFT); + insert_keycode!(AKEYCODE_DPAD_DOWN_LEFT); + insert_keycode!(AKEYCODE_DPAD_UP_RIGHT); + insert_keycode!(AKEYCODE_DPAD_DOWN_RIGHT); + insert_keycode!(AKEYCODE_MEDIA_SKIP_FORWARD); + insert_keycode!(AKEYCODE_MEDIA_SKIP_BACKWARD); + insert_keycode!(AKEYCODE_MEDIA_STEP_FORWARD); + insert_keycode!(AKEYCODE_MEDIA_STEP_BACKWARD); + insert_keycode!(AKEYCODE_SOFT_SLEEP); + insert_keycode!(AKEYCODE_CUT); + insert_keycode!(AKEYCODE_COPY); + insert_keycode!(AKEYCODE_PASTE); + insert_keycode!(AKEYCODE_SYSTEM_NAVIGATION_UP); + insert_keycode!(AKEYCODE_SYSTEM_NAVIGATION_DOWN); + insert_keycode!(AKEYCODE_SYSTEM_NAVIGATION_LEFT); + insert_keycode!(AKEYCODE_SYSTEM_NAVIGATION_RIGHT); + insert_keycode!(AKEYCODE_ALL_APPS); + insert_keycode!(AKEYCODE_REFRESH); + insert_keycode!(AKEYCODE_THUMBS_UP); + insert_keycode!(AKEYCODE_THUMBS_DOWN); + insert_keycode!(AKEYCODE_PROFILE_SWITCH); + insert_keycode!(AKEYCODE_VIDEO_APP_1); + insert_keycode!(AKEYCODE_VIDEO_APP_2); + insert_keycode!(AKEYCODE_VIDEO_APP_3); + insert_keycode!(AKEYCODE_VIDEO_APP_4); + insert_keycode!(AKEYCODE_VIDEO_APP_5); + insert_keycode!(AKEYCODE_VIDEO_APP_6); + insert_keycode!(AKEYCODE_VIDEO_APP_7); + insert_keycode!(AKEYCODE_VIDEO_APP_8); + insert_keycode!(AKEYCODE_FEATURED_APP_1); + insert_keycode!(AKEYCODE_FEATURED_APP_2); + insert_keycode!(AKEYCODE_FEATURED_APP_3); + insert_keycode!(AKEYCODE_FEATURED_APP_4); + insert_keycode!(AKEYCODE_DEMO_APP_1); + insert_keycode!(AKEYCODE_DEMO_APP_2); + insert_keycode!(AKEYCODE_DEMO_APP_3); + insert_keycode!(AKEYCODE_DEMO_APP_4); + insert_keycode!(AKEYCODE_KEYBOARD_BACKLIGHT_DOWN); + insert_keycode!(AKEYCODE_KEYBOARD_BACKLIGHT_UP); + insert_keycode!(AKEYCODE_KEYBOARD_BACKLIGHT_TOGGLE); + insert_keycode!(AKEYCODE_STYLUS_BUTTON_PRIMARY); + insert_keycode!(AKEYCODE_STYLUS_BUTTON_SECONDARY); + insert_keycode!(AKEYCODE_STYLUS_BUTTON_TERTIARY); + insert_keycode!(AKEYCODE_STYLUS_BUTTON_TAIL); + insert_keycode!(AKEYCODE_RECENT_APPS); + insert_keycode!(AKEYCODE_MACRO_1); + insert_keycode!(AKEYCODE_MACRO_2); + insert_keycode!(AKEYCODE_MACRO_3); + insert_keycode!(AKEYCODE_MACRO_4); + + map +} + +// Build the axes map +fn build_axes_map() -> HashMap { + let mut map = HashMap::new(); + macro_rules! insert_axis { + ($name:ident) => { + let (name, value) = define_axis!($name); + map.insert(name, value); + }; + } + insert_axis!(AMOTION_EVENT_AXIS_X); + insert_axis!(AMOTION_EVENT_AXIS_Y); + insert_axis!(AMOTION_EVENT_AXIS_PRESSURE); + insert_axis!(AMOTION_EVENT_AXIS_SIZE); + insert_axis!(AMOTION_EVENT_AXIS_TOUCH_MAJOR); + insert_axis!(AMOTION_EVENT_AXIS_TOUCH_MINOR); + insert_axis!(AMOTION_EVENT_AXIS_TOOL_MAJOR); + insert_axis!(AMOTION_EVENT_AXIS_TOOL_MINOR); + insert_axis!(AMOTION_EVENT_AXIS_ORIENTATION); + insert_axis!(AMOTION_EVENT_AXIS_VSCROLL); + insert_axis!(AMOTION_EVENT_AXIS_HSCROLL); + insert_axis!(AMOTION_EVENT_AXIS_Z); + insert_axis!(AMOTION_EVENT_AXIS_RX); + insert_axis!(AMOTION_EVENT_AXIS_RY); + insert_axis!(AMOTION_EVENT_AXIS_RZ); + insert_axis!(AMOTION_EVENT_AXIS_HAT_X); + insert_axis!(AMOTION_EVENT_AXIS_HAT_Y); + insert_axis!(AMOTION_EVENT_AXIS_LTRIGGER); + insert_axis!(AMOTION_EVENT_AXIS_RTRIGGER); + insert_axis!(AMOTION_EVENT_AXIS_THROTTLE); + insert_axis!(AMOTION_EVENT_AXIS_RUDDER); + insert_axis!(AMOTION_EVENT_AXIS_WHEEL); + insert_axis!(AMOTION_EVENT_AXIS_GAS); + insert_axis!(AMOTION_EVENT_AXIS_BRAKE); + insert_axis!(AMOTION_EVENT_AXIS_DISTANCE); + insert_axis!(AMOTION_EVENT_AXIS_TILT); + insert_axis!(AMOTION_EVENT_AXIS_SCROLL); + insert_axis!(AMOTION_EVENT_AXIS_RELATIVE_X); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_1); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_2); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_3); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_4); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_5); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_6); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_7); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_8); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_9); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_10); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_11); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_12); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_13); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_14); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_15); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_16); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT); + map +} + +// Build the flags map +fn build_flags_map() -> HashMap { + let mut map = HashMap::new(); + macro_rules! insert_flag { + ($name:ident) => { + let (name, value) = define_flag!($name); + map.insert(name, value); + }; + } + insert_flag!(POLICY_FLAG_VIRTUAL); + insert_flag!(POLICY_FLAG_FUNCTION); + insert_flag!(POLICY_FLAG_GESTURE); + insert_flag!(POLICY_FLAG_WAKE); + insert_flag!(POLICY_FLAG_FALLBACK_USAGE_MAPPING); + map +} + +// Static lookup tables (lazily initialized) +static KEYCODES: OnceLock> = OnceLock::new(); +static AXES: OnceLock> = OnceLock::new(); +static FLAGS: OnceLock> = OnceLock::new(); + +fn get_keycodes() -> &'static HashMap { + KEYCODES.get_or_init(build_keycodes_map) +} + +fn get_axes() -> &'static HashMap { + AXES.get_or_init(build_axes_map) +} + +fn get_flags() -> &'static HashMap { + FLAGS.get_or_init(build_flags_map) +} + +/// Look up a key code by its label. +pub fn get_key_code_by_label(label: &str) -> Option { + get_keycodes().get(&format!("AKEYCODE_{}", label)).copied() +} + +/// Look up an axis by its label. +pub fn get_axis_by_label(label: &str) -> Option { + get_axes() + .get(&format!("AMOTION_EVENT_AXIS_{}", label)) + .copied() +} + +/// Look up a key flag by its label. +pub fn get_key_flag_by_label(label: &str) -> Option { + get_flags().get(&format!("POLICY_FLAG_{}", label)).copied() +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map.rs new file mode 100644 index 0000000000..83a2994cd3 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map.rs @@ -0,0 +1,390 @@ +//! Key layout map parser for Android key layout files. +//! +//! This module provides functionality to parse Android key layout (.kl) files +//! and map scan codes to Android key codes. +//! +//! AOSP keylayout files can be found at: +//! https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/base/data/keyboards/ + +use crate::android::android_codes::POLICY_FLAG_FUNCTION; +use crate::android::keylayout::input_event_lookup::{ + get_axis_by_label, get_key_code_by_label, get_key_flag_by_label, +}; +use crate::android::keylayout::tokenizer::Tokenizer; +use std::collections::HashMap; + +/// Describes a mapping from keyboard scan codes to Android key codes. +/// +/// This object is immutable after it has been loaded. +pub struct KeyLayoutMap { + keys_by_scan_code: HashMap, + axes: HashMap, +} + +/// Represents a key mapping entry. +#[derive(Debug, Clone)] +pub struct KeyLayoutKey { + pub key_code: u32, + pub flags: u32, +} + +/// Represents axis information for joystick/gamepad axes. +#[derive(Debug, Clone)] +pub struct KeyLayoutAxisInfo { + pub mode: KeyLayoutAxisMode, + pub axis: u32, + pub high_axis: Option, + pub split_value: Option, + pub flat_override: Option, +} + +/// Axis mapping mode. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum KeyLayoutAxisMode { + /// Axis value is reported directly. + Normal, + /// Axis value should be inverted before reporting. + Invert, + /// Axis value should be split into two axes. + Split, +} + +const WHITESPACE: &str = " \t\r"; + +impl KeyLayoutMap { + /// Load a key layout map from a file path. + pub fn load_from_file(file_path: &str) -> Result { + let tokenizer = Tokenizer::from_file(file_path)?; + Self::load(tokenizer) + } + + /// Load a key layout map from file contents (useful for testing). + pub fn load_from_contents(contents: &str) -> Result { + let tokenizer = Tokenizer::from_contents("", contents); + Self::load(tokenizer) + } + + fn load(mut tokenizer: Tokenizer) -> Result { + let mut map = KeyLayoutMap { + keys_by_scan_code: HashMap::new(), + axes: HashMap::new(), + }; + + let mut parser = Parser::new(&mut map, &mut tokenizer); + parser.parse()?; + + Ok(map) + } + + /// Map a scan code to an Android key code. + /// + /// Returns `Ok((key_code, flags))` on success, or `None` if not found. + pub fn map_key(&self, scan_code: u32) -> Option { + self.keys_by_scan_code.get(&scan_code).cloned() + } + + /// Map a scan code to axis information. + /// + /// Returns `Some(axis_info)` if the scan code maps to an axis, or `None` if not found. + pub fn map_axis(&self, scan_code: u32) -> Option { + self.axes.get(&scan_code).cloned() + } + + /// Find all scan codes that map to the given key code. + /// + /// Only considers keys without the FUNCTION flag. + pub fn find_scan_codes_for_key(&self, key_code: u32) -> Vec { + self.keys_by_scan_code + .iter() + .filter_map(|(scan_code, key)| { + if key.key_code == key_code && (key.flags & POLICY_FLAG_FUNCTION) == 0 { + Some(*scan_code) + } else { + None + } + }) + .collect() + } +} + +/// Parser for key layout map files. +struct Parser<'a> { + map: &'a mut KeyLayoutMap, + tokenizer: &'a mut Tokenizer, +} + +impl<'a> Parser<'a> { + fn new(map: &'a mut KeyLayoutMap, tokenizer: &'a mut Tokenizer) -> Self { + Self { map, tokenizer } + } + + fn parse(&mut self) -> Result<(), String> { + while !self.tokenizer.is_eof() { + self.tokenizer.skip_delimiters(WHITESPACE); + + if !self.tokenizer.is_eol() && self.tokenizer.peek_char() != '#' { + let keyword_token = self.tokenizer.next_token(WHITESPACE); + let mut skipped_line: bool = false; + + match keyword_token.as_str() { + "key" => { + self.tokenizer.skip_delimiters(WHITESPACE); + let code_token = self.tokenizer.next_token(WHITESPACE); + + // Skip "usage" entries - we only support scan codes + if code_token == "usage" { + skipped_line = true; + } else { + self.parse_key(&code_token)?; + } + } + "axis" => { + self.tokenizer.skip_delimiters(WHITESPACE); + self.parse_axis()?; + } + "led" | "sensor" => { + // Skip LEDs and sensors, we don't need them + self.tokenizer.next_line(); + continue; + } + "usage" => { + // Skip usage code entries - evdev only provides scan codes + self.tokenizer.next_line(); + continue; + } + _ => { + return Err(format!( + "{}: Expected keyword, got '{}'.", + self.tokenizer.get_location(), + keyword_token + )); + } + } + + self.tokenizer.skip_delimiters(WHITESPACE); + + if !skipped_line && !self.tokenizer.is_eol() && self.tokenizer.peek_char() != '#' { + return Err(format!( + "{}: Expected end of line or trailing comment, got '{}'.", + self.tokenizer.get_location(), + self.tokenizer.peek_remainder_of_line() + )); + } + } + + self.tokenizer.next_line(); + } + + Ok(()) + } + + fn parse_key(&mut self, code_token: &str) -> Result<(), String> { + let scan_code = parse_int(code_token).ok_or_else(|| { + format!( + "{}: Expected key scan code number, got '{}'.", + self.tokenizer.get_location(), + code_token + ) + })?; + + if scan_code < 0 { + return Err(format!( + "{} is not a valid key scan code. Negative numbers are not allowed.", + scan_code + )); + } + + if self.map.keys_by_scan_code.contains_key(&(scan_code as u32)) { + return Err(format!( + "{}: Duplicate entry for key scan code '{}'.", + self.tokenizer.get_location(), + code_token + )); + } + + self.tokenizer.skip_delimiters(WHITESPACE); + let key_code_token = self.tokenizer.next_token(WHITESPACE); + let key_code = get_key_code_by_label(&key_code_token); + + let mut flags = 0u32; + loop { + self.tokenizer.skip_delimiters(WHITESPACE); + if self.tokenizer.is_eol() || self.tokenizer.peek_char() == '#' { + break; + } + + let flag_token = self.tokenizer.next_token(WHITESPACE); + let flag = get_key_flag_by_label(&flag_token).ok_or_else(|| { + format!( + "{}: Expected key flag label, got '{}'.", + self.tokenizer.get_location(), + flag_token + ) + })?; + + if (flags & flag) != 0 { + return Err(format!( + "{}: Duplicate key flag '{}'.", + self.tokenizer.get_location(), + flag_token + )); + } + flags |= flag; + } + + // Only insert if the key code is known + if let Some(key_code) = key_code { + let key = KeyLayoutKey { key_code, flags }; + self.map.keys_by_scan_code.insert(scan_code as u32, key); + } + + Ok(()) + } + + fn parse_axis(&mut self) -> Result<(), String> { + let scan_code_token = self.tokenizer.next_token(WHITESPACE); + let scan_code = parse_int(&scan_code_token).ok_or_else(|| { + format!( + "{}: Expected axis scan code number, got '{}'.", + self.tokenizer.get_location(), + scan_code_token + ) + })?; + + if scan_code < 0 { + return Err(format!( + "{} is not a valid key scan code for an axis. Negative numbers are not allowed.", + scan_code + )); + } + + if self.map.axes.contains_key(&(scan_code as u32)) { + return Err(format!( + "{}: Duplicate entry for axis scan code '{}'.", + self.tokenizer.get_location(), + scan_code_token + )); + } + + let mut axis_mode: KeyLayoutAxisMode = KeyLayoutAxisMode::Normal; + let axis: u32; + let mut split_value: Option = None; + let mut high_axis: Option = None; + let mut flat_override: Option = None; + + self.tokenizer.skip_delimiters(WHITESPACE); + let token = self.tokenizer.next_token(WHITESPACE); + + if token == "invert" { + axis_mode = KeyLayoutAxisMode::Invert; + + self.tokenizer.skip_delimiters(WHITESPACE); + + let axis_token = self.tokenizer.next_token(WHITESPACE); + + axis = get_axis_by_label(&axis_token).ok_or_else(|| { + format!( + "{}: Expected inverted axis label, got '{}'.", + self.tokenizer.get_location(), + axis_token + ) + })?; + } else if token == "split" { + axis_mode = KeyLayoutAxisMode::Split; + + self.tokenizer.skip_delimiters(WHITESPACE); + let split_token = self.tokenizer.next_token(WHITESPACE); + let split_value_raw = parse_int(&split_token).ok_or_else(|| { + format!( + "{}: Expected split value, got '{}'.", + self.tokenizer.get_location(), + split_token + ) + })?; + split_value = Some(split_value_raw); + + self.tokenizer.skip_delimiters(WHITESPACE); + let low_axis_token = self.tokenizer.next_token(WHITESPACE); + axis = get_axis_by_label(&low_axis_token).ok_or_else(|| { + format!( + "{}: Expected low axis label, got '{}'.", + self.tokenizer.get_location(), + low_axis_token + ) + })?; + + self.tokenizer.skip_delimiters(WHITESPACE); + let high_axis_token = self.tokenizer.next_token(WHITESPACE); + let high_axis_raw = get_axis_by_label(&high_axis_token).ok_or_else(|| { + format!( + "{}: Expected high axis label, got '{}'.", + self.tokenizer.get_location(), + high_axis_token + ) + })?; + high_axis = Some(high_axis_raw); + } else { + axis = get_axis_by_label(&token).ok_or_else(|| { + format!( + "{}: Expected axis label, 'split' or 'invert', got '{}'.", + self.tokenizer.get_location(), + token + ) + })?; + } + + loop { + self.tokenizer.skip_delimiters(WHITESPACE); + if self.tokenizer.is_eol() || self.tokenizer.peek_char() == '#' { + break; + } + + let keyword_token = self.tokenizer.next_token(WHITESPACE); + if keyword_token == "flat" { + self.tokenizer.skip_delimiters(WHITESPACE); + let flat_token = self.tokenizer.next_token(WHITESPACE); + let flat_override_raw = parse_int(&flat_token).ok_or_else(|| { + format!( + "{}: Expected flat value, got '{}'.", + self.tokenizer.get_location(), + flat_token + ) + })?; + flat_override = Some(flat_override_raw); + } else { + return Err(format!( + "{}: Expected keyword 'flat', got '{}'.", + self.tokenizer.get_location(), + keyword_token + )); + } + } + + let axis_info: KeyLayoutAxisInfo = KeyLayoutAxisInfo { + mode: axis_mode, + axis, + high_axis, + split_value, + flat_override, + }; + + self.map.axes.insert(scan_code as u32, axis_info); + Ok(()) + } +} + +/// Parse an integer from a string (supports decimal, hex with 0x prefix, and octal with 0 prefix). +pub fn parse_int(s: &str) -> Option { + if s.is_empty() { + return None; + } + + // Handle hex (0x prefix) and octal (0 prefix) + if s.starts_with("0x") || s.starts_with("0X") { + i32::from_str_radix(&s[2..], 16).ok() + } else if s.starts_with('0') && s.len() > 1 { + i32::from_str_radix(&s[1..], 8).ok() + } else { + s.parse::().ok() + } +} 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 new file mode 100644 index 0000000000..2584c32483 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map_manager.rs @@ -0,0 +1,206 @@ +use crate::android::keylayout::key_layout_map::{KeyLayoutKey, KeyLayoutMap}; +use crate::device_identifier::DeviceIdentifier; +use std::collections::HashMap; +use std::error::Error; +use std::fs; +use std::fs::File; +use std::io::ErrorKind; +use std::sync::{Arc, Mutex, OnceLock}; +use std::{env, io}; + +static KEY_LAYOUT_MANAGER: OnceLock> = OnceLock::new(); + +/// Manages KeyLayoutMap caching and key code mapping +/// This is the only file that directly interacts with KeyLayoutMap C bindings +/// and the only file that finds key layout file paths +pub struct KeyLayoutMapManager { + /// KeyLayoutMap cache + /// Maps device path to KeyLayoutMap handle + key_layout_maps: Mutex>>, +} + +impl KeyLayoutMapManager { + pub fn get() -> Arc { + Arc::clone(KEY_LAYOUT_MANAGER.get_or_init(|| Arc::new(Self::new()))) + } + + fn new() -> Self { + Self { + key_layout_maps: Mutex::new(HashMap::with_capacity(32)), + } + } + + /// Map a raw evdev key code to Android key code + /// Returns the android keycode and flags if the key is found in the map, otherwise, `None`. + pub fn map_key( + &self, + device_identifier: &DeviceIdentifier, + scan_code: u32, + ) -> Result, Box> { + self.get_key_layout_map_lazy(device_identifier) + .map(|map| map.map_key(scan_code)) + } + + // TODO use when they call grab devices + pub fn preload_key_layout_map( + &self, + device_identifier: &DeviceIdentifier, + ) -> Result<(), Box> { + self.get_key_layout_map_lazy(device_identifier).map(|_| ()) + } + + // TODO test + fn get_key_layout_map_lazy( + &self, + device_identifier: &DeviceIdentifier, + ) -> Result, Box> { + let mut key_layout_maps = self.key_layout_maps.lock().unwrap(); + + if let Some(key_layout_map) = key_layout_maps.get(device_identifier) { + return Ok(key_layout_map.clone()); + } + + let file_path = match self.find_key_layout_file_by_device_identifier(device_identifier) { + None => { + let error = io::Error::new( + ErrorKind::NotFound, + format!( + "Key layout map file not found for device {:?}", + device_identifier + ), + ); + + return Err(error.into()); + } + Some(path) => path, + }; + + let key_layout_map = Arc::new(KeyLayoutMap::load_from_file(file_path.as_str())?); + + key_layout_maps.insert(device_identifier.clone(), key_layout_map.clone()); + + Ok(key_layout_map) + } + + /// Find key layout file path by name + /// Searches system repository and user repository + /// Returns None if not found + /// + /// This code is translated from AOSP frameworks/native/libs/input/InputDevice.cpp + fn find_key_layout_file_by_name(&self, name: &str) -> Option { + // Search system repository + let mut path_prefixes = vec![ + "/product/usr/".to_string(), + "/system_ext/usr/".to_string(), + "/odm/usr/".to_string(), + "/vendor/usr/".to_string(), + "/system/usr/".to_string(), + ]; + + // ANDROID_ROOT may not be set on host + if let Ok(android_root) = env::var("ANDROID_ROOT") { + path_prefixes.push(format!("{}/usr/", android_root)); + } + + // Try each system path prefix + for prefix in &path_prefixes { + let path = format!("{}keylayout/{}.kl", prefix, name); + + match fs::metadata(&path) { + Ok(metadata) if metadata.is_file() => { + if let Ok(_) = File::open(&path) { + return Some(path); + } + } + Err(e) if e.kind() != ErrorKind::NotFound => { + debug!("Error accessing {}: {}", path, e); + } + _ => {} + } + } + + // Search user repository + if let Ok(android_data) = env::var("ANDROID_DATA") { + let path = format!("{}/system/devices/keylayout/{}.kl", android_data, name); + + match fs::metadata(&path) { + Ok(metadata) if metadata.is_file() => { + if let Ok(_) = File::open(&path) { + return Some(path); + } + } + Err(e) if e.kind() != ErrorKind::NotFound => { + warn!("Error accessing user config file {}: {}", path, e); + } + _ => {} + } + } + + None + } + + /// Find key layout file path by device identifier + /// Tries multiple naming schemes based on vendor/product/version, then device name, then Generic + /// Returns None if not found + /// + /// This code is translated from AOSP frameworks/native/libs/input/InputDevice.cpp + fn find_key_layout_file_by_device_identifier( + &self, + device_identifier: &DeviceIdentifier, + ) -> Option { + let name = device_identifier.name.as_str(); + let vendor = device_identifier.vendor; + let product = device_identifier.product; + let version = device_identifier.version; + + // Try vendor/product/version path first + if vendor != 0 && product != 0 { + if version != 0 { + let version_name = format!( + "Vendor_{:04x}_Product_{:04x}_Version_{:04x}", + vendor, product, version + ); + if let Some(path) = self.find_key_layout_file_by_name(&version_name) { + info!("Found key layout map by version path {}", path); + return Some(path); + } + } + + // Try vendor/product + let product_name = format!("Vendor_{:04x}_Product_{:04x}", vendor, product); + if let Some(path) = self.find_key_layout_file_by_name(&product_name) { + info!("Found key layout map by product path {}", path); + return Some(path); + } + } + + // Try device name (canonical) + let canonical_name = get_canonical_name(name); + if let Some(path) = self.find_key_layout_file_by_name(&canonical_name) { + info!("Found key layout map by name path {}", path); + return Some(path); + } + + // As a last resort, try Generic + self.find_key_layout_file_by_name("Generic") + } +} + +impl Default for KeyLayoutMapManager { + fn default() -> Self { + Self::new() + } +} + +/// Get canonical name with all invalid characters replaced by underscores +fn get_canonical_name(name: &str) -> String { + name.chars() + .map(|ch| { + if ch.is_ascii_alphanumeric() || ch == '-' || ch == '_' { + ch + } else { + '_' + } + }) + .collect() +} 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 new file mode 100644 index 0000000000..3419ae5adc --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/mod.rs @@ -0,0 +1,4 @@ +pub mod input_event_lookup; +pub mod key_layout_map; +pub mod key_layout_map_manager; +pub mod tokenizer; diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/tokenizer.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/tokenizer.rs new file mode 100644 index 0000000000..e97f022f69 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/tokenizer.rs @@ -0,0 +1,130 @@ +//! Simple tokenizer for parsing key layout map files. +//! +//! This tokenizer tracks position in a buffer and provides methods for +//! reading tokens, characters, and navigating through lines. + +use std::str; + +/// Simple tokenizer for parsing ASCII text files line by line. +pub struct Tokenizer { + filename: String, + buffer: String, + current: usize, + line_number: usize, +} + +impl Tokenizer { + /// Create a tokenizer from file contents. + pub fn from_contents(filename: &str, contents: &str) -> Self { + Self { + filename: filename.to_string(), + buffer: contents.to_string(), + current: 0, + line_number: 1, + } + } + + /// Create a tokenizer from a file path. + pub fn from_file(file_path: &str) -> Result { + let contents = std::fs::read_to_string(file_path) + .map_err(|e| format!("Error opening file '{}': {}", file_path, e))?; + Ok(Self::from_contents(file_path, &contents)) + } + + /// Returns true if at the end of the file. + pub fn is_eof(&self) -> bool { + self.current >= self.buffer.len() + } + + /// Returns true if at the end of the line or end of the file. + pub fn is_eol(&self) -> bool { + self.is_eof() || self.peek_char() == '\n' + } + + /// Formats a location string consisting of the filename and current line number. + /// Returns a string like "MyFile.txt:33". + pub fn get_location(&self) -> String { + format!("{}:{}", self.filename, self.line_number) + } + + /// Gets the character at the current position. + /// Returns null character at end of file. + pub fn peek_char(&self) -> char { + if self.is_eof() { + '\0' + } else { + self.buffer[self.current..].chars().next().unwrap_or('\0') + } + } + + /// Gets the remainder of the current line as a string, excluding the newline character. + pub fn peek_remainder_of_line(&self) -> String { + if self.is_eof() { + return String::new(); + } + + let remaining = &self.buffer[self.current..]; + let line_end = remaining + .find('\n') + .map(|pos| self.current + pos) + .unwrap_or(self.buffer.len()); + + self.buffer[self.current..line_end].to_string() + } + + /// Gets the character at the current position and advances past it. + /// Returns null character at end of file. + pub fn next_char(&mut self) -> char { + if self.is_eof() { + return '\0'; + } + + let ch = self.peek_char(); + if ch == '\n' { + self.line_number += 1; + } + self.current += ch.len_utf8(); + ch + } + + /// Gets the next token on this line stopping at the specified delimiters + /// or the end of the line whichever comes first. + /// Returns the token or an empty string if the current character is a delimiter + /// or is at the end of the line. + pub fn next_token(&mut self, delimiters: &str) -> String { + self.skip_delimiters(delimiters); + + if self.is_eol() { + return String::new(); + } + + let start = self.current; + while !self.is_eol() && !is_delimiter(self.peek_char(), delimiters) { + self.next_char(); + } + + self.buffer[start..self.current].to_string() + } + + /// Advances to the next line. + /// Does nothing if already at the end of the file. + pub fn next_line(&mut self) { + while !self.is_eof() && self.peek_char() != '\n' { + self.next_char(); + } + if !self.is_eof() { + self.next_char(); // consume the newline + } + } + + /// Skips over the specified delimiters in the line. + pub fn skip_delimiters(&mut self, delimiters: &str) { + while !self.is_eol() && is_delimiter(self.peek_char(), delimiters) { + self.next_char(); + } + } +} + +fn is_delimiter(ch: char, delimiters: &str) -> bool { + delimiters.contains(ch) +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/mod.rs b/evdev/src/main/rust/evdev_manager/core/src/android/mod.rs new file mode 100644 index 0000000000..44bf1d8111 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/mod.rs @@ -0,0 +1,2 @@ +pub mod android_codes; +pub mod keylayout; diff --git a/evdev/src/main/rust/evdev_manager/core/src/device_identifier.rs b/evdev/src/main/rust/evdev_manager/core/src/device_identifier.rs new file mode 100644 index 0000000000..e3eef5c522 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/device_identifier.rs @@ -0,0 +1,9 @@ +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct DeviceIdentifier { + pub name: String, + pub bus: u16, + pub vendor: u16, + pub product: u16, + /// Version is used internally for key layout matching but not exposed via JNI + pub version: u16, +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_error.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_error.rs new file mode 100644 index 0000000000..49a6028708 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_error.rs @@ -0,0 +1,123 @@ +/// Error type for evdev operations +#[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 { + match -code { + libc::ENOENT => Self::NoSuchFileOrDirectory, + libc::EIO => Self::IoError, + libc::EBADF => Self::BadFileDescriptor, + libc::EAGAIN => Self::WouldBlock, + libc::ENOMEM => Self::OutOfMemory, + libc::EACCES => Self::PermissionDenied, + libc::ENODEV => Self::NoSuchDevice, + libc::EINVAL => Self::InvalidArgument, + _ => Self::Unknown(code), + } + } + + pub fn to_code(self) -> i32 { + -(match self { + Self::NoSuchFileOrDirectory => libc::ENOENT, + Self::IoError => libc::EIO, + Self::BadFileDescriptor => libc::EBADF, + Self::WouldBlock => libc::EAGAIN, + Self::OutOfMemory => libc::ENOMEM, + Self::PermissionDenied => libc::EACCES, + Self::NoSuchDevice => libc::ENODEV, + Self::InvalidArgument => libc::EINVAL, + 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 { + pub fn new(code: i32) -> Self { + let kind = EvdevErrorCode::from_code(code); + let message = if let EvdevErrorCode::Unknown(_) = kind { + format!("evdev error: {}", code) + } else { + format!("evdev error: {} ({})", kind.description(), -code) + }; + + Self { + kind, + code, + message, + } + } + pub fn from_enum(error_code: EvdevErrorCode) -> Self { + let message = format!( + "evdev error: {} ({})", + error_code.description(), + -error_code.to_code() + ); + + Self { + kind: error_code, + code: error_code.to_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 std::error::Error for EvdevError {} + +impl From for std::io::Error { + fn from(err: EvdevError) -> Self { + std::io::Error::from_raw_os_error(-err.code) + } +} + +impl From for EvdevError { + fn from(err: std::io::Error) -> Self { + let code = err.raw_os_error().unwrap_or(-1); + EvdevError::new(-code) + } +} 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 new file mode 100644 index 0000000000..dcd6d2227e --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs @@ -0,0 +1,482 @@ +use crate::device_identifier::DeviceIdentifier; +use crate::evdev_error::EvdevError; +use crate::grabbed_device::GrabbedDevice; +use crate::runtime::get_runtime; +use evdev::util::event_code_to_int; +use evdev::{DeviceWrapper, InputEvent, ReadFlag, ReadStatus}; +use mio::event::Event; +use mio::unix::SourceFd; +use mio::{Events, Interest, Poll, Registry, 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 tokio::task::JoinHandle; + +/// Key for device path map - only JNI-exposed fields (excludes version). +/// Used for O(1) HashMap lookup when matching devices. +#[derive(Hash, Eq, PartialEq)] +struct DeviceIdentifierKey { + name: String, + bus: u16, + vendor: u16, + product: u16, +} + +impl From<&DeviceIdentifier> for DeviceIdentifierKey { + fn from(id: &DeviceIdentifier) -> Self { + DeviceIdentifierKey { + name: id.name.clone(), + bus: id.bus, + vendor: id.vendor, + product: id.product, + } + } +} + +static EVENT_LOOP_MANAGER: OnceLock = OnceLock::new(); + +const WAKER_TOKEN: Token = Token(0); + +/// 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: &DeviceIdentifier, event: &InputEvent) -> bool; + +pub struct EventLoopManager { + stop_flag: Arc, + poll: Arc>, + registry: Arc, + join_handle: RwLock>>, + waker: Waker, + observers: Arc>>, + grabbed_devices: Arc>>, +} + +impl EventLoopManager { + pub fn get() -> &'static EventLoopManager { + EVENT_LOOP_MANAGER.get_or_init(Self::new) + } + + fn new() -> 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 { + stop_flag: Arc::new(AtomicBool::new(false)), + poll: poll_lock, + registry: Arc::new(registry), + join_handle: RwLock::new(None), + waker, + observers: Arc::new(RwLock::new(Slab::with_capacity(16))), + grabbed_devices: Arc::new(RwLock::new(Slab::with_capacity(32))), + } + } + + pub fn start(&self) -> Result<(), EvdevError> { + let is_running = { self.join_handle.read().unwrap().is_some() }; + + 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); + + let observers = self.observers.clone(); + let grabbed_devices = self.grabbed_devices.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(); + EventLoopThread::new(stop_flag_clone, poll_lock_clone, observers, grabbed_devices) + .start(); + }); + + 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)) + } + } + } + } + + pub fn stop(&self) -> Result<(), io::Error> { + let handle_option = self.join_handle.write().unwrap().take(); + + match handle_option { + None => { + error!("Event loop not running"); + } + Some(handle) => { + self.stop_flag.store(true, Ordering::Relaxed); + self.waker.wake()?; + + // Wait for the loop to finish (with timeout) + let start = Instant::now(); + while !handle.is_finished() { + if start.elapsed() > Duration::from_secs(2) { + error!("Event loop did not stop in time, aborting"); + handle.abort(); + + return Err(io::Error::new( + ErrorKind::TimedOut, + "Event loop did not stop in time", + )); + } + std::thread::sleep(Duration::from_millis(10)); + } + } + } + + Ok(()) + } + + /// 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, DeviceIdentifier)> { + let mut devices = self.grabbed_devices.write().unwrap(); + + // Find devices to ungrab (currently grabbed but not in requested list) + // Compare by JNI-exposed fields only (name, bus, vendor, product), not version + let keys_to_remove: Vec = devices + .iter() + .filter(|(_, device)| { + !requested_devices + .iter() + .any(|req| device_id_matches_jni_fields(&device.device_id, req)) + }) + .map(|(key, _)| key) + .collect(); + + // Ungrab devices that are no longer requested + for key in keys_to_remove { + if let Some(device) = devices.get(key) { + let fd = device.evdev.lock().unwrap().as_raw_fd(); + let mut source_fd = SourceFd(&fd); + self.registry + .deregister(&mut source_fd) + .inspect_err(|e| { + error!("Failed to deregister device {}: {}", device.device_path, e) + }) + .ok(); + info!("Ungrabbed device: {}", device.device_path); + } + devices.remove(key); + } + + // Find devices to grab (requested but not currently grabbed) + // Compare by JNI-exposed fields only (name, bus, vendor, product), not version + let devices_to_grab: Vec<&DeviceIdentifier> = requested_devices + .iter() + .filter(|req| { + !devices + .iter() + .any(|(_, device)| device_id_matches_jni_fields(&device.device_id, req)) + }) + .collect(); + + // Build device path map once for all devices to grab - O(m) instead of O(n*m) + if !devices_to_grab.is_empty() { + // Collect uinput paths while we still hold the lock + let uinput_paths: Vec = devices + .iter() + .map(|(_, device)| device.uinput.devnode().unwrap().to_string()) + .collect(); + + let device_path_map = build_device_path_map(&uinput_paths); + + for device_id in devices_to_grab { + let key = DeviceIdentifierKey::from(device_id); + match device_path_map.get(&key) { + Some(path) => { + if let Err(e) = self.grab_device_internal(&mut devices, path) { + error!("Failed to grab device {}: {:?}", path, e); + } + } + None => { + warn!("Device not found: {:?}", device_id); + } + } + } + } + + // 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 + .iter() + .map(|(slab_key, device)| (slab_key, device.device_id.clone())) + .collect() + } + + /// Internal method to grab a device and register it with the poll + fn grab_device_internal( + &self, + devices: &mut Slab, + path: &str, + ) -> Result> { + let device = GrabbedDevice::new(path)?; + let fd = device.evdev.lock().unwrap().as_raw_fd(); + let key = devices.insert(device); + + let mut source_fd = SourceFd(&fd); + + // Register with key + 1 because 0 is reserved for the waker + self.registry + .register(&mut source_fd, Token(key + 1), Interest::READABLE) + .inspect_err(|e| { + // Remove device on registration failure + devices.remove(key); + error!("Failed to register device {}: {}", path, e); + })?; + + info!("Grabbed device: {}", path); + Ok(key) + } + + /// Write an event to a grabbed device's uinput. + /// The device_id is the slab key returned by set_grabbed_devices(), enabling O(1) lookup. + pub fn write_event( + &self, + device_id: usize, + event_type: u32, + 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))?; + device.write_event(event_type, code, value) + } + + pub fn register_observer(&self, observer: EvdevObserver) { + self.observers.write().unwrap().insert(observer); + } + + /// Get the paths to all the real (non uinput) connected devices. + pub fn get_all_real_devices(&self) -> Result, EvdevError> { + let mut paths: Vec = Vec::new(); + + let dir = read_dir("/dev/input")?; + let grabbed_devices = self.grabbed_devices.read().unwrap(); + let uinput_paths: Vec<&str> = grabbed_devices + .iter() + .map(|(_, device)| device.uinput.devnode().unwrap()) + .collect(); + + for entry_result in dir { + match entry_result { + Ok(entry) => { + let path = entry.path(); + + // Do not return paths to uinput devices that were created. + if uinput_paths.contains(&path.to_str().unwrap()) { + continue; + } + + paths.push(path); + } + Err(_) => { + debug!( + "Failed to read /dev/input entry: {}", + entry_result.unwrap_err() + ); + } + } + } + + debug!("EvdevManager: get real devices: {:?}", paths); + Ok(paths) + } +} + +struct EventLoopThread { + stop_flag: Arc, + poll: Arc>, + observers: Arc>>, + grabbed_devices: Arc>>, +} + +impl EventLoopThread { + pub fn new( + stop_flag: Arc, + poll: Arc>, + observers: Arc>>, + grabbed_devices: Arc>>, + ) -> Self { + EventLoopThread { + stop_flag, + poll, + observers, + grabbed_devices, + } + } + + /// This blocks until the stop flag is set. + fn start(&self) { + let mut events = Events::with_capacity(128); + + info!("Started evdev event loop"); + + 'main: loop { + let mut poll = self.poll.write().unwrap(); + + match poll.poll(&mut events, None) { + Ok(_) => { + for event in events.iter() { + // Break out of the loop if the stop flag is set. + if event.token() == WAKER_TOKEN && self.stop_flag.load(Ordering::SeqCst) { + info!("Received waker token. Stopping evdev event loop"); + break 'main; + } + + self.on_poll_event(event); + } + } + Err(e) if e.kind() == ErrorKind::Interrupted => { + // Interrupted, continue polling + continue; + } + Err(e) => { + error!("EvdevManager poll error. Stopping loop: {}", e); + break; + } + } + } + + info!("Stopped evdev event loop"); + } + + fn on_poll_event(&self, event: &Event) { + let Token(key) = event.token(); + // Subtract 1 because Token(0) is reserved + let slab_key = key - 1; + + 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_observers(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; + } + } + } + } + + fn process_observers( + &self, + device_id: usize, + event: &InputEvent, + grabbed_device: &GrabbedDevice, + ) { + let mut consume = false; + + for (_, observer) in self.observers.read().unwrap().iter() { + if observer(device_id, &grabbed_device.device_id, event) { + consume = true; + } + } + + if !consume { + let (event_type, event_code) = event_code_to_int(&event.event_code); + grabbed_device + .write_event(event_type, event_code, event.value) + .inspect_err(|e| { + error!( + "Failed to passthrough event to {}. Event: {:?}. Error: {:?}", + grabbed_device.device_path, event, e + ) + }) + .ok(); + } + } +} + +/// Compare DeviceIdentifiers by JNI-exposed fields only (name, bus, vendor, product). +/// Version is excluded as it's not exposed through the Java API. +fn device_id_matches_jni_fields(a: &DeviceIdentifier, b: &DeviceIdentifier) -> bool { + a.name == b.name && a.bus == b.bus && a.vendor == b.vendor && a.product == b.product +} + +/// Build a map of DeviceIdentifierKey -> device path. +/// Scans /dev/input once for O(m) instead of O(n*m) when grabbing multiple devices. +fn build_device_path_map(uinput_paths: &[String]) -> HashMap { + let mut map = HashMap::new(); + + let Ok(dir) = read_dir("/dev/input") else { + return map; + }; + + for entry in dir.flatten() { + let path = entry.path(); + + // Skip uinput devices we created + if let Some(path_str) = path.to_str() { + if uinput_paths.iter().any(|p| p == path_str) { + continue; + } + } + + if let Ok(device) = evdev::Device::new_from_path(&path) { + let key = DeviceIdentifierKey { + name: device.name().unwrap_or("").to_string(), + bus: device.bustype(), + vendor: device.vendor_id(), + product: device.product_id(), + }; + if let Some(path_str) = path.to_str() { + map.insert(key, path_str.to_string()); + } + } + } + + map +} 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 new file mode 100644 index 0000000000..3bf9e382ff --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs @@ -0,0 +1,76 @@ +use crate::device_identifier::DeviceIdentifier; +use crate::evdev_error::EvdevError; +use evdev::enums::EV_SYN; +use evdev::{Device, DeviceWrapper, GrabMode, UInputDevice}; +use std::fs::OpenOptions; +use std::os::unix::fs::OpenOptionsExt; +use std::sync::Mutex; + +/// Device context containing all information about a grabbed evdev device +pub struct GrabbedDevice { + pub device_path: String, + pub device_id: DeviceIdentifier, + /// The libevdev Device can not be shared safely across threads so wrap it in a mutex. + pub evdev: Mutex, + pub uinput: UInputDevice, +} + +impl GrabbedDevice { + /// Create a new device context by grabbing a device + pub fn new(device_path: &str) -> Result { + // Open device with O_NONBLOCK so that the loop reading events eventually returns + // due to an EAGAIN error + + let file = OpenOptions::new() + .read(true) + .custom_flags(libc::O_NONBLOCK) + .open(device_path) + .map_err(EvdevError::from)?; + + // Create device from file + let mut evdev = Device::new_from_file(file).map_err(EvdevError::from)?; + + // Grab the device + evdev.grab(GrabMode::Grab).map_err(EvdevError::from)?; + + // Create uinput device for forwarding unconsumed events + let uinput = UInputDevice::create_from_device(&evdev).map_err(EvdevError::from)?; + + let device_id: DeviceIdentifier = DeviceIdentifier { + name: evdev.name().unwrap_or("").to_string(), + bus: evdev.bustype(), + vendor: evdev.vendor_id(), + product: evdev.product_id(), + version: evdev.version(), + }; + + Ok(Self { + evdev: Mutex::new(evdev), + device_id, + uinput, + device_path: device_path.to_string(), + }) + } + + /// Write an event to the uinput device + pub fn write_event(&self, event_type: u32, code: u32, value: i32) -> Result<(), EvdevError> { + self.uinput + .write_event(event_type, code, value) + .map_err(EvdevError::from)?; + + // Send SYN_REPORT + self.uinput + .write_syn_event(EV_SYN::SYN_REPORT) + .map_err(EvdevError::from)?; + + Ok(()) + } +} + +impl Drop for GrabbedDevice { + fn drop(&mut self) { + // Ungrab the device + let _ = self.evdev.lock().unwrap().grab(GrabMode::Ungrab); + // uinput device is dropped automatically + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/lib.rs b/evdev/src/main/rust/evdev_manager/core/src/lib.rs new file mode 100644 index 0000000000..a104ebb56e --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/lib.rs @@ -0,0 +1,8 @@ +#[macro_use] +extern crate log; +pub mod android; +pub mod device_identifier; +pub mod evdev_error; +pub mod event_loop; +pub mod grabbed_device; +pub mod runtime; diff --git a/evdev/src/main/rust/evdev_manager/core/src/runtime.rs b/evdev/src/main/rust/evdev_manager/core/src/runtime.rs new file mode 100644 index 0000000000..ba238adcf6 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/runtime.rs @@ -0,0 +1,14 @@ +use std::sync::OnceLock; +use tokio::runtime::Runtime; + +static RUNTIME: OnceLock = OnceLock::new(); + +pub fn get_runtime() -> &'static Runtime { + RUNTIME.get_or_init(|| { + tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) // Optional: limit threads to save resources on Android + .enable_all() + .build() + .expect("Failed to create Tokio runtime") + }) +} diff --git a/evdev/src/main/rust/evdev_manager/core/tests/input_event_lookup_test.rs b/evdev/src/main/rust/evdev_manager/core/tests/input_event_lookup_test.rs new file mode 100644 index 0000000000..ff0a4d83ef --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/input_event_lookup_test.rs @@ -0,0 +1,30 @@ +//! Integration tests for key layout map parsing. + +use evdev_manager_core::android::android_codes::{ + AKEYCODE_A, AKEYCODE_FUNCTION, AMOTION_EVENT_AXIS_X, POLICY_FLAG_VIRTUAL, +}; +use evdev_manager_core::android::keylayout::input_event_lookup; + +#[test] +fn test_function_key_lookup() { + let key_code = input_event_lookup::get_key_code_by_label("FUNCTION"); + assert_eq!(key_code, Some(AKEYCODE_FUNCTION)); +} + +#[test] +fn test_letter_key_lookup() { + let key_code = input_event_lookup::get_key_code_by_label("A"); + assert_eq!(key_code, Some(AKEYCODE_A)); +} + +#[test] +fn test_policy_flag_lookup() { + let code = input_event_lookup::get_key_flag_by_label("VIRTUAL"); + assert_eq!(code, Some(POLICY_FLAG_VIRTUAL)); +} + +#[test] +fn test_axis_lookup() { + let code = input_event_lookup::get_axis_by_label("X"); + assert_eq!(code, Some(AMOTION_EVENT_AXIS_X)); +} diff --git a/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_test.rs b/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_test.rs new file mode 100644 index 0000000000..5a7d2cb4bd --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_test.rs @@ -0,0 +1,262 @@ +//! Integration tests for key layout map parsing. +use evdev_manager_core::android::android_codes::{ + AKEYCODE_F4, AKEYCODE_FUNCTION, POLICY_FLAG_FUNCTION, +}; +use evdev_manager_core::android::keylayout::key_layout_map; +use evdev_manager_core::android::keylayout::key_layout_map::{KeyLayoutAxisMode, KeyLayoutMap}; +use std::fs; +use std::path::Path; + +fn get_test_data_dir() -> &'static str { + env!("CARGO_MANIFEST_DIR") +} + +fn get_test_data_path() -> String { + format!("{}/tests/test_data", get_test_data_dir()) +} + +fn load_key_layout_map(file_name: &str) -> KeyLayoutMap { + let file_path = format!("{}/{}", get_test_data_path(), file_name); + KeyLayoutMap::load_from_file(&file_path).unwrap() +} + +#[test] +fn test_parse_int() { + assert_eq!(key_layout_map::parse_int("123"), Some(123)); + assert_eq!(key_layout_map::parse_int("-999"), Some(-999)); + assert_eq!(key_layout_map::parse_int("0x1a"), Some(26)); + assert_eq!(key_layout_map::parse_int("0XFF"), Some(255)); + assert_eq!(key_layout_map::parse_int("077"), Some(63)); // octal + assert_eq!(key_layout_map::parse_int(""), None); + assert_eq!(key_layout_map::parse_int("abc"), None); +} + +#[test] +fn test_load_from_contents_simple() { + let contents = "key 1 ESCAPE\nkey 2 1\n"; + let map = KeyLayoutMap::load_from_contents(contents).unwrap(); + + let key = map.map_key(1).unwrap(); + assert_eq!(key.key_code, 111); // ESCAPE + assert_eq!(key.flags, 0); + + let key = map.map_key(2).unwrap(); + assert_eq!(key.key_code, 8); // KEYCODE_1 + assert_eq!(key.flags, 0); +} + +#[test] +fn test_load_from_contents_with_flags() { + let contents = "key 465 ESCAPE FUNCTION\n"; + let map = KeyLayoutMap::load_from_contents(contents).unwrap(); + + let key = map.map_key(465).unwrap(); + assert_eq!(key.key_code, 111); // ESCAPE + assert_eq!(key.flags, 0x00000004); // FUNCTION flag +} + +#[test] +fn test_load_from_contents_axis() { + let contents = "axis 0x00 X\naxis 0x01 Y\n"; + let map = KeyLayoutMap::load_from_contents(contents).unwrap(); + + let axis_info = map.map_axis(0x00).unwrap(); + assert_eq!(axis_info.mode, KeyLayoutAxisMode::Normal); + assert_eq!(axis_info.axis, 0); // X axis + + let axis_info = map.map_axis(0x01).unwrap(); + assert_eq!(axis_info.mode, KeyLayoutAxisMode::Normal); + assert_eq!(axis_info.axis, 1); // Y axis +} + +#[test] +fn test_function_key_line() { + let content = "key 464 FUNCTION"; + let map = KeyLayoutMap::load_from_contents(content).unwrap(); + let key = map.map_key(464).unwrap(); + assert_eq!(key.key_code, AKEYCODE_FUNCTION); +} + +#[test] +fn test_f4_key_line() { + let content = "key 469 F4 FUNCTION"; + let map = KeyLayoutMap::load_from_contents(content).unwrap(); + let key = map.map_key(469).unwrap(); + assert_eq!(key.key_code, AKEYCODE_F4); + assert_eq!(key.flags, POLICY_FLAG_FUNCTION); +} + +#[test] +fn test_brightness_usage_key_lines() { + let content = "\ + key usage 0x0c0067 WINDOW + key usage 0x0c006F BRIGHTNESS_UP + key usage 0x0c0070 BRIGHTNESS_DOWN"; + + KeyLayoutMap::load_from_contents(content).unwrap(); + // Just do not crash because it should be skipped. +} + +#[test] +fn test_load_generic_kl() { + let map = load_key_layout_map("Generic.kl"); + + // Test some basic key mappings + let key = map.map_key(1).expect("Scan code 1 should map to ESCAPE"); + assert_eq!(key.key_code, 111); // ESCAPE + + let key = map.map_key(2).expect("Scan code 2 should map to 1"); + assert_eq!(key.key_code, 8); // KEYCODE_1 + + let key = map.map_key(15).expect("Scan code 15 should map to TAB"); + assert_eq!(key.key_code, 61); // TAB +} + +#[test] +fn test_parse_all_key_entries() { + let map = load_key_layout_map("Generic.kl"); + + // Test various key types + let test_cases = vec![ + (1, 111), // ESCAPE + (15, 61), // TAB + (28, 66), // ENTER + (57, 62), // SPACE + (59, 131), // F1 + (70, 116), // SCROLL_LOCK + ]; + + for (scan_code, expected_key_code) in test_cases { + let key = map + .map_key(scan_code) + .unwrap_or_else(|| panic!("Scan code {} should be mapped", scan_code)); + assert_eq!( + key.key_code, expected_key_code, + "Scan code {} should map to key code {}", + scan_code, expected_key_code + ); + } +} + +#[test] +fn test_parse_axis_entries() { + let map = load_key_layout_map("Generic.kl"); + + // Test axis mappings + let axis_info = map.map_axis(0x00).expect("Axis 0x00 should exist"); + assert_eq!(axis_info.axis, 0); // X axis + assert_eq!(axis_info.mode, KeyLayoutAxisMode::Normal); + + let axis_info = map.map_axis(0x01).expect("Axis 0x01 should exist"); + assert_eq!(axis_info.axis, 1); // Y axis + + let axis_info = map.map_axis(0x03).expect("Axis 0x03 should exist"); + assert_eq!(axis_info.axis, 12); // RX axis +} + +#[test] +fn test_error_handling_malformed_file() { + // Test with invalid key entry + let contents = "key invalid ESCAPE\n"; + let result = KeyLayoutMap::load_from_contents(contents); + assert!(result.is_err(), "Should fail to parse invalid scan code"); + + // Test with missing key code + let contents = "key 1\n"; + let result = KeyLayoutMap::load_from_contents(contents); + // This should succeed but the key won't be inserted (unknown key code) + assert!(result.is_ok()); + + // Test with duplicate scan code + let contents = "key 1 ESCAPE\nkey 1 TAB\n"; + let result = KeyLayoutMap::load_from_contents(contents); + assert!(result.is_err(), "Should fail on duplicate scan code"); +} + +#[test] +fn test_map_key_with_scan_codes() { + let map = load_key_layout_map("Generic.kl"); + + // Test mapping various scan codes + let key = map.map_key(1).unwrap(); + assert_eq!(key.key_code, 111); // ESCAPE + assert_eq!(key.flags, 0); + + // Test a key with FUNCTION flag + let key = map.map_key(465).unwrap(); + assert_eq!(key.key_code, 111); // ESCAPE + assert_eq!(key.flags, 0x00000004); // FUNCTION flag +} + +#[test] +fn test_map_axis_functionality() { + let map = load_key_layout_map("Generic.kl"); + + // Test normal axis + let axis_info = map.map_axis(0x00).unwrap(); + assert_eq!(axis_info.mode, KeyLayoutAxisMode::Normal); + assert_eq!(axis_info.axis, 0); + assert_eq!(axis_info.high_axis, None); + assert_eq!(axis_info.split_value, None); + assert_eq!(axis_info.flat_override, None); + + // Test that non-existent axis returns None + assert!(map.map_axis(9999).is_none()); +} + +#[test] +fn test_find_scan_codes_for_key() { + let map = load_key_layout_map("Generic.kl"); + + // Find scan codes for ESCAPE (should find scan code 1, but not 465 which has FUNCTION flag) + let scan_codes = map.find_scan_codes_for_key(111); // ESCAPE + assert!( + scan_codes.contains(&1), + "Should find scan code 1 for ESCAPE" + ); + assert!( + !scan_codes.contains(&465), + "Should not find scan code 465 (has FUNCTION flag)" + ); + + // Find scan codes for a common key + let scan_codes = map.find_scan_codes_for_key(8); // KEYCODE_1 + assert!( + scan_codes.contains(&2), + "Should find scan code 2 for KEYCODE_1" + ); +} + +#[test] +fn test_load_all_files_in_test_data() { + let test_data_path = get_test_data_path(); + let test_data_dir = Path::new(&test_data_path); + if !test_data_dir.exists() { + panic!("Test data directory does not exist: {}", test_data_path); + } + + let entries = fs::read_dir(test_data_dir).expect("Failed to read test data directory"); + + for entry in entries { + let entry = entry.expect("Failed to read directory entry"); + let path = entry.path(); + + if path.extension().and_then(|s| s.to_str()) == Some("kl") { + let file_path = path.to_str().expect("Invalid file path"); + println!("Testing file: {}", file_path); + + let result = KeyLayoutMap::load_from_file(file_path); + assert!( + result.is_ok(), + "Failed to load key layout file: {} - {:?}", + file_path, + result.err() + ); + + // At least verify it's not empty + result.unwrap(); + // We can't check exact counts as files may vary + println!(" Loaded successfully"); + } + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/Generic.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/Generic.kl new file mode 100644 index 0000000000..31092536ba --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/Generic.kl @@ -0,0 +1,482 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Generic key layout file for full alphabetic US English PC style external keyboards. +# +# This file is intentionally very generic and is intended to support a broad range of keyboards. +# Do not edit the generic key layout to support a specific keyboard; instead, create +# a new key layout file with the required keyboard configuration. +# + +key 1 ESCAPE +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 13 EQUALS +key 14 DEL +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 26 LEFT_BRACKET +key 27 RIGHT_BRACKET +key 28 ENTER +key 29 CTRL_LEFT +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE +key 41 GRAVE +key 42 SHIFT_LEFT +key 43 BACKSLASH +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT +key 55 NUMPAD_MULTIPLY +key 56 ALT_LEFT +key 57 SPACE +key 58 CAPS_LOCK +key 59 F1 +key 60 F2 +key 61 F3 +key 62 F4 +key 63 F5 +key 64 F6 +key 65 F7 +key 66 F8 +key 67 F9 +key 68 F10 +key 69 NUM_LOCK +key 70 SCROLL_LOCK +key 71 NUMPAD_7 +key 72 NUMPAD_8 +key 73 NUMPAD_9 +key 74 NUMPAD_SUBTRACT +key 75 NUMPAD_4 +key 76 NUMPAD_5 +key 77 NUMPAD_6 +key 78 NUMPAD_ADD +key 79 NUMPAD_1 +key 80 NUMPAD_2 +key 81 NUMPAD_3 +key 82 NUMPAD_0 +key 83 NUMPAD_DOT +# key 84 (undefined) +key 85 ZENKAKU_HANKAKU +key 86 BACKSLASH +key 87 F11 +key 88 F12 +key 89 RO +# key 90 "KEY_KATAKANA" +# key 91 "KEY_HIRAGANA" +key 92 HENKAN +key 93 KATAKANA_HIRAGANA +key 94 MUHENKAN +key 95 NUMPAD_COMMA +key 96 NUMPAD_ENTER +key 97 CTRL_RIGHT +key 98 NUMPAD_DIVIDE +key 99 SYSRQ +key 100 ALT_RIGHT +# key 101 "KEY_LINEFEED" +key 102 MOVE_HOME +key 103 DPAD_UP +key 104 PAGE_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 107 MOVE_END +key 108 DPAD_DOWN +key 109 PAGE_DOWN +key 110 INSERT +key 111 FORWARD_DEL +# key 112 "KEY_MACRO" +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP +key 116 POWER +key 117 NUMPAD_EQUALS +# key 118 "KEY_KPPLUSMINUS" +key 119 BREAK +key 120 RECENT_APPS +key 121 NUMPAD_COMMA +key 122 KANA +key 123 EISU +key 124 YEN +key 125 META_LEFT +key 126 META_RIGHT +key 127 MENU +key 128 MEDIA_STOP +# key 129 "KEY_AGAIN" +# key 130 "KEY_PROPS" +# key 131 "KEY_UNDO" +# key 132 "KEY_FRONT" +key 133 COPY +# key 134 "KEY_OPEN" +key 135 PASTE +# key 136 "KEY_FIND" +key 137 CUT +# key 138 "KEY_HELP" +key 139 MENU +key 140 CALCULATOR +# key 141 "KEY_SETUP" +key 142 SLEEP +key 143 WAKEUP +# key 144 "KEY_FILE" +# key 145 "KEY_SENDFILE" +# key 146 "KEY_DELETEFILE" +# key 147 "KEY_XFER" +# key 148 "KEY_PROG1" +# key 149 "KEY_PROG2" +key 150 EXPLORER +# key 151 "KEY_MSDOS" +key 152 POWER +# key 153 "KEY_DIRECTION" +# key 154 "KEY_CYCLEWINDOWS" +key 155 ENVELOPE +key 156 BOOKMARK +# key 157 "KEY_COMPUTER" +key 158 BACK +key 159 FORWARD +key 160 MEDIA_CLOSE +key 161 MEDIA_EJECT +key 162 MEDIA_EJECT +key 163 MEDIA_NEXT +key 164 MEDIA_PLAY_PAUSE +key 165 MEDIA_PREVIOUS +key 166 MEDIA_STOP +key 167 MEDIA_RECORD +key 168 MEDIA_REWIND +key 169 CALL +# key 170 "KEY_ISO" +key 171 MUSIC +key 172 HOME +key 173 REFRESH +# key 174 "KEY_EXIT" +# key 175 "KEY_MOVE" +# key 176 "KEY_EDIT" +key 177 PAGE_UP +key 178 PAGE_DOWN +key 179 NUMPAD_LEFT_PAREN +key 180 NUMPAD_RIGHT_PAREN +# key 181 "KEY_NEW" +# key 182 "KEY_REDO" +# key 183 F13 +# key 184 F14 +# key 185 F15 +# key 186 F16 +# key 187 F17 +# key 188 F18 +# key 189 F19 +# key 190 F20 +# key 191 F21 +# key 192 F22 +# key 193 F23 +# key 194 F24 +# key 195 (undefined) +# key 196 (undefined) +# key 197 (undefined) +# key 198 (undefined) +# key 199 (undefined) +key 200 MEDIA_PLAY +key 201 MEDIA_PAUSE +# key 202 "KEY_PROG3" +# key 203 "KEY_PROG4" +key 204 NOTIFICATION +# key 205 "KEY_SUSPEND" +# key 206 "KEY_CLOSE" +key 207 MEDIA_PLAY +key 208 MEDIA_FAST_FORWARD +# key 209 "KEY_BASSBOOST" +# key 210 "KEY_PRINT" +# key 211 "KEY_HP" +key 212 CAMERA +key 213 MUSIC +# key 214 "KEY_QUESTION" +key 215 ENVELOPE +# key 216 "KEY_CHAT" +key 217 SEARCH +# key 218 "KEY_CONNECT" +# key 219 "KEY_FINANCE" +# key 220 "KEY_SPORT" +# key 221 "KEY_SHOP" +# key 222 "KEY_ALTERASE" +# key 223 "KEY_CANCEL" +key 224 BRIGHTNESS_DOWN +key 225 BRIGHTNESS_UP +key 226 HEADSETHOOK +key 228 KEYBOARD_BACKLIGHT_TOGGLE +key 229 KEYBOARD_BACKLIGHT_DOWN +key 230 KEYBOARD_BACKLIGHT_UP +key 248 MUTE + +key 256 BUTTON_1 +key 257 BUTTON_2 +key 258 BUTTON_3 +key 259 BUTTON_4 +key 260 BUTTON_5 +key 261 BUTTON_6 +key 262 BUTTON_7 +key 263 BUTTON_8 +key 264 BUTTON_9 +key 265 BUTTON_10 +key 266 BUTTON_11 +key 267 BUTTON_12 +key 268 BUTTON_13 +key 269 BUTTON_14 +key 270 BUTTON_15 +key 271 BUTTON_16 + +key 288 BUTTON_1 +key 289 BUTTON_2 +key 290 BUTTON_3 +key 291 BUTTON_4 +key 292 BUTTON_5 +key 293 BUTTON_6 +key 294 BUTTON_7 +key 295 BUTTON_8 +key 296 BUTTON_9 +key 297 BUTTON_10 +key 298 BUTTON_11 +key 299 BUTTON_12 +key 300 BUTTON_13 +key 301 BUTTON_14 +key 302 BUTTON_15 +key 303 BUTTON_16 + + +key 304 BUTTON_A +key 305 BUTTON_B +key 306 BUTTON_C +key 307 BUTTON_X +key 308 BUTTON_Y +key 309 BUTTON_Z +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 312 BUTTON_L2 +key 313 BUTTON_R2 +key 314 BUTTON_SELECT +key 315 BUTTON_START +key 316 BUTTON_MODE +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + + +key 329 STYLUS_BUTTON_TERTIARY +key 331 STYLUS_BUTTON_PRIMARY +key 332 STYLUS_BUTTON_SECONDARY + + +# key 352 "KEY_OK" +key 353 DPAD_CENTER +# key 354 "KEY_GOTO" +# key 355 "KEY_CLEAR" +# key 356 "KEY_POWER2" +# key 357 "KEY_OPTION" +# key 358 "KEY_INFO" +# key 359 "KEY_TIME" +# key 360 "KEY_VENDOR" +# key 361 "KEY_ARCHIVE" +key 362 GUIDE +# key 363 "KEY_CHANNEL" +# key 364 "KEY_FAVORITES" +# key 365 "KEY_EPG" +key 366 DVR +# key 367 "KEY_MHP" +# key 368 "KEY_LANGUAGE" +# key 369 "KEY_TITLE" +key 370 CAPTIONS +# key 371 "KEY_ANGLE" +# key 372 "KEY_ZOOM" +# key 373 "KEY_MODE" +# key 374 "KEY_KEYBOARD" +# key 375 "KEY_SCREEN" +# key 376 "KEY_PC" +key 377 TV +# key 378 "KEY_TV2" +# key 379 "KEY_VCR" +# key 380 "KEY_VCR2" +# key 381 "KEY_SAT" +# key 382 "KEY_SAT2" +# key 383 "KEY_CD" +# key 384 "KEY_TAPE" +# key 385 "KEY_RADIO" +# key 386 "KEY_TUNER" +# key 387 "KEY_PLAYER" +# key 388 "KEY_TEXT" +# key 389 "KEY_DVD" +# key 390 "KEY_AUX" +# key 391 "KEY_MP3" +# key 392 "KEY_AUDIO" +# key 393 "KEY_VIDEO" +# key 394 "KEY_DIRECTORY" +# key 395 "KEY_LIST" +# key 396 "KEY_MEMO" +key 397 CALENDAR +key 398 PROG_RED +key 399 PROG_GREEN +key 400 PROG_YELLOW +key 401 PROG_BLUE +key 402 CHANNEL_UP +key 403 CHANNEL_DOWN +# key 404 "KEY_FIRST" +key 405 LAST_CHANNEL +# key 406 "KEY_AB" +# key 407 "KEY_NEXT" +# key 408 "KEY_RESTART" +# key 409 "KEY_SLOW" +# key 410 "KEY_SHUFFLE" +# key 411 "KEY_BREAK" +# key 412 "KEY_PREVIOUS" +# key 413 "KEY_DIGITS" +# key 414 "KEY_TEEN" +# key 415 "KEY_TWEN" +# key 418 "KEY_ZOOM_IN" +key 418 ZOOM_IN +# key 419 "KEY_ZOOM_OUT" +key 419 ZOOM_OUT +key 528 FOCUS + +key 429 CONTACTS + +# key 448 "KEY_DEL_EOL" +# key 449 "KEY_DEL_EOS" +# key 450 "KEY_INS_LINE" +# key 451 "KEY_DEL_LINE" + + +key 464 FUNCTION +key 465 ESCAPE FUNCTION +key 466 F1 FUNCTION +key 467 F2 FUNCTION +key 468 F3 FUNCTION +key 469 F4 FUNCTION +key 470 F5 FUNCTION +key 471 F6 FUNCTION +key 472 F7 FUNCTION +key 473 F8 FUNCTION +key 474 F9 FUNCTION +key 475 F10 FUNCTION +key 476 F11 FUNCTION +key 477 F12 FUNCTION +key 478 1 FUNCTION +key 479 2 FUNCTION +key 480 D FUNCTION +key 481 E FUNCTION +key 482 F FUNCTION +key 483 S FUNCTION +key 484 B FUNCTION + + +# key 497 KEY_BRL_DOT1 +# key 498 KEY_BRL_DOT2 +# key 499 KEY_BRL_DOT3 +# key 500 KEY_BRL_DOT4 +# key 501 KEY_BRL_DOT5 +# key 502 KEY_BRL_DOT6 +# key 503 KEY_BRL_DOT7 +# key 504 KEY_BRL_DOT8 + +key 522 STAR +key 523 POUND +key 580 APP_SWITCH +key 582 VOICE_ASSIST +# Linux KEY_ASSISTANT +key 583 ASSIST +key 656 MACRO_1 +key 657 MACRO_2 +key 658 MACRO_3 +key 659 MACRO_4 + +# Keys defined by HID usages +key usage 0x0c0067 WINDOW +key usage 0x0c006F BRIGHTNESS_UP +key usage 0x0c0070 BRIGHTNESS_DOWN +key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP +key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN +key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE +key usage 0x0c0173 MEDIA_AUDIO_TRACK +key usage 0x0c019C PROFILE_SWITCH +key usage 0x0c01A2 ALL_APPS +# TODO(b/297094448): Add stylus button mappings as a fallback when we have a way to determine +# if a device can actually report it. +# key usage 0x0d0044 STYLUS_BUTTON_PRIMARY +# key usage 0x0d005a STYLUS_BUTTON_SECONDARY + +# Joystick and game controller axes. +# Axes that are not mapped will be assigned generic axis numbers by the input subsystem. +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x03 RX +axis 0x04 RY +axis 0x05 RZ +axis 0x06 THROTTLE +axis 0x07 RUDDER +axis 0x08 WHEEL +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# LEDs +led 0x00 NUM_LOCK +led 0x01 CAPS_LOCK +led 0x02 SCROLL_LOCK +led 0x03 COMPOSE +led 0x04 KANA +led 0x05 SLEEP +led 0x06 SUSPEND +led 0x07 MUTE +led 0x08 MISC +led 0x09 MAIL +led 0x0a CHARGING + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/tokenizer_test.rs b/evdev/src/main/rust/evdev_manager/core/tests/tokenizer_test.rs new file mode 100644 index 0000000000..d008c06193 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/tokenizer_test.rs @@ -0,0 +1,142 @@ +use evdev_manager_core::android::keylayout::tokenizer::Tokenizer; + +#[test] +fn test_is_eof() { + let tokenizer = Tokenizer::from_contents("test.txt", ""); + assert!(tokenizer.is_eof()); + + let tokenizer = Tokenizer::from_contents("test.txt", "a"); + assert!(!tokenizer.is_eof()); +} + +#[test] +fn test_is_eol() { + let tokenizer = Tokenizer::from_contents("test.txt", ""); + assert!(tokenizer.is_eol()); + + let mut tokenizer = Tokenizer::from_contents("test.txt", "a\nb"); + assert!(!tokenizer.is_eol()); + tokenizer.next_char(); + assert!(tokenizer.is_eol()); +} + +#[test] +fn test_peek_char() { + let mut tokenizer = Tokenizer::from_contents("test.txt", "abc"); + assert_eq!(tokenizer.peek_char(), 'a'); + assert_eq!(tokenizer.peek_char(), 'a'); // Should not advance + + tokenizer.next_char(); + assert_eq!(tokenizer.peek_char(), 'b'); +} + +#[test] +fn test_next_char() { + let mut tokenizer = Tokenizer::from_contents("test.txt", "abc"); + assert_eq!(tokenizer.next_char(), 'a'); + assert_eq!(tokenizer.next_char(), 'b'); + assert_eq!(tokenizer.next_char(), 'c'); + assert_eq!(tokenizer.next_char(), '\0'); +} + +#[test] +fn test_next_token() { + let mut tokenizer = Tokenizer::from_contents("test.txt", "key 1 ESCAPE"); + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "1"); + assert_eq!(tokenizer.next_token(" \t"), "ESCAPE"); + assert_eq!(tokenizer.next_token(" \t"), ""); +} + +#[test] +fn test_next_token_with_multiple_delimiters() { + let mut tokenizer = Tokenizer::from_contents("test.txt", "key\t1\nESCAPE"); + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "1"); + tokenizer.next_line(); + assert_eq!(tokenizer.next_token(" \t"), "ESCAPE"); +} + +#[test] +fn test_next_line() { + let mut tokenizer = Tokenizer::from_contents("test.txt", "line1\nline2\nline3"); + assert_eq!(tokenizer.peek_remainder_of_line(), "line1"); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), "line2"); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), "line3"); +} + +#[test] +fn test_skip_delimiters() { + let mut tokenizer = Tokenizer::from_contents("test.txt", " key"); + tokenizer.skip_delimiters(" \t"); + assert_eq!(tokenizer.peek_char(), 'k'); +} + +#[test] +fn test_get_location() { + let mut tokenizer = Tokenizer::from_contents("test.txt", "line1\nline2"); + assert_eq!(tokenizer.get_location(), "test.txt:1"); + tokenizer.next_line(); + assert_eq!(tokenizer.get_location(), "test.txt:2"); +} + +#[test] +fn test_peek_remainder_of_line() { + let tokenizer = Tokenizer::from_contents("test.txt", "key 1 ESCAPE\nnext line"); + assert_eq!(tokenizer.peek_remainder_of_line(), "key 1 ESCAPE"); + + let mut tokenizer = Tokenizer::from_contents("test.txt", "key 1 ESCAPE\nnext line"); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), "next line"); +} + +#[test] +fn test_handles_comments() { + let mut tokenizer = Tokenizer::from_contents("test.txt", "key 1 ESCAPE # comment"); + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "1"); + assert_eq!(tokenizer.next_token(" \t"), "ESCAPE"); + assert_eq!(tokenizer.peek_char(), ' '); + tokenizer.skip_delimiters(" \t"); + assert_eq!(tokenizer.peek_char(), '#'); +} + +#[test] +fn test_handles_empty_lines() { + let mut tokenizer = Tokenizer::from_contents("test.txt", "line1\n\nline3"); + assert_eq!(tokenizer.peek_remainder_of_line(), "line1"); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), ""); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), "line3"); +} + +#[test] +fn test_handles_whitespace_only_lines() { + let mut tokenizer = Tokenizer::from_contents("test.txt", "line1\n \nline3"); + assert_eq!(tokenizer.peek_remainder_of_line(), "line1"); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), " "); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), "line3"); +} + +#[test] +fn test_multiple_lines_sequential() { + let mut tokenizer = Tokenizer::from_contents("test.txt", "key 1 A\nkey 2 B\nkey 3 C"); + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "1"); + assert_eq!(tokenizer.next_token(" \t"), "A"); + tokenizer.next_line(); + + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "2"); + assert_eq!(tokenizer.next_token(" \t"), "B"); + tokenizer.next_line(); + + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "3"); + assert_eq!(tokenizer.next_token(" \t"), "C"); +} diff --git a/evdev/src/main/rust/evdev_manager/jni/Cargo.toml b/evdev/src/main/rust/evdev_manager/jni/Cargo.toml new file mode 100644 index 0000000000..029eaf86ba --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "evdev_manager" +version = "0.1.0" +edition = "2021" + +[lib] +name = "evdev_manager" +crate-type = ["cdylib"] + +[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" + +[build-dependencies] +# No C++ compilation needed since we're using pure JNI diff --git a/evdev/src/main/rust/evdev_manager/jni/build.rs b/evdev/src/main/rust/evdev_manager/jni/build.rs new file mode 100644 index 0000000000..be1f4d1098 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/build.rs @@ -0,0 +1,16 @@ +fn main() { + // This crate now uses pure JNI without C++ Binder layer. + // No C++ compilation needed. + + // Just check that we're building for Android + let target = std::env::var("TARGET").expect("TARGET environment variable not set"); + let is_android = target.contains("android"); + + if !is_android { + eprintln!( + "Warning: Building for non-Android target '{}'. This crate is designed for Android.", + target + ); + eprintln!("Use Gradle for actual builds."); + } +} 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 new file mode 100644 index 0000000000..dafe60428d --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs @@ -0,0 +1,142 @@ +use evdev::{util::event_code_to_int, InputEvent}; +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::KeyLayoutKey; +use evdev_manager_core::android::keylayout::key_layout_map_manager::KeyLayoutMapManager; +use evdev_manager_core::device_identifier::DeviceIdentifier; +use jni::objects::{GlobalRef, JValue}; +use jni::JavaVM; +use std::process; +use std::sync::{Arc, Mutex}; + +pub struct EvdevJniObserver { + jvm: Arc, + system_bridge: GlobalRef, + key_layout_map_manager: Arc, + power_button_down_time: Mutex, +} + +impl std::fmt::Debug for EvdevJniObserver { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EvdevJniObserver") + .field("jvm", &"") + .field("system_bridge", &"") + .field("key_layout_map_manager", &"") + .finish() + } +} + +/// Observer that forwards events to BaseSystemBridge via JNI +/// Performs KeyLayoutMap conversion from raw evdev codes to Android keycodes +impl EvdevJniObserver { + pub fn new( + jvm: Arc, + system_bridge: GlobalRef, + key_layout_map_manager: Arc, + ) -> Self { + EvdevJniObserver { + jvm, + system_bridge, + key_layout_map_manager, + power_button_down_time: Mutex::new(0), + } + } + + /// Handle power button emergency kill. + fn handle_power_button( + &self, + ev_code: u32, + android_code: u32, + value: i32, + time_sec: libc::time_t, + ) { + let mut time_guard = self.power_button_down_time.lock().unwrap(); + // KEY_POWER scan code = 116 + if ev_code == 116 || android_code == android_codes::AKEYCODE_POWER { + if value == 1 { + *time_guard = time_sec; + } else if value == 0 { + // Button up - check if held for 10+ seconds + let down_time = *time_guard; + if down_time > 0 && time_sec - down_time >= 10 { + // Call BaseSystemBridge.onEmergencyKillSystemBridge() via JNI + if let Ok(mut env) = self.jvm.attach_current_thread() { + let _ = env.call_method( + &self.system_bridge, + "onEmergencyKillSystemBridge", + "()V", + &[], + ); + } + process::exit(0); + } + *time_guard = 0 + } + } + } + + const UNKNOWN_KEY: KeyLayoutKey = KeyLayoutKey { + key_code: AKEYCODE_UNKNOWN, + flags: 0, + }; + + pub fn on_event( + &self, + device_id: usize, + device_identifier: &DeviceIdentifier, + event: &InputEvent, + ) -> bool { + let mut env = self + .jvm + .attach_current_thread_permanently() + .expect("Failed to attach to JVM thread"); + + // Extract event type and code from EventCode + let (ev_type, ev_code) = event_code_to_int(&event.event_code); + + // Convert raw evdev code to Android keycode + let android_code = self + .key_layout_map_manager + .map_key(device_identifier, ev_code) + .unwrap_or(Some(Self::UNKNOWN_KEY)) + .map(|key| key.key_code) + .unwrap_or(AKEYCODE_UNKNOWN); + + // Handle power button emergency kill + self.handle_power_button(ev_code, android_code, event.value, event.time.tv_sec); + + // Call BaseSystemBridge.onEvdevEvent() via JNI + + let result = env.call_method( + &self.system_bridge, + "onEvdevEvent", + "(IJJIIII)Z", + &[ + JValue::Int(device_id as i32), + #[allow(clippy::unnecessary_cast)] + // When building for 32 bit the tv_sec type may be i32 + JValue::Long(event.time.tv_sec as i64), + JValue::Long(event.time.tv_usec.into()), + JValue::Int(ev_type as i32), + JValue::Int(ev_code as i32), + JValue::Int(event.value), + JValue::Int(android_code as i32), + ], + ); + + match result { + Ok(value) => { + // The method returns a primitive boolean (Z) + // Extract the boolean value using z() method which returns Result + value.z().unwrap_or_else(|e| { + error!("Failed to extract boolean from result: {:?}", e); + false + }) + } + Err(e) => { + error!("Failed to call onEvdevEvent: {:?}", e); + false + } + } + } +} 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 new file mode 100644 index 0000000000..596fd66a46 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs @@ -0,0 +1,362 @@ +use crate::evdev_jni_observer::EvdevJniObserver; +use evdev::{Device, DeviceWrapper}; +use evdev_manager_core::android::keylayout::key_layout_map_manager::KeyLayoutMapManager; +use evdev_manager_core::device_identifier::DeviceIdentifier; +use evdev_manager_core::event_loop::EventLoopManager; +use jni::objects::{JClass, 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}; + +static JNI_OBSERVER: OnceLock = OnceLock::new(); + +fn get_jni_observer() -> &'static EvdevJniObserver { + JNI_OBSERVER.get().expect("JNI observer not initialized") +} + +/// MUST only be called once in the lifetime of the process. +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_initEvdevManager( + env: JNIEnv, + this: JObject, +) { + android_log::init("KeyMapperSystemBridge").unwrap(); + // Set log level: Warn for production builds, Debug for debug builds + let log_level = if cfg!(debug_assertions) { + LevelFilter::Debug + } else { + LevelFilter::Warn + }; + 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"); + + // Create a global reference to the BaseSystemBridge instance + let system_bridge = env + .new_global_ref(this) + .expect("Failed to create global reference to BaseSystemBridge"); + + // Initialize the JNI observer + let key_layout_manager = KeyLayoutMapManager::get(); + let observer = EvdevJniObserver::new(Arc::new(jvm), system_bridge, key_layout_manager); + + if JNI_OBSERVER.set(observer).is_err() { + panic!("JNI observer already initialized"); + } + + // Register the observer with the event loop + EventLoopManager::get().register_observer(|device_id, device_identifier, event| { + get_jni_observer().on_event(device_id, device_identifier, event) + }); + + // Start the event loop + EventLoopManager::get() + .start() + .inspect_err(|e| error!("Failed to start event loop: {:?}", e)) + .unwrap(); +} + +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_destroyEvdevManager( + _env: JNIEnv, + _class: JClass, +) { + info!("Destroying evdev manager"); + + EventLoopManager::get() + .stop() + .inspect_err(|e| error!("Failed to stop event loop: {:?}", e)) + .unwrap(); +} + +/// Set the list of grabbed devices. Takes an array of EvdevDeviceInfo and returns an array of GrabbedDeviceHandle. +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_setGrabbedDevicesNative( + mut env: JNIEnv, + _class: JClass, + j_devices: jobjectArray, +) -> jobjectArray { + // Parse the input array of EvdevDeviceInfo + let mut requested_devices: Vec = Vec::new(); + + // Convert raw jobjectArray to JObjectArray + let devices_array: JObjectArray = unsafe { JObjectArray::from_raw(j_devices) }; + + let array_length = match env.get_array_length(&devices_array) { + Ok(len) => len, + Err(e) => { + error!("Failed to get array length: {:?}", e); + return ptr::null_mut(); + } + }; + + for i in 0..array_length { + let obj = match env.get_object_array_element(&devices_array, i) { + Ok(o) => o, + Err(e) => { + error!("Failed to get array element {}: {:?}", i, e); + continue; + } + }; + + match parse_evdev_device_info(&mut env, &obj) { + Ok(device_identifier) => requested_devices.push(device_identifier), + Err(e) => { + error!("Failed to parse EvdevDeviceInfo at index {}: {:?}", i, e); + } + } + } + + let grabbed_devices = EventLoopManager::get().set_grabbed_devices(requested_devices); + create_java_grabbed_device_handle_array(&mut env, grabbed_devices) +} + +/// Write an event to a grabbed device using its device ID +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_writeEvdevEventNative( + _env: JNIEnv, + _class: JClass, + j_device_id: jint, + j_type: jint, + j_code: jint, + j_value: jint, +) -> jboolean { + EventLoopManager::get() + .write_event(j_device_id as usize, j_type as u32, j_code as u32, j_value) + .is_ok() as jboolean +} + +/// Get all available evdev devices (returns EvdevDeviceInfo array) +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_BaseSystemBridge_getEvdevDevicesNative( + mut env: JNIEnv, + _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); + } + } + } + None => continue, + } + } + } + Err(e) => { + error!("Failed to get input device paths: {:?}", e); + } + } + + // Create Java array + let 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 ptr::null_mut(); + } + }; + + let array = match env.new_object_array(device_infos.len() as i32, class, JObject::null()) { + Ok(a) => a, + Err(e) => { + error!("Failed to create EvdevDeviceInfo array: {:?}", e); + return ptr::null_mut(); + } + }; + + // Fill array + for (i, info) in device_infos.iter().enumerate() { + if let Err(e) = + env.set_object_array_element(&array, i as i32, unsafe { JObject::from_raw(*info) }) + { + error!("Failed to set array element: {:?}", e); + } + } + + 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( + env: &mut JNIEnv, + obj: &JObject, +) -> Result { + // Get name field + let name_field = env.get_field(obj, "name", "Ljava/lang/String;")?; + let name_obj = name_field.l()?; + let name_jstring = JString::from(name_obj); + let name: String = env + .get_string(&name_jstring)? + .to_string_lossy() + .into_owned(); + + // Get bus field + let bus = env.get_field(obj, "bus", "I")?.i()? as u16; + + // Get vendor field + let vendor = env.get_field(obj, "vendor", "I")?.i()? as u16; + + // Get product field + let product = env.get_field(obj, "product", "I")?.i()? as u16; + + Ok(DeviceIdentifier { + name, + bus, + vendor, + product, + version: 0, // Version is not exposed in Java API, set to 0 + }) +} + +fn create_java_grabbed_device_handle_array( + mut env: &mut JNIEnv, + grabbed_devices: Vec<(usize, DeviceIdentifier)>, +) -> jobjectArray { + let handle_class = + match env.find_class("io/github/sds100/keymapper/common/models/GrabbedDeviceHandle") { + Ok(c) => c, + Err(e) => { + panic!("Failed to find GrabbedDeviceHandle class: {:?}", e); + } + }; + + let array = + match env.new_object_array(grabbed_devices.len() as i32, &handle_class, JObject::null()) { + Ok(a) => a, + Err(e) => { + panic!("Failed to create GrabbedDeviceHandle array: {:?}", e); + } + }; + + // 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() { + 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, + ) { + Ok(handle) => { + if let Err(e) = env.set_object_array_element(&array, i as i32, unsafe { + JObject::from_raw(handle) + }) { + error!("Failed to set array element: {:?}", e); + } + } + Err(e) => { + error!("Failed to create GrabbedDeviceHandle: {:?}", e); + } + } + } + + array.into_raw() +} + +/// Create a Java EvdevDeviceInfo object +fn create_java_evdev_device_info( + env: &mut JNIEnv, + name: &str, + bus: i32, + vendor: i32, + product: i32, +) -> Result { + let class = env.find_class("io/github/sds100/keymapper/common/models/EvdevDeviceInfo")?; + let name_str = env.new_string(name)?; + + let obj = env.new_object( + class, + "(Ljava/lang/String;III)V", + &[ + JValue::Object(&name_str.into()), + JValue::Int(bus), + JValue::Int(vendor), + JValue::Int(product), + ], + )?; + + Ok(obj.into_raw()) +} + +/// Create a Java GrabbedDeviceHandle object +fn create_java_grabbed_device_handle( + env: &mut JNIEnv, + id: i32, + name: &str, + bus: i32, + vendor: i32, + product: i32, +) -> Result { + let class = env.find_class("io/github/sds100/keymapper/common/models/GrabbedDeviceHandle")?; + let name_str = env.new_string(name)?; + + let obj = env.new_object( + class, + "(ILjava/lang/String;III)V", + &[ + JValue::Int(id), + JValue::Object(&name_str.into()), + JValue::Int(bus), + JValue::Int(vendor), + JValue::Int(product), + ], + )?; + + 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 new file mode 100644 index 0000000000..5ed013c32f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/src/lib.rs @@ -0,0 +1,6 @@ +mod evdev_jni_observer; +mod jni_bridge; + +#[macro_use] +extern crate log; +extern crate android_log; diff --git a/evdev/src/main/rust/evdev_manager/jni/tests/evdevcallback_binder_observer_test.rs b/evdev/src/main/rust/evdev_manager/jni/tests/evdevcallback_binder_observer_test.rs new file mode 100644 index 0000000000..a97a575374 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/tests/evdevcallback_binder_observer_test.rs @@ -0,0 +1,169 @@ +// use evdev_manager::{EmergencyKillCallback, EvdevCallbackBinderObserver, EvdevEvent, EventType}; +// use std::sync::atomic::{AtomicBool, Ordering}; +// +// static EMERGENCY_KILL_CALLED: AtomicBool = AtomicBool::new(false); +// +// /// Test emergency kill callback that sets a flag instead of exiting +// fn test_emergency_kill() { +// EMERGENCY_KILL_CALLED.store(true, Ordering::SeqCst); +// } +// +// #[test] +// fn test_emergency_kill_on_power_button_held_10_seconds() { +// // Reset the flag +// EMERGENCY_KILL_CALLED.store(false, Ordering::SeqCst); +// +// // Create observer with test callback +// let observer = EvdevCallbackBinderObserver::with_emergency_kill_callback(test_emergency_kill); +// +// // Simulate power button down at time 0 +// let power_down_event = EvdevEvent { +// time_sec: 0, +// time_usec: 0, +// event_type: EventType::Key, +// code: 116, // KEY_POWER +// value: 1, // Button down +// }; +// +// // Process the down event +// observer.on_event("/dev/input/event0", &power_down_event); +// +// // Verify emergency kill not called yet +// assert!( +// !EMERGENCY_KILL_CALLED.load(Ordering::SeqCst), +// "Emergency kill should not be called on button down" +// ); +// +// // Simulate power button up at time 10 (10 seconds later) +// let power_up_event = EvdevEvent { +// time_sec: 10, +// time_usec: 0, +// event_type: EventType::Key, +// code: 116, // KEY_POWER +// value: 0, // Button up +// }; +// +// // Process the up event +// observer.on_event("/dev/input/event0", &power_up_event); +// +// // Verify emergency kill was called +// assert!( +// EMERGENCY_KILL_CALLED.load(Ordering::SeqCst), +// "Emergency kill should be called when power button held for 10+ seconds" +// ); +// } +// +// #[test] +// fn test_emergency_kill_not_called_on_short_press() { +// // Reset the flag +// EMERGENCY_KILL_CALLED.store(false, Ordering::SeqCst); +// +// // Create observer with test callback +// let observer = EvdevCallbackBinderObserver::with_emergency_kill_callback(test_emergency_kill); +// +// // Simulate power button down at time 0 +// let power_down_event = EvdevEvent { +// time_sec: 0, +// time_usec: 0, +// event_type: EventType::Key, +// code: 116, // KEY_POWER +// value: 1, // Button down +// }; +// +// // Process the down event +// observer.on_event("/dev/input/event0", &power_down_event); +// +// // Simulate power button up at time 5 (5 seconds later - less than 10) +// let power_up_event = EvdevEvent { +// time_sec: 5, +// time_usec: 0, +// event_type: EventType::Key, +// code: 116, // KEY_POWER +// value: 0, // Button up +// }; +// +// // Process the up event +// observer.on_event("/dev/input/event0", &power_up_event); +// +// // Verify emergency kill was NOT called +// assert!( +// !EMERGENCY_KILL_CALLED.load(Ordering::SeqCst), +// "Emergency kill should not be called when power button held for less than 10 seconds" +// ); +// } +// +// #[test] +// fn test_emergency_kill_with_android_keycode() { +// // Reset the flag +// EMERGENCY_KILL_CALLED.store(false, Ordering::SeqCst); +// +// // Create observer with test callback +// let observer = EvdevCallbackBinderObserver::with_emergency_kill_callback(test_emergency_kill); +// +// // Simulate power button down using Android keycode (26 = KEYCODE_POWER) +// // Note: This test assumes the key layout map will map some code to Android keycode 26 +// // For a more complete test, we'd need to set up a mock key layout map +// // For now, we'll test with the raw evdev code (116 = KEY_POWER) +// +// let power_down_event = EvdevEvent { +// time_sec: 0, +// time_usec: 0, +// event_type: EventType::Key, +// code: 116, // KEY_POWER +// value: 1, // Button down +// }; +// +// observer.on_event("/dev/input/event0", &power_down_event); +// +// let power_up_event = EvdevEvent { +// time_sec: 10, +// time_usec: 0, +// event_type: EventType::Key, +// code: 116, // KEY_POWER +// value: 0, // Button up +// }; +// +// observer.on_event("/dev/input/event0", &power_up_event); +// +// // Verify emergency kill was called +// assert!( +// EMERGENCY_KILL_CALLED.load(Ordering::SeqCst), +// "Emergency kill should be called when power button (via Android keycode) held for 10+ seconds" +// ); +// } +// +// #[test] +// fn test_emergency_kill_not_called_for_non_power_button() { +// // Reset the flag +// EMERGENCY_KILL_CALLED.store(false, Ordering::SeqCst); +// +// // Create observer with test callback +// let observer = EvdevCallbackBinderObserver::with_emergency_kill_callback(test_emergency_kill); +// +// // Simulate a different key down +// let key_down_event = EvdevEvent { +// time_sec: 0, +// time_usec: 0, +// event_type: EventType::Key, +// code: 1, // Some other key, not power +// value: 1, // Button down +// }; +// +// observer.on_event("/dev/input/event0", &key_down_event); +// +// let key_up_event = EvdevEvent { +// time_sec: 10, +// time_usec: 0, +// event_type: EventType::Key, +// code: 1, // Some other key +// value: 0, // Button up +// }; +// +// observer.on_event("/dev/input/event0", &key_up_event); +// +// // Verify emergency kill was NOT called +// assert!( +// !EMERGENCY_KILL_CALLED.load(Ordering::SeqCst), +// "Emergency kill should not be called for non-power button keys" +// ); +// } diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 7137a88244..46249c87f1 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -32,6 +32,12 @@ lane :production do # Don't create changelog for f-droid because not committing it # File.write("metadata/android/en-US/changelogs/" + version_code + ".txt", whats_new) + # Check Rust formatting and run tests + Dir.chdir("../evdev/src/main/rust/evdev_manager") do + sh("cargo fmt --check") + sh("cargo test --package evdev_manager_core") + end + Dir.chdir("..") do gradle(task: "testDebugUnitTest") end @@ -88,6 +94,18 @@ lane :internal do version_code = get_properties_value(key: "VERSION_CODE", path: "./app/version.properties") version_name = get_properties_value(key: "VERSION_NAME", path: "./app/version.properties") + # Check Rust formatting and run tests + Dir.chdir("../evdev/src/main/rust/evdev_manager") do + sh("cargo fmt --check") + sh("cargo test --package evdev_manager_core") + end + + Dir.chdir("..") do + gradle(task: "ktlintFormat") + gradle(task: "ktlintCheck") + gradle(task: "testDebugUnitTest") + end + # Don't create changelog for f-droid because not committing it # File.write("metadata/android/en-US/changelogs/" + version_code + ".txt", whats_new) @@ -101,15 +119,7 @@ lane :internal do secure_text: true ) -# Do not release a debug build for pro version. -# gradle(task: "assembleDebug") - - Dir.chdir("..") do - # Release the free build to GitHub because billing only works if signed by Google Play - gradle(task: "ktlintFormat") - gradle(task: "ktlintCheck") - gradle(task: "testDebugUnitTest") gradle(task: "app:bundleRelease") end diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9b4fd25842..f3555e0dac 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/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..cb39071d59 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,18 +11,12 @@ 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. + // System bridge originally only supported Android 10+ because that was the min sdk + // for binder-ndk library. Even though that is no longer used, it is extra effort to support + // the changes to the internal Android APIs on Android 8 and 9. minSdk = 29 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -114,106 +106,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..fb8ce05be0 --- /dev/null +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl @@ -0,0 +1,12 @@ +package io.github.sds100.keymapper.evdev; + +import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle; + +interface IEvdevCallback { + /** + * deviceId is the internal system bridge ID for the device. This is used rather than referencing + * with a path because primitives have lower overhead and are safer over the JNI boundary. + */ + boolean onEvdevEvent(int deviceId, 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/IEvdevCallback.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/IEvdevCallback.aidl deleted file mode 100644 index f6b4039a6d..0000000000 --- a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/IEvdevCallback.aidl +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.sds100.keymapper.sysbridge; - -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 64f7a0afe3..f23dac1780 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,7 +1,8 @@ package io.github.sds100.keymapper.sysbridge; -import io.github.sds100.keymapper.sysbridge.IEvdevCallback; -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle; +import io.github.sds100.keymapper.evdev.IEvdevCallback; +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo; +import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle; import io.github.sds100.keymapper.common.models.ShellResult; import android.view.InputEvent; @@ -11,19 +12,15 @@ interface ISystemBridge { int getVersionCode() = 16777112; ShellResult executeCommand(String command, long timeoutMillis) = 16777111; - boolean grabEvdevDevice(String devicePath) = 1; - boolean grabEvdevDeviceArray(in String[] devicePath) = 2; - - boolean ungrabEvdevDevice(String devicePath) = 3; - boolean ungrabAllEvdevDevices() = 4; + GrabbedDeviceHandle[] setGrabbedDevices(in EvdevDeviceInfo[] devices) = 1; void registerEvdevCallback(IEvdevCallback callback) = 5; void unregisterEvdevCallback() = 6; - boolean writeEvdevEvent(String devicePath, int type, int code, int value) = 7; + boolean writeEvdevEvent(int deviceId, int type, int code, int value) = 7; boolean injectInputEvent(in InputEvent event, int mode) = 8; - EvdevDeviceHandle[] getEvdevInputDevices() = 9; + EvdevDeviceInfo[] getEvdevInputDevices() = 9; boolean setWifiEnabled(boolean enable) = 10; 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/cpp/android/ftl/algorithm.h b/sysbridge/src/main/cpp/android/ftl/algorithm.h deleted file mode 100644 index 81993de177..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/algorithm.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include - -namespace android::ftl { - -// Determines if a container contains a value. This is a simplified version of the C++23 -// std::ranges::contains function. -// -// const ftl::StaticVector vector = {1, 2, 3}; -// assert(ftl::contains(vector, 1)); -// -// TODO: Remove in C++23. - template - auto contains(const Container &container, const Value &value) -> bool { - return std::find(container.begin(), container.end(), value) != container.end(); - } - -// Adapter for std::find_if that converts the return value from iterator to optional. -// -// const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv}; -// assert(ftl::find_if(vector, [](const auto& str) { return str.front() == 'c'; }) == "cake"sv); -// - template - constexpr auto find_if(const Container &container, Predicate &&predicate) - -> Optional> { - const auto it = std::find_if(std::cbegin(container), std::cend(container), - std::forward(predicate)); - if (it == std::cend(container)) return {}; - return std::cref(*it); - } - -// Transformers for ftl::find_if on a map-like `Container` that contains key-value pairs. -// -// const ftl::SmallMap map = ftl::init::map>( -// 12, "snow"sv, "cone"sv)(13, "tiramisu"sv)(14, "upside"sv, "down"sv, "cake"sv); -// -// using Map = decltype(map); -// -// assert(14 == ftl::find_if(map, [](const auto& pair) { -// return pair.second.size() == 3; -// }).transform(ftl::to_key)); -// -// const auto opt = ftl::find_if(map, [](const auto& pair) { -// return pair.second.size() == 1; -// }).transform(ftl::to_mapped_ref); -// -// assert(opt); -// assert(opt->get() == ftl::StaticVector("tiramisu"sv)); -// - template - constexpr auto to_key(const Pair &pair) -> Key { - return pair.first; - } - - template - constexpr auto to_mapped_ref(const Pair &pair) -> std::reference_wrapper { - return std::cref(pair.second); - } - -// Combinator for ftl::Optional::or_else when T is std::reference_wrapper. Given a -// lambda argument that returns a `constexpr` value, ftl::static_ref binds a reference to a -// static T initialized to that constant. -// -// const ftl::SmallMap map = ftl::init::map(13, "tiramisu"sv)(14, "upside-down cake"sv); -// assert("???"sv == -// map.get(20).or_else(ftl::static_ref([] { return "???"sv; }))->get()); -// -// using Map = decltype(map); -// -// assert("snow cone"sv == -// ftl::find_if(map, [](const auto& pair) { return pair.second.front() == 's'; }) -// .transform(ftl::to_mapped_ref) -// .or_else(ftl::static_ref([] { return "snow cone"sv; })) -// ->get()); -// - template - constexpr auto static_ref(F &&f) { - return [f = std::forward(f)] { - constexpr auto kInitializer = f(); - static const T kValue = kInitializer; - return Optional(std::cref(kValue)); - }; - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/cast.h b/sysbridge/src/main/cpp/android/ftl/cast.h deleted file mode 100644 index 64129a1a58..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/cast.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include - -namespace android::ftl { - - enum class CastSafety { - kSafe, kUnderflow, kOverflow - }; - -// Returns whether static_cast(v) is safe, or would result in underflow or overflow. -// -// static_assert(ftl::cast_safety(-1) == ftl::CastSafety::kUnderflow); -// static_assert(ftl::cast_safety(128u) == ftl::CastSafety::kOverflow); -// -// static_assert(ftl::cast_safety(-.1f) == ftl::CastSafety::kUnderflow); -// static_assert(ftl::cast_safety(static_cast(INT32_MAX)) == -// ftl::CastSafety::kOverflow); -// -// static_assert(ftl::cast_safety(-DBL_MAX) == ftl::CastSafety::kUnderflow); -// - template - constexpr CastSafety cast_safety(T v) { - static_assert(std::is_arithmetic_v); - static_assert(std::is_arithmetic_v); - - constexpr bool kFromSigned = std::is_signed_v; - constexpr bool kToSigned = std::is_signed_v; - - using details::max_exponent; - - // If the R range contains the T range, then casting is always safe. - if constexpr ((kFromSigned == kToSigned && max_exponent >= max_exponent) || - (!kFromSigned && kToSigned && max_exponent > max_exponent)) { - return CastSafety::kSafe; - } - - using C = std::common_type_t; - - if constexpr (kFromSigned) { - using L = details::safe_limits; - - if constexpr (kToSigned) { - // Signed to signed. - if (v < L::lowest()) return CastSafety::kUnderflow; - return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow; - } else { - // Signed to unsigned. - if (v < 0) return CastSafety::kUnderflow; - return static_cast(v) <= static_cast(L::max()) ? CastSafety::kSafe - : CastSafety::kOverflow; - } - } else { - using L = std::numeric_limits; - - if constexpr (kToSigned) { - // Unsigned to signed. - return static_cast(v) <= static_cast(L::max()) ? CastSafety::kSafe - : CastSafety::kOverflow; - } else { - // Unsigned to unsigned. - return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow; - } - } - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/concat.h b/sysbridge/src/main/cpp/android/ftl/concat.h deleted file mode 100644 index b826a2e858..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/concat.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace android::ftl { - -// Lightweight (not allocating nor sprintf-based) concatenation. The variadic arguments can be -// values of integral type (including bool and char), string literals, or strings whose length -// is constrained: -// -// std::string_view name = "Volume"; -// ftl::Concat string(ftl::truncated<3>(name), ": ", -3, " dB"); -// -// assert(string.str() == "Vol: -3 dB"); -// assert(string.c_str()[string.size()] == '\0'); -// - template - struct Concat; - - template - struct Concat : Concat::N, Ts...> { - explicit constexpr Concat(T v, Ts... args) { append(v, args...); } - - protected: - constexpr Concat() = default; - - constexpr void append(T v, Ts... args) { - using Str = details::StaticString; - const Str str(v); - - // TODO: Replace with constexpr std::copy in C++20. - for (auto it = str.view.begin(); it != str.view.end();) { - *this->end_++ = *it++; - } - - using Base = Concat; - this->Base::append(args...); - } - }; - - template - struct Concat { - static constexpr std::size_t max_size() { return N; } - - constexpr std::size_t size() const { return static_cast(end_ - buffer_); } - - constexpr const char *c_str() const { return buffer_; } - - constexpr std::string_view str() const { - // TODO: Replace with {buffer_, end_} in C++20. - return {buffer_, size()}; - } - - protected: - constexpr Concat() : end_(buffer_) {} - - constexpr Concat(const Concat &) = delete; - - constexpr void append() { *end_ = '\0'; } - - char buffer_[N + 1]; - char *end_; - }; - -// Deduction guide. - template - Concat(Ts &&...) -> Concat<0, Ts...>; - - template - constexpr auto truncated(std::string_view v) { - return details::Truncated{v}; - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/details/array_traits.h b/sysbridge/src/main/cpp/android/ftl/details/array_traits.h deleted file mode 100644 index f66742598c..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/array_traits.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -#define FTL_ARRAY_TRAIT(T, U) using U = typename details::ArrayTraits::U - -namespace android::ftl::details { - - template - struct ArrayTraits { - using value_type = T; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - - using pointer = value_type *; - using reference = value_type &; - using iterator = pointer; - using reverse_iterator = std::reverse_iterator; - - using const_pointer = const value_type *; - using const_reference = const value_type &; - using const_iterator = const_pointer; - using const_reverse_iterator = std::reverse_iterator; - - template - static constexpr pointer construct_at(const_iterator it, Args &&... args) { - void *const ptr = const_cast(static_cast(it)); - if constexpr (std::is_constructible_v) { - // TODO: Replace with std::construct_at in C++20. - return new(ptr) value_type(std::forward(args)...); - } else { - // Fall back to list initialization. - return new(ptr) value_type{std::forward(args)...}; - } - } - - // TODO: Make constexpr in C++20. - template - static reference replace_at(const_iterator it, Args &&... args) { - value_type element{std::forward(args)...}; - return replace_at(it, std::move(element)); - } - - // TODO: Make constexpr in C++20. - static reference replace_at(const_iterator it, value_type &&value) { - std::destroy_at(it); - // This is only safe because exceptions are disabled. - return *construct_at(it, std::move(value)); - } - - // TODO: Make constexpr in C++20. - static void in_place_swap(reference a, reference b) { - value_type c{std::move(a)}; - replace_at(&a, std::move(b)); - replace_at(&b, std::move(c)); - } - - // TODO: Make constexpr in C++20. - static void in_place_swap_ranges(iterator first1, iterator last1, iterator first2) { - while (first1 != last1) { - in_place_swap(*first1++, *first2++); - } - } - - // TODO: Replace with std::uninitialized_copy in C++20. - template - static void uninitialized_copy(Iterator first, Iterator last, const_iterator out) { - while (first != last) { - construct_at(out++, *first++); - } - } - }; - -// CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end. - template - class ArrayIterators { - FTL_ARRAY_TRAIT(T, size_type); - - FTL_ARRAY_TRAIT(T, reference); - FTL_ARRAY_TRAIT(T, iterator); - FTL_ARRAY_TRAIT(T, reverse_iterator); - - FTL_ARRAY_TRAIT(T, const_reference); - FTL_ARRAY_TRAIT(T, const_iterator); - FTL_ARRAY_TRAIT(T, const_reverse_iterator); - - Self &self() const { return *const_cast(static_cast(this)); } - - public: - const_iterator begin() const { return cbegin(); } - - const_iterator cbegin() const { return self().begin(); } - - const_iterator end() const { return cend(); } - - const_iterator cend() const { return self().end(); } - - reverse_iterator rbegin() { return std::make_reverse_iterator(self().end()); } - - const_reverse_iterator rbegin() const { return crbegin(); } - - const_reverse_iterator crbegin() const { return self().rbegin(); } - - reverse_iterator rend() { return std::make_reverse_iterator(self().begin()); } - - const_reverse_iterator rend() const { return crend(); } - - const_reverse_iterator crend() const { return self().rend(); } - - iterator last() { return self().end() - 1; } - - const_iterator last() const { return self().last(); } - - reference front() { return *self().begin(); } - - const_reference front() const { return self().front(); } - - reference back() { return *last(); } - - const_reference back() const { return self().back(); } - - reference operator[](size_type i) { return *(self().begin() + i); } - - const_reference operator[](size_type i) const { return self()[i]; } - }; - -// Mixin to define comparison operators for an array-like template. -// TODO: Replace with operator<=> in C++20. - template class Array> - struct ArrayComparators { - template - friend bool operator==(const Array &lhs, const Array &rhs) { - return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); - } - - template - friend bool operator<(const Array &lhs, const Array &rhs) { - return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); - } - - template - friend bool operator>(const Array &lhs, const Array &rhs) { - return rhs < lhs; - } - - template - friend bool operator!=(const Array &lhs, const Array &rhs) { - return !(lhs == rhs); - } - - template - friend bool operator>=(const Array &lhs, const Array &rhs) { - return !(lhs < rhs); - } - - template - friend bool operator<=(const Array &lhs, const Array &rhs) { - return !(lhs > rhs); - } - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/cast.h b/sysbridge/src/main/cpp/android/ftl/details/cast.h deleted file mode 100644 index 8196ba61a7..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/cast.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace android::ftl::details { - -// Exponent whose power of 2 is the (exclusive) upper bound of T. - template> - constexpr int max_exponent = std::is_floating_point_v ? L::max_exponent : L::digits; - -// Extension of std::numeric_limits that reduces the maximum for integral types T such that it -// has an exact representation for floating-point types F. For example, the maximum int32_t value -// is 2'147'483'647, but casting it to float commonly rounds up to 2'147'483'650.f, which cannot -// be safely converted back lest the signed overflow invokes undefined behavior. This pitfall is -// avoided by clearing the lower (31 - 24 =) 7 bits of precision to 2'147'483'520. Note that the -// minimum is representable. - template - struct safe_limits : std::numeric_limits { - static constexpr T max() { - using Base = std::numeric_limits; - - if constexpr (std::is_integral_v && std::is_floating_point_v) { - // Assume the mantissa is 24 bits for float, or 53 bits for double. - using Float = std::numeric_limits; - static_assert(Float::is_iec559); - - // If the integer is wider than the mantissa, clear the excess bits of precision. - constexpr int kShift = Base::digits - Float::digits; - if constexpr (kShift > 0) { - using U = std::make_unsigned_t; - constexpr U kOne = static_cast(1); - return static_cast(Base::max()) & ~((kOne << kShift) - kOne); - } - } - - return Base::max(); - } - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/concat.h b/sysbridge/src/main/cpp/android/ftl/details/concat.h deleted file mode 100644 index c1462a658c..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/concat.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include -#include - -namespace android::ftl::details { - - template - struct StaticString; - -// Booleans. - template - struct StaticString>> { - static constexpr std::size_t N = 5; // Length of "false". - - explicit constexpr StaticString(bool b) : view(b ? "true" : "false") {} - - const std::string_view view; - }; - -// Characters. - template - struct StaticString>> { - static constexpr std::size_t N = 1; - - explicit constexpr StaticString(char c) : character(c) {} - - const char character; - const std::string_view view{&character, 1u}; - }; - -// Integers, including the integer value of other character types like char32_t. - template - struct StaticString< - T, std::enable_if_t< - std::is_integral_v> && !is_bool_v && !is_char_v>> { - using U = remove_cvref_t; - static constexpr std::size_t N = to_chars_length_v; - - // TODO: Mark this and to_chars as `constexpr` in C++23. - explicit StaticString(U v) : view(to_chars(buffer, v)) {} - - to_chars_buffer_t buffer; - const std::string_view view; - }; - -// Character arrays. - template - struct StaticString { - static constexpr std::size_t N = M - 1; - - explicit constexpr StaticString(const char (&str)[M]) : view(str, N) {} - - const std::string_view view; - }; - - template - struct Truncated { - std::string_view view; - }; - -// Strings with constrained length. - template - struct StaticString, void> { - static constexpr std::size_t N = M; - - explicit constexpr StaticString(Truncated str) : view(str.view.substr(0, N)) {} - - const std::string_view view; - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/function.h b/sysbridge/src/main/cpp/android/ftl/details/function.h deleted file mode 100644 index 8d7fc9bd58..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/function.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace android::ftl::details { - -// The maximum allowed value for the template argument `N` in -// `ftl::Function`. - constexpr size_t kFunctionMaximumN = 14; - -// Converts a member function pointer type `Ret(Class::*)(Args...)` to an equivalent non-member -// function type `Ret(Args...)`. - - template - struct remove_member_function_pointer; - - template - struct remove_member_function_pointer { - using type = Ret(Args...); - }; - - template - struct remove_member_function_pointer { - using type = Ret(Args...); - }; - - template - using remove_member_function_pointer_t = - typename remove_member_function_pointer::type; - -// Helper functions for binding to the supported targets. - - template - auto bind_opaque_no_op() -> Ret (*)(void *, Args...) { - return [](void *, Args...) -> Ret { - if constexpr (!std::is_void_v) { - return Ret{}; - } - }; - } - - template - auto bind_opaque_function_object(const F &) -> Ret (*)(void *, Args...) { - return [](void *opaque, Args... args) -> Ret { - return std::invoke(*static_cast(opaque), std::forward(args)...); - }; - } - - template - auto bind_member_function(Class *instance, Ret (*)(Args...) = nullptr) { - return [instance](Args... args) -> Ret { - return std::invoke(MemberFunction, instance, std::forward(args)...); - }; - } - - template - auto bind_free_function(Ret (*)(Args...) = nullptr) { - return [](Args... args) -> Ret { - return std::invoke(FreeFunction, std::forward(args)...); - }; - } - -// Traits class for the opaque storage used by Function. - - template - struct function_opaque_storage { - // The actual type used for the opaque storage. An `N` of zero specifies the minimum useful size, - // which allows a lambda with zero or one capture args. - using type = std::array; - - template - static constexpr bool require_trivially_copyable = std::is_trivially_copyable_v; - - template - static constexpr bool require_trivially_destructible = std::is_trivially_destructible_v; - - template - static constexpr bool require_will_fit_in_opaque_storage = sizeof(S) <= sizeof(type); - - template - static constexpr bool require_alignment_compatible = - std::alignment_of_v <= std::alignment_of_v; - - // Copies `src` into the opaque storage, and returns that storage. - template - static type opaque_copy(const S &src) { - // TODO: Replace with C++20 concepts/constraints which can give more details. - static_assert(require_trivially_copyable, - "ftl::Function can only store lambdas that capture trivially copyable data."); - static_assert( - require_trivially_destructible, - "ftl::Function can only store lambdas that capture trivially destructible data."); - static_assert(require_will_fit_in_opaque_storage, - "ftl::Function has limited storage for lambda captured state. Maybe you need to " - "increase N?"); - static_assert(require_alignment_compatible); - - type opaque; - std::memcpy(opaque.data(), &src, sizeof(S)); - return opaque; - } - }; - -// Traits class to help determine the template parameters to use for a ftl::Function, given a -// function object. - - template - struct function_traits { - // The function type `F` with which to instantiate the `Function` template. - using type = remove_member_function_pointer_t<&F::operator()>; - - // The (minimum) size `N` with which to instantiate the `Function` template. - static constexpr std::size_t size = - (std::max(sizeof(std::intptr_t), sizeof(F)) - 1) / sizeof(std::intptr_t); - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/future.h b/sysbridge/src/main/cpp/android/ftl/details/future.h deleted file mode 100644 index ec3926ba8c..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/future.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -namespace android::ftl { - - template class> - class Future; - - namespace details { - - template - struct future_result { - using type = T; - }; - - template - struct future_result> { - using type = T; - }; - - template - struct future_result> { - using type = T; -}; - -template class FutureImpl> -struct future_result> { -using type = T; -}; - -template -using future_result_t = typename future_result::type; - -struct ValueTag { -}; - -template class> -class BaseFuture; - -template -class BaseFuture { - using Impl = std::future; - -public: - Future share() { - if (T *value = std::get_if(&self())) { - return {ValueTag{}, std::move(*value)}; - } - - return std::get(self()).share(); - } - -protected: - T get() { - if (T *value = std::get_if(&self())) { - return std::move(*value); - } - - return std::get(self()).get(); - } - - template - std::future_status wait_for(const std::chrono::duration &timeout_duration) const { - if (std::holds_alternative(self())) { - return std::future_status::ready; - } - - return std::get(self()).wait_for(timeout_duration); - } - -private: - auto &self() { return static_cast(*this).future_; } - - const auto &self() const { return static_cast(*this).future_; } -}; - -template -class BaseFuture { - using Impl = std::shared_future; - -protected: - const T &get() const { - if (const T *value = std::get_if(&self())) { - return *value; - } - - return std::get(self()).get(); - } - - template - std::future_status wait_for(const std::chrono::duration &timeout_duration) const { - if (std::holds_alternative(self())) { - return std::future_status::ready; - } - - return std::get(self()).wait_for(timeout_duration); - } - -private: - const auto &self() const { return static_cast(*this).future_; } -}; - -} // namespace details -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/details/hash.h b/sysbridge/src/main/cpp/android/ftl/details/hash.h deleted file mode 100644 index cb9939036a..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/hash.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace android::ftl::details { - -// Based on CityHash64 v1.0.1 (http://code.google.com/p/cityhash/), but slightly -// modernized and trimmed for cases with bounded lengths. - - template - inline T read_unaligned(const void *ptr) { - T v; - std::memcpy(&v, ptr, sizeof(T)); - return v; - } - - template - constexpr std::uint64_t rotate(std::uint64_t v, std::uint8_t shift) { - if constexpr (!NonZeroShift) { - if (shift == 0) return v; - } - return (v >> shift) | (v << (64 - shift)); - } - - constexpr std::uint64_t shift_mix(std::uint64_t v) { - return v ^ (v >> 47); - } - - __attribute__((no_sanitize("unsigned-integer-overflow"))) - constexpr std::uint64_t hash_length_16(std::uint64_t u, std::uint64_t v) { - constexpr std::uint64_t kPrime = 0x9ddfea08eb382d69ull; - auto a = (u ^ v) * kPrime; - a ^= (a >> 47); - auto b = (v ^ a) * kPrime; - b ^= (b >> 47); - b *= kPrime; - return b; - } - - constexpr std::uint64_t kPrime0 = 0xc3a5c85c97cb3127ull; - constexpr std::uint64_t kPrime1 = 0xb492b66fbe98f273ull; - constexpr std::uint64_t kPrime2 = 0x9ae16a3b2f90404full; - constexpr std::uint64_t kPrime3 = 0xc949d7c7509e6557ull; - - __attribute__((no_sanitize("unsigned-integer-overflow"))) - inline std::uint64_t hash_length_0_to_16(const char *str, std::uint64_t length) { - if (length > 8) { - const auto a = read_unaligned(str); - const auto b = read_unaligned(str + length - 8); - return hash_length_16(a, rotate(b + length, static_cast(length))) ^ - b; - } - if (length >= 4) { - const auto a = read_unaligned(str); - const auto b = read_unaligned(str + length - 4); - return hash_length_16(length + (a << 3), b); - } - if (length > 0) { - const auto a = static_cast(str[0]); - const auto b = static_cast(str[length >> 1]); - const auto c = static_cast(str[length - 1]); - const auto y = static_cast(a) + (static_cast(b) << 8); - const auto z = - static_cast(length) + (static_cast(c) << 2); - return shift_mix(y * kPrime2 ^ z * kPrime3) * kPrime2; - } - return kPrime2; - } - - __attribute__((no_sanitize("unsigned-integer-overflow"))) - inline std::uint64_t hash_length_17_to_32(const char *str, std::uint64_t length) { - const auto a = read_unaligned(str) * kPrime1; - const auto b = read_unaligned(str + 8); - const auto c = read_unaligned(str + length - 8) * kPrime2; - const auto d = read_unaligned(str + length - 16) * kPrime0; - return hash_length_16(rotate(a - b, 43) + rotate(c, 30) + d, - a + rotate(b ^ kPrime3, 20) - c + length); - } - - __attribute__((no_sanitize("unsigned-integer-overflow"))) - inline std::uint64_t hash_length_33_to_64(const char *str, std::uint64_t length) { - auto z = read_unaligned(str + 24); - auto a = read_unaligned(str) + (length + read_unaligned(str + length - 16)) * kPrime0; - auto b = rotate(a + z, 52); - auto c = rotate(a, 37); - - a += read_unaligned(str + 8); - c += rotate(a, 7); - a += read_unaligned(str + 16); - - const auto vf = a + z; - const auto vs = b + rotate(a, 31) + c; - - a = read_unaligned(str + 16) + read_unaligned(str + length - 32); - z += read_unaligned(str + length - 8); - b = rotate(a + z, 52); - c = rotate(a, 37); - a += read_unaligned(str + length - 24); - c += rotate(a, 7); - a += read_unaligned(str + length - 16); - - const auto wf = a + z; - const auto ws = b + rotate(a, 31) + c; - const auto r = shift_mix((vf + ws) * kPrime2 + (wf + vs) * kPrime0); - return shift_mix(r * kPrime0 + vs) * kPrime2; - } - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/match.h b/sysbridge/src/main/cpp/android/ftl/details/match.h deleted file mode 100644 index cee77c83ee..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/match.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace android::ftl::details { - - template - struct Matcher : Ms ... { - using Ms::operator()...; - }; - -// Deduction guide. - template - Matcher(Ms...) -> Matcher; - - template - constexpr bool is_exhaustive_match_v = (std::is_invocable_v && ...); - - template - struct Match; - - template - struct Match { - template - static decltype(auto) match(Variant &variant, const Matcher &matcher) { - if (auto *const ptr = std::get_if(&variant)) { - return matcher(*ptr); - } else { - return Match::match(variant, matcher); - } - } - }; - - template - struct Match { - template - static decltype(auto) match(Variant &variant, const Matcher &matcher) { - return matcher(std::get(variant)); - } - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/mixins.h b/sysbridge/src/main/cpp/android/ftl/details/mixins.h deleted file mode 100644 index a52d74c504..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/mixins.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -namespace android::ftl::details { - - template class> - class Mixin { - protected: - constexpr Self &self() { return *static_cast(this); } - - constexpr const Self &self() const { return *static_cast(this); } - - constexpr auto &mut() { return self().value_; } - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/optional.h b/sysbridge/src/main/cpp/android/ftl/details/optional.h deleted file mode 100644 index bcbd8826c0..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/optional.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include - -namespace android::ftl { - - template - struct Optional; - - namespace details { - - template - struct is_optional : std::false_type { - }; - - template - struct is_optional> : std::true_type { - }; - - template - struct is_optional> : std::true_type { - }; - - template - struct transform_result { - using type = Optional>>; - }; - - template - using transform_result_t = typename transform_result::type; - - template - struct and_then_result { - using type = remove_cvref_t>; - static_assert(is_optional{}, "and_then function must return an optional"); - }; - - template - using and_then_result_t = typename and_then_result::type; - - template - struct or_else_result { - using type = remove_cvref_t>; - static_assert( - std::is_same_v> || std::is_same_v>, - "or_else function must return an optional T"); - }; - - template - using or_else_result_t = typename or_else_result::type; - - } // namespace details -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/details/type_traits.h b/sysbridge/src/main/cpp/android/ftl/details/type_traits.h deleted file mode 100644 index 70a602c330..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/type_traits.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace android::ftl::details { - -// TODO: Replace with std::remove_cvref_t in C++20. - template - using remove_cvref_t = std::remove_cv_t>; - - template - constexpr bool is_bool_v = std::is_same_v, bool>; - - template - constexpr bool is_char_v = std::is_same_v, char>; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/enum.h b/sysbridge/src/main/cpp/android/ftl/enum.h deleted file mode 100644 index e31c0a1322..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/enum.h +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -// Returns the name of enumerator E::V and optionally the class (i.e. "E::V" or "V") as -// std::optional by parsing the compiler-generated string literal for the -// signature of this function. The function is defined in the global namespace with a short name -// and inferred return type to reduce bloat in the read-only data segment. -template -constexpr auto ftl_enum_builder() { - static_assert(std::is_enum_v); - - using R = std::optional; - using namespace std::literals; - - // The "pretty" signature has the following format: - // - // auto ftl_enum() [E = android::test::Enum, V = android::test::Enum::kValue] - // - std::string_view view = __PRETTY_FUNCTION__; - const auto template_begin = view.rfind('['); - const auto template_end = view.rfind(']'); - if (template_begin == view.npos || template_end == view.npos) return R{}; - - // Extract the template parameters without the enclosing brackets. Example (cont'd): - // - // E = android::test::Enum, V = android::test::Enum::kValue - // - view = view.substr(template_begin + 1, template_end - template_begin - 1); - const auto value_begin = view.rfind("V = "sv); - if (value_begin == view.npos) return R{}; - - // Example (cont'd): - // - // V = android::test::Enum::kValue - // - view = view.substr(value_begin); - const auto pos = S ? view.rfind("::"sv) - 2 : view.npos; - - const auto name_begin = view.rfind("::"sv, pos); - if (name_begin == view.npos) return R{}; - - // Chop off the leading "::". - const auto name = view.substr(name_begin + 2); - - // A value that is not enumerated has the format "Enum)42". - return name.find(')') == view.npos ? R{name} : R{}; -} - -// Returns the name of enumerator E::V (i.e. "V") as std::optional -template -constexpr auto ftl_enum() { - return ftl_enum_builder(); -} - -// Returns the name of enumerator and class E::V (i.e. "E::V") as std::optional -template -constexpr auto ftl_enum_full() { - return ftl_enum_builder(); -} - -namespace android::ftl { - -// Trait for determining whether a type is specifically a scoped enum or not. By definition, a -// scoped enum is one that is not implicitly convertible to its underlying type. -// -// TODO: Replace with std::is_scoped_enum in C++23. -// - template> - struct is_scoped_enum : std::false_type { - }; - - template - struct is_scoped_enum - : std::negation>> { - }; - - template - inline constexpr bool is_scoped_enum_v = is_scoped_enum::value; - -// Shorthand for casting an enumerator to its integral value. -// -// TODO: Replace with std::to_underlying in C++23. -// -// enum class E { A, B, C }; -// static_assert(ftl::to_underlying(E::B) == 1); -// - template>> - constexpr auto to_underlying(E v) { - return static_cast>(v); - } - -// Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named -// ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1 -// where N is the bit width of the underlying type, but only if that type is unsigned, assuming the -// enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of- -// range values results in undefined behavior if the underlying type is not fixed. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// static_assert(ftl::enum_begin_v == E::A); -// static_assert(ftl::enum_last_v == E::F); -// static_assert(ftl::enum_size_v == 6); -// -// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; -// -// static_assert(ftl::enum_begin_v == F{0}); -// static_assert(ftl::enum_last_v == F{15}); -// static_assert(ftl::enum_size_v == 16); -// - template - struct enum_begin { - static_assert(is_scoped_enum_v, "Missing ftl_first enumerator"); - static constexpr E value{0}; - }; - - template - struct enum_begin> { - static constexpr E value = E::ftl_first; - }; - - template - inline constexpr E enum_begin_v = enum_begin::value; - - template - struct enum_end { - using U = std::underlying_type_t; - static_assert(is_scoped_enum_v && std::is_unsigned_v, "Missing ftl_last enumerator"); - - static constexpr E value{std::numeric_limits::digits}; - }; - - template - struct enum_end> { - static constexpr E value = E{to_underlying(E::ftl_last) + 1}; - }; - - template - inline constexpr E enum_end_v = enum_end::value; - - template - inline constexpr E enum_last_v = E{to_underlying(enum_end_v) - 1}; - - template - struct enum_size { - static constexpr auto kBegin = to_underlying(enum_begin_v); - static constexpr auto kEnd = to_underlying(enum_end_v); - static_assert(kBegin < kEnd, "Invalid range"); - - static constexpr std::size_t value = kEnd - kBegin; - static_assert(value <= 64, "Excessive range size"); - }; - - template - inline constexpr std::size_t enum_size_v = enum_size::value; - - namespace details { - - template - struct Identity { - static constexpr auto value = V; - }; - - template - using make_enum_sequence = std::make_integer_sequence, enum_size_v>; - - template class = Identity, typename = make_enum_sequence> - struct EnumRange; - - template class F, typename T, T... Vs> - struct EnumRange> { - static constexpr auto kBegin = to_underlying(enum_begin_v); - static constexpr auto kSize = enum_size_v; - - using R = decltype(F::value); - const R values[kSize] = {F(Vs + kBegin)>::value...}; - - constexpr const auto *begin() const { return values; } - - constexpr const auto *end() const { return values + kSize; } - }; - - template - struct EnumName { - static constexpr auto value = ftl_enum(); - }; - - template - struct EnumNameFull { - static constexpr auto value = ftl_enum_full(); - }; - - template - struct FlagName { - using E = decltype(I); - using U = std::underlying_type_t; - - static constexpr E V{U{1} << to_underlying(I)}; - static constexpr auto value = ftl_enum(); - }; - - } // namespace details - -// Returns an iterable over the range of an enum. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// std::string string; -// for (E v : ftl::enum_range()) { -// string += ftl::enum_name(v).value_or("?"); -// } -// -// assert(string == "ABC??F"); -// - template - constexpr auto enum_range() { - return details::EnumRange{}; - } - -// Returns a stringified enumerator at compile time. -// -// enum class E { A, B, C }; -// static_assert(ftl::enum_name() == "B"); -// - template - constexpr std::string_view enum_name() { - constexpr auto kName = ftl_enum(); - static_assert(kName, "Unknown enumerator"); - return *kName; - } - -// Returns a stringified enumerator with class at compile time. -// -// enum class E { A, B, C }; -// static_assert(ftl::enum_name() == "E::B"); -// - template - constexpr std::string_view enum_name_full() { - constexpr auto kName = ftl_enum_full(); - static_assert(kName, "Unknown enumerator"); - return *kName; - } - -// Returns a stringified enumerator, possibly at compile time. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// static_assert(ftl::enum_name(E::C).value_or("?") == "C"); -// static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); -// - template - constexpr std::optional enum_name(E v) { - const auto value = to_underlying(v); - - constexpr auto kBegin = to_underlying(enum_begin_v); - constexpr auto kLast = to_underlying(enum_last_v); - if (value < kBegin || value > kLast) return {}; - - constexpr auto kRange = details::EnumRange{}; - return kRange.values[value - kBegin]; - } - -// Returns a stringified enumerator with class, possibly at compile time. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// static_assert(ftl::enum_name(E::C).value_or("?") == "E::C"); -// static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); -// - template - constexpr std::optional enum_name_full(E v) { - const auto value = to_underlying(v); - - constexpr auto kBegin = to_underlying(enum_begin_v); - constexpr auto kLast = to_underlying(enum_last_v); - if (value < kBegin || value > kLast) return {}; - - constexpr auto kRange = details::EnumRange{}; - return kRange.values[value - kBegin]; - } - -// Returns a stringified flag enumerator, possibly at compile time. -// -// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; -// -// static_assert(ftl::flag_name(F::Z).value_or("?") == "Z"); -// static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?"); -// - template - constexpr std::optional flag_name(E v) { - const auto value = to_underlying(v); - - // TODO: Replace with std::popcount and std::countr_zero in C++20. - if (__builtin_popcountll(value) != 1) return {}; - - constexpr auto kRange = details::EnumRange{}; - return kRange.values[__builtin_ctzll(value)]; - } - -// Returns a stringified enumerator, or its integral value if not named. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// assert(ftl::enum_string(E::C) == "C"); -// assert(ftl::enum_string(E{3}) == "3"); -// - template - inline std::string enum_string(E v) { - if (const auto name = enum_name(v)) { - return std::string(*name); - } - return to_string(to_underlying(v)); - } - -// Returns a stringified enumerator with class, or its integral value if not named. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// assert(ftl::enum_string(E::C) == "E::C"); -// assert(ftl::enum_string(E{3}) == "3"); -// - template - inline std::string enum_string_full(E v) { - if (const auto name = enum_name_full(v)) { - return std::string(*name); - } - return to_string(to_underlying(v)); - } - -// Returns a stringified flag enumerator, or its integral value if not named. -// -// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; -// -// assert(ftl::flag_string(F::Z) == "Z"); -// assert(ftl::flag_string(F{7}) == "0b111"); -// - template - inline std::string flag_string(E v) { - if (const auto name = flag_name(v)) { - return std::string(*name); - } - constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex; - return to_string(to_underlying(v), radix); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/expected.h b/sysbridge/src/main/cpp/android/ftl/expected.h deleted file mode 100644 index 5ed362e536..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/expected.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "../libbase/expected.h" -#include -#include - -#include - -// Given an expression `expr` that evaluates to an ftl::Expected result (R for short), FTL_TRY -// unwraps T out of R, or bails out of the enclosing function F if R has an error E. The return type -// of F must be R, since FTL_TRY propagates R in the error case. As a special case, ftl::Unit may be -// used as the error E to allow FTL_TRY expressions when F returns `void`. -// -// The non-standard syntax requires `-Wno-gnu-statement-expression-from-macro-expansion` to compile. -// The UnitToVoid conversion allows the macro to be used for early exit from a function that returns -// `void`. -// -// Example usage: -// -// using StringExp = ftl::Expected; -// -// StringExp repeat(StringExp exp) { -// const std::string str = FTL_TRY(exp); -// return StringExp(str + str); -// } -// -// assert(StringExp("haha"s) == repeat(StringExp("ha"s))); -// assert(repeat(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) { -// return e == std::errc::bad_message; -// })); -// -// -// FTL_TRY may be used in void-returning functions by using ftl::Unit as the error type: -// -// void uppercase(char& c, ftl::Optional opt) { -// c = std::toupper(FTL_TRY(std::move(opt).ok_or(ftl::Unit()))); -// } -// -// char c = '?'; -// uppercase(c, std::nullopt); -// assert(c == '?'); -// -// uppercase(c, 'a'); -// assert(c == 'A'); -// -#define FTL_TRY(expr) \ - ({ \ - auto exp_ = (expr); \ - if (!exp_.has_value()) { \ - using E = decltype(exp_)::error_type; \ - return android::ftl::details::UnitToVoid::from(std::move(exp_)); \ - } \ - exp_.value(); \ - }) - -// Given an expression `expr` that evaluates to an ftl::Expected result (R for short), -// FTL_EXPECT unwraps T out of R, or bails out of the enclosing function F if R has an error E. -// While FTL_TRY bails out with R, FTL_EXPECT bails out with E, which is useful when F does not -// need to propagate R because T is not relevant to the caller. -// -// Example usage: -// -// using StringExp = ftl::Expected; -// -// std::errc repeat(StringExp exp, std::string& out) { -// const std::string str = FTL_EXPECT(exp); -// out = str + str; -// return std::errc::operation_in_progress; -// } -// -// std::string str; -// assert(std::errc::operation_in_progress == repeat(StringExp("ha"s), str)); -// assert("haha"s == str); -// assert(std::errc::bad_message == repeat(ftl::Unexpected(std::errc::bad_message), str)); -// assert("haha"s == str); -// -#define FTL_EXPECT(expr) \ - ({ \ - auto exp_ = (expr); \ - if (!exp_.has_value()) { \ - return std::move(exp_.error()); \ - } \ - exp_.value(); \ - }) - -namespace android::ftl { - -// Superset of base::expected with monadic operations. -// -// TODO: Extend std::expected in C++23. -// - template - struct Expected final : base::expected { - using Base = base::expected; - using Base::expected; - - using Base::error; - using Base::has_value; - using Base::value; - - template - constexpr bool has_error(P predicate) const { - return !has_value() && predicate(error()); - } - - constexpr Optional value_opt() const &{ - return has_value() ? Optional(value()) : std::nullopt; - } - - constexpr Optional value_opt() &&{ - return has_value() ? Optional(std::move(value())) : std::nullopt; - } - - // Delete new for this class. Its base doesn't have a virtual destructor, and - // if it got deleted via base class pointer, it would cause undefined - // behavior. There's not a good reason to allocate this object on the heap - // anyway. - static void *operator new(size_t) = delete; - - static void *operator new[](size_t) = delete; - }; - - template - constexpr auto Unexpected(E &&error) { - return base::unexpected(std::forward(error)); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/fake_guard.h b/sysbridge/src/main/cpp/android/ftl/fake_guard.h deleted file mode 100644 index 056564a1a1..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/fake_guard.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define FTL_ATTRIBUTE(a) __attribute__((a)) - -namespace android::ftl { - -// Granular alternative to [[clang::no_thread_safety_analysis]]. Given a std::mutex-like object, -// FakeGuard suppresses enforcement of thread-safe access to guarded variables within its scope. -// While FakeGuard is scoped to a block, there are macro shorthands for a single expression, as -// well as function/lambda scope (though calls must be indirect, e.g. virtual or std::function): -// -// struct { -// std::mutex mutex; -// int x FTL_ATTRIBUTE(guarded_by(mutex)) = -1; -// -// int f() { -// { -// ftl::FakeGuard guard(mutex); -// x = 0; -// } -// -// return FTL_FAKE_GUARD(mutex, x + 1); -// } -// -// std::function g() const { -// return [this]() FTL_FAKE_GUARD(mutex) { return x; }; -// } -// } s; -// -// assert(s.f() == 1); -// assert(s.g()() == 0); -// -// An example of a situation where FakeGuard helps is a mutex that guards writes on Thread 1, and -// reads on Thread 2. Reads on Thread 1, which is the only writer, need not be under lock, so can -// use FakeGuard to appease the thread safety analyzer. Another example is enforcing and documenting -// exclusive access by a single thread. This is done by defining a global constant that represents a -// thread context, and annotating guarded variables as if it were a mutex (though without any effect -// at run time): -// -// constexpr class [[clang::capability("mutex")]] { -// } kMainThreadContext; -// - template - struct [[clang::scoped_lockable]] FakeGuard final { - explicit FakeGuard(const Mutex &mutex) FTL_ATTRIBUTE(acquire_capability(mutex)) {} - - [[clang::release_capability()]] ~FakeGuard() {} - - FakeGuard(const FakeGuard &) = delete; - - FakeGuard &operator=(const FakeGuard &) = delete; - }; - -} // namespace android::ftl - -// TODO: Enable in C++23 once standard attributes can be used on lambdas. -#if 0 -#define FTL_FAKE_GUARD1(mutex) [[using clang: acquire_capability(mutex), release_capability(mutex)]] -#else -#define FTL_FAKE_GUARD1(mutex) \ - FTL_ATTRIBUTE(acquire_capability(mutex)) \ - FTL_ATTRIBUTE(release_capability(mutex)) -#endif - -#define FTL_FAKE_GUARD2(mutex, expr) \ - (android::ftl::FakeGuard(mutex), expr) - -#define FTL_MAKE_FAKE_GUARD(arg1, arg2, guard, ...) guard - -#define FTL_FAKE_GUARD(...) \ - FTL_MAKE_FAKE_GUARD(__VA_ARGS__, FTL_FAKE_GUARD2, FTL_FAKE_GUARD1, )(__VA_ARGS__) diff --git a/sysbridge/src/main/cpp/android/ftl/finalizer.h b/sysbridge/src/main/cpp/android/ftl/finalizer.h deleted file mode 100644 index b3342e959c..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/finalizer.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include -#include -#include - -#include - -namespace android::ftl { - -// An RAII wrapper that invokes a function object as a finalizer when destroyed. -// -// The function object must take no arguments, and must return void. If the function object needs -// any context for the call, it must store it itself, for example with a lambda capture. -// -// The stored function object will be called once (unless canceled via the `cancel()` member -// function) at the first of: -// -// - The Finalizer instance is destroyed. -// - `operator()` is used to invoke the contained function. -// - The Finalizer instance is move-assigned a new value. The function being replaced will be -// invoked, and the replacement will be stored to be called later. -// -// The intent with this class is to keep cleanup code next to the code that requires that -// cleanup be performed. -// -// bool read_file(std::string filename) { -// FILE* f = fopen(filename.c_str(), "rb"); -// if (f == nullptr) return false; -// const auto cleanup = ftl::Finalizer([f]() { fclose(f); }); -// // fread(...), etc -// return true; -// } -// -// The `FinalFunction` template argument to Finalizer allows a polymorphic function -// type for storing the finalization function, such as `std::function` or `ftl::Function`. -// -// For convenience, this header defines a few useful aliases for using those types. -// -// - `FinalizerStd`, an alias for `Finalizer>` -// - `FinalizerFtl`, an alias for `Finalizer>` -// - `FinalizerFtl1`, an alias for `Finalizer>` -// - `FinalizerFtl2`, an alias for `Finalizer>` -// - `FinalizerFtl3`, an alias for `Finalizer>` -// -// Clients of this header are free to define other aliases they need. -// -// A Finalizer that uses a polymorphic function type can be returned from a function call and/or -// stored as member data (to be destroyed along with the containing class). -// -// auto register(Observer* observer) -> ftl::FinalizerStd { -// const auto id = observers.add(observer); -// return ftl::Finalizer([id]() { observers.remove(id); }); -// } -// -// { -// const auto _ = register(observer); -// // do the things that required the registered observer. -// } -// // the observer is removed. -// -// Cautions: -// -// 1. When a Finalizer is stored as member data, you will almost certainly want that cleanup to -// happen first, before the rest of the other member data is destroyed. For safety you should -// assume that the finalization function will access that data directly or indirectly. -// -// This means that Finalizers should be defined last, after all other normal member data in a -// class. -// -// class MyClass { -// public: -// bool initialize() { -// ready_ = true; -// cleanup_ = ftl::Finalizer([this]() { ready_ = false; }); -// return true; -// } -// -// bool ready_ = false; -// -// // Finalizers should be last so other class members can be accessed before being -// // destroyed. -// ftl::FinalizerStd cleanup_; -// }; -// -// 2. Care must be taken to use `ftl::Finalizer()` when constructing locally from a lambda. If you -// forget to do so, you are just creating a lambda that won't be automatically invoked! -// -// const auto bad = [&counter](){ ++counter; }; // Just a lambda instance -// const auto good = ftl::Finalizer([&counter](){ ++counter; }); -// - template - class Finalizer final { - // requires(std::is_invocable_r_v) - static_assert(std::is_invocable_r_v); - - public: - // A default constructed Finalizer does nothing when destroyed. - // requires(std::is_default_constructible_v) - constexpr Finalizer() = default; - - // Constructs a Finalizer from a function object. - // requires(std::is_invocable_v) - template>> - [[nodiscard]] explicit constexpr Finalizer(F &&function) - : Finalizer(std::forward(function), false) {} - - constexpr ~Finalizer() { maybe_invoke(); } - - // Disallow copying. - Finalizer(const Finalizer &that) = delete; - - auto operator=(const Finalizer &that) = delete; - - // Move construction - // requires(std::is_move_constructible_v) - [[nodiscard]] constexpr Finalizer(Finalizer &&that) - : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} - - // Implicit conversion move construction - // requires(!std::is_same_v>) - template>>> - // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-rvalue-reference-param-not-moved) - [[nodiscard]] constexpr Finalizer(Finalizer &&that) - : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} - - // Move assignment - // requires(std::is_move_assignable_v) - constexpr auto operator=(Finalizer &&that) -> Finalizer & { - maybe_invoke(); - - function_ = std::move(that.function_); - canceled_ = std::exchange(that.canceled_, true); - - return *this; - } - - // Implicit conversion move assignment - // requires(!std::is_same_v>) - template>>> - // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) - constexpr auto operator=(Finalizer &&that) -> Finalizer & { - *this = Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)); - return *this; - } - - // Cancels the final function, preventing it from being invoked. - constexpr void cancel() { - canceled_ = true; - maybe_nullify_function(); - } - - // Invokes the final function now, if not already invoked. - constexpr void operator()() { maybe_invoke(); } - - private: - template - friend - class Finalizer; - - template>> - [[nodiscard]] explicit constexpr Finalizer(F &&function, bool canceled) - : function_(std::forward(function)), canceled_(canceled) {} - - constexpr void maybe_invoke() { - if (!std::exchange(canceled_, true)) { - std::invoke(function_); - maybe_nullify_function(); - } - } - - constexpr void maybe_nullify_function() { - // Sets function_ to nullptr if that is supported for the backing type. - if constexpr (std::is_assignable_v) { - function_ = nullptr; - } - } - - FinalFunction function_; - bool canceled_ = true; - }; - - template - Finalizer(F &&) -> Finalizer>; - -// A standard alias for using `std::function` as the polymorphic function type. - using FinalizerStd = Finalizer>; - -// Helpful aliases for using `ftl::Function` as the polymorphic function type. - using FinalizerFtl = Finalizer>; - using FinalizerFtl1 = Finalizer>; - using FinalizerFtl2 = Finalizer>; - using FinalizerFtl3 = Finalizer>; - -} // namespace android::ftl \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/ftl/flags.h b/sysbridge/src/main/cpp/android/ftl/flags.h deleted file mode 100644 index 338acc240e..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/flags.h +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -// TODO(b/185536303): Align with FTL style. - -namespace android::ftl { - -/* A class for handling flags defined by an enum or enum class in a type-safe way. */ - template - class Flags { - // F must be an enum or its underlying type is undefined. Theoretically we could specialize this - // further to avoid this restriction but in general we want to encourage the use of enums - // anyways. - static_assert(std::is_enum_v, "Flags type must be an enum"); - using U = std::underlying_type_t; - - public: - constexpr Flags(F f) : mFlags(static_cast(f)) {} - - constexpr Flags(std::initializer_list fs) : mFlags(combine(fs)) {} - - constexpr Flags() : mFlags(0) {} - - constexpr Flags(const Flags &f) : mFlags(f.mFlags) {} - - // Provide a non-explicit construct for non-enum classes since they easily convert to their - // underlying types (e.g. when used with bitwise operators). For enum classes, however, we - // should force them to be explicitly constructed from their underlying types to make full use - // of the type checker. - template - constexpr Flags(T t, std::enable_if_t, T> * = nullptr) : mFlags(t) {} - - template - explicit constexpr Flags(T t, std::enable_if_t, T> * = nullptr) - : mFlags(t) {} - - class Iterator { - using Bits = std::uint64_t; - static_assert(sizeof(U) <= sizeof(Bits)); - - public: - constexpr Iterator() = default; - - Iterator(Flags flags) : mRemainingFlags(flags.mFlags) { (*this)++; } - - // Pre-fix ++ - Iterator &operator++() { - if (mRemainingFlags.none()) { - mCurrFlag = 0; - } else { - // TODO: Replace with std::countr_zero in C++20. - const Bits bit = static_cast(__builtin_ctzll( - mRemainingFlags.to_ullong())); - mRemainingFlags.reset(static_cast(bit)); - mCurrFlag = static_cast(static_cast(1) << bit); - } - return *this; - } - - // Post-fix ++ - Iterator operator++(int) { - Iterator iter = *this; - ++*this; - return iter; - } - - bool operator==(Iterator other) const { - return mCurrFlag == other.mCurrFlag && mRemainingFlags == other.mRemainingFlags; - } - - bool operator!=(Iterator other) const { return !(*this == other); } - - F operator*() const { return F{mCurrFlag}; } - - // iterator traits - - // In the future we could make this a bidirectional const iterator instead of a forward - // iterator but it doesn't seem worth the added complexity at this point. This could not, - // however, be made a non-const iterator as assigning one flag to another is a non-sensical - // operation. - using iterator_category = std::input_iterator_tag; - using value_type = F; - // Per the C++ spec, because input iterators are not assignable the iterator's reference - // type does not actually need to be a reference. In fact, making it a reference would imply - // that modifying it would change the underlying Flags object, which is obviously wrong for - // the same reason this can't be a non-const iterator. - using reference = F; - using difference_type = void; - using pointer = void; - - private: - std::bitset mRemainingFlags; - U mCurrFlag = 0; - }; - - /* - * Tests whether the given flag is set. - */ - bool test(F flag) const { - U f = static_cast(flag); - return (f & mFlags) == f; - } - - /* Tests whether any of the given flags are set */ - bool any(Flags f = ~Flags()) const { return (mFlags & f.mFlags) != 0; } - - /* Tests whether all of the given flags are set */ - bool all(Flags f) const { return (mFlags & f.mFlags) == f.mFlags; } - - constexpr Flags operator|(Flags rhs) const { - return static_cast(mFlags | rhs.mFlags); - } - - Flags &operator|=(Flags rhs) { - mFlags = mFlags | rhs.mFlags; - return *this; - } - - Flags operator&(Flags rhs) const { return static_cast(mFlags & rhs.mFlags); } - - Flags &operator&=(Flags rhs) { - mFlags = mFlags & rhs.mFlags; - return *this; - } - - Flags operator^(Flags rhs) const { return static_cast(mFlags ^ rhs.mFlags); } - - Flags &operator^=(Flags rhs) { - mFlags = mFlags ^ rhs.mFlags; - return *this; - } - - Flags operator~() { return static_cast(~mFlags); } - - bool operator==(Flags rhs) const { return mFlags == rhs.mFlags; } - - bool operator!=(Flags rhs) const { return !operator==(rhs); } - - Flags &operator=(const Flags &rhs) { - mFlags = rhs.mFlags; - return *this; - } - - inline Flags &clear(Flags f = static_cast(~static_cast(0))) { - return *this &= ~f; - } - - Iterator begin() const { return Iterator(*this); } - - Iterator end() const { return Iterator(); } - - /* - * Returns the stored set of flags. - * - * Note that this returns the underlying type rather than the base enum class. This is because - * the value is no longer necessarily a strict member of the enum since the returned value could - * be multiple enum variants OR'd together. - */ - U get() const { return mFlags; } - - std::string string() const { - std::string result; - bool first = true; - U unstringified = 0; - for (const F f: *this) { - if (const auto flagName = flag_name(f)) { - appendFlag(result, flagName.value(), first); - } else { - unstringified |= static_cast(f); - } - } - - if (unstringified != 0) { - constexpr auto radix = sizeof(U) == 1 ? Radix::kBin : Radix::kHex; - appendFlag(result, to_string(unstringified, radix), first); - } - - if (first) { - result += "0x0"; - } - - return result; - } - - private: - U mFlags; - - static constexpr U combine(std::initializer_list fs) { - U result = 0; - for (const F f: fs) { - result |= static_cast(f); - } - return result; - } - - static void appendFlag(std::string &str, const std::string_view &flag, bool &first) { - if (first) { - first = false; - } else { - str += " | "; - } - str += flag; - } - }; - -// This namespace provides operator overloads for enum classes to make it easier to work with them -// as flags. In order to use these, add them via a `using namespace` declaration. - namespace flag_operators { - - template>> - inline Flags operator~(F f) { - return static_cast(~to_underlying(f)); - } - - template>> - constexpr Flags operator|(F lhs, F rhs) { - return static_cast(to_underlying(lhs) | to_underlying(rhs)); - } - - } // namespace flag_operators -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/function.h b/sysbridge/src/main/cpp/android/ftl/function.h deleted file mode 100644 index 80caf4e743..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/function.h +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -#include - -namespace android::ftl { - -// ftl::Function is a container for function object, and can mostly be used in place of -// std::function. -// -// Unlike std::function, a ftl::Function: -// -// * Uses a static amount of memory (controlled by N), and never any dynamic allocation. -// * Satisfies the std::is_trivially_copyable<> trait. -// * Satisfies the std::is_trivially_destructible<> trait. -// -// However those same limits are also required from the contained function object in turn. -// -// The size of a ftl::Function is guaranteed to be: -// -// sizeof(std::intptr_t) * (N + 2) -// -// A ftl::Function can always be implicitly converted to a larger size ftl::Function. -// Trying to convert the other way leads to a compilation error. -// -// A default-constructed ftl::Function is in an empty state. The operator bool() overload returns -// false in this state. It is undefined behavior to attempt to invoke the function in this state. -// -// The ftl::Function can also be constructed or assigned from ftl::no_op. This sets up the -// ftl::Function to be non-empty, with a function that when called does nothing except -// default-constructs a return value. -// -// The ftl::make_function() helpers construct a ftl::Function, including deducing the -// values of F and N from the arguments it is given. -// -// The static ftl::Function::make() helpers construct a ftl::Function without that -// deduction, and also allow for implicit argument conversion if the target being called needs them. -// -// The construction helpers allow any of the following types of functions to be stored: -// -// * Any SMALL function object (as defined by the C++ Standard), such as a lambda with a small -// capture, or other "functor". The requirements are: -// -// 1) The function object must be trivial to destroy (in fact, the destructor will never -// actually be called once copied to the internal storage). -// 2) The function object must be trivial to copy (the raw bytes will be copied as the -// ftl::Function is copied/moved). -// 3) The size of the function object cannot be larger than sizeof(std::intptr_t) * (N + 1), -// and it cannot require stricter alignment than alignof(std::intptr_t). -// -// With the default of N=0, a lambda can only capture a single pointer-sized argument. This is -// enough to capture `this`, which is why N=0 is the default. -// -// * A member function, with the address passed as the template value argument to the construction -// helper function, along with the instance pointer needed to invoke it passed as an ordinary -// argument. -// -// ftl::make_function<&Class::member_function>(this); -// -// Note that the indicated member function will be invoked non-virtually. If you need it to be -// invoked virtually, you should invoke it yourself with a small lambda like so: -// -// ftl::function([this] { virtual_member_function(); }); -// -// * An ordinary function ("free function"), with the address of the function passed as a template -// value argument. -// -// ftl::make_function<&std::atoi>(); -// -// As with the member function helper, as the function is known at compile time, it will be called -// directly. -// -// Example usage: -// -// class MyClass { -// public: -// void on_event() const {} -// int on_string(int*, std::string_view) { return 1; } -// -// auto get_function() { -// return ftl::function([this] { on_event(); }); -// } -// } cls; -// -// // A function container with no arguments, and returning no value. -// ftl::Function f; -// -// // Construct a ftl::Function containing a small lambda. -// f = cls.get_function(); -// -// // Construct a ftl::Function that calls `cls.on_event()`. -// f = ftl::function<&MyClass::on_event>(&cls); -// -// // Create a do-nothing function. -// f = ftl::no_op; -// -// // Invoke the contained function. -// f(); -// -// // Also invokes it. -// std::invoke(f); -// -// // Create a typedef to give a more meaningful name and bound the size. -// using MyFunction = ftl::Function; -// int* ptr = nullptr; -// auto f1 = MyFunction::make( -// [cls = &cls, ptr](std::string_view sv) { -// return cls->on_string(ptr, sv); -// }); -// int r = f1("abc"sv); -// -// // Returns a default-constructed int (0). -// f1 = ftl::no_op; -// r = f1("abc"sv); -// assert(r == 0); - - template - class Function; - -// Used to construct a Function that does nothing. - struct NoOpTag { - }; - - constexpr NoOpTag no_op; - -// Detects that a type is a `ftl::Function` regardless of what `F` and `N` are. - template - struct is_function : public std::false_type { - }; - - template - struct is_function> : public std::true_type { - }; - - template - constexpr bool is_function_v = is_function::value; - - template - class Function final { - // Enforce a valid size, with an arbitrary maximum allowed size for the container of - // sizeof(std::intptr_t) * 16, though that maximum can be relaxed. - static_assert(N <= details::kFunctionMaximumN); - - using OpaqueStorageTraits = details::function_opaque_storage; - - public: - // Defining result_type allows ftl::Function to be substituted for std::function. - using result_type = Ret; - - // Constructs an empty ftl::Function. - Function() = default; - - // Constructing or assigning from nullptr_t also creates an empty ftl::Function. - Function(std::nullptr_t) {} - - Function &operator=(std::nullptr_t) { return *this = Function(nullptr); } - - // Constructing from NoOpTag sets up a a special no-op function which is valid to call, and which - // returns a default constructed return value. - Function(NoOpTag) : function_(details::bind_opaque_no_op()) {} - - Function &operator=(NoOpTag) { return *this = Function(no_op); } - - // Constructing/assigning from a function object stores a copy of that function object, however: - // * It must be trivially copyable, as the implementation makes a copy with memcpy(). - // * It must be trivially destructible, as the implementation doesn't destroy the copy! - // * It must fit in the limited internal storage, which enforces size/alignment restrictions. - - template>> - Function(const F &f) - : opaque_(OpaqueStorageTraits::opaque_copy(f)), - function_(details::bind_opaque_function_object(f)) {} - - template>> - Function &operator=(const F &f) noexcept { - return *this = Function{OpaqueStorageTraits::opaque_copy(f), - details::bind_opaque_function_object(f)}; - } - - // Constructing/assigning from a smaller ftl::Function is allowed, but not anything else. - - template - Function(const Function &other) - : opaque_{OpaqueStorageTraits::opaque_copy(other.opaque_)}, - function_(other.function_) {} - - template - auto &operator=(const Function &other) { - return *this = Function{OpaqueStorageTraits::opaque_copy(other.opaque_), - other.function_}; - } - - // Returns true if a function is set. - explicit operator bool() const { return function_ != nullptr; } - - // Checks if the other function has the same contents as this one. - bool operator==(const Function &other) const { - return other.opaque_ == opaque_ && other.function_ == function_; - } - - bool operator!=(const Function &other) const { return !operator==(other); } - - // Alternative way of testing for a function being set. - bool operator==(std::nullptr_t) const { return function_ == nullptr; } - - bool operator!=(std::nullptr_t) const { return function_ != nullptr; } - - // Invokes the function. - Ret operator()(Args... args) const { - return std::invoke(function_, opaque_.data(), std::forward(args)...); - } - - // Creation helper for function objects, such as lambdas. - template - static auto make(const F &f) -> decltype(Function{f}) { - return Function{f}; - } - - // Creation helper for a class pointer and a compile-time chosen member function to call. - template - static auto make(Class *instance) -> decltype(Function{ - details::bind_member_function(instance, - static_cast(nullptr))}) { - return Function{details::bind_member_function( - instance, static_cast(nullptr))}; - } - - // Creation helper for a compile-time chosen free function to call. - template - static auto make() -> decltype(Function{ - details::bind_free_function( - static_cast(nullptr))}) { - return Function{ - details::bind_free_function( - static_cast(nullptr))}; - } - - private: - // Needed so a Function can be converted to a Function. - template - friend - class Function; - - // The function pointer type of function stored in `function_`. The first argument is always - // `&opaque_`. - using StoredFunction = Ret(void *, Args...); - - // The type of the opaque storage, used to hold an appropriate function object. - // The type stored here is ONLY known to the StoredFunction. - // We always use at least one std::intptr_t worth of storage, and always a multiple of that size. - using OpaqueStorage = typename OpaqueStorageTraits::type; - - // Internal constructor for creating from a raw opaque blob + function pointer. - Function(const OpaqueStorage &opaque, StoredFunction *function) - : opaque_(opaque), function_(function) {} - - // Note: `mutable` so that `operator() const` can use it. - mutable OpaqueStorage opaque_{}; - StoredFunction *function_{nullptr}; - }; - -// Makes a ftl::Function given a function object `F`. - template> - Function(const F &) -> Function; - - template - auto make_function(const F &f) -> decltype(Function{f}) { - return Function{f}; - } - -// Makes a ftl::Function given a `MemberFunction` and a instance pointer to the associated `Class`. - template - auto make_function(Class *instance) - -> decltype(Function{details::bind_member_function( - instance, - static_cast *>(nullptr))}) { - return Function{details::bind_member_function( - instance, - static_cast *>(nullptr))}; - } - -// Makes a ftl::Function given an ordinary free function. - template - auto make_function() -> decltype(Function{ - details::bind_free_function( - static_cast(nullptr))}) { - return Function{ - details::bind_free_function( - static_cast(nullptr))}; - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/future.h b/sysbridge/src/main/cpp/android/ftl/future.h deleted file mode 100644 index 4110674618..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/future.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -#include - -namespace android::ftl { - -// Thin wrapper around FutureImpl (concretely std::future or std::shared_future) with -// extensions for pure values (created via ftl::yield) and continuations. -// -// See also SharedFuture shorthand below. -// - template class FutureImpl = std::future> - class Future final : public details::BaseFuture, T, FutureImpl> { - using Base = details::BaseFuture; - - friend Base; // For BaseFuture<...>::self. - friend details::BaseFuture, T, std::future>; // For BaseFuture<...>::share. - - public: - // Constructs an invalid future. - Future() : future_(std::in_place_type>) {} - - // Constructs a future from its standard counterpart, implicitly. - Future(FutureImpl &&f) : future_(std::move(f)) {} - - bool valid() const { - return std::holds_alternative(future_) || std::get>(future_).valid(); - } - - // Forwarding functions. Base::share is only defined when FutureImpl is std::future, whereas the - // following are defined for either FutureImpl: - using Base::get; - using Base::wait_for; - - // Attaches a continuation to the future. The continuation is a function that maps T to either R - // or ftl::Future. In the former case, the chain wraps the result in a future as if by - // ftl::yield. - // - // auto future = ftl::yield(123); - // ftl::Future futures[] = {ftl::yield('a'), ftl::yield('b')}; - // - // auto chain = - // ftl::Future(std::move(future)) - // .then([](int x) { return static_cast(x % 2); }) - // .then([&futures](std::size_t i) { return std::move(futures[i]); }); - // - // assert(chain.get() == 'b'); - // - template> - auto then(F &&op) && -> Future> { - return defer( - [](auto &&f, F &&op) { - R r = op(f.get()); - if constexpr (std::is_same_v>) { - return r; - } else { - return r.get(); - } - }, - std::move(*this), std::forward(op)); - } - - private: - template - friend Future yield(V &&); - - template - friend Future yield(Args &&...); - - template - Future(details::ValueTag, Args &&... args) - : future_(std::in_place_type, std::forward(args)...) {} - - std::variant> future_; - }; - - template - using SharedFuture = Future; - -// Deduction guide for implicit conversion. - template class FutureImpl> - Future(FutureImpl &&) -> Future; - -// Creates a future that wraps a value. -// -// auto future = ftl::yield(42); -// assert(future.get() == 42); -// -// auto ptr = std::make_unique('!'); -// auto future = ftl::yield(std::move(ptr)); -// assert(*future.get() == '!'); -// - template - inline Future yield(V &&value) { - return {details::ValueTag{}, std::move(value)}; - } - - template - inline Future yield(Args &&... args) { - return {details::ValueTag{}, std::forward(args)...}; - } - -// Creates a future that defers a function call until its result is queried. -// -// auto future = ftl::defer([](int x) { return x + 1; }, 99); -// assert(future.get() == 100); -// - template - inline auto defer(F &&f, Args &&... args) { - return Future( - std::async(std::launch::deferred, std::forward(f), std::forward(args)...)); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/hash.h b/sysbridge/src/main/cpp/android/ftl/hash.h deleted file mode 100644 index 6d13672e7e..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/hash.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include - -namespace android::ftl { - -// Non-cryptographic hash function (namely CityHash64) for strings with at most 64 characters. -// Unlike std::hash, which returns std::size_t and is only required to produce the same result -// for the same input within a single execution of a program, this hash is stable. - inline std::optional stable_hash(std::string_view view) { - const auto length = view.length(); - if (length <= 16) { - return details::hash_length_0_to_16(view.data(), length); - } - if (length <= 32) { - return details::hash_length_17_to_32(view.data(), length); - } - if (length <= 64) { - return details::hash_length_33_to_64(view.data(), length); - } - return {}; - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/ignore.h b/sysbridge/src/main/cpp/android/ftl/ignore.h deleted file mode 100644 index bd33511747..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/ignore.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2025 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -namespace android::ftl { - -// An alternative to `std::ignore` that makes it easy to ignore multiple values. -// -// Examples: -// -// void ftl_ignore_multiple(int arg1, const char* arg2, std::string arg3) { -// // When invoked, all the arguments are ignored. -// ftl::ignore(arg1, arg2, arg3); -// } -// -// void ftl_ignore_single(int arg) { -// // It can be used like std::ignore to ignore a single value -// ftl::ignore = arg; -// } -// - inline constexpr struct { - // NOLINTNEXTLINE(misc-unconventional-assign-operator, readability-named-parameter) - constexpr auto operator=(auto &&) const -> decltype(*this) { return *this; } - - // NOLINTNEXTLINE(readability-named-parameter) - constexpr void operator()(auto &&...) const {} - } ignore; - -} // namespace android::ftl \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/ftl/initializer_list.h b/sysbridge/src/main/cpp/android/ftl/initializer_list.h deleted file mode 100644 index e6d15c3061..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/initializer_list.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -namespace android::ftl { - -// Compile-time counterpart of std::initializer_list that stores per-element constructor -// arguments with heterogeneous types. For a container with elements of type T, given Sizes -// (S0, S1, ..., SN), N elements are initialized: the first element is initialized with the -// first S0 arguments, the second element is initialized with the next S1 arguments, and so -// on. The list of Types (T0, ..., TM) is flattened, so M is equal to the sum of the Sizes. -// -// An InitializerList is created using ftl::init::list, and is consumed by constructors of -// containers. The function call operator is overloaded such that arguments are accumulated -// in a tuple with each successive call. For instance, the following calls initialize three -// strings using different constructors, i.e. string literal, default, and count/character: -// -// ... = ftl::init::list("abc")()(3u, '?'); -// -// The following syntax is a shorthand for key-value pairs, where the first argument is the -// key, and the rest construct the value. The types of the key and value are deduced if the -// first pair contains exactly two arguments: -// -// ... = ftl::init::map(-1, "abc")(-2)(-3, 3u, '?'); -// -// ... = ftl::init::map(0, 'a')(1, 'b')(2, 'c'); -// -// WARNING: The InitializerList returned by an ftl::init::list expression must be consumed -// immediately, since temporary arguments are destroyed after the full expression. Storing -// an InitializerList results in dangling references. -// - template, typename... Types> - struct InitializerList; - - template - struct InitializerList, Types...> { - // Creates a superset InitializerList by appending the number of arguments to Sizes, and - // expanding Types with forwarding references for each argument. - template - [[nodiscard]] constexpr auto operator()(Args &&... args) && -> InitializerList< - T, std::index_sequence, Types..., Args && ...> { - return {std::tuple_cat(std::move(tuple), - std::forward_as_tuple(std::forward(args)...))}; - } - - // The temporary InitializerList returned by operator() is bound to an rvalue reference in - // container constructors, which extends the lifetime of any temporary arguments that this - // tuple refers to until the completion of the full expression containing the construction. - std::tuple tuple; - }; - - template> - struct KeyValue { - }; - -// Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the -// value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works -// with the latter. - template - struct InitializerList, std::index_sequence, Types...> { - // Accumulate the three arguments to std::pair's piecewise constructor. - template - [[nodiscard]] constexpr auto operator()(K &&k, Args &&... args) && -> InitializerList< - KeyValue, std::index_sequence, Types..., std::piecewise_construct_t, - std::tuple, std::tuple> { - return {std::tuple_cat( - std::move(tuple), - std::forward_as_tuple(std::piecewise_construct, - std::forward_as_tuple(std::forward(k)), - std::forward_as_tuple(std::forward(args)...)))}; - } - - std::tuple tuple; - }; - - namespace init { - - template - [[nodiscard]] constexpr auto list(Args &&... args) { - return InitializerList{}(std::forward(args)...); - } - - template, typename... Args> - [[nodiscard]] constexpr auto map(Args &&... args) { - return list>(std::forward(args)...); - } - - template - [[nodiscard]] constexpr auto map(K &&k, V &&v) { - return list>(std::forward(k), std::forward(v)); - } - - } // namespace init -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/match.h b/sysbridge/src/main/cpp/android/ftl/match.h deleted file mode 100644 index 490a4e72eb..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/match.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include - -namespace android::ftl { - -// Concise alternative to std::visit that compiles to branches rather than a dispatch table. For -// std::variant where N is small, this is slightly faster since the branches can be -// inlined unlike the function pointers. -// -// using namespace std::chrono; -// std::variant duration = 119min; -// -// // Mutable match. -// ftl::match(duration, [](auto& d) { ++d; }); -// -// // Immutable match. Exhaustive due to minutes being convertible to seconds. -// assert("2 hours"s == -// ftl::match(duration, -// [](const seconds& s) { -// const auto h = duration_cast(s); -// return std::to_string(h.count()) + " hours"s; -// }, -// [](const hours& h) { return std::to_string(h.count() / 24) + " days"s; })); -// - template - decltype(auto) match(std::variant &variant, Ms &&... matchers) { - const auto matcher = details::Matcher{std::forward(matchers)...}; - static_assert(details::is_exhaustive_match_v, - "Non-exhaustive match"); - - return details::Match::match(variant, matcher); - } - - template - decltype(auto) match(const std::variant &variant, Ms &&... matchers) { - const auto matcher = details::Matcher{std::forward(matchers)...}; - static_assert(details::is_exhaustive_match_v, - "Non-exhaustive match"); - - return details::Match::match(variant, matcher); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/mixins.h b/sysbridge/src/main/cpp/android/ftl/mixins.h deleted file mode 100644 index 0cfae3ce27..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/mixins.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace android::ftl { - -// CRTP mixins for defining type-safe wrappers that are distinct from their underlying type. Common -// uses are IDs, opaque handles, and physical quantities. The constructor is provided by (and must -// be inherited from) the `Constructible` mixin, whereas operators (equality, ordering, arithmetic, -// etc.) are enabled through inheritance: -// -// struct Id : ftl::Constructible, ftl::Equatable { -// using Constructible::Constructible; -// }; -// -// static_assert(!std::is_default_constructible_v); -// -// Unlike `Constructible`, `DefaultConstructible` allows default construction. The default value is -// zero-initialized unless specified: -// -// struct Color : ftl::DefaultConstructible, -// ftl::Equatable, -// ftl::Orderable { -// using DefaultConstructible::DefaultConstructible; -// }; -// -// static_assert(Color() == Color(0u)); -// static_assert(ftl::to_underlying(Color(-1)) == 255u); -// static_assert(Color(1u) < Color(2u)); -// -// struct Sequence : ftl::DefaultConstructible, -// ftl::Equatable, -// ftl::Orderable, -// ftl::Incrementable { -// using DefaultConstructible::DefaultConstructible; -// }; -// -// static_assert(Sequence() == Sequence(-1)); -// -// The underlying type need not be a fundamental type: -// -// struct Timeout : ftl::DefaultConstructible, -// ftl::Equatable, -// ftl::Addable { -// using DefaultConstructible::DefaultConstructible; -// }; -// -// using namespace std::chrono_literals; -// static_assert(Timeout() + Timeout(5s) == Timeout(15s)); -// - template - struct Constructible { - explicit constexpr Constructible(T value) : value_(value) {} - - explicit constexpr operator const T &() const { return value_; } - - private: - template class> - friend - class details::Mixin; - - T value_; - }; - - template - struct DefaultConstructible : Constructible { - using Constructible::Constructible; - - constexpr DefaultConstructible() : DefaultConstructible(T{kDefault}) {} - }; - -// Shorthand for casting a type-safe wrapper to its underlying value. - template - constexpr const T &to_underlying(const Constructible &c) { - return static_cast(c); - } - -// Comparison operators for equality. - template - struct Equatable : details::Mixin { - constexpr bool operator==(const Self &other) const { - return to_underlying(this->self()) == to_underlying(other); - } - - constexpr bool operator!=(const Self &other) const { return !(*this == other); } - }; - -// Comparison operators for ordering. - template - struct Orderable : details::Mixin { - constexpr bool operator<(const Self &other) const { - return to_underlying(this->self()) < to_underlying(other); - } - - constexpr bool operator>(const Self &other) const { return other < this->self(); } - - constexpr bool operator>=(const Self &other) const { return !(*this < other); } - - constexpr bool operator<=(const Self &other) const { return !(*this > other); } - }; - -// Pre-increment and post-increment operators. - template - struct Incrementable : details::Mixin { - constexpr Self &operator++() { - ++this->mut(); - return this->self(); - } - - constexpr Self operator++(int) { - const Self tmp = this->self(); - operator++(); - return tmp; - } - }; - -// Additive operators, including incrementing. - template - struct Addable : details::Mixin, Incrementable { - constexpr Self &operator+=(const Self &other) { - this->mut() += to_underlying(other); - return this->self(); - } - - constexpr Self operator+(const Self &other) const { - Self tmp = this->self(); - return tmp += other; - } - - private: - using Base = details::Mixin; - using Base::mut; - using Base::self; - }; - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/non_null.h b/sysbridge/src/main/cpp/android/ftl/non_null.h deleted file mode 100644 index df3e49dfd9..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/non_null.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -namespace android::ftl { - -// Enforces and documents non-null pre/post-condition for (raw or smart) pointers. -// -// void get_length(const ftl::NonNull>& string_ptr, -// ftl::NonNull length_ptr) { -// // No need for `nullptr` checks. -// *length_ptr = string_ptr->length(); -// } -// -// const auto string_ptr = ftl::as_non_null(std::make_shared("android")); -// std::size_t size; -// get_length(string_ptr, ftl::as_non_null(&size)); -// assert(size == 7u); -// -// For compatibility with std::unique_ptr and performance with std::shared_ptr, move -// operations are allowed despite breaking the invariant: -// -// using Pair = std::pair>, std::shared_ptr>; -// -// Pair dupe_if(ftl::NonNull> non_null_ptr, bool condition) { -// // Move the underlying pointer out, so `non_null_ptr` must not be accessed after this point. -// auto unique_ptr = std::move(non_null_ptr).take(); -// -// auto non_null_shared_ptr = ftl::as_non_null(std::shared_ptr(std::move(unique_ptr))); -// auto nullable_shared_ptr = condition ? non_null_shared_ptr.get() : nullptr; -// -// return {std::move(non_null_shared_ptr), std::move(nullable_shared_ptr)}; -// } -// -// auto ptr = ftl::as_non_null(std::make_unique(42)); -// const auto [ptr1, ptr2] = dupe_if(std::move(ptr), true); -// assert(ptr1.get() == ptr2); -// - template - class NonNull final { - struct Passkey { - }; - - public: - // Disallow `nullptr` explicitly for clear compilation errors. - NonNull() = delete; - - NonNull(std::nullptr_t) = delete; - - // Copy operations. - - constexpr NonNull(const NonNull &) = default; - - constexpr NonNull &operator=(const NonNull &) = default; - - template>> - constexpr NonNull(const NonNull &other) : pointer_(other.get()) {} - - template>> - constexpr NonNull &operator=(const NonNull &other) { - pointer_ = other.get(); - return *this; - } - - [[nodiscard]] constexpr const Pointer &get() const { return pointer_; } - - [[nodiscard]] constexpr explicit operator const Pointer &() const { return get(); } - - // Move operations. These break the invariant, so care must be taken to avoid subsequent access. - - constexpr NonNull(NonNull &&) = default; - - constexpr NonNull &operator=(NonNull &&) = default; - - [[nodiscard]] constexpr Pointer take() &&{ return std::move(pointer_); } - - [[nodiscard]] constexpr explicit operator Pointer() && { return take(); } - - // Dereferencing. - [[nodiscard]] constexpr decltype(auto) operator*() const { return *get(); } - - [[nodiscard]] constexpr decltype(auto) operator->() const { return get(); } - - [[nodiscard]] constexpr explicit operator bool() const { return !(pointer_ == nullptr); } - - // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions - // through the passkey idiom, for clear compilation errors. - template - constexpr NonNull(Passkey, P &&pointer) : pointer_(std::forward

(pointer)) { - if (pointer_ == nullptr) std::abort(); - } - - private: - template - friend constexpr auto as_non_null(P &&) -> NonNull>; - - Pointer pointer_; - }; - - template - [[nodiscard]] constexpr auto as_non_null(P &&pointer) -> NonNull> { - using Passkey = typename NonNull>::Passkey; - return {Passkey{}, std::forward

(pointer)}; - } - -// NonNull

<=> NonNull - - template - constexpr bool operator==(const NonNull

&lhs, const NonNull &rhs) { - return lhs.get() == rhs.get(); - } - - template - constexpr bool operator!=(const NonNull

&lhs, const NonNull &rhs) { - return !operator==(lhs, rhs); - } - - template - constexpr bool operator<(const NonNull

&lhs, const NonNull &rhs) { - return lhs.get() < rhs.get(); - } - - template - constexpr bool operator<=(const NonNull

&lhs, const NonNull &rhs) { - return lhs.get() <= rhs.get(); - } - - template - constexpr bool operator>=(const NonNull

&lhs, const NonNull &rhs) { - return lhs.get() >= rhs.get(); - } - - template - constexpr bool operator>(const NonNull

&lhs, const NonNull &rhs) { - return lhs.get() > rhs.get(); - } - -// NonNull

<=> Q - - template - constexpr bool operator==(const NonNull

&lhs, const Q &rhs) { - return lhs.get() == rhs; - } - - template - constexpr bool operator!=(const NonNull

&lhs, const Q &rhs) { - return lhs.get() != rhs; - } - - template - constexpr bool operator<(const NonNull

&lhs, const Q &rhs) { - return lhs.get() < rhs; - } - - template - constexpr bool operator<=(const NonNull

&lhs, const Q &rhs) { - return lhs.get() <= rhs; - } - - template - constexpr bool operator>=(const NonNull

&lhs, const Q &rhs) { - return lhs.get() >= rhs; - } - - template - constexpr bool operator>(const NonNull

&lhs, const Q &rhs) { - return lhs.get() > rhs; - } - -// P <=> NonNull - - template - constexpr bool operator==(const P &lhs, const NonNull &rhs) { - return lhs == rhs.get(); - } - - template - constexpr bool operator!=(const P &lhs, const NonNull &rhs) { - return lhs != rhs.get(); - } - - template - constexpr bool operator<(const P &lhs, const NonNull &rhs) { - return lhs < rhs.get(); - } - - template - constexpr bool operator<=(const P &lhs, const NonNull &rhs) { - return lhs <= rhs.get(); - } - - template - constexpr bool operator>=(const P &lhs, const NonNull &rhs) { - return lhs >= rhs.get(); - } - - template - constexpr bool operator>(const P &lhs, const NonNull &rhs) { - return lhs > rhs.get(); - } - -} // namespace android::ftl - -// Specialize std::hash for ftl::NonNull -template -struct std::hash> { - std::size_t operator()(const android::ftl::NonNull

&ptr) const { - return std::hash

()(ptr.get()); - } -}; diff --git a/sysbridge/src/main/cpp/android/ftl/optional.h b/sysbridge/src/main/cpp/android/ftl/optional.h deleted file mode 100644 index c7b5d26a67..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/optional.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "../libbase/expected.h" - -#include - -namespace android::ftl { - -// Superset of std::optional with monadic operations, as proposed in https://wg21.link/P0798R8. -// -// TODO: Remove standard APIs in C++23. -// - template - struct Optional final : std::optional { - using std::optional::optional; - - // Implicit downcast. - Optional(std::optional other) : std::optional(std::move(other)) {} - - using std::optional::has_value; - using std::optional::value; - - // Returns Optional where F is a function that maps T to U. - template - constexpr auto transform(F &&f) const &{ - using R = details::transform_result_t; - if (has_value()) return R(std::invoke(std::forward(f), value())); - return R(); - } - - template - constexpr auto transform(F &&f) &{ - using R = details::transform_result_t; - if (has_value()) return R(std::invoke(std::forward(f), value())); - return R(); - } - - template - constexpr auto transform(F &&f) const &&{ - using R = details::transform_result_t; - if (has_value()) return R(std::invoke(std::forward(f), std::move(value()))); - return R(); - } - - template - constexpr auto transform(F &&f) &&{ - using R = details::transform_result_t; - if (has_value()) return R(std::invoke(std::forward(f), std::move(value()))); - return R(); - } - - // Returns Optional where F is a function that maps T to Optional. - template - constexpr auto and_then(F &&f) const &{ - using R = details::and_then_result_t; - if (has_value()) return std::invoke(std::forward(f), value()); - return R(); - } - - template - constexpr auto and_then(F &&f) &{ - using R = details::and_then_result_t; - if (has_value()) return std::invoke(std::forward(f), value()); - return R(); - } - - template - constexpr auto and_then(F &&f) const &&{ - using R = details::and_then_result_t; - if (has_value()) return std::invoke(std::forward(f), std::move(value())); - return R(); - } - - template - constexpr auto and_then(F &&f) &&{ - using R = details::and_then_result_t; - if (has_value()) return std::invoke(std::forward(f), std::move(value())); - return R(); - } - - // Returns this Optional if not nullopt, or else the Optional returned by the function F. - template - constexpr auto or_else(F &&f) const & -> details::or_else_result_t { - if (has_value()) return *this; - return std::forward(f)(); - } - - template - constexpr auto or_else(F &&f) && -> details::or_else_result_t { - if (has_value()) return std::move(*this); - return std::forward(f)(); - } - - // Maps this Optional to expected where nullopt becomes E. - template - constexpr auto ok_or(E &&e) && -> base::expected { - if (has_value()) return std::move(value()); - return base::unexpected(std::forward(e)); - } - - // Delete new for this class. Its base doesn't have a virtual destructor, and - // if it got deleted via base class pointer, it would cause undefined - // behavior. There's not a good reason to allocate this object on the heap - // anyway. - static void *operator new(size_t) = delete; - - static void *operator new[](size_t) = delete; - }; - - template - constexpr bool operator==(const Optional &lhs, const Optional &rhs) { - return static_cast>(lhs) == static_cast>(rhs); - } - - template - constexpr bool operator!=(const Optional &lhs, const Optional &rhs) { - return !(lhs == rhs); - } - -// Deduction guides. - template - Optional(T) -> Optional; - - template - Optional(std::optional) -> Optional; - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/shared_mutex.h b/sysbridge/src/main/cpp/android/ftl/shared_mutex.h deleted file mode 100644 index 3008313038..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/shared_mutex.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace android::ftl { - -// Wrapper around std::shared_mutex to provide capabilities for thread-safety -// annotations. -// TODO(b/257958323): This class is no longer needed once b/135688034 is fixed (currently blocked on -// b/175635923). - class [[clang::capability("shared_mutex")]] SharedMutex final { - public: - [[clang::acquire_capability()]] void lock() { - mutex_.lock(); - } - - [[clang::release_capability()]] void unlock() { - mutex_.unlock(); - } - - [[clang::acquire_shared_capability()]] void lock_shared() { - mutex_.lock_shared(); - } - - [[clang::release_shared_capability()]] void unlock_shared() { - mutex_.unlock_shared(); - } - - private: - std::shared_mutex mutex_; - }; - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/small_map.h b/sysbridge/src/main/cpp/android/ftl/small_map.h deleted file mode 100644 index f564909cef..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/small_map.h +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -namespace android::ftl { - -// Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are -// stored in contiguous storage for cache efficiency. The map is allocated statically until its size -// exceeds N, at which point mappings are relocated to dynamic memory. The try_emplace operation has -// a non-standard analogue try_replace that destructively emplaces. The API also defines an in-place -// counterpart to insert_or_assign: emplace_or_replace. Lookup is done not via a subscript operator, -// but immutable getters that can optionally transform the value. -// -// SmallMap unconditionally allocates on the heap. -// -// Example usage: -// -// ftl::SmallMap map; -// assert(map.empty()); -// assert(!map.dynamic()); -// -// map = ftl::init::map(123, "abc")(-1)(42, 3u, '?'); -// assert(map.size() == 3u); -// assert(!map.dynamic()); -// -// assert(map.contains(123)); -// assert(map.get(42).transform([](const std::string& s) { return s.size(); }) == 3u); -// -// const auto opt = map.get(-1); -// assert(opt); -// -// std::string& ref = *opt; -// assert(ref.empty()); -// ref = "xyz"; -// -// map.emplace_or_replace(0, "vanilla", 2u, 3u); -// assert(map.dynamic()); -// -// assert(map == SmallMap(ftl::init::map(-1, "xyz"sv)(0, "nil"sv)(42, "???"sv)(123, "abc"sv))); -// - template> - class SmallMap final { - using Map = SmallVector, N>; - - template - friend - class SmallMap; - - public: - using key_type = K; - using mapped_type = V; - - using value_type = typename Map::value_type; - using size_type = typename Map::size_type; - using difference_type = typename Map::difference_type; - - using reference = typename Map::reference; - using iterator = typename Map::iterator; - - using const_reference = typename Map::const_reference; - using const_iterator = typename Map::const_iterator; - - // Creates an empty map. - SmallMap() = default; - - // Constructs at most N key-value pairs in place by forwarding per-pair constructor arguments. - // The template arguments K, V, and N are inferred using the deduction guide defined below. - // The syntax for listing pairs is as follows: - // - // ftl::SmallMap map = ftl::init::map(123, "abc")(-1)(42, 3u, '?'); - // static_assert(std::is_same_v>); - // - // The types of the key and value are deduced if the first pair contains exactly two arguments: - // - // ftl::SmallMap map = ftl::init::map(0, 'a')(1, 'b')(2, 'c'); - // static_assert(std::is_same_v>); - // - template - SmallMap(InitializerList, Types...> &&list) - : map_(std::move(list)) { - deduplicate(); - } - - // Copies or moves key-value pairs from a convertible map. - template - SmallMap(SmallMap other) : map_(std::move(other.map_)) {} - - static constexpr size_type static_capacity() { return N; } - - size_type max_size() const { return map_.max_size(); } - - size_type size() const { return map_.size(); } - - bool empty() const { return map_.empty(); } - - // Returns whether the map is backed by static or dynamic storage. - bool dynamic() const { - if constexpr (static_capacity() > 0) { - return map_.dynamic(); - } else { - return true; - } - } - - iterator begin() { return map_.begin(); } - - const_iterator begin() const { return cbegin(); } - - const_iterator cbegin() const { return map_.cbegin(); } - - iterator end() { return map_.end(); } - - const_iterator end() const { return cend(); } - - const_iterator cend() const { return map_.cend(); } - - // Returns whether a mapping exists for the given key. - bool contains(const key_type &key) const { return get(key).has_value(); } - - // Returns a reference to the value for the given key, or std::nullopt if the key was not found. - // - // ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); - // - // const auto opt = map.get('c'); - // assert(opt == 'C'); - // - // char d = 'd'; - // const auto ref = map.get('d').value_or(std::ref(d)); - // ref.get() = 'D'; - // assert(d == 'D'); - // - auto get(const key_type &key) const -> Optional> { - for (const auto &[k, v]: *this) { - if (KeyEqual{}(k, key)) { - return std::cref(v); - } - } - return {}; - } - - auto get(const key_type &key) -> Optional> { - for (auto &[k, v]: *this) { - if (KeyEqual{}(k, key)) { - return std::ref(v); - } - } - return {}; - } - - // Returns an iterator to an existing mapping for the given key, or the end() iterator otherwise. - const_iterator find(const key_type &key) const { - return const_cast(*this).find(key); - } - - iterator find(const key_type &key) { return find(key, begin()); } - - // Inserts a mapping unless it exists. Returns an iterator to the inserted or existing mapping, - // and whether the mapping was inserted. - // - // On emplace, if the map reaches its static or dynamic capacity, then all iterators are - // invalidated. Otherwise, only the end() iterator is invalidated. - // - template - std::pair try_emplace(const key_type &key, Args &&... args) { - if (const auto it = find(key); it != end()) { - return {it, false}; - } - - decltype(auto) ref_or_it = - map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key), - std::forward_as_tuple(std::forward(args)...)); - - if constexpr (static_capacity() > 0) { - return {&ref_or_it, true}; - } else { - return {ref_or_it, true}; - } - } - - // Replaces a mapping if it exists, and returns an iterator to it. Returns the end() iterator - // otherwise. - // - // The value is replaced via move constructor, so type V does not need to define copy/move - // assignment, e.g. its data members may be const. - // - // The arguments may directly or indirectly refer to the mapping being replaced. - // - // Iterators to the replaced mapping point to its replacement, and others remain valid. - // - template - iterator try_replace(const key_type &key, Args &&... args) { - const auto it = find(key); - if (it == end()) return it; - map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key), - std::forward_as_tuple(std::forward(args)...)); - return it; - } - - // In-place counterpart of std::unordered_map's insert_or_assign. Returns true on emplace, or - // false on replace. - // - // The value is emplaced and replaced via move constructor, so type V does not need to define - // copy/move assignment, e.g. its data members may be const. - // - // On emplace, if the map reaches its static or dynamic capacity, then all iterators are - // invalidated. Otherwise, only the end() iterator is invalidated. On replace, iterators - // to the replaced mapping point to its replacement, and others remain valid. - // - template - std::pair emplace_or_replace(const key_type &key, Args &&... args) { - const auto [it, ok] = try_emplace(key, std::forward(args)...); - if (ok) return {it, ok}; - map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key), - std::forward_as_tuple(std::forward(args)...)); - return {it, ok}; - } - - // Removes a mapping if it exists, and returns whether it did. - // - // The last() and end() iterators, as well as those to the erased mapping, are invalidated. - // - bool erase(const key_type &key) { return erase(key, begin()); } - - // Removes a mapping. - // - // The last() and end() iterators, as well as those to the erased mapping, are invalidated. - // - void erase(iterator it) { map_.unstable_erase(it); } - - // Removes all mappings. - // - // All iterators are invalidated. - // - void clear() { map_.clear(); } - - private: - iterator find(const key_type &key, iterator first) { - return std::find_if(first, end(), - [&key](const auto &pair) { return KeyEqual{}(pair.first, key); }); - } - - bool erase(const key_type &key, iterator first) { - const auto it = find(key, first); - if (it == end()) return false; - map_.unstable_erase(it); - return true; - } - - void deduplicate() { - for (auto it = begin(); it != end();) { - if (const auto key = it->first; ++it != end()) { - while (erase(key, it)); - } - } - } - - Map map_; - }; - -// Deduction guide for in-place constructor. - template - SmallMap(InitializerList, std::index_sequence, Types...> &&) - -> SmallMap; - -// Returns whether the key-value pairs of two maps are equal. - template - bool operator==(const SmallMap &lhs, const SmallMap &rhs) { - if (lhs.size() != rhs.size()) return false; - - for (const auto &[k, v]: lhs) { - const auto &lv = v; - if (!rhs.get(k).transform([&lv](const W &rv) { return lv == rv; }).value_or(false)) { - return false; - } - } - - return true; - } - -// TODO: Remove in C++20. - template - inline bool operator!=(const SmallMap &lhs, const SmallMap &rhs) { - return !(lhs == rhs); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/small_vector.h b/sysbridge/src/main/cpp/android/ftl/small_vector.h deleted file mode 100644 index ebbe63f16e..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/small_vector.h +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - -#include - -namespace android::ftl { - - template - struct is_small_vector; - -// ftl::StaticVector that promotes to std::vector when full. SmallVector is a drop-in replacement -// for std::vector with statically allocated storage for N elements, whose goal is to improve run -// time by avoiding heap allocation and increasing probability of cache hits. The standard API is -// augmented by an unstable_erase operation that does not preserve order, and a replace operation -// that destructively emplaces. -// -// Unlike std::vector, T does not require copy/move assignment, so may be an object with const data -// members, or be const itself. -// -// SmallVector is a specialization that thinly wraps std::vector. -// -// Example usage: -// -// ftl::SmallVector vector; -// assert(vector.empty()); -// assert(!vector.dynamic()); -// -// vector = {'a', 'b', 'c'}; -// assert(vector.size() == 3u); -// assert(!vector.dynamic()); -// -// vector.push_back('d'); -// assert(vector.dynamic()); -// -// vector.unstable_erase(vector.begin()); -// assert(vector == (ftl::SmallVector{'d', 'b', 'c'})); -// -// vector.pop_back(); -// assert(vector.back() == 'b'); -// assert(vector.dynamic()); -// -// const char array[] = "hi"; -// vector = ftl::SmallVector(array); -// assert(vector == (ftl::SmallVector{'h', 'i', '\0'})); -// assert(!vector.dynamic()); -// -// ftl::SmallVector strings = ftl::init::list("abc")("123456", 3u)(3u, '?'); -// assert(strings.size() == 3u); -// assert(!strings.dynamic()); -// -// assert(strings[0] == "abc"); -// assert(strings[1] == "123"); -// assert(strings[2] == "???"); -// - template - class SmallVector final : details::ArrayTraits, details::ArrayComparators { - using Static = StaticVector; - using Dynamic = SmallVector; - - public: - FTL_ARRAY_TRAIT(T, value_type); - FTL_ARRAY_TRAIT(T, size_type); - FTL_ARRAY_TRAIT(T, difference_type); - - FTL_ARRAY_TRAIT(T, pointer); - FTL_ARRAY_TRAIT(T, reference); - FTL_ARRAY_TRAIT(T, iterator); - FTL_ARRAY_TRAIT(T, reverse_iterator); - - FTL_ARRAY_TRAIT(T, const_pointer); - FTL_ARRAY_TRAIT(T, const_reference); - FTL_ARRAY_TRAIT(T, const_iterator); - FTL_ARRAY_TRAIT(T, const_reverse_iterator); - - // Creates an empty vector. - SmallVector() = default; - - // Constructs at most N elements. See StaticVector for underlying constructors. - template>{}>> - SmallVector(Arg &&arg, Args &&... args) - : vector_(std::in_place_type, std::forward(arg), - std::forward(args)...) {} - - // Copies or moves elements from a smaller convertible vector. - template 0)>> - SmallVector(SmallVector other) : vector_(convert(std::move(other))) {} - - void swap(SmallVector &other) { vector_.swap(other.vector_); } - - // Returns whether the vector is backed by static or dynamic storage. - bool dynamic() const { return std::holds_alternative(vector_); } - - // Avoid std::visit as it generates a dispatch table. -#define DISPATCH(T, F, ...) \ - T F() __VA_ARGS__ { \ - return dynamic() ? std::get(vector_).F() : std::get(vector_).F(); \ - } - - DISPATCH(size_type, max_size, const) - - DISPATCH(size_type, size, const) - - DISPATCH(bool, empty, const) - - DISPATCH(iterator, begin,) - - DISPATCH(const_iterator, begin, const) - - DISPATCH(const_iterator, cbegin, const) - - DISPATCH(iterator, end,) - - DISPATCH(const_iterator, end, const) - - DISPATCH(const_iterator, cend, const) - - DISPATCH(reverse_iterator, rbegin,) - - DISPATCH(const_reverse_iterator, rbegin, const) - - DISPATCH(const_reverse_iterator, crbegin, const) - - DISPATCH(reverse_iterator, rend,) - - DISPATCH(const_reverse_iterator, rend, const) - - DISPATCH(const_reverse_iterator, crend, const) - - DISPATCH(iterator, last,) - - DISPATCH(const_iterator, last, const) - - DISPATCH(reference, front,) - - DISPATCH(const_reference, front, const) - - DISPATCH(reference, back,) - - DISPATCH(const_reference, back, const) - - reference operator[](size_type i) { - return dynamic() ? std::get(vector_)[i] : std::get(vector_)[i]; - } - - const_reference - operator[](size_type i) const { return const_cast(*this)[i]; } - - // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so - // replacing at end() is erroneous. - // - // The element is emplaced via move constructor, so type T does not need to define copy/move - // assignment, e.g. its data members may be const. - // - // The arguments may directly or indirectly refer to the element being replaced. - // - // Iterators to the replaced element point to its replacement, and others remain valid. - // - template - reference replace(const_iterator it, Args &&... args) { - if (dynamic()) { - return std::get(vector_).replace(it, std::forward(args)...); - } else { - return std::get(vector_).replace(it, std::forward(args)...); - } - } - - // Appends an element, and returns a reference to it. - // - // If the vector reaches its static or dynamic capacity, then all iterators are invalidated. - // Otherwise, only the end() iterator is invalidated. - // - template - reference emplace_back(Args &&... args) { - constexpr auto kInsertStatic = &Static::template emplace_back; - constexpr auto kInsertDynamic = &Dynamic::template emplace_back; - return *insert(std::forward(args)...); - } - - // Appends an element. - // - // If the vector reaches its static or dynamic capacity, then all iterators are invalidated. - // Otherwise, only the end() iterator is invalidated. - // - void push_back(const value_type &v) { - constexpr auto kInsertStatic = - static_cast(&Static::push_back); - constexpr auto kInsertDynamic = - static_cast(&Dynamic::push_back); - insert(v); - } - - void push_back(value_type &&v) { - constexpr auto kInsertStatic = static_cast(&Static::push_back); - constexpr auto kInsertDynamic = - static_cast(&Dynamic::push_back); - insert(std::move(v)); - } - - // Removes the last element. The vector must not be empty, or the call is erroneous. - // - // The last() and end() iterators are invalidated. - // - DISPATCH(void, pop_back,) - - // Removes all elements. - // - // All iterators are invalidated. - // - DISPATCH(void, clear,) - -#undef DISPATCH - - // Erases an element, but does not preserve order. Rather than shifting subsequent elements, - // this moves the last element to the slot of the erased element. - // - // The last() and end() iterators, as well as those to the erased element, are invalidated. - // - void unstable_erase(iterator it) { - if (dynamic()) { - std::get(vector_).unstable_erase(it); - } else { - std::get(vector_).unstable_erase(it); - } - } - - // Extracts the elements as std::vector. - std::vector> promote() &&{ - if (dynamic()) { - return std::get(std::move(vector_)).promote(); - } else { - return {std::make_move_iterator(begin()), std::make_move_iterator(end())}; - } - } - - private: - template - friend - class SmallVector; - - template - static std::variant convert(SmallVector &&other) { - using Other = SmallVector; - - if (other.dynamic()) { - return std::get(std::move(other.vector_)); - } else { - return std::get(std::move(other.vector_)); - } - } - - template - auto insert(Args &&... args) { - if (Dynamic *const vector = std::get_if(&vector_)) { - return (vector->*InsertDynamic)(std::forward(args)...); - } - - auto &vector = std::get(vector_); - if (vector.full()) { - return (promote(vector).*InsertDynamic)(std::forward(args)...); - } else { - return (vector.*InsertStatic)(std::forward(args)...); - } - } - - Dynamic &promote(Static &static_vector) { - assert(static_vector.full()); - - // Allocate double capacity to reduce probability of reallocation. - Dynamic vector; - vector.reserve(Static::max_size() * 2); - std::move(static_vector.begin(), static_vector.end(), std::back_inserter(vector)); - - return vector_.template emplace(std::move(vector)); - } - - std::variant vector_; - }; - -// Partial specialization without static storage. - template - class SmallVector final : details::ArrayTraits, - details::ArrayComparators, - details::ArrayIterators, T>, - std::vector> { - using details::ArrayTraits::replace_at; - - using Iter = details::ArrayIterators; - using Impl = std::vector>; - - friend Iter; - - public: - FTL_ARRAY_TRAIT(T, value_type); - FTL_ARRAY_TRAIT(T, size_type); - FTL_ARRAY_TRAIT(T, difference_type); - - FTL_ARRAY_TRAIT(T, pointer); - FTL_ARRAY_TRAIT(T, reference); - FTL_ARRAY_TRAIT(T, iterator); - FTL_ARRAY_TRAIT(T, reverse_iterator); - - FTL_ARRAY_TRAIT(T, const_pointer); - FTL_ARRAY_TRAIT(T, const_reference); - FTL_ARRAY_TRAIT(T, const_iterator); - FTL_ARRAY_TRAIT(T, const_reverse_iterator); - - // See std::vector for underlying constructors. - using Impl::Impl; - - // Copies and moves a vector, respectively. - SmallVector(const SmallVector &) = default; - - SmallVector(SmallVector &&) = default; - - // Constructs elements in place. See StaticVector for underlying constructor. - template - SmallVector(InitializerList, Types...> &&list) - : SmallVector(SmallVector(std::move(list))) {} - - // Copies or moves elements from a convertible vector. - template - SmallVector(SmallVector other) : Impl(convert(std::move(other))) {} - - SmallVector &operator=(SmallVector other) { - // Define copy/move assignment in terms of copy/move construction. - swap(other); - return *this; - } - - void swap(SmallVector &other) { Impl::swap(other); } - - using Impl::empty; - using Impl::max_size; - using Impl::size; - - using Impl::reserve; - - // std::vector iterators are not necessarily raw pointers. - iterator begin() { return Impl::data(); } - - iterator end() { return Impl::data() + size(); } - - using Iter::begin; - using Iter::end; - - using Iter::cbegin; - using Iter::cend; - - using Iter::rbegin; - using Iter::rend; - - using Iter::crbegin; - using Iter::crend; - - using Iter::last; - - using Iter::back; - using Iter::front; - - using Iter::operator[]; - - template - reference replace(const_iterator it, Args &&... args) { - return replace_at(it, std::forward(args)...); - } - - template - iterator emplace_back(Args &&... args) { - return &Impl::emplace_back(std::forward(args)...); - } - - bool push_back(const value_type &v) { - Impl::push_back(v); - return true; - } - - bool push_back(value_type &&v) { - Impl::push_back(std::move(v)); - return true; - } - - using Impl::clear; - using Impl::pop_back; - - void unstable_erase(iterator it) { - if (it != last()) replace(it, std::move(back())); - pop_back(); - } - - std::vector> promote() &&{ return std::move(*this); } - - private: - template - static Impl convert(SmallVector &&other) { - if constexpr (std::is_constructible_v> &&>) { - return std::move(other).promote(); - } else { - SmallVector vector(other.size()); - - // Consistently with StaticVector, T only requires copy/move construction from U, rather than - // copy/move assignment. - auto it = vector.begin(); - for (auto &element: other) { - vector.replace(it++, std::move(element)); - } - - return vector; - } - } - }; - - template - struct is_small_vector : std::false_type { - }; - - template - struct is_small_vector> : std::true_type { - }; - -// Deduction guide for array constructor. - template - SmallVector(T (&)[N]) -> SmallVector, N>; - -// Deduction guide for variadic constructor. - template, - typename = std::enable_if_t<(std::is_constructible_v && ...)>> - SmallVector(T &&, Us &&...) -> SmallVector; - -// Deduction guide for in-place constructor. - template - SmallVector(InitializerList, Types...> &&) - -> SmallVector; - -// Deduction guide for StaticVector conversion. - template - SmallVector(StaticVector &&) -> SmallVector; - - template - inline void swap(SmallVector &lhs, SmallVector &rhs) { - lhs.swap(rhs); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/static_vector.h b/sysbridge/src/main/cpp/android/ftl/static_vector.h deleted file mode 100644 index d14c12b390..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/static_vector.h +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace android::ftl { - - constexpr struct IteratorRangeTag { - } kIteratorRange; - -// Fixed-capacity, statically allocated counterpart of std::vector. Like std::array, StaticVector -// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather -// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a -// default constructor, since elements are constructed in place as the vector grows. Operations that -// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise -// adheres to standard containers, except the unstable_erase operation that does not preserve order, -// and the replace operation that destructively emplaces. -// -// Unlike std::vector, T does not require copy/move assignment, so may be an object with const data -// members, or be const itself. -// -// StaticVector is analogous to an iterable std::optional. -// StaticVector is an error. -// -// Example usage: -// -// ftl::StaticVector vector; -// assert(vector.empty()); -// -// vector = {'a', 'b'}; -// assert(vector.size() == 2u); -// -// vector.push_back('c'); -// assert(vector.full()); -// -// assert(!vector.push_back('d')); -// assert(vector.size() == 3u); -// -// vector.unstable_erase(vector.begin()); -// assert(vector == (ftl::StaticVector{'c', 'b'})); -// -// vector.pop_back(); -// assert(vector.back() == 'c'); -// -// const char array[] = "hi"; -// vector = ftl::StaticVector(array); -// assert(vector == (ftl::StaticVector{'h', 'i', '\0'})); -// -// ftl::StaticVector strings = ftl::init::list("abc")("123456", 3u)(3u, '?'); -// assert(strings.size() == 3u); -// assert(strings[0] == "abc"); -// assert(strings[1] == "123"); -// assert(strings[2] == "???"); -// - template - class StaticVector final : details::ArrayTraits, - details::ArrayIterators, T>, - details::ArrayComparators { - static_assert(N > 0); - - // For constructor that moves from a smaller convertible vector. - template - friend - class StaticVector; - - using details::ArrayTraits::construct_at; - using details::ArrayTraits::replace_at; - using details::ArrayTraits::in_place_swap_ranges; - using details::ArrayTraits::uninitialized_copy; - - using Iter = details::ArrayIterators; - friend Iter; - - // There is ambiguity when constructing from two iterator-like elements like pointers: - // they could be an iterator range, or arguments for in-place construction. Assume the - // latter unless they are input iterators and cannot be used to construct elements. If - // the former is intended, the caller can pass an IteratorRangeTag to disambiguate. - template> - using is_input_iterator = - std::conjunction, - std::negation>>; - - public: - FTL_ARRAY_TRAIT(T, value_type); - FTL_ARRAY_TRAIT(T, size_type); - FTL_ARRAY_TRAIT(T, difference_type); - - FTL_ARRAY_TRAIT(T, pointer); - FTL_ARRAY_TRAIT(T, reference); - FTL_ARRAY_TRAIT(T, iterator); - FTL_ARRAY_TRAIT(T, reverse_iterator); - - FTL_ARRAY_TRAIT(T, const_pointer); - FTL_ARRAY_TRAIT(T, const_reference); - FTL_ARRAY_TRAIT(T, const_iterator); - FTL_ARRAY_TRAIT(T, const_reverse_iterator); - - // Creates an empty vector. - StaticVector() = default; - - // Copies and moves a vector, respectively. - StaticVector(const StaticVector &other) - : StaticVector(kIteratorRange, other.begin(), other.end()) {} - - StaticVector(StaticVector &&other) { swap(other); } - - // Copies at most N elements from a smaller convertible vector. - template - StaticVector(const StaticVector &other) - : StaticVector(kIteratorRange, other.begin(), other.end()) { - static_assert(N >= M, "Insufficient capacity"); - } - - // Copies at most N elements from a smaller convertible array. - template - explicit StaticVector(U (&array)[M]) - : StaticVector(kIteratorRange, std::begin(array), std::end(array)) { - static_assert(N >= M, "Insufficient capacity"); - } - - // Copies at most N elements from the range [first, last). - // - // IteratorRangeTag disambiguates with initialization from two iterator-like elements. - // - template{}>> - StaticVector(Iterator first, Iterator last) : StaticVector(kIteratorRange, first, last) { - using V = typename std::iterator_traits::value_type; - static_assert(std::is_constructible_v, "Incompatible iterator range"); - } - - template - StaticVector(IteratorRangeTag, Iterator first, Iterator last) - : size_(std::min(max_size(), static_cast(std::distance(first, last)))) { - uninitialized_copy(first, first + size_, begin()); - } - - // Moves at most N elements from a smaller convertible vector. - template - StaticVector(StaticVector &&other) { - static_assert(N >= M, "Insufficient capacity"); - - // Same logic as swap, though M need not be equal to N. - std::uninitialized_move(other.begin(), other.end(), begin()); - std::destroy(other.begin(), other.end()); - std::swap(size_, other.size_); - } - - // Constructs at most N elements. The template arguments T and N are inferred using the - // deduction guide defined below. Note that T is determined from the first element, and - // subsequent elements must have convertible types: - // - // ftl::StaticVector vector = {1, 2, 3}; - // static_assert(std::is_same_v>); - // - // const auto copy = "quince"s; - // auto move = "tart"s; - // ftl::StaticVector vector = {copy, std::move(move)}; - // - // static_assert(std::is_same_v>); - // - template>> - StaticVector(E &&element, Es &&... elements) - : StaticVector(std::index_sequence<0>{}, std::forward(element), - std::forward(elements)...) { - static_assert(sizeof...(elements) < N, "Too many elements"); - } - - // Constructs at most N elements in place by forwarding per-element constructor arguments. The - // template arguments T and N are inferred using the deduction guide defined below. The syntax - // for listing arguments is as follows: - // - // ftl::StaticVector vector = ftl::init::list("abc")()(3u, '?'); - // - // static_assert(std::is_same_v>); - // assert(vector.full()); - // assert(vector[0] == "abc"); - // assert(vector[1].empty()); - // assert(vector[2] == "???"); - // - template - StaticVector(InitializerList, Types...> &&list) - : StaticVector(std::index_sequence<0, 0, Size>{}, std::make_index_sequence{}, - std::index_sequence{}, list.tuple) { - static_assert(sizeof...(Sizes) < N, "Too many elements"); - } - - ~StaticVector() { std::destroy(begin(), end()); } - - StaticVector &operator=(const StaticVector &other) { - StaticVector copy(other); - swap(copy); - return *this; - } - - StaticVector &operator=(StaticVector &&other) { - clear(); - swap(other); - return *this; - } - - // IsEmpty enables a fast path when the vector is known to be empty at compile time. - template - void swap(StaticVector &); - - static constexpr size_type max_size() { return N; } - - size_type size() const { return size_; } - - bool empty() const { return size() == 0; } - - bool full() const { return size() == max_size(); } - - iterator begin() { return std::launder(reinterpret_cast(data_)); } - - iterator end() { return begin() + size(); } - - using Iter::begin; - using Iter::end; - - using Iter::cbegin; - using Iter::cend; - - using Iter::rbegin; - using Iter::rend; - - using Iter::crbegin; - using Iter::crend; - - using Iter::last; - - using Iter::back; - using Iter::front; - - using Iter::operator[]; - - // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so - // replacing at end() is erroneous. - // - // The element is emplaced via move constructor, so type T does not need to define copy/move - // assignment, e.g. its data members may be const. - // - // The arguments may directly or indirectly refer to the element being replaced. - // - // Iterators to the replaced element point to its replacement, and others remain valid. - // - template - reference replace(const_iterator it, Args &&... args) { - return replace_at(it, std::forward(args)...); - } - - // Appends an element, and returns an iterator to it. If the vector is full, the element is not - // inserted, and the end() iterator is returned. - // - // On success, the end() iterator is invalidated. - // - template - iterator emplace_back(Args &&... args) { - if (full()) return end(); - const iterator it = construct_at(end(), std::forward(args)...); - ++size_; - return it; - } - - // Appends an element unless the vector is full, and returns whether the element was inserted. - // - // On success, the end() iterator is invalidated. - // - bool push_back(const value_type &v) { - // Two statements for sequence point. - const iterator it = emplace_back(v); - return it != end(); - } - - bool push_back(value_type &&v) { - // Two statements for sequence point. - const iterator it = emplace_back(std::move(v)); - return it != end(); - } - - // Removes the last element. The vector must not be empty, or the call is erroneous. - // - // The last() and end() iterators are invalidated. - // - void pop_back() { unstable_erase(last()); } - - // Removes all elements. - // - // All iterators are invalidated. - // - void clear() { - std::destroy(begin(), end()); - size_ = 0; - } - - // Erases an element, but does not preserve order. Rather than shifting subsequent elements, - // this moves the last element to the slot of the erased element. - // - // The last() and end() iterators, as well as those to the erased element, are invalidated. - // - void unstable_erase(const_iterator it) { - std::destroy_at(it); - if (it != last()) { - // Move last element and destroy its source for destructor side effects. This is only - // safe because exceptions are disabled. - construct_at(it, std::move(back())); - std::destroy_at(last()); - } - --size_; - } - - private: - // Recursion for variadic constructor. - template - StaticVector(std::index_sequence, E &&element, Es &&... elements) - : StaticVector(std::index_sequence{}, std::forward(elements)...) { - construct_at(begin() + I, std::forward(element)); - } - - // Base case for variadic constructor. - template - explicit StaticVector(std::index_sequence) : size_(I) {} - - // Recursion for in-place constructor. - // - // Construct element I by extracting its arguments from the InitializerList tuple. ArgIndex - // is the position of its first argument in Args, and ArgCount is the number of arguments. - // The Indices sequence corresponds to [0, ArgCount). - // - // The Sizes sequence lists the argument counts for elements after I, so Size is the ArgCount - // for the next element. The recursion stops when Sizes is empty for the last element. - // - template - StaticVector(std::index_sequence, std::index_sequence, - std::index_sequence, std::tuple &tuple) - : StaticVector(std::index_sequence{}, - std::make_index_sequence{}, std::index_sequence{}, - tuple) { - construct_at(begin() + I, std::move(std::get(tuple))...); - } - - // Base case for in-place constructor. - template - StaticVector(std::index_sequence, std::index_sequence, - std::index_sequence<>, std::tuple &tuple) - : size_(I + 1) { - construct_at(begin() + I, std::move(std::get(tuple))...); - } - - size_type size_ = 0; - std::aligned_storage_t data_[N]; - }; - -// Deduction guide for array constructor. - template - StaticVector(T (&)[N]) -> StaticVector, N>; - -// Deduction guide for variadic constructor. - template, - typename = std::enable_if_t<(std::is_constructible_v && ...)>> - StaticVector(T &&, Us &&...) -> StaticVector; - -// Deduction guide for in-place constructor. - template - StaticVector(InitializerList, Types...> &&) - -> StaticVector; - - template - template - void StaticVector::swap(StaticVector &other) { - auto [to, from] = std::make_pair(this, &other); - if (from == this) return; - - // Assume this vector has fewer elements, so the excess of the other vector will be moved to it. - auto [min, max] = std::make_pair(size(), other.size()); - - // No elements to swap if moving into an empty vector. - if constexpr (IsEmpty) { - assert(min == 0); - } else { - if (min > max) { - std::swap(from, to); - std::swap(min, max); - } - - // Swap elements [0, min). - in_place_swap_ranges(begin(), begin() + min, other.begin()); - - // No elements to move if sizes are equal. - if (min == max) return; - } - - // Move elements [min, max) and destroy their source for destructor side effects. - const auto [first, last] = std::make_pair(from->begin() + min, from->begin() + max); - std::uninitialized_move(first, last, to->begin() + min); - std::destroy(first, last); - - std::swap(size_, other.size_); - } - - template - inline void swap(StaticVector &lhs, StaticVector &rhs) { - lhs.swap(rhs); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/string.h b/sysbridge/src/main/cpp/android/ftl/string.h deleted file mode 100644 index 46e45e8169..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/string.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace android::ftl { - - enum class Radix { - kBin = 2, kDec = 10, kHex = 16 - }; - - template - struct to_chars_length { - static_assert(std::is_integral_v); - // Maximum binary digits, plus minus sign and radix prefix. - static constexpr std::size_t value = - std::numeric_limits>::digits + 3; - }; - - template - constexpr std::size_t to_chars_length_v = to_chars_length::value; - - template - using to_chars_buffer_t = char[to_chars_length_v]; - -// Lightweight (not allocating nor sprintf-based) alternative to std::to_string for integers, with -// optional radix. See also ftl::to_string below. -// -// ftl::to_chars_buffer_t<> buffer; -// -// assert(ftl::to_chars(buffer, 123u) == "123"); -// assert(ftl::to_chars(buffer, -42, ftl::Radix::kBin) == "-0b101010"); -// assert(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex) == "0xcafe"); -// assert(ftl::to_chars(buffer, '*', ftl::Radix::kHex) == "0x2a"); -// - template - std::string_view to_chars(char (&buffer)[N], T v, Radix radix = Radix::kDec) { - static_assert(N >= to_chars_length_v); - - auto begin = buffer + 2; - const auto [end, err] = std::to_chars(begin, buffer + N, v, static_cast(radix)); - assert(err == std::errc()); - - if (radix == Radix::kDec) { - // TODO: Replace with {begin, end} in C++20. - return {begin, static_cast(end - begin)}; - } - - const auto prefix = radix == Radix::kBin ? 'b' : 'x'; - if constexpr (std::is_unsigned_v) { - buffer[0] = '0'; - buffer[1] = prefix; - } else { - if (*begin == '-') { - *buffer = '-'; - } else { - --begin; - } - - *begin-- = prefix; - *begin = '0'; - } - - // TODO: Replace with {buffer, end} in C++20. - return {buffer, static_cast(end - buffer)}; - } - -// Lightweight (not sprintf-based) alternative to std::to_string for integers, with optional radix. -// -// assert(ftl::to_string(123u) == "123"); -// assert(ftl::to_string(-42, ftl::Radix::kBin) == "-0b101010"); -// assert(ftl::to_string(0xcafe, ftl::Radix::kHex) == "0xcafe"); -// assert(ftl::to_string('*', ftl::Radix::kHex) == "0x2a"); -// - template - inline std::string to_string(T v, Radix radix = Radix::kDec) { - to_chars_buffer_t buffer; - return std::string(to_chars(buffer, v, radix)); - } - - std::string to_string(bool) = delete; - - std::string to_string(bool, Radix) = delete; - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/unit.h b/sysbridge/src/main/cpp/android/ftl/unit.h deleted file mode 100644 index 2dd71e43a1..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/unit.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace android::ftl { - -// The unit type, and its only value. - constexpr struct Unit { - } unit; - - constexpr bool operator==(Unit, Unit) { - return true; - } - - constexpr bool operator!=(Unit, Unit) { - return false; - } - -// Adapts a function object F to return Unit. The return value of F is ignored. -// -// As a practical use, the function passed to ftl::Optional::transform is not allowed to return -// void (cf. https://wg21.link/P0798R8#mapping-functions-returning-void), but may return Unit if -// only its side effects are meaningful: -// -// ftl::Optional opt = "food"s; -// opt.transform(ftl::unit_fn([](std::string& str) { str.pop_back(); })); -// assert(opt == "foo"s); -// - template - struct UnitFn { - F f; - - template - Unit operator()(Args &&... args) { - return f(std::forward(args)...), unit; - } - }; - - template - constexpr auto unit_fn(F &&f) -> UnitFn> { - return {std::forward(f)}; - } - - namespace details { - -// Identity function for all T except Unit, which maps to void. - template - struct UnitToVoid { - template - static auto from(U &&value) { - return value; - } - }; - - template<> - struct UnitToVoid { - template - static void from(U &&) {} - }; - - } // namespace details -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/input/Input.cpp b/sysbridge/src/main/cpp/android/input/Input.cpp deleted file mode 100644 index 7af317a488..0000000000 --- a/sysbridge/src/main/cpp/android/input/Input.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#define LOG_TAG "Input" - -namespace android { - - bool isFromSource(uint32_t source, uint32_t test) { - return (source & test) == test; - } -} \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/input/Input.h b/sysbridge/src/main/cpp/android/input/Input.h deleted file mode 100644 index b07479be2a..0000000000 --- a/sysbridge/src/main/cpp/android/input/Input.h +++ /dev/null @@ -1,71 +0,0 @@ - -/* - * Flags that flow alongside events in the input dispatch system to help with certain - * policy decisions such as waking from device sleep. - * - * These flags are also defined in frameworks/base/core/java/android/view/WindowManagerPolicy.java. - */ -enum { - /* These flags originate in RawEvents and are generally set in the key map. - * NOTE: If you want a flag to be able to set in a keylayout file, then you must add it to - * InputEventLabels.h as well. */ - - // Indicates that the event should wake the device. - POLICY_FLAG_WAKE = 0x00000001, - - // Indicates that the key is virtual, such as a capacitive button, and should - // generate haptic feedback. Virtual keys may be suppressed for some time - // after a recent touch to prevent accidental activation of virtual keys adjacent - // to the touch screen during an edge swipe. - POLICY_FLAG_VIRTUAL = 0x00000002, - - // Indicates that the key is the special function modifier. - POLICY_FLAG_FUNCTION = 0x00000004, - - // Indicates that the key represents a special gesture that has been detected by - // the touch firmware or driver. Causes touch events from the same device to be canceled. - // This policy flag prevents key events from changing touch mode state. - POLICY_FLAG_GESTURE = 0x00000008, - - // Indicates that key usage mapping represents a fallback mapping. - // Fallback mappings cannot be used to definitively determine whether a device - // supports a key code. For example, a HID device can report a key press - // as a HID usage code if it is not mapped to any linux key code in the kernel. - // However, we cannot know which HID usage codes that device supports from - // userspace through the evdev. We can use fallback mappings to convert HID - // usage codes to Android key codes without needing to know if a device can - // actually report the usage code. - POLICY_FLAG_FALLBACK_USAGE_MAPPING = 0x00000010, - - POLICY_FLAG_RAW_MASK = 0x0000ffff, - - /* These flags are set by the input dispatcher. */ - - // Indicates that the input event was injected. - POLICY_FLAG_INJECTED = 0x01000000, - - // Indicates that the input event is from a trusted source such as a directly attached - // input device or an application with system-wide event injection permission. - POLICY_FLAG_TRUSTED = 0x02000000, - - // Indicates that the input event has passed through an input filter. - POLICY_FLAG_FILTERED = 0x04000000, - - // Disables automatic key repeating behavior. - POLICY_FLAG_DISABLE_KEY_REPEAT = 0x08000000, - - /* These flags are set by the input reader policy as it intercepts each event. */ - - // Indicates that the device was in an interactive state when the - // event was intercepted. - POLICY_FLAG_INTERACTIVE = 0x20000000, - - // Indicates that the event should be dispatched to applications. - // The input event should still be sent to the InputDispatcher so that it can see all - // input events received include those that it will not deliver. - POLICY_FLAG_PASS_TO_USER = 0x40000000, -}; - -namespace android { - bool isFromSource(uint32_t source, uint32_t test); -} \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/input/InputDevice.cpp b/sysbridge/src/main/cpp/android/input/InputDevice.cpp deleted file mode 100644 index 66c72d20a7..0000000000 --- a/sysbridge/src/main/cpp/android/input/InputDevice.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include -#include -#include - -#include "../logging.h" -#include "../libbase/stringprintf.h" -#include -#include "InputDevice.h" -#include "InputEventLabels.h" -#include "../ui/LogicalDisplayId.h" - -using android::base::StringPrintf; - -namespace android { - -// Set to true to log detailed debugging messages about IDC file probing. - static constexpr bool DEBUG_PROBE = false; - - static const char *CONFIGURATION_FILE_DIR[] = { - "idc/", - "keylayout/", - "keychars/", - }; - - static const char *CONFIGURATION_FILE_EXTENSION[] = { - ".idc", - ".kl", - ".kcm", - }; - - static bool isValidNameChar(char ch) { - return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); - } - - static void appendInputDeviceConfigurationFileRelativePath(std::string &path, - const std::string &name, - InputDeviceConfigurationFileType type) { - path += CONFIGURATION_FILE_DIR[static_cast(type)]; - path += name; - path += CONFIGURATION_FILE_EXTENSION[static_cast(type)]; - } - - std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier &deviceIdentifier, InputDeviceConfigurationFileType type, - const char *suffix) { - if (deviceIdentifier.vendor != 0 && deviceIdentifier.product != 0) { - if (deviceIdentifier.version != 0) { - // Try vendor product version. - std::string versionPath = - getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%" - "04x_Version_%04x%s", - deviceIdentifier.vendor, - deviceIdentifier.product, - deviceIdentifier.version, - suffix), - type); - if (!versionPath.empty()) { - LOGI("Found key layout map by version path %s", versionPath.c_str()); - return versionPath; - } - } - - // Try vendor product. - std::string productPath = - getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%04x%s", - deviceIdentifier.vendor, - deviceIdentifier.product, - suffix), - type); - if (!productPath.empty()) { - LOGI("Found key layout map by product path %s", productPath.c_str()); - return productPath; - } - } - - // Try device name. - std::string namePath = getInputDeviceConfigurationFilePathByName( - deviceIdentifier.getCanonicalName() + suffix, - type); - - if (!namePath.empty()) { - LOGI("Found key layout map by name path %s", namePath.c_str()); - return namePath; - } - - // As a last resort, just use the Generic file. - return getInputDeviceConfigurationFilePathByName("Generic", type); - } - - std::string getInputDeviceConfigurationFilePathByName( - const std::string &name, InputDeviceConfigurationFileType type) { - // Search system repository. - std::string path; - - // Treblized input device config files will be located /product/usr, /system_ext/usr, - // /odm/usr or /vendor/usr. - std::vector pathPrefixes{ - "/product/usr/", - "/system_ext/usr/", - "/odm/usr/", - "/vendor/usr/", - "/system/usr", - }; - // These files may also be in the APEX pointed by input_device.config_file.apex sysprop. -// if (auto apex = GetProperty("input_device.config_file.apex", ""); !apex.empty()) { -// pathPrefixes.push_back("/apex/" + apex + "/etc/usr/"); -// } - // ANDROID_ROOT may not be set on host - if (auto android_root = getenv("ANDROID_ROOT"); android_root != nullptr) { - pathPrefixes.push_back(std::string(android_root) + "/usr/"); - } - for (const auto &prefix: pathPrefixes) { - path = prefix; - appendInputDeviceConfigurationFileRelativePath(path, name, type); - if (!access(path.c_str(), R_OK)) { - if (DEBUG_PROBE) { - LOGI("Found system-provided input device configuration file at %s", - path.c_str()); - } - return path; - } else if (errno != ENOENT) { - if (DEBUG_PROBE) { - LOGW("Couldn't find a system-provided input device configuration file at %s due to error %d (%s); there may be an IDC file there that cannot be loaded.", - path.c_str(), errno, strerror(errno)); - } - } else { - if (DEBUG_PROBE) { - LOGE("Didn't find system-provided input device configuration file at %s: %s", - path.c_str(), strerror(errno)); - } - } - } - - // Search user repository. - // TODO Should only look here if not in safe mode. - path = ""; - char *androidData = getenv("ANDROID_DATA"); - if (androidData != nullptr) { - path += androidData; - } - path += "/system/devices/"; - appendInputDeviceConfigurationFileRelativePath(path, name, type); - if (!access(path.c_str(), R_OK)) { - if (DEBUG_PROBE) { - LOGI("Found system user input device configuration file at %s", path.c_str()); - } - return path; - } else if (errno != ENOENT) { - LOGW("Couldn't find a system user input device configuration file at %s due to error %d (%s); there may be an IDC file there that cannot be loaded.", - path.c_str(), errno, strerror(errno)); - } else { - if (DEBUG_PROBE) { - LOGE("Didn't find system user input device configuration file at %s: %s", - path.c_str(), strerror(errno)); - } - } - - // Not found. - if (DEBUG_PROBE) { - LOGI("Probe failed to find input device configuration file with name '%s' and type %s", - name.c_str(), ftl::enum_string(type).c_str()); - } - - - return ""; - } - -// --- InputDeviceIdentifier - - std::string InputDeviceIdentifier::getCanonicalName() const { - std::string replacedName = name; - for (char &ch: replacedName) { - if (!isValidNameChar(ch)) { - ch = '_'; - } - } - return replacedName; - } - - -// --- InputDeviceInfo --- - - InputDeviceInfo::InputDeviceInfo() { - initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false, - ui::LogicalDisplayId::INVALID); - } - - InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo &other) - : mId(other.mId), - mGeneration(other.mGeneration), - mControllerNumber(other.mControllerNumber), - mIdentifier(other.mIdentifier), - mAlias(other.mAlias), - mIsExternal(other.mIsExternal), - mHasMic(other.mHasMic), - mKeyboardLayoutInfo(other.mKeyboardLayoutInfo), - mSources(other.mSources), - mKeyboardType(other.mKeyboardType), - mUsiVersion(other.mUsiVersion), - mAssociatedDisplayId(other.mAssociatedDisplayId), - mEnabled(other.mEnabled), - mHasVibrator(other.mHasVibrator), - mHasBattery(other.mHasBattery), - mHasButtonUnderPad(other.mHasButtonUnderPad), - mHasSensor(other.mHasSensor), - mMotionRanges(other.mMotionRanges), - mSensors(other.mSensors), - mLights(other.mLights), - mViewBehavior(other.mViewBehavior) {} - - InputDeviceInfo &InputDeviceInfo::operator=(const InputDeviceInfo &other) { - mId = other.mId; - mGeneration = other.mGeneration; - mControllerNumber = other.mControllerNumber; - mIdentifier = other.mIdentifier; - mAlias = other.mAlias; - mIsExternal = other.mIsExternal; - mHasMic = other.mHasMic; - mKeyboardLayoutInfo = other.mKeyboardLayoutInfo; - mSources = other.mSources; - mKeyboardType = other.mKeyboardType; - mUsiVersion = other.mUsiVersion; - mAssociatedDisplayId = other.mAssociatedDisplayId; - mEnabled = other.mEnabled; - mHasVibrator = other.mHasVibrator; - mHasBattery = other.mHasBattery; - mHasButtonUnderPad = other.mHasButtonUnderPad; - mHasSensor = other.mHasSensor; - mMotionRanges = other.mMotionRanges; - mSensors = other.mSensors; - mLights = other.mLights; - mViewBehavior = other.mViewBehavior; - return *this; - } - - InputDeviceInfo::~InputDeviceInfo() { - } - - void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, - const InputDeviceIdentifier &identifier, - const std::string &alias, - bool isExternal, bool hasMic, - ui::LogicalDisplayId associatedDisplayId, - InputDeviceViewBehavior viewBehavior, bool enabled) { - mId = id; - mGeneration = generation; - mControllerNumber = controllerNumber; - mIdentifier = identifier; - mAlias = alias; - mIsExternal = isExternal; - mHasMic = hasMic; - mSources = 0; - mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; - mAssociatedDisplayId = associatedDisplayId; - mEnabled = enabled; - mHasVibrator = false; - mHasBattery = false; - mHasButtonUnderPad = false; - mHasSensor = false; - mViewBehavior = viewBehavior; - mUsiVersion.reset(); - mMotionRanges.clear(); - mSensors.clear(); - mLights.clear(); - } - - const InputDeviceInfo::MotionRange *InputDeviceInfo::getMotionRange( - int32_t axis, uint32_t source) const { - for (const MotionRange &range: mMotionRanges) { - if (range.axis == axis && isFromSource(range.source, source)) { - return ⦥ - } - } - return nullptr; - } - - void InputDeviceInfo::addSource(uint32_t source) { - mSources |= source; - } - - void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max, - float flat, float fuzz, float resolution) { - MotionRange range = {axis, source, min, max, flat, fuzz, resolution}; - mMotionRanges.push_back(range); - } - - void InputDeviceInfo::addMotionRange(const MotionRange &range) { - mMotionRanges.push_back(range); - } - - void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo &info) { - if (mSensors.find(info.type) != mSensors.end()) { - LOGW("Sensor type %s already exists, will be replaced by new sensor added.", - ftl::enum_string(info.type).c_str()); - } - mSensors.insert_or_assign(info.type, info); - } - - void InputDeviceInfo::addBatteryInfo(const InputDeviceBatteryInfo &info) { - if (mBatteries.find(info.id) != mBatteries.end()) { - LOGW("Battery id %d already exists, will be replaced by new battery added.", info.id); - } - mBatteries.insert_or_assign(info.id, info); - } - - void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo &info) { - if (mLights.find(info.id) != mLights.end()) { - LOGW("Light id %d already exists, will be replaced by new light added.", info.id); - } - mLights.insert_or_assign(info.id, info); - } - - void InputDeviceInfo::setKeyboardType(int32_t keyboardType) { - mKeyboardType = keyboardType; - } - - void InputDeviceInfo::setKeyboardLayoutInfo(KeyboardLayoutInfo layoutInfo) { - mKeyboardLayoutInfo = std::move(layoutInfo); - } - - std::vector InputDeviceInfo::getSensors() { - std::vector infos; - infos.reserve(mSensors.size()); - for (const auto &[type, info]: mSensors) { - infos.push_back(info); - } - return infos; - } - - std::vector InputDeviceInfo::getLights() { - std::vector infos; - infos.reserve(mLights.size()); - for (const auto &[id, info]: mLights) { - infos.push_back(info); - } - return infos; - } - -} // namespace android \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/input/InputDevice.h b/sysbridge/src/main/cpp/android/input/InputDevice.h deleted file mode 100644 index c7af07cc20..0000000000 --- a/sysbridge/src/main/cpp/android/input/InputDevice.h +++ /dev/null @@ -1,455 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "../ftl/flags.h" -#include "../ftl/mixins.h" -#include "Input.h" -#include -#include -#include -#include "../ui/LogicalDisplayId.h" - -namespace android { - -/* - * Identifies a device. - */ - struct InputDeviceIdentifier { - inline InputDeviceIdentifier() : - bus(0), vendor(0), product(0), version(0) { - } - - // Information provided by the kernel. - std::string name; - std::string location; - std::string uniqueId; - int bus; - int vendor; - int product; - int version; - - // A composite input device descriptor string that uniquely identifies the device - // even across reboots or reconnections. The value of this field is used by - // upper layers of the input system to associate settings with individual devices. - // It is hashed from whatever kernel provided information is available. - // Ideally, the way this value is computed should not change between Android releases - // because that would invalidate persistent settings that rely on it. - std::string descriptor; - - // A value added to uniquely identify a device in the absence of a unique id. This - // is intended to be a minimum way to distinguish from other active devices and may - // reuse values that are not associated with an input anymore. - uint16_t nonce; - - // The bluetooth address of the device, if known. - std::optional bluetoothAddress; - - /** - * Return InputDeviceIdentifier.name that has been adjusted as follows: - * - all characters besides alphanumerics, dash, - * and underscore have been replaced with underscores. - * This helps in situations where a file that matches the device name is needed, - * while conforming to the filename limitations. - */ - std::string getCanonicalName() const; - - bool operator==(const InputDeviceIdentifier &) const = default; - - bool operator!=(const InputDeviceIdentifier &) const = default; - }; - -/** - * Holds View related behaviors for an InputDevice. - */ - struct InputDeviceViewBehavior { - /** - * The smooth scroll behavior that applies for all source/axis, if defined by the device. - * Empty optional if the device has not specified the default smooth scroll behavior. - */ - std::optional shouldSmoothScroll; - }; - -/* Types of input device sensors. Keep sync with core/java/android/hardware/Sensor.java */ - enum class InputDeviceSensorType : int32_t { - ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER, - MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD, - ORIENTATION = 3, - GYROSCOPE = ASENSOR_TYPE_GYROSCOPE, - LIGHT = ASENSOR_TYPE_LIGHT, - PRESSURE = ASENSOR_TYPE_PRESSURE, - TEMPERATURE = 7, - PROXIMITY = ASENSOR_TYPE_PROXIMITY, - GRAVITY = ASENSOR_TYPE_GRAVITY, - LINEAR_ACCELERATION = ASENSOR_TYPE_LINEAR_ACCELERATION, - ROTATION_VECTOR = ASENSOR_TYPE_ROTATION_VECTOR, - RELATIVE_HUMIDITY = ASENSOR_TYPE_RELATIVE_HUMIDITY, - AMBIENT_TEMPERATURE = ASENSOR_TYPE_AMBIENT_TEMPERATURE, - MAGNETIC_FIELD_UNCALIBRATED = ASENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED, - GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR, - GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED, - SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION, - - ftl_first = ACCELEROMETER, - ftl_last = SIGNIFICANT_MOTION - }; - - enum class InputDeviceSensorAccuracy : int32_t { - NONE = 0, - LOW = 1, - MEDIUM = 2, - HIGH = 3, - - ftl_last = HIGH, - }; - - enum class InputDeviceSensorReportingMode : int32_t { - CONTINUOUS = 0, - ON_CHANGE = 1, - ONE_SHOT = 2, - SPECIAL_TRIGGER = 3, - }; - - enum class InputDeviceLightType : int32_t { - INPUT = 0, - PLAYER_ID = 1, - KEYBOARD_BACKLIGHT = 2, - KEYBOARD_MIC_MUTE = 3, - KEYBOARD_VOLUME_MUTE = 4, - - ftl_last = KEYBOARD_VOLUME_MUTE - }; - - enum class InputDeviceLightCapability : uint32_t { - /** Capability to change brightness of the light */ - BRIGHTNESS = 0x00000001, - /** Capability to change color of the light */ - RGB = 0x00000002, - }; - - struct InputDeviceSensorInfo { - explicit InputDeviceSensorInfo(std::string name, std::string vendor, int32_t version, - InputDeviceSensorType type, - InputDeviceSensorAccuracy accuracy, - float maxRange, float resolution, float power, - int32_t minDelay, - int32_t fifoReservedEventCount, int32_t fifoMaxEventCount, - std::string stringType, int32_t maxDelay, int32_t flags, - int32_t id) - : name(name), - vendor(vendor), - version(version), - type(type), - accuracy(accuracy), - maxRange(maxRange), - resolution(resolution), - power(power), - minDelay(minDelay), - fifoReservedEventCount(fifoReservedEventCount), - fifoMaxEventCount(fifoMaxEventCount), - stringType(stringType), - maxDelay(maxDelay), - flags(flags), - id(id) {} - - // Name string of the sensor. - std::string name; - // Vendor string of this sensor. - std::string vendor; - // Version of the sensor's module. - int32_t version; - // Generic type of this sensor. - InputDeviceSensorType type; - // The current accuracy of sensor event. - InputDeviceSensorAccuracy accuracy; - // Maximum range of the sensor in the sensor's unit. - float maxRange; - // Resolution of the sensor in the sensor's unit. - float resolution; - // The power in mA used by this sensor while in use. - float power; - // The minimum delay allowed between two events in microsecond or zero if this sensor only - // returns a value when the data it's measuring changes. - int32_t minDelay; - // Number of events reserved for this sensor in the batch mode FIFO. - int32_t fifoReservedEventCount; - // Maximum number of events of this sensor that could be batched. - int32_t fifoMaxEventCount; - // The type of this sensor as a string. - std::string stringType; - // The delay between two sensor events corresponding to the lowest frequency that this sensor - // supports. - int32_t maxDelay; - // Sensor flags - int32_t flags; - // Sensor id, same as the input device ID it belongs to. - int32_t id; - }; - - struct BrightnessLevel : ftl::DefaultConstructible, - ftl::Equatable, - ftl::Orderable, - ftl::Addable { - using DefaultConstructible::DefaultConstructible; - }; - - struct InputDeviceLightInfo { - explicit InputDeviceLightInfo(std::string name, int32_t id, InputDeviceLightType type, - ftl::Flags capabilityFlags, - int32_t ordinal, - std::set preferredBrightnessLevels) - : name(name), - id(id), - type(type), - capabilityFlags(capabilityFlags), - ordinal(ordinal), - preferredBrightnessLevels(std::move(preferredBrightnessLevels)) {} - - // Name string of the light. - std::string name; - // Light id - int32_t id; - // Type of the light. - InputDeviceLightType type; - // Light capabilities. - ftl::Flags capabilityFlags; - // Ordinal of the light - int32_t ordinal; - // Custom brightness levels for the light - std::set preferredBrightnessLevels; - }; - - struct InputDeviceBatteryInfo { - explicit InputDeviceBatteryInfo(std::string name, int32_t id) : name(name), id(id) {} - - // Name string of the battery. - std::string name; - // Battery id - int32_t id; - }; - - struct KeyboardLayoutInfo { - explicit KeyboardLayoutInfo(std::string languageTag, std::string layoutType) - : languageTag(languageTag), layoutType(layoutType) {} - - // A BCP 47 conformant language tag such as "en-US". - std::string languageTag; - // The layout type such as QWERTY or AZERTY. - std::string layoutType; - - inline bool operator==(const KeyboardLayoutInfo &other) const { - return languageTag == other.languageTag && layoutType == other.layoutType; - } - - inline bool operator!=(const KeyboardLayoutInfo &other) const { return !(*this == other); } - }; - -// The version of the Universal Stylus Initiative (USI) protocol supported by the input device. - struct InputDeviceUsiVersion { - int32_t majorVersion = -1; - int32_t minorVersion = -1; - }; - -/* - * Describes the characteristics and capabilities of an input device. - */ - class InputDeviceInfo { - public: - InputDeviceInfo(); - - InputDeviceInfo(const InputDeviceInfo &other); - - InputDeviceInfo &operator=(const InputDeviceInfo &other); - - ~InputDeviceInfo(); - - struct MotionRange { - int32_t axis; - uint32_t source; - float min; - float max; - float flat; - float fuzz; - float resolution; - }; - - void initialize(int32_t id, int32_t generation, int32_t controllerNumber, - const InputDeviceIdentifier &identifier, const std::string &alias, - bool isExternal, bool hasMic, ui::LogicalDisplayId associatedDisplayId, - InputDeviceViewBehavior viewBehavior = {{}}, bool enabled = true); - - inline int32_t getId() const { return mId; } - - inline int32_t getControllerNumber() const { return mControllerNumber; } - - inline int32_t getGeneration() const { return mGeneration; } - - inline const InputDeviceIdentifier &getIdentifier() const { return mIdentifier; } - - inline const std::string &getAlias() const { return mAlias; } - - inline const std::string &getDisplayName() const { - return mAlias.empty() ? mIdentifier.name : mAlias; - } - - inline bool isExternal() const { return mIsExternal; } - - inline bool hasMic() const { return mHasMic; } - - inline uint32_t getSources() const { return mSources; } - - const MotionRange *getMotionRange(int32_t axis, uint32_t source) const; - - void addSource(uint32_t source); - - void addMotionRange(int32_t axis, uint32_t source, - float min, float max, float flat, float fuzz, float resolution); - - void addMotionRange(const MotionRange &range); - - void addSensorInfo(const InputDeviceSensorInfo &info); - - void addBatteryInfo(const InputDeviceBatteryInfo &info); - - void addLightInfo(const InputDeviceLightInfo &info); - - void setKeyboardType(int32_t keyboardType); - - inline int32_t getKeyboardType() const { return mKeyboardType; } - - void setKeyboardLayoutInfo(KeyboardLayoutInfo keyboardLayoutInfo); - - inline const std::optional &getKeyboardLayoutInfo() const { - return mKeyboardLayoutInfo; - } - - inline const InputDeviceViewBehavior &getViewBehavior() const { return mViewBehavior; } - - inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; } - - inline bool hasVibrator() const { return mHasVibrator; } - - inline void setHasBattery(bool hasBattery) { mHasBattery = hasBattery; } - - inline bool hasBattery() const { return mHasBattery; } - - inline void setButtonUnderPad(bool hasButton) { mHasButtonUnderPad = hasButton; } - - inline bool hasButtonUnderPad() const { return mHasButtonUnderPad; } - - inline void setHasSensor(bool hasSensor) { mHasSensor = hasSensor; } - - inline bool hasSensor() const { return mHasSensor; } - - inline const std::vector &getMotionRanges() const { - return mMotionRanges; - } - - std::vector getSensors(); - - std::vector getLights(); - - inline void setUsiVersion(std::optional usiVersion) { - mUsiVersion = std::move(usiVersion); - } - - inline std::optional getUsiVersion() const { return mUsiVersion; } - - inline ui::LogicalDisplayId getAssociatedDisplayId() const { return mAssociatedDisplayId; } - - inline void setEnabled(bool enabled) { mEnabled = enabled; } - - inline bool isEnabled() const { return mEnabled; } - - private: - int32_t mId; - int32_t mGeneration; - int32_t mControllerNumber; - InputDeviceIdentifier mIdentifier; - std::string mAlias; - bool mIsExternal; - bool mHasMic; - std::optional mKeyboardLayoutInfo; - uint32_t mSources; - int32_t mKeyboardType; - std::optional mUsiVersion; - ui::LogicalDisplayId mAssociatedDisplayId{ui::LogicalDisplayId::INVALID}; - bool mEnabled; - - bool mHasVibrator; - bool mHasBattery; - bool mHasButtonUnderPad; - bool mHasSensor; - - std::vector mMotionRanges; - std::unordered_map mSensors; - /* Map from light ID to light info */ - std::unordered_map mLights; - /* Map from battery ID to battery info */ - std::unordered_map mBatteries; - /** The View related behaviors for the device. */ - InputDeviceViewBehavior mViewBehavior; - }; - -/* Types of input device configuration files. */ - enum class InputDeviceConfigurationFileType : int32_t { - CONFIGURATION = 0, /* .idc file */ - KEY_LAYOUT = 1, /* .kl file */ - KEY_CHARACTER_MAP = 2, /* .kcm file */ - ftl_last = KEY_CHARACTER_MAP, - }; - -/* - * Gets the path of an input device configuration file, if one is available. - * Considers both system provided and user installed configuration files. - * The optional suffix is appended to the end of the file name (before the - * extension). - * - * The device identifier is used to construct several default configuration file - * names to try based on the device name, vendor, product, and version. - * - * Returns an empty string if not found. - */ - extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier &deviceIdentifier, InputDeviceConfigurationFileType type, - const char *suffix = ""); - -/* - * Gets the path of an input device configuration file, if one is available. - * Considers both system provided and user installed configuration files. - * - * The name is case-sensitive and is used to construct the filename to resolve. - * All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores. - * - * Returns an empty string if not found. - */ - extern std::string getInputDeviceConfigurationFilePathByName( - const std::string &name, InputDeviceConfigurationFileType type); - - enum ReservedInputDeviceId : int32_t { - // Device id representing an invalid device - INVALID_INPUT_DEVICE_ID = -2, - // Device id of a special "virtual" keyboard that is always present. - VIRTUAL_KEYBOARD_ID = -1, - // Device id of the "built-in" keyboard if there is one. - BUILT_IN_KEYBOARD_ID = 0, - // First device id available for dynamic devices - END_RESERVED_ID = 1, - }; - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/input/InputEventLabels.cpp b/sysbridge/src/main/cpp/android/input/InputEventLabels.cpp deleted file mode 100644 index d8d3c5b0ca..0000000000 --- a/sysbridge/src/main/cpp/android/input/InputEventLabels.cpp +++ /dev/null @@ -1,1422 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "InputEventLabels.h" - -#include -#include -#include -#include "Input.h" - -#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key } -#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis } -#define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag } - -namespace android { - -// clang-format off - -// NOTE: If you add a new keycode here you must also add it to several other files. -// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. -#define KEYCODES_SEQUENCE \ - DEFINE_KEYCODE(UNKNOWN), \ - DEFINE_KEYCODE(SOFT_LEFT), \ - DEFINE_KEYCODE(SOFT_RIGHT), \ - DEFINE_KEYCODE(HOME), \ - DEFINE_KEYCODE(BACK), \ - DEFINE_KEYCODE(CALL), \ - DEFINE_KEYCODE(ENDCALL), \ - DEFINE_KEYCODE(0), \ - DEFINE_KEYCODE(1), \ - DEFINE_KEYCODE(2), \ - DEFINE_KEYCODE(3), \ - DEFINE_KEYCODE(4), \ - DEFINE_KEYCODE(5), \ - DEFINE_KEYCODE(6), \ - DEFINE_KEYCODE(7), \ - DEFINE_KEYCODE(8), \ - DEFINE_KEYCODE(9), \ - DEFINE_KEYCODE(STAR), \ - DEFINE_KEYCODE(POUND), \ - DEFINE_KEYCODE(DPAD_UP), \ - DEFINE_KEYCODE(DPAD_DOWN), \ - DEFINE_KEYCODE(DPAD_LEFT), \ - DEFINE_KEYCODE(DPAD_RIGHT), \ - DEFINE_KEYCODE(DPAD_CENTER), \ - DEFINE_KEYCODE(VOLUME_UP), \ - DEFINE_KEYCODE(VOLUME_DOWN), \ - DEFINE_KEYCODE(POWER), \ - DEFINE_KEYCODE(CAMERA), \ - DEFINE_KEYCODE(CLEAR), \ - DEFINE_KEYCODE(A), \ - DEFINE_KEYCODE(B), \ - DEFINE_KEYCODE(C), \ - DEFINE_KEYCODE(D), \ - DEFINE_KEYCODE(E), \ - DEFINE_KEYCODE(F), \ - DEFINE_KEYCODE(G), \ - DEFINE_KEYCODE(H), \ - DEFINE_KEYCODE(I), \ - DEFINE_KEYCODE(J), \ - DEFINE_KEYCODE(K), \ - DEFINE_KEYCODE(L), \ - DEFINE_KEYCODE(M), \ - DEFINE_KEYCODE(N), \ - DEFINE_KEYCODE(O), \ - DEFINE_KEYCODE(P), \ - DEFINE_KEYCODE(Q), \ - DEFINE_KEYCODE(R), \ - DEFINE_KEYCODE(S), \ - DEFINE_KEYCODE(T), \ - DEFINE_KEYCODE(U), \ - DEFINE_KEYCODE(V), \ - DEFINE_KEYCODE(W), \ - DEFINE_KEYCODE(X), \ - DEFINE_KEYCODE(Y), \ - DEFINE_KEYCODE(Z), \ - DEFINE_KEYCODE(COMMA), \ - DEFINE_KEYCODE(PERIOD), \ - DEFINE_KEYCODE(ALT_LEFT), \ - DEFINE_KEYCODE(ALT_RIGHT), \ - DEFINE_KEYCODE(SHIFT_LEFT), \ - DEFINE_KEYCODE(SHIFT_RIGHT), \ - DEFINE_KEYCODE(TAB), \ - DEFINE_KEYCODE(SPACE), \ - DEFINE_KEYCODE(SYM), \ - DEFINE_KEYCODE(EXPLORER), \ - DEFINE_KEYCODE(ENVELOPE), \ - DEFINE_KEYCODE(ENTER), \ - DEFINE_KEYCODE(DEL), \ - DEFINE_KEYCODE(GRAVE), \ - DEFINE_KEYCODE(MINUS), \ - DEFINE_KEYCODE(EQUALS), \ - DEFINE_KEYCODE(LEFT_BRACKET), \ - DEFINE_KEYCODE(RIGHT_BRACKET), \ - DEFINE_KEYCODE(BACKSLASH), \ - DEFINE_KEYCODE(SEMICOLON), \ - DEFINE_KEYCODE(APOSTROPHE), \ - DEFINE_KEYCODE(SLASH), \ - DEFINE_KEYCODE(AT), \ - DEFINE_KEYCODE(NUM), \ - DEFINE_KEYCODE(HEADSETHOOK), \ - DEFINE_KEYCODE(FOCUS), \ - DEFINE_KEYCODE(PLUS), \ - DEFINE_KEYCODE(MENU), \ - DEFINE_KEYCODE(NOTIFICATION), \ - DEFINE_KEYCODE(SEARCH), \ - DEFINE_KEYCODE(MEDIA_PLAY_PAUSE), \ - DEFINE_KEYCODE(MEDIA_STOP), \ - DEFINE_KEYCODE(MEDIA_NEXT), \ - DEFINE_KEYCODE(MEDIA_PREVIOUS), \ - DEFINE_KEYCODE(MEDIA_REWIND), \ - DEFINE_KEYCODE(MEDIA_FAST_FORWARD), \ - DEFINE_KEYCODE(MUTE), \ - DEFINE_KEYCODE(PAGE_UP), \ - DEFINE_KEYCODE(PAGE_DOWN), \ - DEFINE_KEYCODE(PICTSYMBOLS), \ - DEFINE_KEYCODE(SWITCH_CHARSET), \ - DEFINE_KEYCODE(BUTTON_A), \ - DEFINE_KEYCODE(BUTTON_B), \ - DEFINE_KEYCODE(BUTTON_C), \ - DEFINE_KEYCODE(BUTTON_X), \ - DEFINE_KEYCODE(BUTTON_Y), \ - DEFINE_KEYCODE(BUTTON_Z), \ - DEFINE_KEYCODE(BUTTON_L1), \ - DEFINE_KEYCODE(BUTTON_R1), \ - DEFINE_KEYCODE(BUTTON_L2), \ - DEFINE_KEYCODE(BUTTON_R2), \ - DEFINE_KEYCODE(BUTTON_THUMBL), \ - DEFINE_KEYCODE(BUTTON_THUMBR), \ - DEFINE_KEYCODE(BUTTON_START), \ - DEFINE_KEYCODE(BUTTON_SELECT), \ - DEFINE_KEYCODE(BUTTON_MODE), \ - DEFINE_KEYCODE(ESCAPE), \ - DEFINE_KEYCODE(FORWARD_DEL), \ - DEFINE_KEYCODE(CTRL_LEFT), \ - DEFINE_KEYCODE(CTRL_RIGHT), \ - DEFINE_KEYCODE(CAPS_LOCK), \ - DEFINE_KEYCODE(SCROLL_LOCK), \ - DEFINE_KEYCODE(META_LEFT), \ - DEFINE_KEYCODE(META_RIGHT), \ - DEFINE_KEYCODE(FUNCTION), \ - DEFINE_KEYCODE(SYSRQ), \ - DEFINE_KEYCODE(BREAK), \ - DEFINE_KEYCODE(MOVE_HOME), \ - DEFINE_KEYCODE(MOVE_END), \ - DEFINE_KEYCODE(INSERT), \ - DEFINE_KEYCODE(FORWARD), \ - DEFINE_KEYCODE(MEDIA_PLAY), \ - DEFINE_KEYCODE(MEDIA_PAUSE), \ - DEFINE_KEYCODE(MEDIA_CLOSE), \ - DEFINE_KEYCODE(MEDIA_EJECT), \ - DEFINE_KEYCODE(MEDIA_RECORD), \ - DEFINE_KEYCODE(F1), \ - DEFINE_KEYCODE(F2), \ - DEFINE_KEYCODE(F3), \ - DEFINE_KEYCODE(F4), \ - DEFINE_KEYCODE(F5), \ - DEFINE_KEYCODE(F6), \ - DEFINE_KEYCODE(F7), \ - DEFINE_KEYCODE(F8), \ - DEFINE_KEYCODE(F9), \ - DEFINE_KEYCODE(F10), \ - DEFINE_KEYCODE(F11), \ - DEFINE_KEYCODE(F12), \ - DEFINE_KEYCODE(NUM_LOCK), \ - DEFINE_KEYCODE(NUMPAD_0), \ - DEFINE_KEYCODE(NUMPAD_1), \ - DEFINE_KEYCODE(NUMPAD_2), \ - DEFINE_KEYCODE(NUMPAD_3), \ - DEFINE_KEYCODE(NUMPAD_4), \ - DEFINE_KEYCODE(NUMPAD_5), \ - DEFINE_KEYCODE(NUMPAD_6), \ - DEFINE_KEYCODE(NUMPAD_7), \ - DEFINE_KEYCODE(NUMPAD_8), \ - DEFINE_KEYCODE(NUMPAD_9), \ - DEFINE_KEYCODE(NUMPAD_DIVIDE), \ - DEFINE_KEYCODE(NUMPAD_MULTIPLY), \ - DEFINE_KEYCODE(NUMPAD_SUBTRACT), \ - DEFINE_KEYCODE(NUMPAD_ADD), \ - DEFINE_KEYCODE(NUMPAD_DOT), \ - DEFINE_KEYCODE(NUMPAD_COMMA), \ - DEFINE_KEYCODE(NUMPAD_ENTER), \ - DEFINE_KEYCODE(NUMPAD_EQUALS), \ - DEFINE_KEYCODE(NUMPAD_LEFT_PAREN), \ - DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN), \ - DEFINE_KEYCODE(VOLUME_MUTE), \ - DEFINE_KEYCODE(INFO), \ - DEFINE_KEYCODE(CHANNEL_UP), \ - DEFINE_KEYCODE(CHANNEL_DOWN), \ - DEFINE_KEYCODE(ZOOM_IN), \ - DEFINE_KEYCODE(ZOOM_OUT), \ - DEFINE_KEYCODE(TV), \ - DEFINE_KEYCODE(WINDOW), \ - DEFINE_KEYCODE(GUIDE), \ - DEFINE_KEYCODE(DVR), \ - DEFINE_KEYCODE(BOOKMARK), \ - DEFINE_KEYCODE(CAPTIONS), \ - DEFINE_KEYCODE(SETTINGS), \ - DEFINE_KEYCODE(TV_POWER), \ - DEFINE_KEYCODE(TV_INPUT), \ - DEFINE_KEYCODE(STB_POWER), \ - DEFINE_KEYCODE(STB_INPUT), \ - DEFINE_KEYCODE(AVR_POWER), \ - DEFINE_KEYCODE(AVR_INPUT), \ - DEFINE_KEYCODE(PROG_RED), \ - DEFINE_KEYCODE(PROG_GREEN), \ - DEFINE_KEYCODE(PROG_YELLOW), \ - DEFINE_KEYCODE(PROG_BLUE), \ - DEFINE_KEYCODE(APP_SWITCH), \ - DEFINE_KEYCODE(BUTTON_1), \ - DEFINE_KEYCODE(BUTTON_2), \ - DEFINE_KEYCODE(BUTTON_3), \ - DEFINE_KEYCODE(BUTTON_4), \ - DEFINE_KEYCODE(BUTTON_5), \ - DEFINE_KEYCODE(BUTTON_6), \ - DEFINE_KEYCODE(BUTTON_7), \ - DEFINE_KEYCODE(BUTTON_8), \ - DEFINE_KEYCODE(BUTTON_9), \ - DEFINE_KEYCODE(BUTTON_10), \ - DEFINE_KEYCODE(BUTTON_11), \ - DEFINE_KEYCODE(BUTTON_12), \ - DEFINE_KEYCODE(BUTTON_13), \ - DEFINE_KEYCODE(BUTTON_14), \ - DEFINE_KEYCODE(BUTTON_15), \ - DEFINE_KEYCODE(BUTTON_16), \ - DEFINE_KEYCODE(LANGUAGE_SWITCH), \ - DEFINE_KEYCODE(MANNER_MODE), \ - DEFINE_KEYCODE(3D_MODE), \ - DEFINE_KEYCODE(CONTACTS), \ - DEFINE_KEYCODE(CALENDAR), \ - DEFINE_KEYCODE(MUSIC), \ - DEFINE_KEYCODE(CALCULATOR), \ - DEFINE_KEYCODE(ZENKAKU_HANKAKU), \ - DEFINE_KEYCODE(EISU), \ - DEFINE_KEYCODE(MUHENKAN), \ - DEFINE_KEYCODE(HENKAN), \ - DEFINE_KEYCODE(KATAKANA_HIRAGANA), \ - DEFINE_KEYCODE(YEN), \ - DEFINE_KEYCODE(RO), \ - DEFINE_KEYCODE(KANA), \ - DEFINE_KEYCODE(ASSIST), \ - DEFINE_KEYCODE(BRIGHTNESS_DOWN), \ - DEFINE_KEYCODE(BRIGHTNESS_UP), \ - DEFINE_KEYCODE(MEDIA_AUDIO_TRACK), \ - DEFINE_KEYCODE(SLEEP), \ - DEFINE_KEYCODE(WAKEUP), \ - DEFINE_KEYCODE(PAIRING), \ - DEFINE_KEYCODE(MEDIA_TOP_MENU), \ - DEFINE_KEYCODE(11), \ - DEFINE_KEYCODE(12), \ - DEFINE_KEYCODE(LAST_CHANNEL), \ - DEFINE_KEYCODE(TV_DATA_SERVICE), \ - DEFINE_KEYCODE(VOICE_ASSIST), \ - DEFINE_KEYCODE(TV_RADIO_SERVICE), \ - DEFINE_KEYCODE(TV_TELETEXT), \ - DEFINE_KEYCODE(TV_NUMBER_ENTRY), \ - DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG), \ - DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL), \ - DEFINE_KEYCODE(TV_SATELLITE), \ - DEFINE_KEYCODE(TV_SATELLITE_BS), \ - DEFINE_KEYCODE(TV_SATELLITE_CS), \ - DEFINE_KEYCODE(TV_SATELLITE_SERVICE), \ - DEFINE_KEYCODE(TV_NETWORK), \ - DEFINE_KEYCODE(TV_ANTENNA_CABLE), \ - DEFINE_KEYCODE(TV_INPUT_HDMI_1), \ - DEFINE_KEYCODE(TV_INPUT_HDMI_2), \ - DEFINE_KEYCODE(TV_INPUT_HDMI_3), \ - DEFINE_KEYCODE(TV_INPUT_HDMI_4), \ - DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1), \ - DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2), \ - DEFINE_KEYCODE(TV_INPUT_COMPONENT_1), \ - DEFINE_KEYCODE(TV_INPUT_COMPONENT_2), \ - DEFINE_KEYCODE(TV_INPUT_VGA_1), \ - DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION), \ - DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP), \ - DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN), \ - DEFINE_KEYCODE(TV_ZOOM_MODE), \ - DEFINE_KEYCODE(TV_CONTENTS_MENU), \ - DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU), \ - DEFINE_KEYCODE(TV_TIMER_PROGRAMMING), \ - DEFINE_KEYCODE(HELP), \ - DEFINE_KEYCODE(NAVIGATE_PREVIOUS), \ - DEFINE_KEYCODE(NAVIGATE_NEXT), \ - DEFINE_KEYCODE(NAVIGATE_IN), \ - DEFINE_KEYCODE(NAVIGATE_OUT), \ - DEFINE_KEYCODE(STEM_PRIMARY), \ - DEFINE_KEYCODE(STEM_1), \ - DEFINE_KEYCODE(STEM_2), \ - DEFINE_KEYCODE(STEM_3), \ - DEFINE_KEYCODE(DPAD_UP_LEFT), \ - DEFINE_KEYCODE(DPAD_DOWN_LEFT), \ - DEFINE_KEYCODE(DPAD_UP_RIGHT), \ - DEFINE_KEYCODE(DPAD_DOWN_RIGHT), \ - DEFINE_KEYCODE(MEDIA_SKIP_FORWARD), \ - DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD), \ - DEFINE_KEYCODE(MEDIA_STEP_FORWARD), \ - DEFINE_KEYCODE(MEDIA_STEP_BACKWARD), \ - DEFINE_KEYCODE(SOFT_SLEEP), \ - DEFINE_KEYCODE(CUT), \ - DEFINE_KEYCODE(COPY), \ - DEFINE_KEYCODE(PASTE), \ - DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), \ - DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), \ - DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), \ - DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), \ - DEFINE_KEYCODE(ALL_APPS), \ - DEFINE_KEYCODE(REFRESH), \ - DEFINE_KEYCODE(THUMBS_UP), \ - DEFINE_KEYCODE(THUMBS_DOWN), \ - DEFINE_KEYCODE(PROFILE_SWITCH), \ - DEFINE_KEYCODE(VIDEO_APP_1), \ - DEFINE_KEYCODE(VIDEO_APP_2), \ - DEFINE_KEYCODE(VIDEO_APP_3), \ - DEFINE_KEYCODE(VIDEO_APP_4), \ - DEFINE_KEYCODE(VIDEO_APP_5), \ - DEFINE_KEYCODE(VIDEO_APP_6), \ - DEFINE_KEYCODE(VIDEO_APP_7), \ - DEFINE_KEYCODE(VIDEO_APP_8), \ - DEFINE_KEYCODE(FEATURED_APP_1), \ - DEFINE_KEYCODE(FEATURED_APP_2), \ - DEFINE_KEYCODE(FEATURED_APP_3), \ - DEFINE_KEYCODE(FEATURED_APP_4), \ - DEFINE_KEYCODE(DEMO_APP_1), \ - DEFINE_KEYCODE(DEMO_APP_2), \ - DEFINE_KEYCODE(DEMO_APP_3), \ - DEFINE_KEYCODE(DEMO_APP_4), \ - DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_DOWN), \ - DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_UP), \ - DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_TOGGLE), \ - DEFINE_KEYCODE(STYLUS_BUTTON_PRIMARY), \ - DEFINE_KEYCODE(STYLUS_BUTTON_SECONDARY), \ - DEFINE_KEYCODE(STYLUS_BUTTON_TERTIARY), \ - DEFINE_KEYCODE(STYLUS_BUTTON_TAIL), \ - DEFINE_KEYCODE(RECENT_APPS), \ - DEFINE_KEYCODE(MACRO_1), \ - DEFINE_KEYCODE(MACRO_2), \ - DEFINE_KEYCODE(MACRO_3), \ - DEFINE_KEYCODE(MACRO_4) -// DEFINE_KEYCODE(EMOJI_PICKER), \ -// DEFINE_KEYCODE(SCREENSHOT), \ -// DEFINE_KEYCODE(DICTATE), \ -// DEFINE_KEYCODE(NEW), \ -// DEFINE_KEYCODE(CLOSE), \ -// DEFINE_KEYCODE(DO_NOT_DISTURB), \ -// DEFINE_KEYCODE(PRINT), \ -// DEFINE_KEYCODE(LOCK), \ -// DEFINE_KEYCODE(FULLSCREEN), \ -// DEFINE_KEYCODE(F13), \ -// DEFINE_KEYCODE(F14), \ -// DEFINE_KEYCODE(F15), \ -// DEFINE_KEYCODE(F16), \ -// DEFINE_KEYCODE(F17), \ -// DEFINE_KEYCODE(F18), \ -// DEFINE_KEYCODE(F19),\ -// DEFINE_KEYCODE(F20), \ -// DEFINE_KEYCODE(F21), \ -// DEFINE_KEYCODE(F22), \ -// DEFINE_KEYCODE(F23), \ -// DEFINE_KEYCODE(F24) - -// NOTE: If you add a new axis here you must also add it to several other files. -// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. -#define AXES_SEQUENCE \ - DEFINE_AXIS(X), \ - DEFINE_AXIS(Y), \ - DEFINE_AXIS(PRESSURE), \ - DEFINE_AXIS(SIZE), \ - DEFINE_AXIS(TOUCH_MAJOR), \ - DEFINE_AXIS(TOUCH_MINOR), \ - DEFINE_AXIS(TOOL_MAJOR), \ - DEFINE_AXIS(TOOL_MINOR), \ - DEFINE_AXIS(ORIENTATION), \ - DEFINE_AXIS(VSCROLL), \ - DEFINE_AXIS(HSCROLL), \ - DEFINE_AXIS(Z), \ - DEFINE_AXIS(RX), \ - DEFINE_AXIS(RY), \ - DEFINE_AXIS(RZ), \ - DEFINE_AXIS(HAT_X), \ - DEFINE_AXIS(HAT_Y), \ - DEFINE_AXIS(LTRIGGER), \ - DEFINE_AXIS(RTRIGGER), \ - DEFINE_AXIS(THROTTLE), \ - DEFINE_AXIS(RUDDER), \ - DEFINE_AXIS(WHEEL), \ - DEFINE_AXIS(GAS), \ - DEFINE_AXIS(BRAKE), \ - DEFINE_AXIS(DISTANCE), \ - DEFINE_AXIS(TILT), \ - DEFINE_AXIS(SCROLL), \ - DEFINE_AXIS(RELATIVE_X), \ - DEFINE_AXIS(RELATIVE_Y), \ - {"RESERVED_29", 29}, \ - {"RESERVED_30", 30}, \ - {"RESERVED_31", 31}, \ - DEFINE_AXIS(GENERIC_1), \ - DEFINE_AXIS(GENERIC_2), \ - DEFINE_AXIS(GENERIC_3), \ - DEFINE_AXIS(GENERIC_4), \ - DEFINE_AXIS(GENERIC_5), \ - DEFINE_AXIS(GENERIC_6), \ - DEFINE_AXIS(GENERIC_7), \ - DEFINE_AXIS(GENERIC_8), \ - DEFINE_AXIS(GENERIC_9), \ - DEFINE_AXIS(GENERIC_10), \ - DEFINE_AXIS(GENERIC_11), \ - DEFINE_AXIS(GENERIC_12), \ - DEFINE_AXIS(GENERIC_13), \ - DEFINE_AXIS(GENERIC_14), \ - DEFINE_AXIS(GENERIC_15), \ - DEFINE_AXIS(GENERIC_16), \ - DEFINE_AXIS(GESTURE_X_OFFSET), \ - DEFINE_AXIS(GESTURE_Y_OFFSET), \ - DEFINE_AXIS(GESTURE_SCROLL_X_DISTANCE), \ - DEFINE_AXIS(GESTURE_SCROLL_Y_DISTANCE), \ - DEFINE_AXIS(GESTURE_PINCH_SCALE_FACTOR), \ - DEFINE_AXIS(GESTURE_SWIPE_FINGER_COUNT) - -#define FLAGS_SEQUENCE \ - DEFINE_FLAG(VIRTUAL), \ - DEFINE_FLAG(FUNCTION), \ - DEFINE_FLAG(GESTURE), \ - DEFINE_FLAG(WAKE), \ - DEFINE_FLAG(FALLBACK_USAGE_MAPPING) - -// clang-format on - -// --- InputEventLookup --- - - InputEventLookup::InputEventLookup() - : KEYCODES({KEYCODES_SEQUENCE}), - KEY_NAMES({KEYCODES_SEQUENCE}), - AXES({AXES_SEQUENCE}), - AXES_NAMES({AXES_SEQUENCE}), - FLAGS({FLAGS_SEQUENCE}) {} - - std::optional InputEventLookup::lookupValueByLabel( - const std::unordered_map &map, const char *literal) { - std::string str(literal); - auto it = map.find(str); - return it != map.end() ? std::make_optional(it->second) : std::nullopt; - } - - const char *InputEventLookup::lookupLabelByValue(const std::vector &vec, - int value) { - if (static_cast(value) < vec.size()) { - return vec[value].literal; - } - return nullptr; - } - - std::optional InputEventLookup::getKeyCodeByLabel(const char *label) { - const auto &self = get(); - return self.lookupValueByLabel(self.KEYCODES, label); - } - - const char *InputEventLookup::getLabelByKeyCode(int32_t keyCode) { - const auto &self = get(); - if (keyCode >= 0 && static_cast(keyCode) < self.KEYCODES.size()) { - return get().lookupLabelByValue(self.KEY_NAMES, keyCode); - } - return nullptr; - } - - std::optional InputEventLookup::getKeyFlagByLabel(const char *label) { - const auto &self = get(); - return lookupValueByLabel(self.FLAGS, label); - } - - std::optional InputEventLookup::getAxisByLabel(const char *label) { - const auto &self = get(); - return lookupValueByLabel(self.AXES, label); - } - - const char *InputEventLookup::getAxisLabel(int32_t axisId) { - const auto &self = get(); - return lookupLabelByValue(self.AXES_NAMES, axisId); - } - - std::optional InputEventLookup::getLedByLabel(const char *label) { - const auto &self = get(); - return lookupValueByLabel(self.LEDS, label); - } - - namespace { - - struct label { - const char *name; - int value; - }; - -#define LABEL(constant) \ - { #constant, constant } -#define LABEL_END \ - { nullptr, -1 } - -// Inserted from the file: out/soong/.intermediates/system/core/toolbox/toolbox_input_labels/gen/input.h-labels.h - static struct label ev_key_value_labels[] = { - {"UP", 0}, - {"DOWN", 1}, - {"REPEAT", 2}, - LABEL_END, - }; - - - static struct label input_prop_labels[] = { - LABEL(INPUT_PROP_POINTER), - LABEL(INPUT_PROP_DIRECT), - LABEL(INPUT_PROP_BUTTONPAD), - LABEL(INPUT_PROP_SEMI_MT), - LABEL(INPUT_PROP_TOPBUTTONPAD), - LABEL(INPUT_PROP_POINTING_STICK), - LABEL(INPUT_PROP_ACCELEROMETER), - LABEL(INPUT_PROP_MAX), - LABEL_END, - }; - static struct label ev_labels[] = { - LABEL(EV_VERSION), - LABEL(EV_SYN), - LABEL(EV_KEY), - LABEL(EV_REL), - LABEL(EV_ABS), - LABEL(EV_MSC), - LABEL(EV_SW), - LABEL(EV_LED), - LABEL(EV_SND), - LABEL(EV_REP), - LABEL(EV_FF), - LABEL(EV_PWR), - LABEL(EV_FF_STATUS), - LABEL(EV_MAX), - LABEL_END, - }; - static struct label syn_labels[] = { - LABEL(SYN_REPORT), - LABEL(SYN_CONFIG), - LABEL(SYN_MT_REPORT), - LABEL(SYN_DROPPED), - LABEL(SYN_MAX), - LABEL_END, - }; - static struct label key_labels[] = { - LABEL(KEY_RESERVED), - LABEL(KEY_ESC), - LABEL(KEY_1), - LABEL(KEY_2), - LABEL(KEY_3), - LABEL(KEY_4), - LABEL(KEY_5), - LABEL(KEY_6), - LABEL(KEY_7), - LABEL(KEY_8), - LABEL(KEY_9), - LABEL(KEY_0), - LABEL(KEY_MINUS), - LABEL(KEY_EQUAL), - LABEL(KEY_BACKSPACE), - LABEL(KEY_TAB), - LABEL(KEY_Q), - LABEL(KEY_W), - LABEL(KEY_E), - LABEL(KEY_R), - LABEL(KEY_T), - LABEL(KEY_Y), - LABEL(KEY_U), - LABEL(KEY_I), - LABEL(KEY_O), - LABEL(KEY_P), - LABEL(KEY_LEFTBRACE), - LABEL(KEY_RIGHTBRACE), - LABEL(KEY_ENTER), - LABEL(KEY_LEFTCTRL), - LABEL(KEY_A), - LABEL(KEY_S), - LABEL(KEY_D), - LABEL(KEY_F), - LABEL(KEY_G), - LABEL(KEY_H), - LABEL(KEY_J), - LABEL(KEY_K), - LABEL(KEY_L), - LABEL(KEY_SEMICOLON), - LABEL(KEY_APOSTROPHE), - LABEL(KEY_GRAVE), - LABEL(KEY_LEFTSHIFT), - LABEL(KEY_BACKSLASH), - LABEL(KEY_Z), - LABEL(KEY_X), - LABEL(KEY_C), - LABEL(KEY_V), - LABEL(KEY_B), - LABEL(KEY_N), - LABEL(KEY_M), - LABEL(KEY_COMMA), - LABEL(KEY_DOT), - LABEL(KEY_SLASH), - LABEL(KEY_RIGHTSHIFT), - LABEL(KEY_KPASTERISK), - LABEL(KEY_LEFTALT), - LABEL(KEY_SPACE), - LABEL(KEY_CAPSLOCK), - LABEL(KEY_F1), - LABEL(KEY_F2), - LABEL(KEY_F3), - LABEL(KEY_F4), - LABEL(KEY_F5), - LABEL(KEY_F6), - LABEL(KEY_F7), - LABEL(KEY_F8), - LABEL(KEY_F9), - LABEL(KEY_F10), - LABEL(KEY_NUMLOCK), - LABEL(KEY_SCROLLLOCK), - LABEL(KEY_KP7), - LABEL(KEY_KP8), - LABEL(KEY_KP9), - LABEL(KEY_KPMINUS), - LABEL(KEY_KP4), - LABEL(KEY_KP5), - LABEL(KEY_KP6), - LABEL(KEY_KPPLUS), - LABEL(KEY_KP1), - LABEL(KEY_KP2), - LABEL(KEY_KP3), - LABEL(KEY_KP0), - LABEL(KEY_KPDOT), - LABEL(KEY_ZENKAKUHANKAKU), - LABEL(KEY_102ND), - LABEL(KEY_F11), - LABEL(KEY_F12), - LABEL(KEY_RO), - LABEL(KEY_KATAKANA), - LABEL(KEY_HIRAGANA), - LABEL(KEY_HENKAN), - LABEL(KEY_KATAKANAHIRAGANA), - LABEL(KEY_MUHENKAN), - LABEL(KEY_KPJPCOMMA), - LABEL(KEY_KPENTER), - LABEL(KEY_RIGHTCTRL), - LABEL(KEY_KPSLASH), - LABEL(KEY_SYSRQ), - LABEL(KEY_RIGHTALT), - LABEL(KEY_LINEFEED), - LABEL(KEY_HOME), - LABEL(KEY_UP), - LABEL(KEY_PAGEUP), - LABEL(KEY_LEFT), - LABEL(KEY_RIGHT), - LABEL(KEY_END), - LABEL(KEY_DOWN), - LABEL(KEY_PAGEDOWN), - LABEL(KEY_INSERT), - LABEL(KEY_DELETE), - LABEL(KEY_MACRO), - LABEL(KEY_MUTE), - LABEL(KEY_VOLUMEDOWN), - LABEL(KEY_VOLUMEUP), - LABEL(KEY_POWER), - LABEL(KEY_KPEQUAL), - LABEL(KEY_KPPLUSMINUS), - LABEL(KEY_PAUSE), - LABEL(KEY_SCALE), - LABEL(KEY_KPCOMMA), - LABEL(KEY_HANGEUL), - LABEL(KEY_HANJA), - LABEL(KEY_YEN), - LABEL(KEY_LEFTMETA), - LABEL(KEY_RIGHTMETA), - LABEL(KEY_COMPOSE), - LABEL(KEY_STOP), - LABEL(KEY_AGAIN), - LABEL(KEY_PROPS), - LABEL(KEY_UNDO), - LABEL(KEY_FRONT), - LABEL(KEY_COPY), - LABEL(KEY_OPEN), - LABEL(KEY_PASTE), - LABEL(KEY_FIND), - LABEL(KEY_CUT), - LABEL(KEY_HELP), - LABEL(KEY_MENU), - LABEL(KEY_CALC), - LABEL(KEY_SETUP), - LABEL(KEY_SLEEP), - LABEL(KEY_WAKEUP), - LABEL(KEY_FILE), - LABEL(KEY_SENDFILE), - LABEL(KEY_DELETEFILE), - LABEL(KEY_XFER), - LABEL(KEY_PROG1), - LABEL(KEY_PROG2), - LABEL(KEY_WWW), - LABEL(KEY_MSDOS), - LABEL(KEY_COFFEE), - LABEL(KEY_ROTATE_DISPLAY), - LABEL(KEY_CYCLEWINDOWS), - LABEL(KEY_MAIL), - LABEL(KEY_BOOKMARKS), - LABEL(KEY_COMPUTER), - LABEL(KEY_BACK), - LABEL(KEY_FORWARD), - LABEL(KEY_CLOSECD), - LABEL(KEY_EJECTCD), - LABEL(KEY_EJECTCLOSECD), - LABEL(KEY_NEXTSONG), - LABEL(KEY_PLAYPAUSE), - LABEL(KEY_PREVIOUSSONG), - LABEL(KEY_STOPCD), - LABEL(KEY_RECORD), - LABEL(KEY_REWIND), - LABEL(KEY_PHONE), - LABEL(KEY_ISO), - LABEL(KEY_CONFIG), - LABEL(KEY_HOMEPAGE), - LABEL(KEY_REFRESH), - LABEL(KEY_EXIT), - LABEL(KEY_MOVE), - LABEL(KEY_EDIT), - LABEL(KEY_SCROLLUP), - LABEL(KEY_SCROLLDOWN), - LABEL(KEY_KPLEFTPAREN), - LABEL(KEY_KPRIGHTPAREN), - LABEL(KEY_NEW), - LABEL(KEY_REDO), - LABEL(KEY_F13), - LABEL(KEY_F14), - LABEL(KEY_F15), - LABEL(KEY_F16), - LABEL(KEY_F17), - LABEL(KEY_F18), - LABEL(KEY_F19), - LABEL(KEY_F20), - LABEL(KEY_F21), - LABEL(KEY_F22), - LABEL(KEY_F23), - LABEL(KEY_F24), - LABEL(KEY_PLAYCD), - LABEL(KEY_PAUSECD), - LABEL(KEY_PROG3), - LABEL(KEY_PROG4), - LABEL(KEY_ALL_APPLICATIONS), - LABEL(KEY_SUSPEND), - LABEL(KEY_CLOSE), - LABEL(KEY_PLAY), - LABEL(KEY_FASTFORWARD), - LABEL(KEY_BASSBOOST), - LABEL(KEY_PRINT), - LABEL(KEY_HP), - LABEL(KEY_CAMERA), - LABEL(KEY_SOUND), - LABEL(KEY_QUESTION), - LABEL(KEY_EMAIL), - LABEL(KEY_CHAT), - LABEL(KEY_SEARCH), - LABEL(KEY_CONNECT), - LABEL(KEY_FINANCE), - LABEL(KEY_SPORT), - LABEL(KEY_SHOP), - LABEL(KEY_ALTERASE), - LABEL(KEY_CANCEL), - LABEL(KEY_BRIGHTNESSDOWN), - LABEL(KEY_BRIGHTNESSUP), - LABEL(KEY_MEDIA), - LABEL(KEY_SWITCHVIDEOMODE), - LABEL(KEY_KBDILLUMTOGGLE), - LABEL(KEY_KBDILLUMDOWN), - LABEL(KEY_KBDILLUMUP), - LABEL(KEY_SEND), - LABEL(KEY_REPLY), - LABEL(KEY_FORWARDMAIL), - LABEL(KEY_SAVE), - LABEL(KEY_DOCUMENTS), - LABEL(KEY_BATTERY), - LABEL(KEY_BLUETOOTH), - LABEL(KEY_WLAN), - LABEL(KEY_UWB), - LABEL(KEY_UNKNOWN), - LABEL(KEY_VIDEO_NEXT), - LABEL(KEY_VIDEO_PREV), - LABEL(KEY_BRIGHTNESS_CYCLE), - LABEL(KEY_BRIGHTNESS_AUTO), - LABEL(KEY_DISPLAY_OFF), - LABEL(KEY_WWAN), - LABEL(KEY_RFKILL), - LABEL(KEY_MICMUTE), - LABEL(BTN_MISC), - LABEL(BTN_0), - LABEL(BTN_1), - LABEL(BTN_2), - LABEL(BTN_3), - LABEL(BTN_4), - LABEL(BTN_5), - LABEL(BTN_6), - LABEL(BTN_7), - LABEL(BTN_8), - LABEL(BTN_9), - LABEL(BTN_MOUSE), - LABEL(BTN_LEFT), - LABEL(BTN_RIGHT), - LABEL(BTN_MIDDLE), - LABEL(BTN_SIDE), - LABEL(BTN_EXTRA), - LABEL(BTN_FORWARD), - LABEL(BTN_BACK), - LABEL(BTN_TASK), - LABEL(BTN_JOYSTICK), - LABEL(BTN_TRIGGER), - LABEL(BTN_THUMB), - LABEL(BTN_THUMB2), - LABEL(BTN_TOP), - LABEL(BTN_TOP2), - LABEL(BTN_PINKIE), - LABEL(BTN_BASE), - LABEL(BTN_BASE2), - LABEL(BTN_BASE3), - LABEL(BTN_BASE4), - LABEL(BTN_BASE5), - LABEL(BTN_BASE6), - LABEL(BTN_DEAD), - LABEL(BTN_GAMEPAD), - LABEL(BTN_SOUTH), - LABEL(BTN_EAST), - LABEL(BTN_C), - LABEL(BTN_NORTH), - LABEL(BTN_WEST), - LABEL(BTN_Z), - LABEL(BTN_TL), - LABEL(BTN_TR), - LABEL(BTN_TL2), - LABEL(BTN_TR2), - LABEL(BTN_SELECT), - LABEL(BTN_START), - LABEL(BTN_MODE), - LABEL(BTN_THUMBL), - LABEL(BTN_THUMBR), - LABEL(BTN_DIGI), - LABEL(BTN_TOOL_PEN), - LABEL(BTN_TOOL_RUBBER), - LABEL(BTN_TOOL_BRUSH), - LABEL(BTN_TOOL_PENCIL), - LABEL(BTN_TOOL_AIRBRUSH), - LABEL(BTN_TOOL_FINGER), - LABEL(BTN_TOOL_MOUSE), - LABEL(BTN_TOOL_LENS), - LABEL(BTN_TOOL_QUINTTAP), - LABEL(BTN_STYLUS3), - LABEL(BTN_TOUCH), - LABEL(BTN_STYLUS), - LABEL(BTN_STYLUS2), - LABEL(BTN_TOOL_DOUBLETAP), - LABEL(BTN_TOOL_TRIPLETAP), - LABEL(BTN_TOOL_QUADTAP), - LABEL(BTN_WHEEL), - LABEL(BTN_GEAR_DOWN), - LABEL(BTN_GEAR_UP), - LABEL(KEY_OK), - LABEL(KEY_SELECT), - LABEL(KEY_GOTO), - LABEL(KEY_CLEAR), - LABEL(KEY_POWER2), - LABEL(KEY_OPTION), - LABEL(KEY_INFO), - LABEL(KEY_TIME), - LABEL(KEY_VENDOR), - LABEL(KEY_ARCHIVE), - LABEL(KEY_PROGRAM), - LABEL(KEY_CHANNEL), - LABEL(KEY_FAVORITES), - LABEL(KEY_EPG), - LABEL(KEY_PVR), - LABEL(KEY_MHP), - LABEL(KEY_LANGUAGE), - LABEL(KEY_TITLE), - LABEL(KEY_SUBTITLE), - LABEL(KEY_ANGLE), - LABEL(KEY_FULL_SCREEN), - LABEL(KEY_MODE), - LABEL(KEY_KEYBOARD), - LABEL(KEY_ASPECT_RATIO), - LABEL(KEY_PC), - LABEL(KEY_TV), - LABEL(KEY_TV2), - LABEL(KEY_VCR), - LABEL(KEY_VCR2), - LABEL(KEY_SAT), - LABEL(KEY_SAT2), - LABEL(KEY_CD), - LABEL(KEY_TAPE), - LABEL(KEY_RADIO), - LABEL(KEY_TUNER), - LABEL(KEY_PLAYER), - LABEL(KEY_TEXT), - LABEL(KEY_DVD), - LABEL(KEY_AUX), - LABEL(KEY_MP3), - LABEL(KEY_AUDIO), - LABEL(KEY_VIDEO), - LABEL(KEY_DIRECTORY), - LABEL(KEY_LIST), - LABEL(KEY_MEMO), - LABEL(KEY_CALENDAR), - LABEL(KEY_RED), - LABEL(KEY_GREEN), - LABEL(KEY_YELLOW), - LABEL(KEY_BLUE), - LABEL(KEY_CHANNELUP), - LABEL(KEY_CHANNELDOWN), - LABEL(KEY_FIRST), - LABEL(KEY_LAST), - LABEL(KEY_AB), - LABEL(KEY_NEXT), - LABEL(KEY_RESTART), - LABEL(KEY_SLOW), - LABEL(KEY_SHUFFLE), - LABEL(KEY_BREAK), - LABEL(KEY_PREVIOUS), - LABEL(KEY_DIGITS), - LABEL(KEY_TEEN), - LABEL(KEY_TWEN), - LABEL(KEY_VIDEOPHONE), - LABEL(KEY_GAMES), - LABEL(KEY_ZOOMIN), - LABEL(KEY_ZOOMOUT), - LABEL(KEY_ZOOMRESET), - LABEL(KEY_WORDPROCESSOR), - LABEL(KEY_EDITOR), - LABEL(KEY_SPREADSHEET), - LABEL(KEY_GRAPHICSEDITOR), - LABEL(KEY_PRESENTATION), - LABEL(KEY_DATABASE), - LABEL(KEY_NEWS), - LABEL(KEY_VOICEMAIL), - LABEL(KEY_ADDRESSBOOK), - LABEL(KEY_MESSENGER), - LABEL(KEY_DISPLAYTOGGLE), - LABEL(KEY_SPELLCHECK), - LABEL(KEY_LOGOFF), - LABEL(KEY_DOLLAR), - LABEL(KEY_EURO), - LABEL(KEY_FRAMEBACK), - LABEL(KEY_FRAMEFORWARD), - LABEL(KEY_CONTEXT_MENU), - LABEL(KEY_MEDIA_REPEAT), - LABEL(KEY_10CHANNELSUP), - LABEL(KEY_10CHANNELSDOWN), - LABEL(KEY_IMAGES), - LABEL(KEY_NOTIFICATION_CENTER), - LABEL(KEY_PICKUP_PHONE), - LABEL(KEY_HANGUP_PHONE), - LABEL(KEY_DEL_EOL), - LABEL(KEY_DEL_EOS), - LABEL(KEY_INS_LINE), - LABEL(KEY_DEL_LINE), - LABEL(KEY_FN), - LABEL(KEY_FN_ESC), - LABEL(KEY_FN_F1), - LABEL(KEY_FN_F2), - LABEL(KEY_FN_F3), - LABEL(KEY_FN_F4), - LABEL(KEY_FN_F5), - LABEL(KEY_FN_F6), - LABEL(KEY_FN_F7), - LABEL(KEY_FN_F8), - LABEL(KEY_FN_F9), - LABEL(KEY_FN_F10), - LABEL(KEY_FN_F11), - LABEL(KEY_FN_F12), - LABEL(KEY_FN_1), - LABEL(KEY_FN_2), - LABEL(KEY_FN_D), - LABEL(KEY_FN_E), - LABEL(KEY_FN_F), - LABEL(KEY_FN_S), - LABEL(KEY_FN_B), - LABEL(KEY_FN_RIGHT_SHIFT), - LABEL(KEY_BRL_DOT1), - LABEL(KEY_BRL_DOT2), - LABEL(KEY_BRL_DOT3), - LABEL(KEY_BRL_DOT4), - LABEL(KEY_BRL_DOT5), - LABEL(KEY_BRL_DOT6), - LABEL(KEY_BRL_DOT7), - LABEL(KEY_BRL_DOT8), - LABEL(KEY_BRL_DOT9), - LABEL(KEY_BRL_DOT10), - LABEL(KEY_NUMERIC_0), - LABEL(KEY_NUMERIC_1), - LABEL(KEY_NUMERIC_2), - LABEL(KEY_NUMERIC_3), - LABEL(KEY_NUMERIC_4), - LABEL(KEY_NUMERIC_5), - LABEL(KEY_NUMERIC_6), - LABEL(KEY_NUMERIC_7), - LABEL(KEY_NUMERIC_8), - LABEL(KEY_NUMERIC_9), - LABEL(KEY_NUMERIC_STAR), - LABEL(KEY_NUMERIC_POUND), - LABEL(KEY_NUMERIC_A), - LABEL(KEY_NUMERIC_B), - LABEL(KEY_NUMERIC_C), - LABEL(KEY_NUMERIC_D), - LABEL(KEY_CAMERA_FOCUS), - LABEL(KEY_WPS_BUTTON), - LABEL(KEY_TOUCHPAD_TOGGLE), - LABEL(KEY_TOUCHPAD_ON), - LABEL(KEY_TOUCHPAD_OFF), - LABEL(KEY_CAMERA_ZOOMIN), - LABEL(KEY_CAMERA_ZOOMOUT), - LABEL(KEY_CAMERA_UP), - LABEL(KEY_CAMERA_DOWN), - LABEL(KEY_CAMERA_LEFT), - LABEL(KEY_CAMERA_RIGHT), - LABEL(KEY_ATTENDANT_ON), - LABEL(KEY_ATTENDANT_OFF), - LABEL(KEY_ATTENDANT_TOGGLE), - LABEL(KEY_LIGHTS_TOGGLE), - LABEL(BTN_DPAD_UP), - LABEL(BTN_DPAD_DOWN), - LABEL(BTN_DPAD_LEFT), - LABEL(BTN_DPAD_RIGHT), - LABEL(KEY_ALS_TOGGLE), - LABEL(KEY_ROTATE_LOCK_TOGGLE), -// LABEL(KEY_REFRESH_RATE_TOGGLE), - LABEL(KEY_BUTTONCONFIG), - LABEL(KEY_TASKMANAGER), - LABEL(KEY_JOURNAL), - LABEL(KEY_CONTROLPANEL), - LABEL(KEY_APPSELECT), - LABEL(KEY_SCREENSAVER), - LABEL(KEY_VOICECOMMAND), - LABEL(KEY_ASSISTANT), - LABEL(KEY_KBD_LAYOUT_NEXT), - LABEL(KEY_EMOJI_PICKER), - LABEL(KEY_DICTATE), - LABEL(KEY_CAMERA_ACCESS_ENABLE), - LABEL(KEY_CAMERA_ACCESS_DISABLE), - LABEL(KEY_CAMERA_ACCESS_TOGGLE), -// LABEL(KEY_ACCESSIBILITY), -// LABEL(KEY_DO_NOT_DISTURB), - LABEL(KEY_BRIGHTNESS_MIN), - LABEL(KEY_BRIGHTNESS_MAX), - LABEL(KEY_KBDINPUTASSIST_PREV), - LABEL(KEY_KBDINPUTASSIST_NEXT), - LABEL(KEY_KBDINPUTASSIST_PREVGROUP), - LABEL(KEY_KBDINPUTASSIST_NEXTGROUP), - LABEL(KEY_KBDINPUTASSIST_ACCEPT), - LABEL(KEY_KBDINPUTASSIST_CANCEL), - LABEL(KEY_RIGHT_UP), - LABEL(KEY_RIGHT_DOWN), - LABEL(KEY_LEFT_UP), - LABEL(KEY_LEFT_DOWN), - LABEL(KEY_ROOT_MENU), - LABEL(KEY_MEDIA_TOP_MENU), - LABEL(KEY_NUMERIC_11), - LABEL(KEY_NUMERIC_12), - LABEL(KEY_AUDIO_DESC), - LABEL(KEY_3D_MODE), - LABEL(KEY_NEXT_FAVORITE), - LABEL(KEY_STOP_RECORD), - LABEL(KEY_PAUSE_RECORD), - LABEL(KEY_VOD), - LABEL(KEY_UNMUTE), - LABEL(KEY_FASTREVERSE), - LABEL(KEY_SLOWREVERSE), - LABEL(KEY_DATA), - LABEL(KEY_ONSCREEN_KEYBOARD), - LABEL(KEY_PRIVACY_SCREEN_TOGGLE), - LABEL(KEY_SELECTIVE_SCREENSHOT), - LABEL(KEY_NEXT_ELEMENT), - LABEL(KEY_PREVIOUS_ELEMENT), - LABEL(KEY_AUTOPILOT_ENGAGE_TOGGLE), - LABEL(KEY_MARK_WAYPOINT), - LABEL(KEY_SOS), - LABEL(KEY_NAV_CHART), - LABEL(KEY_FISHING_CHART), - LABEL(KEY_SINGLE_RANGE_RADAR), - LABEL(KEY_DUAL_RANGE_RADAR), - LABEL(KEY_RADAR_OVERLAY), - LABEL(KEY_TRADITIONAL_SONAR), - LABEL(KEY_CLEARVU_SONAR), - LABEL(KEY_SIDEVU_SONAR), - LABEL(KEY_NAV_INFO), - LABEL(KEY_BRIGHTNESS_MENU), - LABEL(KEY_MACRO1), - LABEL(KEY_MACRO2), - LABEL(KEY_MACRO3), - LABEL(KEY_MACRO4), - LABEL(KEY_MACRO5), - LABEL(KEY_MACRO6), - LABEL(KEY_MACRO7), - LABEL(KEY_MACRO8), - LABEL(KEY_MACRO9), - LABEL(KEY_MACRO10), - LABEL(KEY_MACRO11), - LABEL(KEY_MACRO12), - LABEL(KEY_MACRO13), - LABEL(KEY_MACRO14), - LABEL(KEY_MACRO15), - LABEL(KEY_MACRO16), - LABEL(KEY_MACRO17), - LABEL(KEY_MACRO18), - LABEL(KEY_MACRO19), - LABEL(KEY_MACRO20), - LABEL(KEY_MACRO21), - LABEL(KEY_MACRO22), - LABEL(KEY_MACRO23), - LABEL(KEY_MACRO24), - LABEL(KEY_MACRO25), - LABEL(KEY_MACRO26), - LABEL(KEY_MACRO27), - LABEL(KEY_MACRO28), - LABEL(KEY_MACRO29), - LABEL(KEY_MACRO30), - LABEL(KEY_MACRO_RECORD_START), - LABEL(KEY_MACRO_RECORD_STOP), - LABEL(KEY_MACRO_PRESET_CYCLE), - LABEL(KEY_MACRO_PRESET1), - LABEL(KEY_MACRO_PRESET2), - LABEL(KEY_MACRO_PRESET3), - LABEL(KEY_KBD_LCD_MENU1), - LABEL(KEY_KBD_LCD_MENU2), - LABEL(KEY_KBD_LCD_MENU3), - LABEL(KEY_KBD_LCD_MENU4), - LABEL(KEY_KBD_LCD_MENU5), - LABEL(BTN_TRIGGER_HAPPY), - LABEL(BTN_TRIGGER_HAPPY1), - LABEL(BTN_TRIGGER_HAPPY2), - LABEL(BTN_TRIGGER_HAPPY3), - LABEL(BTN_TRIGGER_HAPPY4), - LABEL(BTN_TRIGGER_HAPPY5), - LABEL(BTN_TRIGGER_HAPPY6), - LABEL(BTN_TRIGGER_HAPPY7), - LABEL(BTN_TRIGGER_HAPPY8), - LABEL(BTN_TRIGGER_HAPPY9), - LABEL(BTN_TRIGGER_HAPPY10), - LABEL(BTN_TRIGGER_HAPPY11), - LABEL(BTN_TRIGGER_HAPPY12), - LABEL(BTN_TRIGGER_HAPPY13), - LABEL(BTN_TRIGGER_HAPPY14), - LABEL(BTN_TRIGGER_HAPPY15), - LABEL(BTN_TRIGGER_HAPPY16), - LABEL(BTN_TRIGGER_HAPPY17), - LABEL(BTN_TRIGGER_HAPPY18), - LABEL(BTN_TRIGGER_HAPPY19), - LABEL(BTN_TRIGGER_HAPPY20), - LABEL(BTN_TRIGGER_HAPPY21), - LABEL(BTN_TRIGGER_HAPPY22), - LABEL(BTN_TRIGGER_HAPPY23), - LABEL(BTN_TRIGGER_HAPPY24), - LABEL(BTN_TRIGGER_HAPPY25), - LABEL(BTN_TRIGGER_HAPPY26), - LABEL(BTN_TRIGGER_HAPPY27), - LABEL(BTN_TRIGGER_HAPPY28), - LABEL(BTN_TRIGGER_HAPPY29), - LABEL(BTN_TRIGGER_HAPPY30), - LABEL(BTN_TRIGGER_HAPPY31), - LABEL(BTN_TRIGGER_HAPPY32), - LABEL(BTN_TRIGGER_HAPPY33), - LABEL(BTN_TRIGGER_HAPPY34), - LABEL(BTN_TRIGGER_HAPPY35), - LABEL(BTN_TRIGGER_HAPPY36), - LABEL(BTN_TRIGGER_HAPPY37), - LABEL(BTN_TRIGGER_HAPPY38), - LABEL(BTN_TRIGGER_HAPPY39), - LABEL(BTN_TRIGGER_HAPPY40), - LABEL(KEY_MAX), - LABEL_END, - }; - static struct label rel_labels[] = { - LABEL(REL_X), - LABEL(REL_Y), - LABEL(REL_Z), - LABEL(REL_RX), - LABEL(REL_RY), - LABEL(REL_RZ), - LABEL(REL_HWHEEL), - LABEL(REL_DIAL), - LABEL(REL_WHEEL), - LABEL(REL_MISC), - LABEL(REL_RESERVED), - LABEL(REL_WHEEL_HI_RES), - LABEL(REL_HWHEEL_HI_RES), - LABEL(REL_MAX), - LABEL_END, - }; - static struct label abs_labels[] = { - LABEL(ABS_X), - LABEL(ABS_Y), - LABEL(ABS_Z), - LABEL(ABS_RX), - LABEL(ABS_RY), - LABEL(ABS_RZ), - LABEL(ABS_THROTTLE), - LABEL(ABS_RUDDER), - LABEL(ABS_WHEEL), - LABEL(ABS_GAS), - LABEL(ABS_BRAKE), - LABEL(ABS_HAT0X), - LABEL(ABS_HAT0Y), - LABEL(ABS_HAT1X), - LABEL(ABS_HAT1Y), - LABEL(ABS_HAT2X), - LABEL(ABS_HAT2Y), - LABEL(ABS_HAT3X), - LABEL(ABS_HAT3Y), - LABEL(ABS_PRESSURE), - LABEL(ABS_DISTANCE), - LABEL(ABS_TILT_X), - LABEL(ABS_TILT_Y), - LABEL(ABS_TOOL_WIDTH), - LABEL(ABS_VOLUME), - LABEL(ABS_PROFILE), - LABEL(ABS_MISC), - LABEL(ABS_RESERVED), - LABEL(ABS_MT_SLOT), - LABEL(ABS_MT_TOUCH_MAJOR), - LABEL(ABS_MT_TOUCH_MINOR), - LABEL(ABS_MT_WIDTH_MAJOR), - LABEL(ABS_MT_WIDTH_MINOR), - LABEL(ABS_MT_ORIENTATION), - LABEL(ABS_MT_POSITION_X), - LABEL(ABS_MT_POSITION_Y), - LABEL(ABS_MT_TOOL_TYPE), - LABEL(ABS_MT_BLOB_ID), - LABEL(ABS_MT_TRACKING_ID), - LABEL(ABS_MT_PRESSURE), - LABEL(ABS_MT_DISTANCE), - LABEL(ABS_MT_TOOL_X), - LABEL(ABS_MT_TOOL_Y), - LABEL(ABS_MAX), - LABEL_END, - }; - static struct label sw_labels[] = { - LABEL(SW_LID), - LABEL(SW_TABLET_MODE), - LABEL(SW_HEADPHONE_INSERT), - LABEL(SW_RFKILL_ALL), - LABEL(SW_MICROPHONE_INSERT), - LABEL(SW_DOCK), - LABEL(SW_LINEOUT_INSERT), - LABEL(SW_JACK_PHYSICAL_INSERT), - LABEL(SW_VIDEOOUT_INSERT), - LABEL(SW_CAMERA_LENS_COVER), - LABEL(SW_KEYPAD_SLIDE), - LABEL(SW_FRONT_PROXIMITY), - LABEL(SW_ROTATE_LOCK), - LABEL(SW_LINEIN_INSERT), - LABEL(SW_MUTE_DEVICE), - LABEL(SW_PEN_INSERTED), - LABEL(SW_MACHINE_COVER), - LABEL(SW_MAX), - LABEL_END, - }; - static struct label msc_labels[] = { - LABEL(MSC_SERIAL), - LABEL(MSC_PULSELED), - LABEL(MSC_GESTURE), - LABEL(MSC_RAW), - LABEL(MSC_SCAN), - LABEL(MSC_TIMESTAMP), - LABEL(MSC_MAX), - LABEL_END, - }; - static struct label led_labels[] = { - LABEL(LED_NUML), - LABEL(LED_CAPSL), - LABEL(LED_SCROLLL), - LABEL(LED_COMPOSE), - LABEL(LED_KANA), - LABEL(LED_SLEEP), - LABEL(LED_SUSPEND), - LABEL(LED_MUTE), - LABEL(LED_MISC), - LABEL(LED_MAIL), - LABEL(LED_CHARGING), - LABEL(LED_MAX), - LABEL_END, - }; - static struct label rep_labels[] = { - LABEL(REP_DELAY), - LABEL(REP_PERIOD), - LABEL(REP_MAX), - LABEL_END, - }; - static struct label snd_labels[] = { - LABEL(SND_CLICK), - LABEL(SND_BELL), - LABEL(SND_TONE), - LABEL(SND_MAX), - LABEL_END, - }; - static struct label mt_tool_labels[] = { - LABEL(MT_TOOL_FINGER), - LABEL(MT_TOOL_PEN), - LABEL(MT_TOOL_PALM), - LABEL(MT_TOOL_DIAL), - LABEL(MT_TOOL_MAX), - LABEL_END, - }; - static struct label ff_status_labels[] = { - LABEL(FF_STATUS_STOPPED), - LABEL(FF_STATUS_PLAYING), - LABEL(FF_STATUS_MAX), - LABEL_END, - }; - static struct label ff_labels[] = { - LABEL(FF_RUMBLE), - LABEL(FF_PERIODIC), - LABEL(FF_CONSTANT), - LABEL(FF_SPRING), - LABEL(FF_FRICTION), - LABEL(FF_DAMPER), - LABEL(FF_INERTIA), - LABEL(FF_RAMP), - LABEL(FF_SQUARE), - LABEL(FF_TRIANGLE), - LABEL(FF_SINE), - LABEL(FF_SAW_UP), - LABEL(FF_SAW_DOWN), - LABEL(FF_CUSTOM), - LABEL(FF_GAIN), - LABEL(FF_AUTOCENTER), - LABEL(FF_MAX), - LABEL_END, - }; - -#undef LABEL -#undef LABEL_END - - std::string getLabel(const label *labels, int value) { - if (labels == nullptr) return std::to_string(value); - while (labels->name != nullptr && value != labels->value) { - labels++; - } - return labels->name != nullptr ? labels->name : std::to_string(value); - } - - std::optional getValue(const label *labels, const char *searchLabel) { - if (labels == nullptr) return {}; - while (labels->name != nullptr && ::strcasecmp(labels->name, searchLabel) != 0) { - labels++; - } - return labels->name != nullptr ? std::make_optional(labels->value) : std::nullopt; - } - - const label *getCodeLabelsForType(int32_t type) { - switch (type) { - case EV_SYN: - return syn_labels; - case EV_KEY: - return key_labels; - case EV_REL: - return rel_labels; - case EV_ABS: - return abs_labels; - case EV_SW: - return sw_labels; - case EV_MSC: - return msc_labels; - case EV_LED: - return led_labels; - case EV_REP: - return rep_labels; - case EV_SND: - return snd_labels; - case EV_FF: - return ff_labels; - case EV_FF_STATUS: - return ff_status_labels; - default: - return nullptr; - } - } - - const label *getValueLabelsForTypeAndCode(int32_t type, int32_t code) { - if (type == EV_KEY) { - return ev_key_value_labels; - } - if (type == EV_ABS && code == ABS_MT_TOOL_TYPE) { - return mt_tool_labels; - } - return nullptr; - } - - } // namespace - - EvdevEventLabel - InputEventLookup::getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value) { - return { - .type = getLabel(ev_labels, type), - .code = getLabel(getCodeLabelsForType(type), code), - .value = getLabel(getValueLabelsForTypeAndCode(type, code), value), - }; - } - - std::optional InputEventLookup::getLinuxEvdevEventTypeByLabel(const char *label) { - return getValue(ev_labels, label); - } - - std::optional InputEventLookup::getLinuxEvdevEventCodeByLabel(int32_t type, - const char *label) { - return getValue(getCodeLabelsForType(type), label); - } - - std::optional InputEventLookup::getLinuxEvdevInputPropByLabel(const char *label) { - return getValue(input_prop_labels, label); - } - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/input/InputEventLabels.h b/sysbridge/src/main/cpp/android/input/InputEventLabels.h deleted file mode 100644 index 066ff06ca7..0000000000 --- a/sysbridge/src/main/cpp/android/input/InputEventLabels.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace android { - - template - size_t size(T (&)[N]) { return N; } - - struct InputEventLabel { - const char *literal; - int value; - }; - - struct EvdevEventLabel { - std::string type; - std::string code; - std::string value; - }; - -// NOTE: If you want a new key code, axis code, led code or flag code in keylayout file, -// then you must add it to InputEventLabels.cpp. - - class InputEventLookup { - /** - * This class is not purely static, but uses a singleton pattern in order to delay the - * initialization of the maps that it contains. If it were purely static, the maps could be - * created early, and would cause sanitizers to report memory leaks. - */ - public: - InputEventLookup(InputEventLookup& other) = delete; - - void operator=(const InputEventLookup&) = delete; - - static std::optional lookupValueByLabel(const std::unordered_map& map, - const char* literal); - - static const char* lookupLabelByValue(const std::vector& vec, int value); - - static std::optional getKeyCodeByLabel(const char* label); - - static const char* getLabelByKeyCode(int32_t keyCode); - - static std::optional getKeyFlagByLabel(const char* label); - - static std::optional getAxisByLabel(const char* label); - - static const char* getAxisLabel(int32_t axisId); - - static std::optional getLedByLabel(const char* label); - - static EvdevEventLabel getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value); - - static std::optional getLinuxEvdevEventTypeByLabel(const char* label); - - static std::optional getLinuxEvdevEventCodeByLabel(int32_t type, const char* label); - - static std::optional getLinuxEvdevInputPropByLabel(const char* label); - - private: - InputEventLookup(); - - static const InputEventLookup& get() { - static InputEventLookup sLookup; - return sLookup; - } - - const std::unordered_map KEYCODES; - - const std::vector KEY_NAMES; - - const std::unordered_map AXES; - - const std::vector AXES_NAMES; - - const std::unordered_map LEDS; - - const std::unordered_map FLAGS; - }; - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/input/KeyLayoutMap.cpp b/sysbridge/src/main/cpp/android/input/KeyLayoutMap.cpp deleted file mode 100644 index bbf5a5437b..0000000000 --- a/sysbridge/src/main/cpp/android/input/KeyLayoutMap.cpp +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, Tokenizer.cppeither express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "../../logging.h" -#include -#include "../utils/String8.h" -#include "KeyLayoutMap.h" -#include "../utils/Tokenizer.h" -#include "InputEventLabels.h" -#include - -#include -#include -#include -#include "../libbase/result.h" -#include "../liblog/log_main.h" -#include "Input.h" - -#define DEBUG_MAPPING false -#define DEBUG_PARSER false - -// Enables debug output for parser performance. -#define DEBUG_PARSER_PERFORMANCE 0 - -namespace android { - - namespace { - - std::optional parseInt(const char *str) { - char *end; - errno = 0; - const int value = strtol(str, &end, 0); - if (end == str) { - LOGE("Could not parse %s", str); - return {}; - } - if (errno == ERANGE) { - LOGE("Out of bounds: %s", str); - return {}; - } - return value; - } - - constexpr const char *WHITESPACE = " \t\r"; - - } // namespace - - KeyLayoutMap::KeyLayoutMap() = default; - - KeyLayoutMap::~KeyLayoutMap() = default; - - base::Result> - KeyLayoutMap::loadContents(const std::string &filename, - const char *contents) { - return load(filename, contents); - } - - base::Result> KeyLayoutMap::load(const std::string &filename, - const char *contents) { - Tokenizer *tokenizer; - status_t status; - if (contents == nullptr) { - status = Tokenizer::open(String8(filename.c_str()), &tokenizer); - } else { - status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); - } - - if (status) { - LOGE("Error %d opening key layout map file %s.", status, filename.c_str()); - return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); - } - std::unique_ptr t(tokenizer); - auto ret = load(t.get()); - if (!ret.ok()) { - return ret; - } - const std::shared_ptr &map = *ret; - LOG_ALWAYS_FATAL_IF(map == nullptr, "Returned map should not be null if there's no error"); - - map->mLoadFileName = filename; - return ret; - } - - base::Result> KeyLayoutMap::load(Tokenizer *tokenizer) { - std::shared_ptr map = std::shared_ptr(new KeyLayoutMap()); - status_t status = OK; - if (!map.get()) { - LOGE("Error allocating key layout map."); - return Errorf("Error allocating key layout map."); - } else { -#if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - Parser parser(map.get(), tokenizer); - status = parser.parse(); -#if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - LOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().c_str(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); -#endif - if (!status) { - return std::move(map); - } - } - return Errorf("Load KeyLayoutMap failed {}.", status); - } - - status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, - int32_t *outKeyCode, uint32_t *outFlags) const { - const Key *key = getKey(scanCode, usageCode); - if (!key) { - ALOGD_IF(DEBUG_MAPPING, "mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, - usageCode); - *outKeyCode = AKEYCODE_UNKNOWN; - *outFlags = 0; - return NAME_NOT_FOUND; - } - - *outKeyCode = key->keyCode; - *outFlags = key->flags; - - ALOGD_IF(DEBUG_MAPPING, - "mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", - scanCode, usageCode, *outKeyCode, *outFlags); - return NO_ERROR; - } - - const KeyLayoutMap::Key *KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { - if (usageCode) { - auto it = mKeysByUsageCode.find(usageCode); - if (it != mKeysByUsageCode.end()) { - return &it->second; - } - } - if (scanCode) { - auto it = mKeysByScanCode.find(scanCode); - if (it != mKeysByScanCode.end()) { - return &it->second; - } - } - return nullptr; - } - - std::vector KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const { - std::vector scanCodes; - // b/354333072: Only consider keys without FUNCTION flag - for (const auto &[scanCode, key]: mKeysByScanCode) { - if (keyCode == key.keyCode && !(key.flags & POLICY_FLAG_FUNCTION)) { - scanCodes.push_back(scanCode); - } - } - return scanCodes; - } - - std::vector KeyLayoutMap::findUsageCodesForKey(int32_t keyCode) const { - std::vector usageCodes; - for (const auto &[usageCode, key]: mKeysByUsageCode) { - if (keyCode == key.keyCode && !(key.flags & POLICY_FLAG_FALLBACK_USAGE_MAPPING)) { - usageCodes.push_back(usageCode); - } - } - return usageCodes; - } - - std::optional KeyLayoutMap::mapAxis(int32_t scanCode) const { - auto it = mAxes.find(scanCode); - if (it == mAxes.end()) { - ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Failed.", scanCode); - return std::nullopt; - } - - const AxisInfo &axisInfo = it->second; - ALOGD_IF(DEBUG_MAPPING, - "mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, - axisInfo.flatOverride); - return axisInfo; - } - -// --- KeyLayoutMap::Parser --- - - KeyLayoutMap::Parser::Parser(KeyLayoutMap *map, Tokenizer *tokenizer) : - mMap(map), mTokenizer(tokenizer) { - } - - KeyLayoutMap::Parser::~Parser() { - } - - status_t KeyLayoutMap::Parser::parse() { - while (!mTokenizer->isEof()) { - ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().c_str(), - mTokenizer->peekRemainderOfLine().c_str()); - - mTokenizer->skipDelimiters(WHITESPACE); - - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - String8 keywordToken = mTokenizer->nextToken(WHITESPACE); - if (keywordToken == "key") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseKey(); - if (status) return status; - } else if (keywordToken == "axis") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseAxis(); - if (status) return status; - } else if (keywordToken == "led") { - // Skip LEDs, we don't need them for Key Mapper - mTokenizer->nextLine(); - continue; - } else if (keywordToken == "sensor") { - // Skip Sensors, we don't need them for Key Mapper - mTokenizer->nextLine(); - continue; - } else if (keywordToken == "requires_kernel_config") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseRequiredKernelConfig(); - if (status) return status; - } else { - LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().c_str(), - keywordToken.c_str()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - LOGW("%s: Expected end of line or trailing comment, got '%s'.", - mTokenizer->getLocation().c_str(), - mTokenizer->peekRemainderOfLine().c_str()); - return BAD_VALUE; - } - } - - mTokenizer->nextLine(); - } - return NO_ERROR; - } - - status_t KeyLayoutMap::Parser::parseKey() { - String8 codeToken = mTokenizer->nextToken(WHITESPACE); - bool mapUsage = false; - if (codeToken == "usage") { - mapUsage = true; - mTokenizer->skipDelimiters(WHITESPACE); - codeToken = mTokenizer->nextToken(WHITESPACE); - } - - std::optional code = parseInt(codeToken.c_str()); - if (!code) { - LOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().c_str(), - mapUsage ? "usage" : "scan code", codeToken.c_str()); - return BAD_VALUE; - } - std::unordered_map &map = - mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; - if (map.find(*code) != map.end()) { - LOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().c_str(), - mapUsage ? "usage" : "scan code", codeToken.c_str()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str()); - - if (!keyCode) { -// LOGW("%s: Unknown key code label %s", mTokenizer->getLocation().c_str(), -// keyCodeToken.c_str()); - // Do not crash at this point because there may be more flags afterwards that need parsing. - } - - uint32_t flags = 0; - for (;;) { - mTokenizer->skipDelimiters(WHITESPACE); - if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; - - String8 flagToken = mTokenizer->nextToken(WHITESPACE); - std::optional flag = InputEventLookup::getKeyFlagByLabel(flagToken.c_str()); - if (!flag) { - LOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().c_str(), - flagToken.c_str()); - return BAD_VALUE; - } - if (flags & *flag) { - LOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().c_str(), - flagToken.c_str()); - return BAD_VALUE; - } - flags |= *flag; - } - - ALOGD_IF(DEBUG_PARSER, "Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", - mapUsage ? "usage" : "scan code", *code, *keyCode, flags); - - // The key code may be unknown so only insert a key if it is known. - if (keyCode) { - Key key; - key.keyCode = *keyCode; - key.flags = flags; - map.insert({*code, key}); - } - - return NO_ERROR; - } - - status_t KeyLayoutMap::Parser::parseAxis() { - String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional scanCode = parseInt(scanCodeToken.c_str()); - if (!scanCode) { - LOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().c_str(), - scanCodeToken.c_str()); - return BAD_VALUE; - } - if (mMap->mAxes.find(*scanCode) != mMap->mAxes.end()) { - LOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().c_str(), - scanCodeToken.c_str()); - return BAD_VALUE; - } - - AxisInfo axisInfo; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 token = mTokenizer->nextToken(WHITESPACE); - if (token == "invert") { - axisInfo.mode = AxisInfo::MODE_INVERT; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 axisToken = mTokenizer->nextToken(WHITESPACE); - std::optional axis = InputEventLookup::getAxisByLabel(axisToken.c_str()); - if (!axis) { - LOGE("%s: Expected inverted axis label, got '%s'.", - mTokenizer->getLocation().c_str(), - axisToken.c_str()); - return BAD_VALUE; - } - axisInfo.axis = *axis; - } else if (token == "split") { - axisInfo.mode = AxisInfo::MODE_SPLIT; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 splitToken = mTokenizer->nextToken(WHITESPACE); - std::optional splitValue = parseInt(splitToken.c_str()); - if (!splitValue) { - LOGE("%s: Expected split value, got '%s'.", mTokenizer->getLocation().c_str(), - splitToken.c_str()); - return BAD_VALUE; - } - axisInfo.splitValue = *splitValue; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); - std::optional axis = InputEventLookup::getAxisByLabel(lowAxisToken.c_str()); - if (!axis) { - LOGE("%s: Expected low axis label, got '%s'.", mTokenizer->getLocation().c_str(), - lowAxisToken.c_str()); - return BAD_VALUE; - } - axisInfo.axis = *axis; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); - std::optional highAxis = InputEventLookup::getAxisByLabel(highAxisToken.c_str()); - if (!highAxis) { - LOGE("%s: Expected high axis label, got '%s'.", mTokenizer->getLocation().c_str(), - highAxisToken.c_str()); - return BAD_VALUE; - } - axisInfo.highAxis = *highAxis; - } else { - std::optional axis = InputEventLookup::getAxisByLabel(token.c_str()); - if (!axis) { - LOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", - mTokenizer->getLocation().c_str(), token.c_str()); - return BAD_VALUE; - } - axisInfo.axis = *axis; - } - - for (;;) { - mTokenizer->skipDelimiters(WHITESPACE); - if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') { - break; - } - String8 keywordToken = mTokenizer->nextToken(WHITESPACE); - if (keywordToken == "flat") { - mTokenizer->skipDelimiters(WHITESPACE); - String8 flatToken = mTokenizer->nextToken(WHITESPACE); - std::optional flatOverride = parseInt(flatToken.c_str()); - if (!flatOverride) { - LOGE("%s: Expected flat value, got '%s'.", mTokenizer->getLocation().c_str(), - flatToken.c_str()); - return BAD_VALUE; - } - axisInfo.flatOverride = *flatOverride; - } else { - LOGE("%s: Expected keyword 'flat', got '%s'.", mTokenizer->getLocation().c_str(), - keywordToken.c_str()); - return BAD_VALUE; - } - } - - ALOGD_IF(DEBUG_PARSER, - "Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - *scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, - axisInfo.flatOverride); - mMap->mAxes.insert({*scanCode, axisInfo}); - return NO_ERROR; - } - -// Parse the name of a required kernel config. -// The layout won't be used if the specified kernel config is not present -// Examples: -// requires_kernel_config CONFIG_HID_PLAYSTATION - status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() { - String8 codeToken = mTokenizer->nextToken(WHITESPACE); - std::string configName = codeToken.c_str(); - - const auto result = mMap->mRequiredKernelConfigs.emplace(configName); - if (!result.second) { - LOGE("%s: Duplicate entry for required kernel config %s.", - mTokenizer->getLocation().c_str(), configName.c_str()); - return BAD_VALUE; - } - -// ALOGD_IF(DEBUG_PARSER, "Parsed required kernel config: name=%s", configName.c_str()); - return NO_ERROR; - } -} // namespace android diff --git a/sysbridge/src/main/cpp/android/input/KeyLayoutMap.h b/sysbridge/src/main/cpp/android/input/KeyLayoutMap.h deleted file mode 100644 index f5646f33e3..0000000000 --- a/sysbridge/src/main/cpp/android/input/KeyLayoutMap.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include "../utils/Tokenizer.h" -#include "../libbase/result.h" -#include - -namespace android { - - struct AxisInfo { - enum Mode { - // Axis value is reported directly. - MODE_NORMAL = 0, - // Axis value should be inverted before reporting. - MODE_INVERT = 1, - // Axis value should be split into two axes - MODE_SPLIT = 2, - }; - - // Axis mode. - Mode mode; - - // Axis id. - // When split, this is the axis used for values smaller than the split position. - int32_t axis; - - // When split, this is the axis used for values after higher than the split position. - int32_t highAxis; - - // The split value, or 0 if not split. - int32_t splitValue; - - // The flat value, or -1 if none. - int32_t flatOverride; - - AxisInfo() : mode(MODE_NORMAL), axis(-1), highAxis(-1), splitValue(0), flatOverride(-1) { - } - }; - -/** - * Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes. - * - * This object is immutable after it has been loaded. - */ - class KeyLayoutMap { - public: - static base::Result> load(const std::string &filename, - const char *contents = nullptr); - - static base::Result> loadContents(const std::string &filename, - const char *contents); - - status_t mapKey(int32_t scanCode, int32_t usageCode, - int32_t *outKeyCode, uint32_t *outFlags) const; - - std::vector findScanCodesForKey(int32_t keyCode) const; - - std::optional findScanCodeForLed(int32_t ledCode) const; - - std::vector findUsageCodesForKey(int32_t keyCode) const; - - std::optional findUsageCodeForLed(int32_t ledCode) const; - - std::optional mapAxis(int32_t scanCode) const; - - virtual ~KeyLayoutMap(); - - private: - static base::Result> load(Tokenizer *tokenizer); - - struct Key { - int32_t keyCode; - uint32_t flags; - }; - - std::unordered_map mKeysByScanCode; - std::unordered_map mKeysByUsageCode; - std::unordered_map mAxes; - std::set mRequiredKernelConfigs; - std::string mLoadFileName; - - KeyLayoutMap(); - - const Key *getKey(int32_t scanCode, int32_t usageCode) const; - - class Parser { - KeyLayoutMap *mMap; - Tokenizer *mTokenizer; - - public: - Parser(KeyLayoutMap *map, Tokenizer *tokenizer); - - ~Parser(); - - status_t parse(); - - private: - status_t parseKey(); - - status_t parseAxis(); - - status_t parseRequiredKernelConfig(); - }; - }; - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/errors.h b/sysbridge/src/main/cpp/android/libbase/errors.h deleted file mode 100644 index 0ad4ecabd8..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/errors.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Portable error handling functions. This is only necessary for host-side -// code that needs to be cross-platform; code that is only run on Unix should -// just use errno and strerror() for simplicity. -// -// There is some complexity since Windows has (at least) three different error -// numbers, not all of which share the same type: -// * errno: for C runtime errors. -// * GetLastError(): Windows non-socket errors. -// * WSAGetLastError(): Windows socket errors. -// errno can be passed to strerror() on all platforms, but the other two require -// special handling to get the error string. Refer to Microsoft documentation -// to determine which error code to check for each function. - -#pragma once - -#include - -#include - -namespace android { -namespace base { - -// Returns a string describing the given system error code. |error_code| must -// be errno on Unix or GetLastError()/WSAGetLastError() on Windows. Passing -// errno on Windows has undefined behavior. -std::string SystemErrorCodeToString(int error_code); - -} // namespace base -} // namespace android - -// Convenient macros for evaluating a statement, checking if the result is error, and returning it -// to the caller. If it is ok then the inner value is unwrapped (if applicable) and returned. -// -// Usage with Result: -// -// Result getFoo() {...} -// -// Result getBar() { -// Foo foo = OR_RETURN(getFoo()); -// return Bar{foo}; -// } -// -// Usage with status_t: -// -// status_t getFoo(Foo*) {...} -// -// status_t getBar(Bar* bar) { -// Foo foo; -// OR_RETURN(getFoo(&foo)); -// *bar = Bar{foo}; -// return OK; -// } -// -// Actually this can be used for any type as long as the OkOrFail contract is satisfied. See -// below. -// If implicit conversion compilation errors occur involving a value type with a templated -// forwarding ref ctor, compilation with cpp20 or explicitly converting to the desired -// return type is required. -#define OR_RETURN(expr) \ - UNWRAP_OR_DO(__or_return_expr, expr, { return ok_or_fail::Fail(std::move(__or_return_expr)); }) - -// Same as OR_RETURN, but aborts if expr is a failure. -#if defined(__BIONIC__) -#define OR_FATAL(expr) \ - UNWRAP_OR_DO(__or_fatal_expr, expr, { \ - __assert(__FILE__, __LINE__, ok_or_fail::ErrorMessage(__or_fatal_expr).c_str()); \ - }) -#else -#define OR_FATAL(expr) \ - UNWRAP_OR_DO(__or_fatal_expr, expr, { \ - fprintf(stderr, "%s:%d: assertion \"%s\" failed", __FILE__, __LINE__, \ - ok_or_fail::ErrorMessage(__or_fatal_expr).c_str()); \ - abort(); \ - }) -#endif - -// Variant for use in gtests, which aborts the test function with an assertion failure on error. -// This is akin to ASSERT_OK_AND_ASSIGN for absl::Status, except the assignment is external. It -// assumes the user depends on libgmock and includes gtest/gtest.h. -#define OR_ASSERT_FAIL(expr) \ - UNWRAP_OR_DO(__or_assert_expr, expr, { \ - FAIL() << "Value of: " << #expr << "\n" \ - << " Actual: " << __or_assert_expr.error().message() << "\n" \ - << "Expected: is ok\n"; \ - }) - -// Generic macro to execute any statement(s) on error. Execution should never reach the end of them. -// result_var is assigned expr and is only visible to on_error_stmts. -#define UNWRAP_OR_DO(result_var, expr, on_error_stmts) \ - ({ \ - decltype(expr)&& result_var = (expr); \ - typedef android::base::OkOrFail> ok_or_fail; \ - if (!ok_or_fail::IsOk(result_var)) { \ - { \ - on_error_stmts; \ - } \ - __builtin_unreachable(); \ - } \ - ok_or_fail::Unwrap(std::move(result_var)); \ - }) - -namespace android { -namespace base { - -// The OkOrFail contract for a type T. This must be implemented for a type T if you want to use -// OR_RETURN(stmt) where stmt evalues to a value of type T. -template -struct OkOrFail { - // Checks if T is ok or fail. - static bool IsOk(const T&); - - // Turns T into the success value. - template - static U Unwrap(T&&); - - // Moves T into OkOrFail, so that we can convert it to other types - OkOrFail(T&& v); - OkOrFail() = delete; - OkOrFail(const T&) = delete; - - // And there need to be one or more conversion operators that turns the error value of T into a - // target type. For example, for T = Result, there can be ... - // - // // for the case where OR_RETURN is called in a function expecting E - // operator E()&& { return val_.error().code(); } - // - // // for the case where OR_RETURN is called in a function expecting Result - // template - // operator Result()&& { return val_.error(); } - - // And there needs to be a method that returns the string representation of the fail value. - // static const std::string& ErrorMessage(const T& v); - // or - // static std::string ErrorMessage(const T& v); -}; - -} // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/expected.h b/sysbridge/src/main/cpp/android/libbase/expected.h deleted file mode 100644 index ddab9e5b57..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/expected.h +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include - -// android::base::expected is a partial implementation of C++23's std::expected -// for Android. -// -// Usage: -// using android::base::expected; -// using android::base::unexpected; -// -// expected safe_divide(double i, double j) { -// if (j == 0) return unexpected("divide by zero"); -// else return i / j; -// } -// -// void test() { -// auto q = safe_divide(10, 0); -// if (q.ok()) { printf("%f\n", q.value()); } -// else { printf("%s\n", q.error().c_str()); } -// } -// -// Once the Android platform has moved to C++23, this will be removed and -// android::base::expected will be type aliased to std::expected. -// - -namespace android { -namespace base { - -// Synopsis -template -class expected; - -template -class unexpected; -template -unexpected(E) -> unexpected; - -template -class bad_expected_access; - -template <> -class bad_expected_access; - -struct unexpect_t { - explicit unexpect_t() = default; -}; -inline constexpr unexpect_t unexpect{}; - -// macros for SFINAE -#define _ENABLE_IF(...) \ - , std::enable_if_t<(__VA_ARGS__)>* = nullptr - -// Define NODISCARD_EXPECTED to prevent expected from being -// ignored when used as a return value. This is off by default. -#ifdef NODISCARD_EXPECTED -#define _NODISCARD_ [[nodiscard]] -#else -#define _NODISCARD_ -#endif - -#define _EXPLICIT(cond) \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wc++20-extensions\"") explicit(cond) \ - _Pragma("clang diagnostic pop") - -#define _COMMA , - -namespace expected_internal { - -template -struct remove_cvref { - using type = std::remove_cv_t>; -}; - -template -using remove_cvref_t = typename remove_cvref::type; - -// Can T be constructed from W (or W converted to T)? W can be lvalue or rvalue, -// const or not. -template -inline constexpr bool converts_from_any_cvref = - std::disjunction_v, std::is_convertible, - std::is_constructible, std::is_convertible, - std::is_constructible, std::is_convertible, - std::is_constructible, std::is_convertible>; - -template -struct is_expected : std::false_type {}; - -template -struct is_expected> : std::true_type {}; - -template -inline constexpr bool is_expected_v = is_expected::value; - -template -struct is_unexpected : std::false_type {}; - -template -struct is_unexpected> : std::true_type {}; - -template -inline constexpr bool is_unexpected_v = is_unexpected::value; - -// Constraints on constructing an expected from an expected -// related to T and U. UF is either "const U&" or "U". -template -inline constexpr bool convert_value_constraints = - std::is_constructible_v && - (std::is_same_v, bool> || !converts_from_any_cvref>); - -// Constraints on constructing an expected<..., E> from an expected -// related to E, G, and expected. GF is either "const G&" or "G". -template -inline constexpr bool convert_error_constraints = - std::is_constructible_v && - !std::is_constructible_v, expected&> && - !std::is_constructible_v, expected> && - !std::is_constructible_v, const expected&> && - !std::is_constructible_v, const expected>; - -// If an exception is thrown in expected::operator=, while changing the expected -// object between a value and an error, the expected object is supposed to -// retain its original value, which is only possible if certain constructors -// are noexcept. This implementation doesn't try to be exception-safe, but -// enforce these constraints anyway because std::expected also will enforce -// them, and we intend to switch to it eventually. -template -inline constexpr bool eh_assign_constraints = - std::is_nothrow_constructible_v || - std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v; - -// Implement expected<..., E>::expected([const] unexpected [&/&&]). -#define _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(GF, ParamType, forward_func) \ - template )> \ - constexpr _EXPLICIT((!std::is_convertible_v)) \ - expected(ParamType e) noexcept(std::is_nothrow_constructible_v) \ - : var_(std::in_place_index<1>, forward_func(e.error())) {} - -// Implement expected<..., E>::operator=([const] unexpected [&/&&]). -#define _ASSIGN_UNEXPECTED_TO_EXPECTED(GF, ParamType, forward_func, extra_constraints) \ - template && \ - std::is_assignable_v) && \ - extra_constraints> \ - constexpr expected& operator=(ParamType e) noexcept(std::is_nothrow_constructible_v && \ - std::is_nothrow_assignable_v) { \ - if (has_value()) { \ - var_.template emplace<1>(forward_func(e.error())); \ - } else { \ - error() = forward_func(e.error()); \ - } \ - return *this; \ - } - -} // namespace expected_internal - -// Class expected -template -class _NODISCARD_ expected { - static_assert(std::is_object_v && !std::is_array_v && - !std::is_same_v, std::in_place_t> && - !std::is_same_v, unexpect_t> && - !expected_internal::is_unexpected_v>, - "expected value type cannot be a reference, a function, an array, in_place_t, " - "unexpect_t, or unexpected"); - - public: - using value_type = T; - using error_type = E; - using unexpected_type = unexpected; - - template - using rebind = expected; - - // Delegate simple operations to the underlying std::variant. std::variant - // doesn't set noexcept well, at least for copy ctor/assign, so set it - // explicitly. Technically the copy/move assignment operators should also be - // deleted if neither T nor E satisfies is_nothrow_move_constructible_v, but - // that would require making these operator= methods into template functions. - constexpr expected() = default; - constexpr expected(const expected& rhs) noexcept( - std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_constructible_v) = default; - constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && - std::is_nothrow_move_constructible_v) = default; - constexpr expected& operator=(const expected& rhs) noexcept( - std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v && - std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v) = default; - constexpr expected& operator=(expected&& rhs) noexcept( - std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v && - std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v) = default; - - // Construct this expected from a different expected type. -#define _CONVERTING_CTOR(UF, GF, ParamType, forward_func) \ - template && \ - expected_internal::convert_error_constraints)> \ - constexpr _EXPLICIT((!std::is_convertible_v || !std::is_convertible_v)) \ - expected(ParamType rhs) noexcept(std::is_nothrow_constructible_v && \ - std::is_nothrow_constructible_v) \ - : var_(rhs.has_value() ? variant_type(std::in_place_index<0>, forward_func(rhs.value())) \ - : variant_type(std::in_place_index<1>, forward_func(rhs.error()))) {} - - // NOLINTNEXTLINE(google-explicit-constructor) - _CONVERTING_CTOR(const U&, const G&, const expected&, ) - // NOLINTNEXTLINE(google-explicit-constructor) - _CONVERTING_CTOR(U, G, expected&&, std::move) - -#undef _CONVERTING_CTOR - - // Construct from (converted) success value, using a forwarding reference. - template , std::in_place_t> && - !std::is_same_v, expected> && - !expected_internal::is_unexpected_v> && - std::is_constructible_v && - (!std::is_same_v, bool> || - !expected_internal::is_expected_v>))> - constexpr _EXPLICIT((!std::is_convertible_v)) - // NOLINTNEXTLINE(google-explicit-constructor) - expected(U&& v) noexcept(std::is_nothrow_constructible_v) - : var_(std::in_place_index<0>, std::forward(v)) {} - - // NOLINTNEXTLINE(google-explicit-constructor) - _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(const G&, const unexpected&, ) - // NOLINTNEXTLINE(google-explicit-constructor) - _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(G, unexpected&&, std::move) - - // in_place_t construction - template )> - constexpr explicit expected(std::in_place_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : var_(std::in_place_index<0>, std::forward(args)...) {} - - // in_place_t with initializer_list construction - template &, Args...>)> - constexpr explicit expected(std::in_place_t, std::initializer_list il, Args&&... args) - noexcept(std::is_nothrow_constructible_v&, Args...>) - : var_(std::in_place_index<0>, il, std::forward(args)...) {} - - // unexpect_t construction - template )> - constexpr explicit expected(unexpect_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : var_(std::in_place_index<1>, unexpected_type(std::forward(args)...)) {} - - // unexpect_t with initializer_list construction - template &, Args...>)> - constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) - noexcept(std::is_nothrow_constructible_v&, Args...>) - : var_(std::in_place_index<1>, unexpected_type(il, std::forward(args)...)) {} - - // Assignment from (converted) success value, using a forwarding reference. - template > && - !expected_internal::is_unexpected_v> && - std::is_constructible_v && std::is_assignable_v && - expected_internal::eh_assign_constraints)> - constexpr expected& operator=(U&& v) noexcept(std::is_nothrow_constructible_v && - std::is_nothrow_assignable_v) { - if (has_value()) { - value() = std::forward(v); - } else { - var_.template emplace<0>(std::forward(v)); - } - return *this; - } - - _ASSIGN_UNEXPECTED_TO_EXPECTED(const G&, const unexpected&, , - (expected_internal::eh_assign_constraints)) - _ASSIGN_UNEXPECTED_TO_EXPECTED(G, unexpected&&, std::move, - (expected_internal::eh_assign_constraints)) - - // modifiers - template )> - constexpr T& emplace(Args&&... args) noexcept { - var_.template emplace<0>(std::forward(args)...); - return value(); - } - - template &, Args...>)> - constexpr T& emplace(std::initializer_list il, Args&&... args) noexcept { - var_.template emplace<0>(il, std::forward(args)...); - return value(); - } - - // Swap. This function takes a template argument so that _ENABLE_IF works. - template && - std::is_swappable_v && std::is_swappable_v && - std::is_move_constructible_v && std::is_move_constructible_v && - (std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v))> - constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && - std::is_nothrow_swappable_v && - std::is_nothrow_move_constructible_v && - std::is_nothrow_swappable_v) { - var_.swap(rhs.var_); - } - - // observers - constexpr const T* operator->() const { return std::addressof(value()); } - constexpr T* operator->() { return std::addressof(value()); } - constexpr const T& operator*() const& { return value(); } - constexpr T& operator*() & { return value(); } - constexpr const T&& operator*() const&& { return std::move(std::get(var_)); } - constexpr T&& operator*() && { return std::move(std::get(var_)); } - - constexpr bool has_value() const noexcept { return var_.index() == 0; } - constexpr bool ok() const noexcept { return has_value(); } - constexpr explicit operator bool() const noexcept { return has_value(); } - - constexpr const T& value() const& { return std::get(var_); } - constexpr T& value() & { return std::get(var_); } - constexpr const T&& value() const&& { return std::move(std::get(var_)); } - constexpr T&& value() && { return std::move(std::get(var_)); } - - constexpr const E& error() const& { return std::get(var_).error(); } - constexpr E& error() & { return std::get(var_).error(); } - constexpr const E&& error() const&& { return std::move(std::get(var_)).error(); } - constexpr E&& error() && { return std::move(std::get(var_)).error(); } - - template && - std::is_convertible_v - )> - constexpr T value_or(U&& v) const& { - if (has_value()) return value(); - else return static_cast(std::forward(v)); - } - - template && - std::is_convertible_v - )> - constexpr T value_or(U&& v) && { - if (has_value()) return std::move(value()); - else return static_cast(std::forward(v)); - } - - // expected equality operators - template - friend constexpr bool operator==(const expected& x, const expected& y); - template - friend constexpr bool operator!=(const expected& x, const expected& y); - - // Comparison with unexpected - template - friend constexpr bool operator==(const expected&, const unexpected&); - template - friend constexpr bool operator==(const unexpected&, const expected&); - template - friend constexpr bool operator!=(const expected&, const unexpected&); - template - friend constexpr bool operator!=(const unexpected&, const expected&); - - private: - using variant_type = std::variant; - variant_type var_; -}; - -template -constexpr bool operator==(const expected& x, const expected& y) { - if (x.has_value() != y.has_value()) return false; - if (!x.has_value()) return x.error() == y.error(); - return *x == *y; -} - -template -constexpr bool operator!=(const expected& x, const expected& y) { - return !(x == y); -} - -// Comparison with unexpected -template -constexpr bool operator==(const expected& x, const unexpected& y) { - return !x.has_value() && (x.error() == y.error()); -} -template -constexpr bool operator==(const unexpected& x, const expected& y) { - return !y.has_value() && (x.error() == y.error()); -} -template -constexpr bool operator!=(const expected& x, const unexpected& y) { - return x.has_value() || (x.error() != y.error()); -} -template -constexpr bool operator!=(const unexpected& x, const expected& y) { - return y.has_value() || (x.error() != y.error()); -} - -template -class _NODISCARD_ expected { - public: - using value_type = void; - using error_type = E; - using unexpected_type = unexpected; - - template - using rebind = expected; - - // Delegate simple operations to the underlying std::variant. - constexpr expected() = default; - constexpr expected(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v) = - default; - constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) = default; - constexpr expected& operator=(const expected& rhs) noexcept( - std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v) = default; - constexpr expected& operator=(expected&& rhs) noexcept( - std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v) = default; - - // Construct this expected from a different expected type. -#define _CONVERTING_CTOR(GF, ParamType, forward_func) \ - template && \ - expected_internal::convert_error_constraints)> \ - constexpr _EXPLICIT((!std::is_convertible_v)) \ - expected(ParamType rhs) noexcept(std::is_nothrow_constructible_v) \ - : var_(rhs.has_value() ? variant_type(std::in_place_index<0>, std::monostate()) \ - : variant_type(std::in_place_index<1>, forward_func(rhs.error()))) {} - - // NOLINTNEXTLINE(google-explicit-constructor) - _CONVERTING_CTOR(const G&, const expected&, ) - // NOLINTNEXTLINE(google-explicit-constructor) - _CONVERTING_CTOR(G, expected&&, std::move) - -#undef _CONVERTING_CTOR - - // NOLINTNEXTLINE(google-explicit-constructor) - _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(const G&, const unexpected&, ) - // NOLINTNEXTLINE(google-explicit-constructor) - _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(G, unexpected&&, std::move) - - // in_place_t construction - constexpr explicit expected(std::in_place_t) noexcept {} - - // unexpect_t construction - template )> - constexpr explicit expected(unexpect_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : var_(std::in_place_index<1>, unexpected_type(std::forward(args)...)) {} - - // unexpect_t with initializer_list construction - template &, Args...>)> - constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) - noexcept(std::is_nothrow_constructible_v&, Args...>) - : var_(std::in_place_index<1>, unexpected_type(il, std::forward(args)...)) {} - - _ASSIGN_UNEXPECTED_TO_EXPECTED(const G&, const unexpected&, , true) - _ASSIGN_UNEXPECTED_TO_EXPECTED(G, unexpected&&, std::move, true) - - // modifiers - constexpr void emplace() noexcept { var_.template emplace<0>(std::monostate()); } - - // Swap. This function takes a template argument so that _ENABLE_IF works. - template && - std::is_swappable_v && std::is_move_constructible_v)> - constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && - std::is_nothrow_swappable_v) { - var_.swap(rhs.var_); - } - - // observers - constexpr bool has_value() const noexcept { return var_.index() == 0; } - constexpr bool ok() const noexcept { return has_value(); } - constexpr explicit operator bool() const noexcept { return has_value(); } - - constexpr void value() const& { if (!has_value()) std::get<0>(var_); } - - constexpr const E& error() const& { return std::get<1>(var_).error(); } - constexpr E& error() & { return std::get<1>(var_).error(); } - constexpr const E&& error() const&& { return std::move(std::get<1>(var_)).error(); } - constexpr E&& error() && { return std::move(std::get<1>(var_)).error(); } - - // expected equality operators - template - friend constexpr bool operator==(const expected& x, const expected& y); - - private: - using variant_type = std::variant; - variant_type var_; -}; - -template -constexpr bool operator==(const expected& x, const expected& y) { - if (x.has_value() != y.has_value()) return false; - if (!x.has_value()) return x.error() == y.error(); - return true; -} - -template -constexpr bool operator==(const expected& x, const expected& y) { - if (x.has_value() != y.has_value()) return false; - if (!x.has_value()) return x.error() == y.error(); - return false; -} - -template -constexpr bool operator==(const expected& x, const expected& y) { - if (x.has_value() != y.has_value()) return false; - if (!x.has_value()) return x.error() == y.error(); - return false; -} - -template &>().swap(std::declval&>()))> -constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) { - x.swap(y); -} - -template -class unexpected { - static_assert(std::is_object_v && !std::is_array_v && !std::is_const_v && - !std::is_volatile_v && !expected_internal::is_unexpected_v, - "unexpected error type cannot be a reference, a function, an array, cv-qualified, " - "or unexpected"); - - public: - // constructors - constexpr unexpected(const unexpected&) = default; - constexpr unexpected(unexpected&&) = default; - - template , unexpected> && - !std::is_same_v, std::in_place_t> && - std::is_constructible_v)> - constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v) - : val_(std::forward(e)) {} - - template )> - constexpr explicit unexpected(std::in_place_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : val_(std::forward(args)...) {} - - template &, Args...>)> - constexpr explicit unexpected(std::in_place_t, std::initializer_list il, Args&&... args) - noexcept(std::is_nothrow_constructible_v&, Args...>) - : val_(il, std::forward(args)...) {} - - constexpr unexpected& operator=(const unexpected&) = default; - constexpr unexpected& operator=(unexpected&&) = default; - - // observer - constexpr const E& error() const& noexcept { return val_; } - constexpr E& error() & noexcept { return val_; } - constexpr const E&& error() const&& noexcept { return std::move(val_); } - constexpr E&& error() && noexcept { return std::move(val_); } - - // Swap. This function takes a template argument so that _ENABLE_IF works. - template && std::is_swappable_v)> - void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v) { - // Make std::swap visible to provide swap for STL and builtin types, but use - // an unqualified swap to invoke argument-dependent lookup to find the swap - // functions for user-declared types. - using std::swap; - swap(val_, other.val_); - } - - template - friend constexpr bool - operator==(const unexpected& e1, const unexpected& e2); - template - friend constexpr bool - operator!=(const unexpected& e1, const unexpected& e2); - - private: - E val_; -}; - -template -constexpr bool -operator==(const unexpected& e1, const unexpected& e2) { - return e1.error() == e2.error(); -} - -template -constexpr bool -operator!=(const unexpected& e1, const unexpected& e2) { - return e1.error() != e2.error(); -} - -template )> -void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) { - x.swap(y); -} - -// TODO: bad_expected_access class - -#undef _ENABLE_IF -#undef _NODISCARD_ -#undef _EXPLICIT -#undef _COMMA -#undef _CONSTRUCT_EXPECTED_FROM_UNEXPECTED -#undef _ASSIGN_UNEXPECTED_TO_EXPECTED - -} // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/result.cpp b/sysbridge/src/main/cpp/android/libbase/result.cpp deleted file mode 100644 index c7163f270b..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/result.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "result.h" - -namespace android { -namespace base { - -ResultError MakeResultErrorWithCode(std::string&& message, Errno code) { - return ResultError(std::move(message) + ": " + code.print(), code); -} - -} // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/result.h b/sysbridge/src/main/cpp/android/libbase/result.h deleted file mode 100644 index 68c93344f8..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/result.h +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Result is the type that is used to pass a success value of type T or an error code of type -// E, optionally together with an error message. T and E can be any type. If E is omitted it -// defaults to int, which is useful when errno(3) is used as the error code. -// -// Passing a success value or an error value: -// -// Result readFile() { -// std::string content; -// if (base::ReadFileToString("path", &content)) { -// return content; // ok case -// } else { -// return ErrnoError() << "failed to read"; // error case -// } -// } -// -// Checking the result and then unwrapping the value or propagating the error: -// -// Result hasAWord() { -// auto content = readFile(); -// if (!content.ok()) { -// return Error() << "failed to process: " << content.error(); -// } -// return (*content.find("happy") != std::string::npos); -// } -// -// Using custom error code type: -// -// enum class MyError { A, B }; // assume that this is the error code you already have -// -// // To use the error code with Result, define a wrapper class that provides the following -// operations and use the wrapper class as the second type parameter (E) when instantiating -// Result -// -// 1. default constructor -// 2. copy constructor / and move constructor if copying is expensive -// 3. conversion operator to the error code type -// 4. value() function that return the error code value -// 5. print() function that gives a string representation of the error ode value -// -// struct MyErrorWrapper { -// MyError val_; -// MyErrorWrapper() : val_(/* reasonable default value */) {} -// MyErrorWrapper(MyError&& e) : val_(std:forward(e)) {} -// operator const MyError&() const { return val_; } -// MyError value() const { return val_; } -// std::string print() const { -// switch(val_) { -// MyError::A: return "A"; -// MyError::B: return "B"; -// } -// } -// }; -// -// #define NewMyError(e) Error(MyError::e) -// -// Result val = NewMyError(A) << "some message"; -// -// Formatting the error message using fmtlib: -// -// Errorf("{} errors", num); // equivalent to Error() << num << " errors"; -// ErrnoErrorf("{} errors", num); // equivalent to ErrnoError() << num << " errors"; -// -// Returning success or failure, but not the value: -// -// Result doSomething() { -// if (success) return {}; -// else return Error() << "error occurred"; -// } -// -// Extracting error code: -// -// Result val = Error(3) << "some error occurred"; -// assert(3 == val.error().code()); -// - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - -#include "errors.h" -#include "expected.h" - -namespace android { - namespace base { - -// Errno is a wrapper class for errno(3). Use this type instead of `int` when instantiating -// `Result` and `Error` template classes. This is required to distinguish errno from other -// integer-based error code types like `status_t`. - struct Errno { - Errno() : val_(0) {} - - Errno(int e) : val_(e) {} - - int value() const { return val_; } - - operator int() const { return value(); } - - const char *print() const { return strerror(value()); } - - int val_; - - // TODO(b/209929099): remove this conversion operator. This currently is needed to not break - // existing places where error().code() is used to construct enum values. - template>> - operator E() const { - return E(val_); - } - }; - - static_assert(std::is_trivially_copyable_v == true); - - template - struct ResultError { - template>> - ResultError(T &&message, P &&code) - : message_(std::forward(message)), code_(E(std::forward

(code))) {} - - ResultError(const ResultError &other) = default; - - ResultError(ResultError &&other) = default; - - ResultError &operator=(const ResultError &other) = default; - - ResultError &operator=(ResultError &&other) = default; - - template - // NOLINTNEXTLINE(google-explicit-constructor) - operator android::base::expected>() &&{ - return android::base::unexpected(std::move(*this)); - } - - template - // NOLINTNEXTLINE(google-explicit-constructor) - operator android::base::expected>() const &{ - return android::base::unexpected(*this); - } - - const std::string &message() const { return message_; } - - const E &code() const { return code_; } - - private: - std::string message_; - E code_; - }; - - template - auto format_as(ResultError error) { - return error.message(); - } - - template - struct ResultError { - template>> - ResultError(P &&code) : code_(E(std::forward

(code))) {} - - template - operator android::base::expected>() const { - return android::base::unexpected(ResultError(code_)); - } - - const E &code() const { return code_; } - - private: - E code_; - }; - - template - inline bool operator==(const ResultError &lhs, const ResultError &rhs) { - return lhs.message() == rhs.message() && lhs.code() == rhs.code(); - } - - template - inline bool operator!=(const ResultError &lhs, const ResultError &rhs) { - return !(lhs == rhs); - } - - template - inline std::ostream &operator<<(std::ostream &os, const ResultError &t) { - os << t.message(); - return os; - } - - namespace internal { -// Stream class that does nothing and is has zero (actually 1) size. It is used instead of -// std::stringstream when include_message is false so that we use less on stack. -// sizeof(std::stringstream) is 280 on arm64. - struct DoNothingStream { - template - DoNothingStream &operator<<(T &&) { - return *this; - } - - std::string str() const { return ""; } - }; - } // namespace internal - - template>> - class Error { - public: - Error() : code_(0), has_code_(false) {} - - template>> - // NOLINTNEXTLINE(google-explicit-constructor) - Error(P &&code) : code_(std::forward

(code)), has_code_(true) {} - - template>> - // NOLINTNEXTLINE(google-explicit-constructor) - operator android::base::expected>() const { - return android::base::unexpected(ResultError

(str(), static_cast

(code_))); - } - - template>> - // NOLINTNEXTLINE(google-explicit-constructor) - operator android::base::expected>() const { - return android::base::unexpected(ResultError(static_cast

(code_))); - } - - template - Error &operator<<(T &&t) { - static_assert(include_message, "<< not supported when include_message = false"); - // NOLINTNEXTLINE(bugprone-suspicious-semicolon) - if constexpr (std::is_same_v>, ResultError>) { - if (!has_code_) { - code_ = t.code(); - } - return (*this) << t.message(); - } - int saved = errno; - ss_ << t; - errno = saved; - return *this; - } - - const std::string str() const { - static_assert(include_message, "str() not supported when include_message = false"); - std::string str = ss_.str(); - if (has_code_) { - if (str.empty()) { - return code_.print(); - } - return std::move(str) + ": " + code_.print(); - } - return str; - } - - Error(const Error &) = delete; - - Error(Error &&) = delete; - - Error &operator=(const Error &) = delete; - - Error &operator=(Error &&) = delete; - - template - friend Error ErrorfImpl(const std::string &fmt, const Args &... args); - - template - friend Error ErrnoErrorfImpl(const std::string &fmt, const Args &... args); - - private: - Error(bool has_code, E code, const std::string &message) : code_(code), - has_code_(has_code) { - (*this) << message; - } - - std::conditional_t ss_; - E code_; - const bool has_code_; - }; - - inline Error ErrnoError() { - return Error(Errno{errno}); - } - - template - inline E ErrorCode(E code) { - return code; - } - -// Return the error code of the last ResultError object, if any. -// Otherwise, return `code` as it is. - template - inline E ErrorCode(E code, T &&t, const Args &... args) { - if constexpr (std::is_same_v>, ResultError>) { - return ErrorCode(t.code(), args...); - } - return ErrorCode(code, args...); - } - - __attribute__((noinline)) ResultError MakeResultErrorWithCode(std::string &&message, - Errno code); - - template - inline ResultError ErrorfImpl(const std::string &fmt, const Args &... args) { - return ResultError(fmt, ErrorCode(Errno{}, args...)); - } - - template - inline ResultError ErrnoErrorfImpl(const std::string &fmt, const Args &... args) { - Errno code{errno}; - return MakeResultErrorWithCode(std::string(fmt), code); - } - -#define Errorf(fmt, ...) android::base::ErrorfImpl(fmt, ##__VA_ARGS__) -#define ErrnoErrorf(fmt, ...) android::base::ErrnoErrorfImpl(fmt, ##__VA_ARGS__) - - template - using Result = android::base::expected>; - -// Specialization of android::base::OkOrFail for V = Result. See android-base/errors.h -// for the contract. - - namespace impl { - template - using Code = std::decay_t().error().code())>; - - template - using ErrorType = std::decay_t().error())>; - - template - constexpr bool IsNumeric = std::is_integral_v || std::is_floating_point_v || - (std::is_enum_v && std::is_convertible_v); - -// This base class exists to take advantage of shadowing -// We include the conversion in this base class so that if the conversion in NumericConversions -// overlaps, we (arbitrarily) choose the implementation in NumericConversions due to shadowing. - template - struct ConversionBase { - ErrorType error_; - - // T is a expected>. - operator T() const & { return unexpected(error_); } - - operator T() && { return unexpected(std::move(error_)); } - - operator Code() const { return error_.code(); } - }; - -// User defined conversions can be followed by numeric conversions -// Although we template specialize for the exact code type, we need -// specializations for conversions to all numeric types to avoid an -// ambiguous conversion sequence. - template - struct NumericConversions : public ConversionBase { - }; - - template - struct NumericConversions>> - > : public ConversionBase { -#pragma push_macro("SPECIALIZED_CONVERSION") -#define SPECIALIZED_CONVERSION(type) \ - operator expected>() const& { return unexpected(this->error_); } \ - operator expected>()&& { return unexpected(std::move(this->error_)); } - - SPECIALIZED_CONVERSION(int) - - SPECIALIZED_CONVERSION(short int) - - SPECIALIZED_CONVERSION(unsigned short int) - - SPECIALIZED_CONVERSION(unsigned int) - - SPECIALIZED_CONVERSION(long int) - - SPECIALIZED_CONVERSION(unsigned long int) - - SPECIALIZED_CONVERSION(long long int) - - SPECIALIZED_CONVERSION(unsigned long long int) - - SPECIALIZED_CONVERSION(bool) - - SPECIALIZED_CONVERSION(char) - - SPECIALIZED_CONVERSION(unsigned char) - - SPECIALIZED_CONVERSION(signed char) - - SPECIALIZED_CONVERSION(wchar_t) - - SPECIALIZED_CONVERSION(char16_t) - - SPECIALIZED_CONVERSION(char32_t) - - SPECIALIZED_CONVERSION(float) - - SPECIALIZED_CONVERSION(double) - - SPECIALIZED_CONVERSION(long double) - -#undef SPECIALIZED_CONVERSION -#pragma pop_macro("SPECIALIZED_CONVERSION") - // For debugging purposes - using IsNumericT = std::true_type; - }; - -#ifdef __cpp_concepts - template -// Define a concept which **any** type matches to - concept Universal = std::is_same_v; -#endif - -// A type that is never used. - struct Never { - }; - } // namespace impl - - template - struct OkOrFail> - : public impl::NumericConversions> { - using V = Result; - using Err = impl::ErrorType; - using C = impl::Code; - private: - OkOrFail(Err &&v) : impl::NumericConversions{std::move(v)} {} - - OkOrFail(const OkOrFail &other) = delete; - - OkOrFail(const OkOrFail &&other) = delete; - - public: - // Checks if V is ok or fail - static bool IsOk(const V &val) { return val.ok(); } - - // Turns V into a success value - static T Unwrap(V &&val) { - if constexpr (std::is_same_v) { - assert(IsOk(val)); - return; - } else { - return std::move(val.value()); - } - } - - // Consumes V when it's a fail value - static OkOrFail Fail(V &&v) { - assert(!IsOk(v)); - return OkOrFail{std::move(v.error())}; - } - - // We specialize as much as possible to avoid ambiguous conversion with templated expected ctor. - // We don't need this specialization if `C` is numeric because that case is already covered by - // `NumericConversions`. - operator Result, impl::Never, C>, E, include_message>() - const & { - return unexpected(this->error_); - } - - operator Result, impl::Never, C>, E, include_message>() && { - return unexpected(std::move(this->error_)); - } - -#ifdef __cpp_concepts - - // The idea here is to match this template method to any type (not simply trivial types). - // The reason for including a constraint is to take advantage of the fact that a constrained - // method always has strictly lower precedence than a non-constrained method in template - // specialization rules (thus avoiding ambiguity). So we use a universally matching constraint to - // mark this function as less preferable (but still accepting of all types). - template - operator Result() const &{ - return unexpected(this->error_); - } - - template - operator Result() &&{ - return unexpected(std::move(this->error_)); - } - -#else - template - operator Result() const& { - return unexpected(this->error_); - } - template - operator Result() && { - return unexpected(std::move(this->error_)); - } -#endif - - static const std::string &ErrorMessage(const V &val) { return val.error().message(); } - }; - -// Macros for testing the results of functions that return android::base::Result. These also work -// with base::android::expected. They assume the user depends on libgmock and includes -// gtest/gtest.h. For advanced matchers and customized error messages, see result-gmock.h. - -#define ASSERT_RESULT_OK(stmt) \ - if (const auto& tmp = (stmt); !tmp.ok()) \ - FAIL() << "Value of: " << #stmt << "\n" \ - << " Actual: " << tmp.error().message() << "\n" \ - << "Expected: is ok\n" - -#define EXPECT_RESULT_OK(stmt) \ - if (const auto& tmp = (stmt); !tmp.ok()) \ - ADD_FAILURE() << "Value of: " << #stmt << "\n" \ - << " Actual: " << tmp.error().message() << "\n" \ - << "Expected: is ok\n" - - } // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/stringprintf.cpp b/sysbridge/src/main/cpp/android/libbase/stringprintf.cpp deleted file mode 100644 index d9eb0e501e..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/stringprintf.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "stringprintf.h" - -#include - -#include - -namespace android { - namespace base { - - void StringAppendV(std::string *dst, const char *format, va_list ap) { - // First try with a small fixed size buffer - char space[1024] __attribute__((__uninitialized__)); - - // It's possible for methods that use a va_list to invalidate - // the data in it upon use. The fix is to make a copy - // of the structure before using it and use that copy instead. - va_list backup_ap; - va_copy(backup_ap, ap); - int result = vsnprintf(space, sizeof(space), format, backup_ap); - va_end(backup_ap); - - if (result < static_cast(sizeof(space))) { - if (result >= 0) { - // Normal case -- everything fit. - dst->append(space, result); - return; - } - - if (result < 0) { - // Just an error. - return; - } - } - - // Increase the buffer size to the size requested by vsnprintf, - // plus one for the closing \0. - int length = result + 1; - char *buf = new char[length]; - - // Restore the va_list before we use it again - va_copy(backup_ap, ap); - result = vsnprintf(buf, length, format, backup_ap); - va_end(backup_ap); - - if (result >= 0 && result < length) { - // It fit - dst->append(buf, result); - } - delete[] buf; - } - - std::string StringPrintf(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - std::string result; - StringAppendV(&result, fmt, ap); - va_end(ap); - return result; - } - - void StringAppendF(std::string *dst, const char *format, ...) { - va_list ap; - va_start(ap, format); - StringAppendV(dst, format, ap); - va_end(ap); - } - - } // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/stringprintf.h b/sysbridge/src/main/cpp/android/libbase/stringprintf.h deleted file mode 100644 index 02cc100d1a..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/stringprintf.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace android { - namespace base { - -// These printf-like functions are implemented in terms of vsnprintf, so they -// use the same attribute for compile-time format string checking. - -// Returns a string corresponding to printf-like formatting of the arguments. - std::string - StringPrintf(const char *fmt, ...) __attribute__((__format__(__printf__, 1, 2))); - -// Appends a printf-like formatting of the arguments to 'dst'. - void StringAppendF(std::string *dst, const char *fmt, ...) - __attribute__((__format__(__printf__, 2, 3))); - -// Appends a printf-like formatting of the arguments to 'dst'. - void StringAppendV(std::string *dst, const char *format, va_list ap) - __attribute__((__format__(__printf__, 2, 0))); - - } // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/liblog/log_main.h b/sysbridge/src/main/cpp/android/liblog/log_main.h deleted file mode 100644 index 8fc5ff4e79..0000000000 --- a/sysbridge/src/main/cpp/android/liblog/log_main.h +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright (C) 2005-2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - - -#include -#include - -#include - -__BEGIN_DECLS - -/* - * Normally we strip the effects of ALOGV (VERBOSE messages), - * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the - * release builds by defining NDEBUG. You can modify this (for - * example with "#define LOG_NDEBUG 0" at the top of your source - * file) to change that behavior. - */ - -#ifndef LOG_NDEBUG -#ifdef NDEBUG -#define LOG_NDEBUG 1 -#else -#define LOG_NDEBUG 0 -#endif -#endif - -/* --------------------------------------------------------------------- */ - -/* - * This file uses ", ## __VA_ARGS__" zero-argument token pasting to - * work around issues with debug-only syntax errors in assertions - * that are missing format strings. See commit - * 19299904343daf191267564fe32e6cd5c165cd42 - */ -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" -#endif - -/* - * Use __VA_ARGS__ if running a static analyzer, - * to avoid warnings of unused variables in __VA_ARGS__. - * Use constexpr function in C++ mode, so these macros can be used - * in other constexpr functions without warning. - */ -#ifdef __clang_analyzer__ -#ifdef __cplusplus -extern "C++" { -template -constexpr int __fake_use_va_args(Ts...) { - return 0; -} -} -#else -extern int __fake_use_va_args(int, ...); -#endif /* __cplusplus */ -#define __FAKE_USE_VA_ARGS(...) ((void)__fake_use_va_args(0, ##__VA_ARGS__)) -#else -#define __FAKE_USE_VA_ARGS(...) ((void)(0)) -#endif /* __clang_analyzer__ */ - -#ifndef __predict_false -#define __predict_false(exp) __builtin_expect((exp) != 0, 0) -#endif - -#define android_writeLog(prio, tag, text) __android_log_write(prio, tag, text) - -#define android_printLog(prio, tag, ...) \ - __android_log_print(prio, tag, __VA_ARGS__) - -#define android_vprintLog(prio, cond, tag, ...) \ - __android_log_vprint(prio, tag, __VA_ARGS__) - -/* - * Log macro that allows you to specify a number for the priority. - */ -#ifndef LOG_PRI -#define LOG_PRI(priority, tag, ...) android_printLog(priority, tag, __VA_ARGS__) -#endif - -/* - * Log macro that allows you to pass in a varargs ("args" is a va_list). - */ -#ifndef LOG_PRI_VA -#define LOG_PRI_VA(priority, tag, fmt, args) \ - android_vprintLog(priority, NULL, tag, fmt, args) -#endif - -/* --------------------------------------------------------------------- */ - -/* XXX Macros to work around syntax errors in places where format string - * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF - * (happens only in debug builds). - */ - -/* Returns 2nd arg. Used to substitute default value if caller's vararg list - * is empty. - */ -#define __android_second(dummy, second, ...) second - -/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise - * returns nothing. - */ -#define __android_rest(first, ...) , ##__VA_ARGS__ - -#define android_printAssert(cond, tag, ...) \ - __android_log_assert(cond, tag, \ - __android_second(0, ##__VA_ARGS__, NULL) \ - __android_rest(__VA_ARGS__)) - -/* - * Log a fatal error. If the given condition fails, this stops program - * execution like a normal assertion, but also generating the given message. - * It is NOT stripped from release builds. Note that the condition test - * is -inverted- from the normal assert() semantics. - */ -#ifndef LOG_ALWAYS_FATAL_IF -#define LOG_ALWAYS_FATAL_IF(cond, ...) \ - ((__predict_false(cond)) ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), \ - ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__))) \ - : ((void)0)) -#endif - -#ifndef LOG_ALWAYS_FATAL -#define LOG_ALWAYS_FATAL(...) \ - (((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__))) -#endif - -/* - * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that - * are stripped out of release builds. - */ - -#if LOG_NDEBUG - -#ifndef LOG_FATAL_IF -#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__) -#endif -#ifndef LOG_FATAL -#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__) -#endif - -#else - -#ifndef LOG_FATAL_IF -#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__) -#endif -#ifndef LOG_FATAL -#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) -#endif - -#endif - -/* - * Assertion that generates a log message when the assertion fails. - * Stripped out of release builds. Uses the current LOG_TAG. - */ -#ifndef ALOG_ASSERT -#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__) -#endif - -/* --------------------------------------------------------------------- */ - -/* - * C/C++ logging functions. See the logging documentation for API details. - * - * We'd like these to be available from C code (in case we import some from - * somewhere), so this has a C interface. - * - * The output will be correct when the log file is shared between multiple - * threads and/or multiple processes so long as the operating system - * supports O_APPEND. These calls have mutex-protected data structures - * and so are NOT reentrant. Do not use LOG in a signal handler. - */ - -/* --------------------------------------------------------------------- */ - -/* - * Simplified macro to send a verbose log message using the current LOG_TAG. - */ -#ifndef ALOGV -#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) -#if LOG_NDEBUG -#define ALOGV(...) \ - do { \ - __FAKE_USE_VA_ARGS(__VA_ARGS__); \ - if (false) { \ - __ALOGV(__VA_ARGS__); \ - } \ - } while (false) -#else -#define ALOGV(...) __ALOGV(__VA_ARGS__) -#endif -#endif - -#ifndef ALOGV_IF -#if LOG_NDEBUG -#define ALOGV_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__) -#else -#define ALOGV_IF(cond, ...) \ - ((__predict_false(cond)) \ - ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ - : ((void)0)) -#endif -#endif - -/* - * Simplified macro to send a debug log message using the current LOG_TAG. - */ -#ifndef ALOGD -#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGD_IF -#define ALOGD_IF(cond, ...) \ - ((__predict_false(cond)) \ - ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ - : ((void)0)) -#endif - -/* - * Simplified macro to send an info log message using the current LOG_TAG. - */ -#ifndef ALOGI -#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGI_IF -#define ALOGI_IF(cond, ...) \ - ((__predict_false(cond)) \ - ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ - : ((void)0)) -#endif - -/* - * Simplified macro to send a warning log message using the current LOG_TAG. - */ -#ifndef ALOGW -#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGW_IF -#define ALOGW_IF(cond, ...) \ - ((__predict_false(cond)) \ - ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ - : ((void)0)) -#endif - -/* - * Simplified macro to send an error log message using the current LOG_TAG. - */ -#ifndef ALOGE -#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGE_IF -#define ALOGE_IF(cond, ...) \ - ((__predict_false(cond)) \ - ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ - : ((void)0)) -#endif - -/* --------------------------------------------------------------------- */ - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * verbose priority. - */ -#ifndef IF_ALOGV -#if LOG_NDEBUG -#define IF_ALOGV() if (false) -#else -#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) -#endif -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * debug priority. - */ -#ifndef IF_ALOGD -#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * info priority. - */ -#ifndef IF_ALOGI -#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * warn priority. - */ -#ifndef IF_ALOGW -#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * error priority. - */ -#ifndef IF_ALOGE -#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) -#endif - -/* --------------------------------------------------------------------- */ - -/* - * Basic log message macro. - * - * Example: - * ALOG(LOG_WARN, NULL, "Failed with error %d", errno); - * - * The second argument may be NULL or "" to indicate the "global" tag. - */ -#ifndef ALOG -#define ALOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) -#endif - -/* - * Conditional given a desired logging priority and tag. - */ -#ifndef IF_ALOG -#define IF_ALOG(priority, tag) if (android_testLog(ANDROID_##priority, tag)) -#endif - -/* --------------------------------------------------------------------- */ - -/* - * IF_ALOG uses android_testLog, but IF_ALOG can be overridden. - * android_testLog will remain constant in its purpose as a wrapper - * for Android logging filter policy, and can be subject to - * change. It can be reused by the developers that override - * IF_ALOG as a convenient means to reimplement their policy - * over Android. - */ - -#if LOG_NDEBUG /* Production */ -#define android_testLog(prio, tag) \ - (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, ANDROID_LOG_DEBUG) != 0) -#else -#define android_testLog(prio, tag) \ - (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, ANDROID_LOG_VERBOSE) != 0) -#endif - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -__END_DECLS diff --git a/sysbridge/src/main/cpp/android/ui/LogicalDisplayId.h b/sysbridge/src/main/cpp/android/ui/LogicalDisplayId.h deleted file mode 100644 index 6cb99396ad..0000000000 --- a/sysbridge/src/main/cpp/android/ui/LogicalDisplayId.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include - -namespace android::ui { - -// Type-safe wrapper for a logical display id. - struct LogicalDisplayId : ftl::Constructible, - ftl::Equatable, - ftl::Orderable { - using Constructible::Constructible; - - constexpr auto val() const { return ftl::to_underlying(*this); } - - constexpr bool isValid() const { return val() >= 0; } - - std::string toString() const { return std::to_string(val()); } - - static const LogicalDisplayId INVALID; - static const LogicalDisplayId DEFAULT; - }; - - constexpr inline LogicalDisplayId LogicalDisplayId::INVALID{-1}; - constexpr inline LogicalDisplayId LogicalDisplayId::DEFAULT{0}; - - inline std::ostream &operator<<(std::ostream &stream, LogicalDisplayId displayId) { - return stream << displayId.val(); - } - -} // namespace android::ui - -namespace std { - template<> - struct hash { - size_t operator()(const android::ui::LogicalDisplayId &displayId) const { - return hash()(displayId.val()); - } - }; -} // namespace std \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/utils/Errors.h b/sysbridge/src/main/cpp/android/utils/Errors.h deleted file mode 100644 index 22fb36d250..0000000000 --- a/sysbridge/src/main/cpp/android/utils/Errors.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -namespace android { - -/** - * The type used to return success/failure from frameworks APIs. - * See the anonymous enum below for valid values. - */ -typedef int32_t status_t; - -/* - * Error codes. - * All error codes are negative values. - */ - -enum { - OK = 0, // Preferred constant for checking success. -#ifndef NO_ERROR - // Win32 #defines NO_ERROR as well. It has the same value, so there's no - // real conflict, though it's a bit awkward. - NO_ERROR = OK, // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows. -#endif - - UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value - - NO_MEMORY = -ENOMEM, - INVALID_OPERATION = -ENOSYS, - BAD_VALUE = -EINVAL, - BAD_TYPE = (UNKNOWN_ERROR + 1), - NAME_NOT_FOUND = -ENOENT, - PERMISSION_DENIED = -EPERM, - NO_INIT = -ENODEV, - ALREADY_EXISTS = -EEXIST, - DEAD_OBJECT = -EPIPE, - FAILED_TRANSACTION = (UNKNOWN_ERROR + 2), -#if !defined(_WIN32) - BAD_INDEX = -EOVERFLOW, - NOT_ENOUGH_DATA = -ENODATA, - WOULD_BLOCK = -EWOULDBLOCK, - TIMED_OUT = -ETIMEDOUT, - UNKNOWN_TRANSACTION = -EBADMSG, -#else - BAD_INDEX = -E2BIG, - NOT_ENOUGH_DATA = (UNKNOWN_ERROR + 3), - WOULD_BLOCK = (UNKNOWN_ERROR + 4), - TIMED_OUT = (UNKNOWN_ERROR + 5), - UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6), -#endif - FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7), - UNEXPECTED_NULL = (UNKNOWN_ERROR + 8), -}; - -// Human readable name of error -std::string statusToString(status_t status); - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/utils/FileMap.cpp b/sysbridge/src/main/cpp/android/utils/FileMap.cpp deleted file mode 100644 index 21b7c65574..0000000000 --- a/sysbridge/src/main/cpp/android/utils/FileMap.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Shared file mapping class. -// - -#define LOG_TAG "filemap" - -#include "FileMap.h" -#include "../../logging.h" -#include - -#if defined(__MINGW32__) && !defined(__USE_MINGW_ANSI_STDIO) -# define PRId32 "I32d" -# define PRIx32 "I32x" -# define PRId64 "I64d" -#else - -#include - -#endif - -#include -#include - -#if !defined(__MINGW32__) - -#include - -#endif - -#include -#include -#include -#include -#include - -using namespace android; - -/*static*/ long FileMap::mPageSize = -1; - -// Constructor. Create an empty object. -FileMap::FileMap(void) - : mFileName(nullptr), - mBasePtr(nullptr), - mBaseLength(0), - mDataPtr(nullptr), - mDataLength(0) -#if defined(__MINGW32__) -, - mFileHandle(INVALID_HANDLE_VALUE), - mFileMapping(NULL) -#endif -{ -} - -// Move Constructor. -FileMap::FileMap(FileMap &&other) noexcept - : mFileName(other.mFileName), - mBasePtr(other.mBasePtr), - mBaseLength(other.mBaseLength), - mDataOffset(other.mDataOffset), - mDataPtr(other.mDataPtr), - mDataLength(other.mDataLength) -#if defined(__MINGW32__) -, - mFileHandle(other.mFileHandle), - mFileMapping(other.mFileMapping) -#endif -{ - other.mFileName = nullptr; - other.mBasePtr = nullptr; - other.mDataPtr = nullptr; -#if defined(__MINGW32__) - other.mFileHandle = INVALID_HANDLE_VALUE; - other.mFileMapping = NULL; -#endif -} - -// Move assign operator. -FileMap &FileMap::operator=(FileMap &&other) noexcept { - mFileName = other.mFileName; - mBasePtr = other.mBasePtr; - mBaseLength = other.mBaseLength; - mDataOffset = other.mDataOffset; - mDataPtr = other.mDataPtr; - mDataLength = other.mDataLength; - other.mFileName = nullptr; - other.mBasePtr = nullptr; - other.mDataPtr = nullptr; -#if defined(__MINGW32__) - mFileHandle = other.mFileHandle; - mFileMapping = other.mFileMapping; - other.mFileHandle = INVALID_HANDLE_VALUE; - other.mFileMapping = NULL; -#endif - return *this; -} - -// Destructor. -FileMap::~FileMap(void) { - if (mFileName != nullptr) { - free(mFileName); - } -#if defined(__MINGW32__) - if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { - LOGD("UnmapViewOfFile(%p) failed, error = %lu\n", mBasePtr, - GetLastError() ); - } - if (mFileMapping != NULL) { - CloseHandle(mFileMapping); - } -#else - if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { - LOGD("munmap(%p, %zu) failed\n", mBasePtr, mBaseLength); - } -#endif -} - - -// Create a new mapping on an open file. -// -// Closing the file descriptor does not unmap the pages, so we don't -// claim ownership of the fd. -// -// Returns "false" on failure. -bool FileMap::create(const char *origFileName, int fd, off64_t offset, size_t length, - bool readOnly) { -#if defined(__MINGW32__) - int adjust; - off64_t adjOffset; - size_t adjLength; - - if (mPageSize == -1) { - SYSTEM_INFO si; - - GetSystemInfo( &si ); - mPageSize = si.dwAllocationGranularity; - } - - DWORD protect = readOnly ? PAGE_READONLY : PAGE_READWRITE; - - mFileHandle = (HANDLE) _get_osfhandle(fd); - mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL); - if (mFileMapping == NULL) { - LOGE("CreateFileMapping(%p, %lx) failed with error %lu\n", - mFileHandle, protect, GetLastError() ); - return false; - } - - adjust = offset % mPageSize; - adjOffset = offset - adjust; - adjLength = length + adjust; - - mBasePtr = MapViewOfFile( mFileMapping, - readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, - 0, - (DWORD)(adjOffset), - adjLength ); - if (mBasePtr == NULL) { - LOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %lu\n", - adjOffset, adjLength, GetLastError() ); - CloseHandle(mFileMapping); - mFileMapping = NULL; - return false; - } -#else // !defined(__MINGW32__) - assert(fd >= 0); - assert(offset >= 0); - assert(length > 0); - - // init on first use - if (mPageSize == -1) { - mPageSize = sysconf(_SC_PAGESIZE); - if (mPageSize == -1) { - LOGE("could not get _SC_PAGESIZE\n"); - return false; - } - } - - int adjust = offset % mPageSize; - off64_t adjOffset = offset - adjust; - size_t adjLength; - if (__builtin_add_overflow(length, adjust, &adjLength)) { - LOGE("adjusted length overflow: length %zu adjust %d", length, adjust); - return false; - } - - int flags = MAP_SHARED; - int prot = PROT_READ; - if (!readOnly) prot |= PROT_WRITE; - - void *ptr = mmap64(nullptr, adjLength, prot, flags, fd, adjOffset); - if (ptr == MAP_FAILED) { - if (errno == EINVAL && length == 0) { - ptr = nullptr; - adjust = 0; - } else { - LOGE("mmap(%lld,%zu) failed: %s\n", (long long) adjOffset, adjLength, strerror(errno)); - return false; - } - } - mBasePtr = ptr; -#endif // !defined(__MINGW32__) - - mFileName = origFileName != nullptr ? strdup(origFileName) : nullptr; - mBaseLength = adjLength; - mDataOffset = offset; - mDataPtr = (char *) mBasePtr + adjust; - mDataLength = length; - - LOGV("MAP: base %p/%zu data %p/%zu\n", - mBasePtr, mBaseLength, mDataPtr, mDataLength); - - return true; -} - -// Provide guidance to the system. -#if !defined(_WIN32) - -int FileMap::advise(MapAdvice advice) { - int cc, sysAdvice; - - switch (advice) { - case NORMAL: - sysAdvice = MADV_NORMAL; - break; - case RANDOM: - sysAdvice = MADV_RANDOM; - break; - case SEQUENTIAL: - sysAdvice = MADV_SEQUENTIAL; - break; - case WILLNEED: - sysAdvice = MADV_WILLNEED; - break; - case DONTNEED: - sysAdvice = MADV_DONTNEED; - break; - default: - assert(false); - return -1; - } - - cc = madvise(mBasePtr, mBaseLength, sysAdvice); - if (cc != 0) - LOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); - return cc; -} - -#else -int FileMap::advise(MapAdvice /* advice */) -{ - return -1; -} -#endif diff --git a/sysbridge/src/main/cpp/android/utils/FileMap.h b/sysbridge/src/main/cpp/android/utils/FileMap.h deleted file mode 100644 index 4b37a5f7f4..0000000000 --- a/sysbridge/src/main/cpp/android/utils/FileMap.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Encapsulate a shared file mapping. -// -#ifndef __LIBS_FILE_MAP_H -#define __LIBS_FILE_MAP_H - -#include - -#if defined(__MINGW32__) -// Ensure that we always pull in winsock2.h before windows.h -#if defined(_WIN32) -#include -#endif -#include -#endif - -namespace android { - -/* - * This represents a memory-mapped file. It might be the entire file or - * only part of it. This requires a little bookkeeping because the mapping - * needs to be aligned on page boundaries, and in some cases we'd like to - * have multiple references to the mapped area without creating additional - * maps. - * - * This always uses MAP_SHARED. - * - * TODO: we should be able to create a new FileMap that is a subset of - * an existing FileMap and shares the underlying mapped pages. Requires - * completing the refcounting stuff and possibly introducing the notion - * of a FileMap hierarchy. - */ - class FileMap { - public: - FileMap(void); - - FileMap(FileMap &&f) noexcept; - - FileMap &operator=(FileMap &&f) noexcept; - - /* - * Create a new mapping on an open file. - * - * Closing the file descriptor does not unmap the pages, so we don't - * claim ownership of the fd. - * - * Returns "false" on failure. - */ - bool create(const char *origFileName, int fd, - off64_t offset, size_t length, bool readOnly); - - ~FileMap(void); - - /* - * Return the name of the file this map came from, if known. - */ - const char *getFileName(void) const { return mFileName; } - - /* - * Get a pointer to the piece of the file we requested. - */ - void *getDataPtr(void) const { return mDataPtr; } - - /* - * Get the length we requested. - */ - size_t getDataLength(void) const { return mDataLength; } - - /* - * Get the data offset used to create this map. - */ - off64_t getDataOffset(void) const { return mDataOffset; } - - /* - * This maps directly to madvise() values, but allows us to avoid - * including everywhere. - */ - enum MapAdvice { - NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED - }; - - /* - * Apply an madvise() call to the entire file. - * - * Returns 0 on success, -1 on failure. - */ - int advise(MapAdvice advice); - - protected: - - private: - // these are not implemented - FileMap(const FileMap &src); - - const FileMap &operator=(const FileMap &src); - - char *mFileName; // original file name, if known - void *mBasePtr; // base of mmap area; page aligned - size_t mBaseLength; // length, measured from "mBasePtr" - off64_t mDataOffset; // offset used when map was created - void *mDataPtr; // start of requested data, offset from base - size_t mDataLength; // length, measured from "mDataPtr" -#if defined(__MINGW32__) - HANDLE mFileHandle; // Win32 file handle - HANDLE mFileMapping; // Win32 file mapping handle -#endif - - static long mPageSize; - }; - -} // namespace android - -#endif // __LIBS_FILE_MAP_H diff --git a/sysbridge/src/main/cpp/android/utils/SharedBuffer.cpp b/sysbridge/src/main/cpp/android/utils/SharedBuffer.cpp deleted file mode 100644 index 8ee62e3bb3..0000000000 --- a/sysbridge/src/main/cpp/android/utils/SharedBuffer.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "sharedbuffer" - -#include "SharedBuffer.h" - -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - - SharedBuffer *SharedBuffer::alloc(size_t size) { - SharedBuffer *sb = static_cast(malloc(sizeof(SharedBuffer) + size)); - if (sb) { - // Should be std::atomic_init(&sb->mRefs, 1); - // But that generates a warning with some compilers. - // The following is OK on Android-supported platforms. - sb->mRefs.store(1, std::memory_order_relaxed); - sb->mSize = size; - sb->mClientMetadata = 0; - } - return sb; - } - - - void SharedBuffer::dealloc(const SharedBuffer *released) { - free(const_cast(released)); - } - - SharedBuffer *SharedBuffer::edit() const { - if (onlyOwner()) { - return const_cast(this); - } - SharedBuffer *sb = alloc(mSize); - if (sb) { - memcpy(sb->data(), data(), size()); - release(); - } - return sb; - } - - SharedBuffer *SharedBuffer::editResize(size_t newSize) const { - if (onlyOwner()) { - SharedBuffer *buf = const_cast(this); - if (buf->mSize == newSize) return buf; - buf = (SharedBuffer *) realloc(reinterpret_cast(buf), - sizeof(SharedBuffer) + newSize); - if (buf != nullptr) { - buf->mSize = newSize; - return buf; - } - } - SharedBuffer *sb = alloc(newSize); - if (sb) { - const size_t mySize = mSize; - memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize); - release(); - } - return sb; - } - - SharedBuffer *SharedBuffer::attemptEdit() const { - if (onlyOwner()) { - return const_cast(this); - } - return nullptr; - } - - SharedBuffer *SharedBuffer::reset(size_t new_size) const { - // cheap-o-reset. - SharedBuffer *sb = alloc(new_size); - if (sb) { - release(); - } - return sb; - } - - void SharedBuffer::acquire() const { - mRefs.fetch_add(1, std::memory_order_relaxed); - } - - int32_t SharedBuffer::release(uint32_t flags) const { - const bool useDealloc = ((flags & eKeepStorage) == 0); - if (onlyOwner()) { - // Since we're the only owner, our reference count goes to zero. - mRefs.store(0, std::memory_order_relaxed); - if (useDealloc) { - dealloc(this); - } - // As the only owner, our previous reference count was 1. - return 1; - } - // There's multiple owners, we need to use an atomic decrement. - int32_t prevRefCount = mRefs.fetch_sub(1, std::memory_order_release); - if (prevRefCount == 1) { - // We're the last reference, we need the acquire fence. - atomic_thread_fence(std::memory_order_acquire); - if (useDealloc) { - dealloc(this); - } - } - return prevRefCount; - } - - -}; // namespace android diff --git a/sysbridge/src/main/cpp/android/utils/SharedBuffer.h b/sysbridge/src/main/cpp/android/utils/SharedBuffer.h deleted file mode 100644 index c170df00e1..0000000000 --- a/sysbridge/src/main/cpp/android/utils/SharedBuffer.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * DEPRECATED. DO NOT USE FOR NEW CODE. - */ - -#ifndef ANDROID_SHARED_BUFFER_H -#define ANDROID_SHARED_BUFFER_H - -#include -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - - class SharedBuffer { - public: - - /* flags to use with release() */ - enum { - eKeepStorage = 0x00000001 - }; - - /*! allocate a buffer of size 'size' and acquire() it. - * call release() to free it. - */ - static SharedBuffer *alloc(size_t size); - - /*! free the memory associated with the SharedBuffer. - * Fails if there are any users associated with this SharedBuffer. - * In other words, the buffer must have been release by all its - * users. - */ - static void dealloc(const SharedBuffer *released); - - //! access the data for read - inline const void *data() const; - - //! access the data for read/write - inline void *data(); - - //! get size of the buffer - inline size_t size() const; - - //! get back a SharedBuffer object from its data - static inline SharedBuffer *bufferFromData(void *data); - - //! get back a SharedBuffer object from its data - static inline const SharedBuffer *bufferFromData(const void *data); - - //! get the size of a SharedBuffer object from its data - static inline size_t sizeFromData(const void *data); - - //! edit the buffer (get a writtable, or non-const, version of it) - SharedBuffer *edit() const; - - //! edit the buffer, resizing if needed - SharedBuffer *editResize(size_t size) const; - - //! like edit() but fails if a copy is required - SharedBuffer *attemptEdit() const; - - //! resize and edit the buffer, loose it's content. - SharedBuffer *reset(size_t size) const; - - //! acquire/release a reference on this buffer - void acquire() const; - - /*! release a reference on this buffer, with the option of not - * freeing the memory associated with it if it was the last reference - * returns the previous reference count - */ - int32_t release(uint32_t flags = 0) const; - - //! returns wether or not we're the only owner - inline bool onlyOwner() const; - - - private: - inline SharedBuffer() {} - - inline ~SharedBuffer() {} - - SharedBuffer(const SharedBuffer &); - - SharedBuffer &operator=(const SharedBuffer &); - - // Must be sized to preserve correct alignment. - mutable std::atomic mRefs; - size_t mSize; - uint32_t mReserved; - public: - // mClientMetadata is reserved for client use. It is initialized to 0 - // and the clients can do whatever they want with it. Note that this is - // placed last so that it is adjcent to the buffer allocated. - uint32_t mClientMetadata; - }; - - static_assert(sizeof(SharedBuffer) % 8 == 0 - && (sizeof(size_t) > 4 || sizeof(SharedBuffer) == 16), - "SharedBuffer has unexpected size"); - -// --------------------------------------------------------------------------- - - const void *SharedBuffer::data() const { - return this + 1; - } - - void *SharedBuffer::data() { - return this + 1; - } - - size_t SharedBuffer::size() const { - return mSize; - } - - SharedBuffer *SharedBuffer::bufferFromData(void *data) { - return data ? static_cast(data) - 1 : nullptr; - } - - const SharedBuffer *SharedBuffer::bufferFromData(const void *data) { - return data ? static_cast(data) - 1 : nullptr; - } - - size_t SharedBuffer::sizeFromData(const void *data) { - return data ? bufferFromData(data)->mSize : 0; - } - - bool SharedBuffer::onlyOwner() const { - return (mRefs.load(std::memory_order_acquire) == 1); - } - -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_VECTOR_H diff --git a/sysbridge/src/main/cpp/android/utils/String16.cpp b/sysbridge/src/main/cpp/android/utils/String16.cpp deleted file mode 100644 index 0d61425d7c..0000000000 --- a/sysbridge/src/main/cpp/android/utils/String16.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "String16.h" - -#include -#include - -#include "SharedBuffer.h" - -#define LIBUTILS_PRAGMA(arg) _Pragma(#arg) -#if defined(__clang__) -#define LIBUTILS_PRAGMA_FOR_COMPILER(arg) LIBUTILS_PRAGMA(clang arg) -#elif defined(__GNUC__) -#define LIBUTILS_PRAGMA_FOR_COMPILER(arg) LIBUTILS_PRAGMA(GCC arg) -#else -#define LIBUTILS_PRAGMA_FOR_COMPILER(arg) -#endif -#define LIBUTILS_IGNORE(warning_flag) \ - LIBUTILS_PRAGMA_FOR_COMPILER(diagnostic push) \ - LIBUTILS_PRAGMA_FOR_COMPILER(diagnostic ignored warning_flag) -#define LIBUTILS_IGNORE_END() LIBUTILS_PRAGMA_FOR_COMPILER(diagnostic pop) - -namespace android { - -static const StaticString16 emptyString(u""); -static inline char16_t* getEmptyString() { - return const_cast(emptyString.c_str()); -} - -// --------------------------------------------------------------------------- - -void* String16::alloc(size_t size) -{ - SharedBuffer* buf = SharedBuffer::alloc(size); - buf->mClientMetadata = kIsSharedBufferAllocated; - return buf; -} - -char16_t* String16::allocFromUTF8(const char* u8str, size_t u8len) -{ - if (u8len == 0) return getEmptyString(); - - const uint8_t* u8cur = (const uint8_t*) u8str; - - const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len); - if (u16len < 0) { - return getEmptyString(); - } - - SharedBuffer* buf = static_cast(alloc(sizeof(char16_t) * (u16len + 1))); - if (buf) { - u8cur = (const uint8_t*) u8str; - char16_t* u16str = (char16_t*)buf->data(); - - utf8_to_utf16(u8cur, u8len, u16str, ((size_t) u16len) + 1); - - //printf("Created UTF-16 string from UTF-8 \"%s\":", in); - //printHexData(1, str, buf->size(), 16, 1); - //printf("\n"); - - return u16str; - } - - return getEmptyString(); -} - -char16_t* String16::allocFromUTF16(const char16_t* u16str, size_t u16len) { - if (u16len >= SIZE_MAX / sizeof(char16_t)) { - abort(); - } - - SharedBuffer* buf = static_cast(alloc((u16len + 1) * sizeof(char16_t))); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str, u16str, u16len * sizeof(char16_t)); - str[u16len] = 0; - return str; - } - return getEmptyString(); -} - -// --------------------------------------------------------------------------- - -String16::String16() - : mString(getEmptyString()) -{ -} - -String16::String16(const String16& o) - : mString(o.mString) -{ - acquire(); -} - -String16::String16(String16&& o) noexcept - : mString(o.mString) -{ - o.mString = getEmptyString(); -} - -String16::String16(const String16& o, size_t len, size_t begin) - : mString(getEmptyString()) -{ - setTo(o, len, begin); -} - -String16::String16(const char16_t* o) : mString(allocFromUTF16(o, strlen16(o))) {} - -String16::String16(const char16_t* o, size_t len) : mString(allocFromUTF16(o, len)) {} - -String16::String16(const String8& o) : mString(allocFromUTF8(o.c_str(), o.size())) {} - -String16::String16(const char* o) - : mString(allocFromUTF8(o, strlen(o))) -{ -} - -String16::String16(const char* o, size_t len) - : mString(allocFromUTF8(o, len)) -{ -} - -String16::~String16() -{ - release(); -} - -String16& String16::operator=(String16&& other) noexcept { - release(); - mString = other.mString; - other.mString = getEmptyString(); - return *this; -} - -size_t String16::size() const -{ - if (isStaticString()) { - return staticStringSize(); - } else { - return SharedBuffer::sizeFromData(mString) / sizeof(char16_t) - 1; - } -} - -void String16::setTo(const String16& other) -{ - release(); - mString = other.mString; - acquire(); -} - -status_t String16::setTo(const String16& other, size_t len, size_t begin) -{ - const size_t N = other.size(); - if (begin >= N) { - release(); - mString = getEmptyString(); - return OK; - } - if ((begin+len) > N) len = N-begin; - if (begin == 0 && len == N) { - setTo(other); - return OK; - } - - return setTo(other.c_str() + begin, len); -} - -status_t String16::setTo(const char16_t* other) -{ - return setTo(other, strlen16(other)); -} - -status_t String16::setTo(const char16_t* other, size_t len) -{ - if (len >= SIZE_MAX / sizeof(char16_t)) { - abort(); - } - - SharedBuffer* buf = static_cast(editResize((len + 1) * sizeof(char16_t))); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memmove(str, other, len*sizeof(char16_t)); - str[len] = 0; - mString = str; - return OK; - } - return NO_MEMORY; -} - -status_t String16::append(const String16& other) { - return append(other.c_str(), other.size()); -} - -status_t String16::append(const char16_t* chrs, size_t otherLen) { - const size_t myLen = size(); - - if (myLen == 0) return setTo(chrs, otherLen); - - if (otherLen == 0) return OK; - - size_t size = myLen; - if (__builtin_add_overflow(size, otherLen, &size) || - __builtin_add_overflow(size, 1, &size) || - __builtin_mul_overflow(size, sizeof(char16_t), &size)) return NO_MEMORY; - - SharedBuffer* buf = static_cast(editResize(size)); - if (!buf) return NO_MEMORY; - - char16_t* str = static_cast(buf->data()); - memcpy(str + myLen, chrs, otherLen * sizeof(char16_t)); - str[myLen + otherLen] = 0; - mString = str; - return OK; -} - -status_t String16::insert(size_t pos, const char16_t* chrs) { - return insert(pos, chrs, strlen16(chrs)); -} - -status_t String16::insert(size_t pos, const char16_t* chrs, size_t otherLen) { - const size_t myLen = size(); - - if (myLen == 0) return setTo(chrs, otherLen); - - if (otherLen == 0) return OK; - - if (pos > myLen) pos = myLen; - - size_t size = myLen; - if (__builtin_add_overflow(size, otherLen, &size) || - __builtin_add_overflow(size, 1, &size) || - __builtin_mul_overflow(size, sizeof(char16_t), &size)) return NO_MEMORY; - - SharedBuffer* buf = static_cast(editResize(size)); - if (!buf) return NO_MEMORY; - - char16_t* str = static_cast(buf->data()); - if (pos < myLen) memmove(str + pos + otherLen, str + pos, (myLen - pos) * sizeof(char16_t)); - memcpy(str + pos, chrs, otherLen * sizeof(char16_t)); - str[myLen + otherLen] = 0; - mString = str; - return OK; -} - -ssize_t String16::findFirst(char16_t c) const -{ - const char16_t* str = string(); - const char16_t* p = str; - const char16_t* e = p + size(); - while (p < e) { - if (*p == c) { - return p-str; - } - p++; - } - return -1; -} - -ssize_t String16::findLast(char16_t c) const -{ - const char16_t* str = string(); - const char16_t* p = str; - const char16_t* e = p + size(); - while (p < e) { - e--; - if (*e == c) { - return e-str; - } - } - return -1; -} - -bool String16::startsWith(const String16& prefix) const -{ - const size_t ps = prefix.size(); - if (ps > size()) return false; - return strzcmp16(mString, ps, prefix.c_str(), ps) == 0; -} - -bool String16::startsWith(const char16_t* prefix) const -{ - const size_t ps = strlen16(prefix); - if (ps > size()) return false; - return strncmp16(mString, prefix, ps) == 0; -} - -bool String16::contains(const char16_t* chrs) const -{ - return strstr16(mString, chrs) != nullptr; -} - -void* String16::edit() { - SharedBuffer* buf; - if (isStaticString()) { - buf = static_cast(alloc((size() + 1) * sizeof(char16_t))); - if (buf) { - memcpy(buf->data(), mString, (size() + 1) * sizeof(char16_t)); - } - } else { - buf = SharedBuffer::bufferFromData(mString)->edit(); - buf->mClientMetadata = kIsSharedBufferAllocated; - } - return buf; -} - -void* String16::editResize(size_t newSize) { - SharedBuffer* buf; - if (isStaticString()) { - size_t copySize = (size() + 1) * sizeof(char16_t); - if (newSize < copySize) { - copySize = newSize; - } - buf = static_cast(alloc(newSize)); - if (buf) { - memcpy(buf->data(), mString, copySize); - } - } else { - buf = SharedBuffer::bufferFromData(mString)->editResize(newSize); - buf->mClientMetadata = kIsSharedBufferAllocated; - } - return buf; -} - -void String16::acquire() -{ - if (!isStaticString()) { - SharedBuffer::bufferFromData(mString)->acquire(); - } -} - -void String16::release() -{ - if (!isStaticString()) { - SharedBuffer::bufferFromData(mString)->release(); - } -} - -bool String16::isStaticString() const { - // See String16.h for notes on the memory layout of String16::StaticData and - // SharedBuffer. - LIBUTILS_IGNORE("-Winvalid-offsetof") - static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4); - LIBUTILS_IGNORE_END() - const uint32_t* p = reinterpret_cast(mString); - return (*(p - 1) & kIsSharedBufferAllocated) == 0; -} - -size_t String16::staticStringSize() const { - // See String16.h for notes on the memory layout of String16::StaticData and - // SharedBuffer. - LIBUTILS_IGNORE("-Winvalid-offsetof") - static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4); - LIBUTILS_IGNORE_END() - const uint32_t* p = reinterpret_cast(mString); - return static_cast(*(p - 1)); -} - -status_t String16::replaceAll(char16_t replaceThis, char16_t withThis) -{ - const size_t N = size(); - const char16_t* str = string(); - char16_t* edited = nullptr; - for (size_t i=0; i(edit()); - if (!buf) { - return NO_MEMORY; - } - edited = (char16_t*)buf->data(); - mString = str = edited; - } - edited[i] = withThis; - } - } - return OK; -} - -}; // namespace android diff --git a/sysbridge/src/main/cpp/android/utils/String16.h b/sysbridge/src/main/cpp/android/utils/String16.h deleted file mode 100644 index 54c6d21ae6..0000000000 --- a/sysbridge/src/main/cpp/android/utils/String16.h +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_STRING16_H -#define ANDROID_STRING16_H - -#include -#include -#include - -#include "Errors.h" -#include "String8.h" -#include "TypeHelpers.h" - -#if __cplusplus >= 202002L -#include -#endif - -// --------------------------------------------------------------------------- - -namespace android { - -// --------------------------------------------------------------------------- - -template -class StaticString16; - -// DO NOT USE: please use std::u16string - -//! This is a string holding UTF-16 characters. -class String16 -{ -public: - String16(); - String16(const String16& o); - String16(String16&& o) noexcept; - String16(const String16& o, - size_t len, - size_t begin=0); - explicit String16(const char16_t* o); - explicit String16(const char16_t* o, size_t len); - explicit String16(const String8& o); - explicit String16(const char* o); - explicit String16(const char* o, size_t len); - - ~String16(); - - inline const char16_t* c_str() const; - - size_t size() const; - inline bool empty() const; - - inline size_t length() const; - - void setTo(const String16& other); - status_t setTo(const char16_t* other); - status_t setTo(const char16_t* other, size_t len); - status_t setTo(const String16& other, - size_t len, - size_t begin=0); - - status_t append(const String16& other); - status_t append(const char16_t* other, size_t len); - - inline String16& operator=(const String16& other); - String16& operator=(String16&& other) noexcept; - - inline String16& operator+=(const String16& other); - inline String16 operator+(const String16& other) const; - - status_t insert(size_t pos, const char16_t* chrs); - status_t insert(size_t pos, - const char16_t* chrs, size_t len); - - ssize_t findFirst(char16_t c) const; - ssize_t findLast(char16_t c) const; - - bool startsWith(const String16& prefix) const; - bool startsWith(const char16_t* prefix) const; - - bool contains(const char16_t* chrs) const; - inline bool contains(const String16& other) const; - - status_t replaceAll(char16_t replaceThis, - char16_t withThis); - - inline int compare(const String16& other) const; - - inline bool operator<(const String16& other) const; - inline bool operator<=(const String16& other) const; - inline bool operator==(const String16& other) const; - inline bool operator!=(const String16& other) const; - inline bool operator>=(const String16& other) const; - inline bool operator>(const String16& other) const; -#if __cplusplus >= 202002L - inline std::strong_ordering operator<=>(const String16& other) const; -#endif - - inline bool operator<(const char16_t* other) const; - inline bool operator<=(const char16_t* other) const; - inline bool operator==(const char16_t* other) const; - inline bool operator!=(const char16_t* other) const; - inline bool operator>=(const char16_t* other) const; - inline bool operator>(const char16_t* other) const; -#if __cplusplus >= 202002L - inline std::strong_ordering operator<=>(const char16_t* other) const; -#endif - - inline operator const char16_t*() const; - - // Implicit cast to std::u16string is not implemented on purpose - u16string_view is much - // lighter and if one needs, they can still create u16string from u16string_view. - inline operator std::u16string_view() const; - - // Static and non-static String16 behave the same for the users, so - // this method isn't of much use for the users. It is public for testing. - bool isStaticString() const; - - private: - /* - * A flag indicating the type of underlying buffer. - */ - static constexpr uint32_t kIsSharedBufferAllocated = 0x80000000; - - /* - * alloc() returns void* so that SharedBuffer class is not exposed. - */ - static void* alloc(size_t size); - static char16_t* allocFromUTF8(const char* u8str, size_t u8len); - static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len); - - /* - * edit() and editResize() return void* so that SharedBuffer class - * is not exposed. - */ - void* edit(); - void* editResize(size_t new_size); - - void acquire(); - void release(); - - size_t staticStringSize() const; - - const char16_t* mString; - -protected: - /* - * Data structure used to allocate static storage for static String16. - * - * Note that this data structure and SharedBuffer are used interchangably - * as the underlying data structure for a String16. Therefore, the layout - * of this data structure must match the part in SharedBuffer that is - * visible to String16. - */ - template - struct StaticData { - // The high bit of 'size' is used as a flag. - static_assert(N - 1 < kIsSharedBufferAllocated, "StaticString16 too long!"); - constexpr StaticData() : size(N - 1), data{0} {} - const uint32_t size; - char16_t data[N]; - - constexpr StaticData(const StaticData&) = default; - }; - - /* - * Helper function for constructing a StaticData object. - */ - template - static constexpr const StaticData makeStaticData(const char16_t (&s)[N]) { - StaticData r; - // The 'size' field is at the same location where mClientMetadata would - // be for a SharedBuffer. We do NOT set kIsSharedBufferAllocated flag - // here. - for (size_t i = 0; i < N - 1; ++i) r.data[i] = s[i]; - return r; - } - - template - explicit constexpr String16(const StaticData& s) : mString(s.data) {} - -// These symbols are for potential backward compatibility with prebuilts. To be removed. -#ifdef ENABLE_STRING16_OBSOLETE_METHODS -public: -#else -private: -#endif - inline const char16_t* string() const; -}; - -// String16 can be trivially moved using memcpy() because moving does not -// require any change to the underlying SharedBuffer contents or reference count. -ANDROID_TRIVIAL_MOVE_TRAIT(String16) - -static inline std::ostream& operator<<(std::ostream& os, const String16& str) { - os << String8(str); - return os; -} - -// --------------------------------------------------------------------------- - -/* - * A StaticString16 object is a specialized String16 object. Instead of holding - * the string data in a ref counted SharedBuffer object, it holds data in a - * buffer within StaticString16 itself. Note that this buffer is NOT ref - * counted and is assumed to be available for as long as there is at least a - * String16 object using it. Therefore, one must be extra careful to NEVER - * assign a StaticString16 to a String16 that outlives the StaticString16 - * object. - * - * THE SAFEST APPROACH IS TO USE StaticString16 ONLY AS GLOBAL VARIABLES. - * - * A StaticString16 SHOULD NEVER APPEAR IN APIs. USE String16 INSTEAD. - */ -template -class StaticString16 : public String16 { -public: - constexpr StaticString16(const char16_t (&s)[N]) : String16(mData), mData(makeStaticData(s)) {} - - constexpr StaticString16(const StaticString16& other) - : String16(mData), mData(other.mData) {} - - constexpr StaticString16(const StaticString16&&) = delete; - - // There is no reason why one would want to 'new' a StaticString16. Delete - // it to discourage misuse. - static void* operator new(std::size_t) = delete; - -private: - const StaticData mData; -}; - -template -StaticString16(const F&)->StaticString16; - -// --------------------------------------------------------------------------- -// No user servicable parts below. - -inline int compare_type(const String16& lhs, const String16& rhs) -{ - return lhs.compare(rhs); -} - -inline int strictly_order_type(const String16& lhs, const String16& rhs) -{ - return compare_type(lhs, rhs) < 0; -} - -inline const char16_t* String16::c_str() const -{ - return mString; -} - -inline const char16_t* String16::string() const -{ - return mString; -} - -inline bool String16::empty() const -{ - return length() == 0; -} - -inline size_t String16::length() const -{ - return size(); -} - -inline bool String16::contains(const String16& other) const -{ - return contains(other.c_str()); -} - -inline String16& String16::operator=(const String16& other) -{ - setTo(other); - return *this; -} - -inline String16& String16::operator+=(const String16& other) -{ - append(other); - return *this; -} - -inline String16 String16::operator+(const String16& other) const -{ - String16 tmp(*this); - tmp += other; - return tmp; -} - -inline int String16::compare(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()); -} - -inline bool String16::operator<(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) < 0; -} - -inline bool String16::operator<=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) <= 0; -} - -inline bool String16::operator==(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) == 0; -} - -inline bool String16::operator!=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) != 0; -} - -inline bool String16::operator>=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) >= 0; -} - -inline bool String16::operator>(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) > 0; -} - -#if __cplusplus >= 202002L -inline std::strong_ordering String16::operator<=>(const String16& other) const { - int result = strzcmp16(mString, size(), other.mString, other.size()); - if (result == 0) { - return std::strong_ordering::equal; - } else if (result < 0) { - return std::strong_ordering::less; - } else { - return std::strong_ordering::greater; - } -} -#endif - -inline bool String16::operator<(const char16_t* other) const -{ - return strcmp16(mString, other) < 0; -} - -inline bool String16::operator<=(const char16_t* other) const -{ - return strcmp16(mString, other) <= 0; -} - -inline bool String16::operator==(const char16_t* other) const -{ - return strcmp16(mString, other) == 0; -} - -inline bool String16::operator!=(const char16_t* other) const -{ - return strcmp16(mString, other) != 0; -} - -inline bool String16::operator>=(const char16_t* other) const -{ - return strcmp16(mString, other) >= 0; -} - -inline bool String16::operator>(const char16_t* other) const -{ - return strcmp16(mString, other) > 0; -} - -#if __cplusplus >= 202002L -inline std::strong_ordering String16::operator<=>(const char16_t* other) const { - int result = strcmp16(mString, other); - if (result == 0) { - return std::strong_ordering::equal; - } else if (result < 0) { - return std::strong_ordering::less; - } else { - return std::strong_ordering::greater; - } -} -#endif - -inline String16::operator const char16_t*() const -{ - return mString; -} - -inline String16::operator std::u16string_view() const -{ - return {mString, length()}; -} - -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STRING16_H diff --git a/sysbridge/src/main/cpp/android/utils/String8.cpp b/sysbridge/src/main/cpp/android/utils/String8.cpp deleted file mode 100644 index 3f8d9d634a..0000000000 --- a/sysbridge/src/main/cpp/android/utils/String8.cpp +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "String8.h" - -#include -#include "String16.h" - -#include -#include - -#include -#include -#include - -#include "SharedBuffer.h" - -/* - * Functions outside android is below the namespace android, since they use - * functions and constants in android namespace. - */ - -// --------------------------------------------------------------------------- - -namespace android { - - static inline char *getEmptyString() { - static SharedBuffer *gEmptyStringBuf = [] { - SharedBuffer *buf = SharedBuffer::alloc(1); - char *str = static_cast(buf->data()); - *str = 0; - return buf; - }(); - - gEmptyStringBuf->acquire(); - return static_cast(gEmptyStringBuf->data()); - } - -// --------------------------------------------------------------------------- - - static char *allocFromUTF8(const char *in, size_t len) { - if (len > 0) { - if (len == SIZE_MAX) { - return nullptr; - } - SharedBuffer *buf = SharedBuffer::alloc(len + 1); -// ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char *str = (char *) buf->data(); - memcpy(str, in, len); - str[len] = 0; - return str; - } - return nullptr; - } - - return getEmptyString(); - } - - static char *allocFromUTF16(const char16_t *in, size_t len) { - if (len == 0) return getEmptyString(); - - // Allow for closing '\0' - const ssize_t resultStrLen = utf16_to_utf8_length(in, len) + 1; - if (resultStrLen < 1) { - return getEmptyString(); - } - - SharedBuffer *buf = SharedBuffer::alloc(resultStrLen); -// ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (!buf) { - return getEmptyString(); - } - - char *resultStr = (char *) buf->data(); - utf16_to_utf8(in, len, resultStr, resultStrLen); - return resultStr; - } - - static char *allocFromUTF32(const char32_t *in, size_t len) { - if (len == 0) { - return getEmptyString(); - } - - const ssize_t resultStrLen = utf32_to_utf8_length(in, len) + 1; - if (resultStrLen < 1) { - return getEmptyString(); - } - - SharedBuffer *buf = SharedBuffer::alloc(resultStrLen); -// ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (!buf) { - return getEmptyString(); - } - - char *resultStr = (char *) buf->data(); - utf32_to_utf8(in, len, resultStr, resultStrLen); - - return resultStr; - } - -// --------------------------------------------------------------------------- - - String8::String8() - : mString(getEmptyString()) { - } - - String8::String8(const String8 &o) - : mString(o.mString) { - SharedBuffer::bufferFromData(mString)->acquire(); - } - - String8::String8(const char *o) - : mString(allocFromUTF8(o, strlen(o))) { - if (mString == nullptr) { - mString = getEmptyString(); - } - } - - String8::String8(const char *o, size_t len) - : mString(allocFromUTF8(o, len)) { - if (mString == nullptr) { - mString = getEmptyString(); - } - } - - String8::String8(const String16 &o) : mString(allocFromUTF16(o.c_str(), o.size())) {} - - String8::String8(const char16_t *o) - : mString(allocFromUTF16(o, strlen16(o))) { - } - - String8::String8(const char16_t *o, size_t len) - : mString(allocFromUTF16(o, len)) { - } - - String8::String8(const char32_t *o) - : mString(allocFromUTF32(o, std::char_traits::length(o))) {} - - String8::String8(const char32_t *o, size_t len) - : mString(allocFromUTF32(o, len)) { - } - - String8::~String8() { - SharedBuffer::bufferFromData(mString)->release(); - } - - size_t String8::length() const { - return SharedBuffer::sizeFromData(mString) - 1; - } - - String8 String8::format(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - - String8 result(formatV(fmt, args)); - - va_end(args); - return result; - } - - String8 String8::formatV(const char *fmt, va_list args) { - String8 result; - result.appendFormatV(fmt, args); - return result; - } - - void String8::clear() { - SharedBuffer::bufferFromData(mString)->release(); - mString = getEmptyString(); - } - - void String8::setTo(const String8 &other) { - SharedBuffer::bufferFromData(other.mString)->acquire(); - SharedBuffer::bufferFromData(mString)->release(); - mString = other.mString; - } - - status_t String8::setTo(const char *other) { - const char *newString = allocFromUTF8(other, strlen(other)); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return OK; - - mString = getEmptyString(); - return NO_MEMORY; - } - - status_t String8::setTo(const char *other, size_t len) { - const char *newString = allocFromUTF8(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return OK; - - mString = getEmptyString(); - return NO_MEMORY; - } - - status_t String8::setTo(const char16_t *other, size_t len) { - const char *newString = allocFromUTF16(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return OK; - - mString = getEmptyString(); - return NO_MEMORY; - } - - status_t String8::setTo(const char32_t *other, size_t len) { - const char *newString = allocFromUTF32(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return OK; - - mString = getEmptyString(); - return NO_MEMORY; - } - - status_t String8::append(const String8 &other) { - const size_t otherLen = other.bytes(); - if (bytes() == 0) { - setTo(other); - return OK; - } else if (otherLen == 0) { - return OK; - } - - return real_append(other.c_str(), otherLen); - } - - status_t String8::append(const char *other) { - return append(other, strlen(other)); - } - - status_t String8::append(const char *other, size_t otherLen) { - if (bytes() == 0) { - return setTo(other, otherLen); - } else if (otherLen == 0) { - return OK; - } - - return real_append(other, otherLen); - } - - status_t String8::appendFormat(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - - status_t result = appendFormatV(fmt, args); - - va_end(args); - return result; - } - - status_t String8::appendFormatV(const char *fmt, va_list args) { - int n, result = OK; - va_list tmp_args; - - /* args is undefined after vsnprintf. - * So we need a copy here to avoid the - * second vsnprintf access undefined args. - */ - va_copy(tmp_args, args); - n = vsnprintf(nullptr, 0, fmt, tmp_args); - va_end(tmp_args); - - if (n < 0) return UNKNOWN_ERROR; - - if (n > 0) { - size_t oldLength = length(); - if (static_cast(n) > std::numeric_limits::max() - 1 || - oldLength > std::numeric_limits::max() - n - 1) { - return NO_MEMORY; - } - char *buf = lockBuffer(oldLength + n); - if (buf) { - vsnprintf(buf + oldLength, n + 1, fmt, args); - } else { - result = NO_MEMORY; - } - } - return result; - } - - status_t String8::real_append(const char *other, size_t otherLen) { - const size_t myLen = bytes(); - - SharedBuffer *buf; - size_t newLen; - if (__builtin_add_overflow(myLen, otherLen, &newLen) || - __builtin_add_overflow(newLen, 1, &newLen) || - (buf = SharedBuffer::bufferFromData(mString)->editResize(newLen)) == nullptr) { - return NO_MEMORY; - } - - char *str = (char *) buf->data(); - mString = str; - str += myLen; - memcpy(str, other, otherLen); - str[otherLen] = '\0'; - return OK; - } - - char *String8::lockBuffer(size_t size) { - SharedBuffer *buf = SharedBuffer::bufferFromData(mString) - ->editResize(size + 1); - if (buf) { - char *str = (char *) buf->data(); - mString = str; - return str; - } - return nullptr; - } - - void String8::unlockBuffer() { - unlockBuffer(strlen(mString)); - } - - status_t String8::unlockBuffer(size_t size) { - if (size != this->size()) { - SharedBuffer *buf = SharedBuffer::bufferFromData(mString) - ->editResize(size + 1); - if (!buf) { - return NO_MEMORY; - } - - char *str = (char *) buf->data(); - str[size] = 0; - mString = str; - } - - return OK; - } - - ssize_t String8::find(const char *other, size_t start) const { - size_t len = size(); - if (start >= len) { - return -1; - } - const char *s = mString + start; - const char *p = strstr(s, other); - return p ? p - mString : -1; - } - - bool String8::removeAll(const char *other) { -// ALOG_ASSERT(other, "String8::removeAll() requires a non-NULL string"); - - if (*other == '\0') - return true; - - ssize_t index = find(other); - if (index < 0) return false; - - char *buf = lockBuffer(size()); - if (!buf) return false; // out of memory - - size_t skip = strlen(other); - size_t len = size(); - size_t tail = index; - while (size_t(index) < len) { - ssize_t next = find(other, index + skip); - if (next < 0) { - next = len; - } - - memmove(buf + tail, buf + index + skip, next - index - skip); - tail += next - index - skip; - index = next; - } - unlockBuffer(tail); - return true; - } - - void String8::toLower() { - const size_t length = size(); - if (length == 0) return; - - char *buf = lockBuffer(length); - for (size_t i = length; i > 0; --i) { - *buf = static_cast(tolower(*buf)); - buf++; - } - unlockBuffer(length); - } - -// --------------------------------------------------------------------------- -// Path functions - -// TODO: we should remove all the path functions from String8 -#if defined(_WIN32) -#define OS_PATH_SEPARATOR '\\' -#else -#define OS_PATH_SEPARATOR '/' -#endif - - String8 String8::getPathDir(void) const { - const char *cp; - const char *const str = mString; - - cp = strrchr(str, OS_PATH_SEPARATOR); - if (cp == nullptr) - return String8(""); - else - return String8(str, cp - str); - } - -/* - * Helper function for finding the start of an extension in a pathname. - * - * Returns a pointer inside mString, or NULL if no extension was found. - */ - static const char *find_extension(const char *str) { - const char *lastSlash; - const char *lastDot; - - // only look at the filename - lastSlash = strrchr(str, OS_PATH_SEPARATOR); - if (lastSlash == nullptr) - lastSlash = str; - else - lastSlash++; - - // find the last dot - lastDot = strrchr(lastSlash, '.'); - if (lastDot == nullptr) - return nullptr; - - // looks good, ship it - return lastDot; - } - - String8 String8::getPathExtension(void) const { - auto ext = find_extension(mString); - if (ext != nullptr) - return String8(ext); - else - return String8(""); - } - -}; // namespace android diff --git a/sysbridge/src/main/cpp/android/utils/String8.h b/sysbridge/src/main/cpp/android/utils/String8.h deleted file mode 100644 index 8875750834..0000000000 --- a/sysbridge/src/main/cpp/android/utils/String8.h +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_STRING8_H -#define ANDROID_STRING8_H - -#include -#include -#include // for strcmp -#include -#include -#include "Errors.h" -#include "Unicode.h" -#include "TypeHelpers.h" - -// --------------------------------------------------------------------------- - -namespace android { - - class String16; - -// DO NOT USE: please use std::string - -//! This is a string holding UTF-8 characters. Does not allow the value more -// than 0x10FFFF, which is not valid unicode codepoint. - class String8 { - public: - String8(); - - String8(const String8 &o); - - explicit String8(const char *o); - - explicit String8(const char *o, size_t numChars); - - explicit String8(std::string_view o); - - explicit String8(const String16 &o); - - explicit String8(const char16_t *o); - - explicit String8(const char16_t *o, size_t numChars); - - explicit String8(const char32_t *o); - - explicit String8(const char32_t *o, size_t numChars); - - ~String8(); - - static String8 format(const char *fmt, ...) __attribute__((format (printf, 1, 2))); - - static String8 formatV(const char *fmt, va_list args); - - inline const char *c_str() const; - - inline size_t size() const; - - inline size_t bytes() const; - - inline bool empty() const; - - size_t length() const; - - void clear(); - - void setTo(const String8 &other); - - status_t setTo(const char *other); - - status_t setTo(const char *other, size_t numChars); - - status_t setTo(const char16_t *other, size_t numChars); - - status_t setTo(const char32_t *other, - size_t length); - - status_t append(const String8 &other); - - status_t append(const char *other); - - status_t append(const char *other, size_t numChars); - - status_t appendFormat(const char *fmt, ...) - __attribute__((format (printf, 2, 3))); - - status_t appendFormatV(const char *fmt, va_list args); - - inline String8 &operator=(const String8 &other); - - inline String8 &operator=(const char *other); - - inline String8 &operator+=(const String8 &other); - - inline String8 operator+(const String8 &other) const; - - inline String8 &operator+=(const char *other); - - inline String8 operator+(const char *other) const; - - inline int compare(const String8 &other) const; - - inline bool operator<(const String8 &other) const; - - inline bool operator<=(const String8 &other) const; - - inline bool operator==(const String8 &other) const; - - inline bool operator!=(const String8 &other) const; - - inline bool operator>=(const String8 &other) const; - - inline bool operator>(const String8 &other) const; - -#if __cplusplus >= 202002L - inline std::strong_ordering operator<=>(const String8& other) const; -#endif - - inline bool operator<(const char *other) const; - - inline bool operator<=(const char *other) const; - - inline bool operator==(const char *other) const; - - inline bool operator!=(const char *other) const; - - inline bool operator>=(const char *other) const; - - inline bool operator>(const char *other) const; - -#if __cplusplus >= 202002L - inline std::strong_ordering operator<=>(const char* other) const; -#endif - - inline operator const char *() const; - - inline explicit operator std::string_view() const; - - char *lockBuffer(size_t size); - - void unlockBuffer(); - - status_t unlockBuffer(size_t size); - - // return the index of the first byte of other in this at or after - // start, or -1 if not found - ssize_t find(const char *other, size_t start = 0) const; - - inline ssize_t find(const String8 &other, size_t start = 0) const; - - // return true if this string contains the specified substring - inline bool contains(const char *other) const; - - inline bool contains(const String8 &other) const; - - // removes all occurrence of the specified substring - // returns true if any were found and removed - bool removeAll(const char *other); - - inline bool removeAll(const String8 &other); - - void toLower(); - - private: - String8 getPathDir(void) const; - - String8 getPathExtension(void) const; - - status_t real_append(const char *other, size_t numChars); - - const char *mString; - -// These symbols are for potential backward compatibility with prebuilts. To be removed. -#ifdef ENABLE_STRING8_OBSOLETE_METHODS - public: -#else - private: -#endif - - inline const char *string() const; - - inline bool isEmpty() const; - }; - -// String8 can be trivially moved using memcpy() because moving does not -// require any change to the underlying SharedBuffer contents or reference count. - ANDROID_TRIVIAL_MOVE_TRAIT(String8) - - static inline std::ostream &operator<<(std::ostream &os, const String8 &str) { - os << str.c_str(); - return os; - } - -// --------------------------------------------------------------------------- -// No user servicable parts below. - - inline int compare_type(const String8 &lhs, const String8 &rhs) { - return lhs.compare(rhs); - } - - inline int strictly_order_type(const String8 &lhs, const String8 &rhs) { - return compare_type(lhs, rhs) < 0; - } - - inline const char *String8::c_str() const { - return mString; - } - - inline const char *String8::string() const { - return mString; - } - - inline size_t String8::size() const { - return length(); - } - - inline bool String8::empty() const { - return length() == 0; - } - - inline bool String8::isEmpty() const { - return length() == 0; - } - - inline size_t String8::bytes() const { - return length(); - } - - inline ssize_t String8::find(const String8 &other, size_t start) const { - return find(other.c_str(), start); - } - - inline bool String8::contains(const char *other) const { - return find(other) >= 0; - } - - inline bool String8::contains(const String8 &other) const { - return contains(other.c_str()); - } - - inline bool String8::removeAll(const String8 &other) { - return removeAll(other.c_str()); - } - - inline String8 &String8::operator=(const String8 &other) { - setTo(other); - return *this; - } - - inline String8 &String8::operator=(const char *other) { - setTo(other); - return *this; - } - - inline String8 &String8::operator+=(const String8 &other) { - append(other); - return *this; - } - - inline String8 String8::operator+(const String8 &other) const { - String8 tmp(*this); - tmp += other; - return tmp; - } - - inline String8 &String8::operator+=(const char *other) { - append(other); - return *this; - } - - inline String8 String8::operator+(const char *other) const { - String8 tmp(*this); - tmp += other; - return tmp; - } - - inline int String8::compare(const String8 &other) const { - return strcmp(mString, other.mString); - } - - inline bool String8::operator<(const String8 &other) const { - return strcmp(mString, other.mString) < 0; - } - - inline bool String8::operator<=(const String8 &other) const { - return strcmp(mString, other.mString) <= 0; - } - - inline bool String8::operator==(const String8 &other) const { - return strcmp(mString, other.mString) == 0; - } - - inline bool String8::operator!=(const String8 &other) const { - return strcmp(mString, other.mString) != 0; - } - - inline bool String8::operator>=(const String8 &other) const { - return strcmp(mString, other.mString) >= 0; - } - - inline bool String8::operator>(const String8 &other) const { - return strcmp(mString, other.mString) > 0; - } - -#if __cplusplus >= 202002L - inline std::strong_ordering String8::operator<=>(const String8& other) const { - int result = strcmp(mString, other.mString); - if (result == 0) { - return std::strong_ordering::equal; - } else if (result < 0) { - return std::strong_ordering::less; - } else { - return std::strong_ordering::greater; - } - } -#endif - - inline bool String8::operator<(const char *other) const { - return strcmp(mString, other) < 0; - } - - inline bool String8::operator<=(const char *other) const { - return strcmp(mString, other) <= 0; - } - - inline bool String8::operator==(const char *other) const { - return strcmp(mString, other) == 0; - } - - inline bool String8::operator!=(const char *other) const { - return strcmp(mString, other) != 0; - } - - inline bool String8::operator>=(const char *other) const { - return strcmp(mString, other) >= 0; - } - - inline bool String8::operator>(const char *other) const { - return strcmp(mString, other) > 0; - } - -#if __cplusplus >= 202002L - inline std::strong_ordering String8::operator<=>(const char* other) const { - int result = strcmp(mString, other); - if (result == 0) { - return std::strong_ordering::equal; - } else if (result < 0) { - return std::strong_ordering::less; - } else { - return std::strong_ordering::greater; - } - } -#endif - - inline String8::operator const char *() const { - return mString; - } - - inline String8::String8(std::string_view o) : String8(o.data(), o.length()) {} - - inline String8::operator std::string_view() const { - return {mString, length()}; - } - -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STRING8_H diff --git a/sysbridge/src/main/cpp/android/utils/Tokenizer.cpp b/sysbridge/src/main/cpp/android/utils/Tokenizer.cpp deleted file mode 100644 index e9ad7bebf3..0000000000 --- a/sysbridge/src/main/cpp/android/utils/Tokenizer.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "../../logging.h" -#include "Tokenizer.h" -#include "Errors.h" -#include "FileMap.h" -#include "String8.h" -#include -#include -#include -#include - -#ifndef DEBUG_TOKENIZER -// Enables debug output for the tokenizer. -#define DEBUG_TOKENIZER 0 -#endif - -namespace android { - - static inline bool isDelimiter(char ch, const char *delimiters) { - return strchr(delimiters, ch) != nullptr; - } - - Tokenizer::Tokenizer(const String8 &filename, FileMap *fileMap, char *buffer, - bool ownBuffer, size_t length) : - mFilename(filename), mFileMap(fileMap), - mBuffer(buffer), mOwnBuffer(ownBuffer), mLength(length), - mCurrent(buffer), mLineNumber(1) { - } - - Tokenizer::~Tokenizer() { - delete mFileMap; - if (mOwnBuffer) { - delete[] mBuffer; - } - } - - status_t Tokenizer::open(const String8 &filename, Tokenizer **outTokenizer) { - *outTokenizer = nullptr; - - int result = OK; - int fd = ::open(filename.c_str(), O_RDONLY); - if (fd < 0) { - result = -errno; - LOGE("Error opening file '%s': %s", filename.c_str(), strerror(errno)); - } else { - struct stat stat; - if (fstat(fd, &stat)) { - result = -errno; - LOGE("Error getting size of file '%s': %s", filename.c_str(), strerror(errno)); - } else { - size_t length = size_t(stat.st_size); - - FileMap *fileMap = new FileMap(); - bool ownBuffer = false; - char *buffer; - if (fileMap->create(nullptr, fd, 0, length, true)) { - fileMap->advise(FileMap::SEQUENTIAL); - buffer = static_cast(fileMap->getDataPtr()); - } else { - delete fileMap; - fileMap = nullptr; - - // Fall back to reading into a buffer since we can't mmap files in sysfs. - // The length we obtained from stat is wrong too (it will always be 4096) - // so we must trust that read will read the entire file. - buffer = new char[length]; - ownBuffer = true; - ssize_t nrd = read(fd, buffer, length); - if (nrd < 0) { - result = -errno; - LOGE("Error reading file '%s': %s", filename.c_str(), strerror(errno)); - delete[] buffer; - buffer = nullptr; - } else { - length = size_t(nrd); - } - } - - if (!result) { - *outTokenizer = new Tokenizer(filename, fileMap, buffer, ownBuffer, length); - } - } - close(fd); - } - return result; - } - - status_t Tokenizer::fromContents(const String8 &filename, - const char *contents, Tokenizer **outTokenizer) { - *outTokenizer = new Tokenizer(filename, nullptr, - const_cast(contents), false, strlen(contents)); - return OK; - } - - String8 Tokenizer::getLocation() const { - String8 result; - result.appendFormat("%s:%d", mFilename.c_str(), mLineNumber); - return result; - } - - String8 Tokenizer::peekRemainderOfLine() const { - const char *end = getEnd(); - const char *eol = mCurrent; - while (eol != end) { - char ch = *eol; - if (ch == '\n') { - break; - } - eol += 1; - } - return String8(mCurrent, eol - mCurrent); - } - - String8 Tokenizer::nextToken(const char *delimiters) { -#if DEBUG_TOKENIZER - LOGD("nextToken"); -#endif - const char *end = getEnd(); - const char *tokenStart = mCurrent; - while (mCurrent != end) { - char ch = *mCurrent; - if (ch == '\n' || isDelimiter(ch, delimiters)) { - break; - } - mCurrent += 1; - } - return String8(tokenStart, mCurrent - tokenStart); - } - - void Tokenizer::nextLine() { -#if DEBUG_TOKENIZER - LOGD("nextLine"); -#endif - const char *end = getEnd(); - while (mCurrent != end) { - char ch = *(mCurrent++); - if (ch == '\n') { - mLineNumber += 1; - break; - } - } - } - - void Tokenizer::skipDelimiters(const char *delimiters) { -#if DEBUG_TOKENIZER - LOGD("skipDelimiters"); -#endif - const char *end = getEnd(); - while (mCurrent != end) { - char ch = *mCurrent; - if (ch == '\n' || !isDelimiter(ch, delimiters)) { - break; - } - mCurrent += 1; - } - } - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/utils/Tokenizer.h b/sysbridge/src/main/cpp/android/utils/Tokenizer.h deleted file mode 100644 index dee36a7bb0..0000000000 --- a/sysbridge/src/main/cpp/android/utils/Tokenizer.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UTILS_TOKENIZER_H -#define _UTILS_TOKENIZER_H - -#include -#include -#include "FileMap.h" -#include "Errors.h" -#include "String8.h" - -namespace android { - -/** - * A simple tokenizer for loading and parsing ASCII text files line by line. - */ -class Tokenizer { - Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, - bool ownBuffer, size_t length); - -public: - ~Tokenizer(); - - /** - * Opens a file and maps it into memory. - * - * Returns OK and a tokenizer for the file, if successful. - * Otherwise returns an error and sets outTokenizer to NULL. - */ - static status_t open(const String8& filename, Tokenizer** outTokenizer); - - /** - * Prepares to tokenize the contents of a string. - * - * Returns OK and a tokenizer for the string, if successful. - * Otherwise returns an error and sets outTokenizer to NULL. - */ - static status_t fromContents(const String8& filename, - const char* contents, Tokenizer** outTokenizer); - - /** - * Returns true if at the end of the file. - */ - inline bool isEof() const { return mCurrent == getEnd(); } - - /** - * Returns true if at the end of the line or end of the file. - */ - inline bool isEol() const { return isEof() || *mCurrent == '\n'; } - - /** - * Gets the name of the file. - */ - inline String8 getFilename() const { return mFilename; } - - /** - * Gets a 1-based line number index for the current position. - */ - inline int32_t getLineNumber() const { return mLineNumber; } - - /** - * Formats a location string consisting of the filename and current line number. - * Returns a string like "MyFile.txt:33". - */ - String8 getLocation() const; - - /** - * Gets the character at the current position. - * Returns null at end of file. - */ - inline char peekChar() const { return isEof() ? '\0' : *mCurrent; } - - /** - * Gets the remainder of the current line as a string, excluding the newline character. - */ - String8 peekRemainderOfLine() const; - - /** - * Gets the character at the current position and advances past it. - * Returns null at end of file. - */ - inline char nextChar() { return isEof() ? '\0' : *(mCurrent++); } - - /** - * Gets the next token on this line stopping at the specified delimiters - * or the end of the line whichever comes first and advances past it. - * Also stops at embedded nulls. - * Returns the token or an empty string if the current character is a delimiter - * or is at the end of the line. - */ - String8 nextToken(const char* delimiters); - - /** - * Advances to the next line. - * Does nothing if already at the end of the file. - */ - void nextLine(); - - /** - * Skips over the specified delimiters in the line. - * Also skips embedded nulls. - */ - void skipDelimiters(const char* delimiters); - -private: - Tokenizer(const Tokenizer& other); // not copyable - - String8 mFilename; - FileMap* mFileMap; - char* mBuffer; - bool mOwnBuffer; - size_t mLength; - - const char* mCurrent; - int32_t mLineNumber; - - inline const char* getEnd() const { return mBuffer + mLength; } - -}; - -} // namespace android - -#endif // _UTILS_TOKENIZER_H diff --git a/sysbridge/src/main/cpp/android/utils/TypeHelpers.h b/sysbridge/src/main/cpp/android/utils/TypeHelpers.h deleted file mode 100644 index d867a9a46c..0000000000 --- a/sysbridge/src/main/cpp/android/utils/TypeHelpers.h +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_TYPE_HELPERS_H -#define ANDROID_TYPE_HELPERS_H - -#include -#include - -#include -#include -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -/* - * Types traits - */ - -template struct trait_trivial_ctor { enum { value = false }; }; -template struct trait_trivial_dtor { enum { value = false }; }; -template struct trait_trivial_copy { enum { value = false }; }; -template struct trait_trivial_move { enum { value = false }; }; -template struct trait_pointer { enum { value = false }; }; -template struct trait_pointer { enum { value = true }; }; - -template -struct traits { - enum { - // whether this type is a pointer - is_pointer = trait_pointer::value, - // whether this type's constructor is a no-op - has_trivial_ctor = is_pointer || trait_trivial_ctor::value, - // whether this type's destructor is a no-op - has_trivial_dtor = is_pointer || trait_trivial_dtor::value, - // whether this type type can be copy-constructed with memcpy - has_trivial_copy = is_pointer || trait_trivial_copy::value, - // whether this type can be moved with memmove - has_trivial_move = is_pointer || trait_trivial_move::value - }; -}; - -template -struct aggregate_traits { - enum { - is_pointer = false, - has_trivial_ctor = - traits::has_trivial_ctor && traits::has_trivial_ctor, - has_trivial_dtor = - traits::has_trivial_dtor && traits::has_trivial_dtor, - has_trivial_copy = - traits::has_trivial_copy && traits::has_trivial_copy, - has_trivial_move = - traits::has_trivial_move && traits::has_trivial_move - }; -}; - -#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \ - template<> struct trait_trivial_ctor< T > { enum { value = true }; }; - -#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \ - template<> struct trait_trivial_dtor< T > { enum { value = true }; }; - -#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \ - template<> struct trait_trivial_copy< T > { enum { value = true }; }; - -#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \ - template<> struct trait_trivial_move< T > { enum { value = true }; }; - -#define ANDROID_BASIC_TYPES_TRAITS( T ) \ - ANDROID_TRIVIAL_CTOR_TRAIT( T ) \ - ANDROID_TRIVIAL_DTOR_TRAIT( T ) \ - ANDROID_TRIVIAL_COPY_TRAIT( T ) \ - ANDROID_TRIVIAL_MOVE_TRAIT( T ) - -// --------------------------------------------------------------------------- - -/* - * basic types traits - */ - -ANDROID_BASIC_TYPES_TRAITS( void ) -ANDROID_BASIC_TYPES_TRAITS( bool ) -ANDROID_BASIC_TYPES_TRAITS( char ) -ANDROID_BASIC_TYPES_TRAITS( unsigned char ) -ANDROID_BASIC_TYPES_TRAITS( short ) -ANDROID_BASIC_TYPES_TRAITS( unsigned short ) -ANDROID_BASIC_TYPES_TRAITS( int ) -ANDROID_BASIC_TYPES_TRAITS( unsigned int ) -ANDROID_BASIC_TYPES_TRAITS( long ) -ANDROID_BASIC_TYPES_TRAITS( unsigned long ) -ANDROID_BASIC_TYPES_TRAITS( long long ) -ANDROID_BASIC_TYPES_TRAITS( unsigned long long ) -ANDROID_BASIC_TYPES_TRAITS( float ) -ANDROID_BASIC_TYPES_TRAITS( double ) - -template struct trait_trivial_ctor { enum { value = true }; }; -template struct trait_trivial_dtor { enum { value = true }; }; -template struct trait_trivial_copy { enum { value = true }; }; -template struct trait_trivial_move { enum { value = true }; }; - -// --------------------------------------------------------------------------- - - -/* - * compare and order types - */ - -template inline -int strictly_order_type(const TYPE& lhs, const TYPE& rhs) { - return (lhs < rhs) ? 1 : 0; -} - -template inline -int compare_type(const TYPE& lhs, const TYPE& rhs) { - return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs); -} - -/* - * create, destroy, copy and move types... - */ - -template inline -void construct_type(TYPE* p, size_t n) { - if (!traits::has_trivial_ctor) { - while (n > 0) { - n--; - new(p++) TYPE; - } - } -} - -template inline -void destroy_type(TYPE* p, size_t n) { - if (!traits::has_trivial_dtor) { - while (n > 0) { - n--; - p->~TYPE(); - p++; - } - } -} - -template -typename std::enable_if::has_trivial_copy>::type -inline -copy_type(TYPE* d, const TYPE* s, size_t n) { - memcpy(d,s,n*sizeof(TYPE)); -} - -template -typename std::enable_if::has_trivial_copy>::type -inline -copy_type(TYPE* d, const TYPE* s, size_t n) { - while (n > 0) { - n--; - new(d) TYPE(*s); - d++, s++; - } -} - -template inline -void splat_type(TYPE* where, const TYPE* what, size_t n) { - if (!traits::has_trivial_copy) { - while (n > 0) { - n--; - new(where) TYPE(*what); - where++; - } - } else { - while (n > 0) { - n--; - *where++ = *what; - } - } -} - -template -struct use_trivial_move : public std::integral_constant::has_trivial_dtor && traits::has_trivial_copy) - || traits::has_trivial_move -> {}; - -template -typename std::enable_if::value>::type -inline -move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { - memmove(reinterpret_cast(d), s, n * sizeof(TYPE)); -} - -template -typename std::enable_if::value>::type -inline -move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { - d += n; - s += n; - while (n > 0) { - n--; - --d, --s; - if (!traits::has_trivial_copy) { - new(d) TYPE(*s); - } else { - *d = *s; - } - if (!traits::has_trivial_dtor) { - s->~TYPE(); - } - } -} - -template -typename std::enable_if::value>::type -inline -move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { - memmove(reinterpret_cast(d), s, n * sizeof(TYPE)); -} - -template -typename std::enable_if::value>::type -inline -move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { - while (n > 0) { - n--; - if (!traits::has_trivial_copy) { - new(d) TYPE(*s); - } else { - *d = *s; - } - if (!traits::has_trivial_dtor) { - s->~TYPE(); - } - d++, s++; - } -} - -// --------------------------------------------------------------------------- - -/* - * a key/value pair - */ - -template -struct key_value_pair_t { - typedef KEY key_t; - typedef VALUE value_t; - - KEY key; - VALUE value; - key_value_pair_t() { } - key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { } - key_value_pair_t& operator=(const key_value_pair_t& o) { - key = o.key; - value = o.value; - return *this; - } - key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { } - explicit key_value_pair_t(const KEY& k) : key(k) { } - inline bool operator < (const key_value_pair_t& o) const { - return strictly_order_type(key, o.key); - } - inline const KEY& getKey() const { - return key; - } - inline const VALUE& getValue() const { - return value; - } -}; - -template -struct trait_trivial_ctor< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_ctor }; }; -template -struct trait_trivial_dtor< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_dtor }; }; -template -struct trait_trivial_copy< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_copy }; }; -template -struct trait_trivial_move< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_move }; }; - -// --------------------------------------------------------------------------- - -/* - * Hash codes. - */ -typedef uint32_t hash_t; - -template -hash_t hash_type(const TKey& key); - -/* Built-in hash code specializations */ -#define ANDROID_INT32_HASH(T) \ - template <> inline hash_t hash_type(const T& value) { return hash_t(value); } -#define ANDROID_INT64_HASH(T) \ - template <> inline hash_t hash_type(const T& value) { \ - return hash_t((value >> 32) ^ value); } -#define ANDROID_REINTERPRET_HASH(T, R) \ - template <> inline hash_t hash_type(const T& value) { \ - R newValue; \ - static_assert(sizeof(newValue) == sizeof(value), "size mismatch"); \ - memcpy(&newValue, &value, sizeof(newValue)); \ - return hash_type(newValue); \ - } - -ANDROID_INT32_HASH(bool) -ANDROID_INT32_HASH(int8_t) -ANDROID_INT32_HASH(uint8_t) -ANDROID_INT32_HASH(int16_t) -ANDROID_INT32_HASH(uint16_t) -ANDROID_INT32_HASH(int32_t) -ANDROID_INT32_HASH(uint32_t) -ANDROID_INT64_HASH(int64_t) -ANDROID_INT64_HASH(uint64_t) -ANDROID_REINTERPRET_HASH(float, uint32_t) -ANDROID_REINTERPRET_HASH(double, uint64_t) - -template inline hash_t hash_type(T* const & value) { - return hash_type(uintptr_t(value)); -} - -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_TYPE_HELPERS_H diff --git a/sysbridge/src/main/cpp/android/utils/Unicode.cpp b/sysbridge/src/main/cpp/android/utils/Unicode.cpp deleted file mode 100644 index 49179cc181..0000000000 --- a/sysbridge/src/main/cpp/android/utils/Unicode.cpp +++ /dev/null @@ -1,538 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "unicode" - -#include -#include "Unicode.h" -#include "../../logging.h" -#include "../liblog/log_main.h" - -#include - -extern "C" { - -static const char32_t kByteMask = 0x000000BF; -static const char32_t kByteMark = 0x00000080; - -// Surrogates aren't valid for UTF-32 characters, so define some -// constants that will let us screen them out. -static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; -// Unused, here for completeness: -// static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; -// static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; -static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; -static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; -static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; -static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; - -// Mask used to set appropriate bits in first byte of UTF-8 sequence, -// indexed by number of bytes in the sequence. -// 0xxxxxxx -// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 -// 110yyyyx 10xxxxxx -// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 -// 1110yyyy 10yxxxxx 10xxxxxx -// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 -// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx -// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 -static const char32_t kFirstByteMark[] = { - 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 -}; - -// -------------------------------------------------------------------------- -// UTF-32 -// -------------------------------------------------------------------------- - -/** - * Return number of UTF-8 bytes required for the character. If the character - * is invalid, return size of 0. - */ -static inline size_t utf32_codepoint_utf8_length(char32_t srcChar) { - // Figure out how many bytes the result will require. - if (srcChar < 0x00000080) { - return 1; - } else if (srcChar < 0x00000800) { - return 2; - } else if (srcChar < 0x00010000) { - if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) { - return 3; - } else { - // Surrogates are invalid UTF-32 characters. - return 0; - } - } - // Max code point for Unicode is 0x0010FFFF. - else if (srcChar <= kUnicodeMaxCodepoint) { - return 4; - } else { - // Invalid UTF-32 character. - return 0; - } -} - -// Write out the source character to . - -static inline void utf32_codepoint_to_utf8(uint8_t *dstP, char32_t srcChar, size_t bytes) { - dstP += bytes; - switch (bytes) { /* note: everything falls through. */ - case 4: - *--dstP = (uint8_t) ((srcChar | kByteMark) & kByteMask); - srcChar >>= 6; - [[fallthrough]]; - case 3: - *--dstP = (uint8_t) ((srcChar | kByteMark) & kByteMask); - srcChar >>= 6; - [[fallthrough]]; - case 2: - *--dstP = (uint8_t) ((srcChar | kByteMark) & kByteMask); - srcChar >>= 6; - [[fallthrough]]; - case 1: - *--dstP = (uint8_t) (srcChar | kFirstByteMark[bytes]); - } -} - -static inline int32_t utf32_at_internal(const char *cur, size_t *num_read) { - const char first_char = *cur; - if ((first_char & 0x80) == 0) { // ASCII - *num_read = 1; - return *cur; - } - cur++; - char32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = first_char; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; - (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - to_ignore_mask |= mask; - utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); - - *num_read = num_to_read; - return static_cast(utf32); -} - -int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index) { - if (index >= src_len) { - return -1; - } - size_t unused_index; - if (next_index == nullptr) { - next_index = &unused_index; - } - size_t num_read; - int32_t ret = utf32_at_internal(src + index, &num_read); - if (ret >= 0) { - *next_index = index + num_read; - } - - return ret; -} - -ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len) { - if (src == nullptr || src_len == 0) { - return -1; - } - - size_t ret = 0; - const char32_t *end = src + src_len; - while (src < end) { - size_t char_len = utf32_codepoint_utf8_length(*src++); - if (SSIZE_MAX - char_len < ret) { - // If this happens, we would overflow the ssize_t type when - // returning from this function, so we cannot express how - // long this string is in an ssize_t. -// android_errorWriteLog(0x534e4554, "37723026"); - return -1; - } - ret += char_len; - } - return ret; -} - -void utf32_to_utf8(const char32_t *src, size_t src_len, char *dst, size_t dst_len) { - if (src == nullptr || src_len == 0 || dst == nullptr) { - return; - } - - const char32_t *cur_utf32 = src; - const char32_t *end_utf32 = src + src_len; - char *cur = dst; - while (cur_utf32 < end_utf32) { - size_t len = utf32_codepoint_utf8_length(*cur_utf32); - LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len); - utf32_codepoint_to_utf8((uint8_t *) cur, *cur_utf32++, len); - cur += len; - dst_len -= len; - } - LOG_ALWAYS_FATAL_IF(dst_len < 1, "dst_len < 1: %zu < 1", dst_len); - *cur = '\0'; -} - -// -------------------------------------------------------------------------- -// UTF-16 -// -------------------------------------------------------------------------- - -int strcmp16(const char16_t *s1, const char16_t *s2) { - char16_t ch; - int d = 0; - - while (1) { - d = (int) (ch = *s1++) - (int) *s2++; - if (d || !ch) - break; - } - - return d; -} - -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) { - char16_t ch; - int d = 0; - - if (n == 0) { - return 0; - } - - do { - d = (int) (ch = *s1++) - (int) *s2++; - if (d || !ch) { - break; - } - } while (--n); - - return d; -} - -size_t strlen16(const char16_t *s) { - const char16_t *ss = s; - while (*ss) - ss++; - return ss - s; -} - -size_t strnlen16(const char16_t *s, size_t maxlen) { - const char16_t *ss = s; - - /* Important: the maxlen test must precede the reference through ss; - since the byte beyond the maximum may segfault */ - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss - s; -} - -char16_t *strstr16(const char16_t *src, const char16_t *target) { - const char16_t needle = *target; - if (needle == '\0') return (char16_t *) src; - - const size_t target_len = strlen16(++target); - do { - do { - if (*src == '\0') { - return nullptr; - } - } while (*src++ != needle); - } while (strncmp16(src, target, target_len) != 0); - src--; - - return (char16_t *) src; -} - -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) { - const char16_t *e1 = s1 + n1; - const char16_t *e2 = s2 + n2; - - while (s1 < e1 && s2 < e2) { - const int d = (int) *s1++ - (int) *s2++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int) *s2) - : (n1 > n2 - ? ((int) *s1 - 0) - : 0); -} - -// is_any_surrogate() returns true if w is either a high or low surrogate -static constexpr bool is_any_surrogate(char16_t w) { - return (w & 0xf800) == 0xd800; -} - -// is_surrogate_pair() returns true if w1 and w2 form a valid surrogate pair -static constexpr bool is_surrogate_pair(char16_t w1, char16_t w2) { - return ((w1 & 0xfc00) == 0xd800) && ((w2 & 0xfc00) == 0xdc00); -} - -// TODO: currently utf16_to_utf8_length() returns -1 if src_len == 0, -// which is inconsistent with utf8_to_utf16_length(), here we keep the -// current behavior as intended not to break compatibility -ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len) { - if (src == nullptr || src_len == 0) - return -1; - - const char16_t *const end = src + src_len; - const char16_t *in = src; - size_t utf8_len = 0; - - while (in < end) { - char16_t w = *in++; - if (w < 0x0080) [[likely]] { - utf8_len += 1; - continue; - } - if (w < 0x0800) [[likely]] { - utf8_len += 2; - continue; - } - if (!is_any_surrogate(w)) [[likely]] { - utf8_len += 3; - continue; - } - if (in < end && is_surrogate_pair(w, *in)) { - utf8_len += 4; - in++; - continue; - } - /* skip if at the end of the string or invalid surrogate pair */ - } - return (in == end && utf8_len < SSIZE_MAX) ? utf8_len : -1; -} - -void utf16_to_utf8(const char16_t *src, size_t src_len, char *dst, size_t dst_len) { - if (src == nullptr || src_len == 0 || dst == nullptr) { - return; - } - - const char16_t *in = src; - const char16_t *const in_end = src + src_len; - char *out = dst; - const char *const out_end = dst + dst_len; - char16_t w2; - - auto err_out = [&out, &out_end, &dst_len]() { - LOG_ALWAYS_FATAL_IF(out >= out_end, - "target utf8 string size %zu too short", dst_len); - }; - - while (in < in_end) { - char16_t w = *in++; - if (w < 0x0080) [[likely]] { - if (out + 1 > out_end) - return err_out(); - *out++ = (char) (w & 0xff); - continue; - } - if (w < 0x0800) [[likely]] { - if (out + 2 > out_end) - return err_out(); - *out++ = (char) (0xc0 | ((w >> 6) & 0x1f)); - *out++ = (char) (0x80 | ((w >> 0) & 0x3f)); - continue; - } - if (!is_any_surrogate(w)) [[likely]] { - if (out + 3 > out_end) - return err_out(); - *out++ = (char) (0xe0 | ((w >> 12) & 0xf)); - *out++ = (char) (0x80 | ((w >> 6) & 0x3f)); - *out++ = (char) (0x80 | ((w >> 0) & 0x3f)); - continue; - } - /* surrogate pair */ - if (in < in_end && (w2 = *in, is_surrogate_pair(w, w2))) { - if (out + 4 > out_end) - return err_out(); - char32_t dw = (char32_t) (0x10000 + ((w - 0xd800) << 10) + (w2 - 0xdc00)); - *out++ = (char) (0xf0 | ((dw >> 18) & 0x07)); - *out++ = (char) (0x80 | ((dw >> 12) & 0x3f)); - *out++ = (char) (0x80 | ((dw >> 6) & 0x3f)); - *out++ = (char) (0x80 | ((dw >> 0) & 0x3f)); - in++; - } - /* We reach here in two cases: - * 1) (in == in_end), which means end of the input string - * 2) (w2 & 0xfc00) != 0xdc00, which means invalid surrogate pair - * In either case, we intentionally do nothing and skip - */ - } - *out = '\0'; - return; -} - -// -------------------------------------------------------------------------- -// UTF-8 -// -------------------------------------------------------------------------- - -static char32_t utf8_4b_to_utf32(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4) { - return ((c1 & 0x07) << 18) | ((c2 & 0x3f) << 12) | ((c3 & 0x3f) << 6) | (c4 & 0x3f); -} - -// TODO: current behavior of converting UTF8 to UTF-16 has a few issues below -// -// 1. invalid trailing bytes (i.e. not b'10xxxxxx) are treated as valid trailing -// bytes and follows normal conversion rules -// 2. invalid leading byte (b'10xxxxxx) is treated as a valid single UTF-8 byte -// 3. invalid leading byte (b'11111xxx) is treated as a valid leading byte -// (same as b'11110xxx) for a 4-byte UTF-8 sequence -// 4. an invalid 4-byte UTF-8 sequence that translates to a codepoint < U+10000 -// will be converted as a valid UTF-16 character -// -// We keep the current behavior as is but with warnings logged, so as not to -// break compatibility. However, this needs to be addressed later. - -ssize_t utf8_to_utf16_length(const uint8_t *u8str, size_t u8len, bool overreadIsFatal) { - if (u8str == nullptr) - return -1; - - const uint8_t *const in_end = u8str + u8len; - const uint8_t *in = u8str; - size_t utf16_len = 0; - - while (in < in_end) { - uint8_t c = *in; - utf16_len++; - if ((c & 0x80) == 0) [[likely]] { - in++; - continue; - } - if (c < 0xc0) [[unlikely]] { - LOGW("Invalid UTF-8 leading byte: 0x%02x", c); - in++; - continue; - } - if (c < 0xe0) [[likely]] { - in += 2; - continue; - } - if (c < 0xf0) [[likely]] { - in += 3; - continue; - } else { - uint8_t c2, c3, c4; - if (c >= 0xf8) [[unlikely]] { - LOGW("Invalid UTF-8 leading byte: 0x%02x", c); - } - c2 = in[1]; - c3 = in[2]; - c4 = in[3]; - if (utf8_4b_to_utf32(c, c2, c3, c4) >= 0x10000) { - utf16_len++; - } - in += 4; - continue; - } - } - if (in == in_end) { - return utf16_len < SSIZE_MAX ? utf16_len : -1; - } - if (overreadIsFatal) - LOG_ALWAYS_FATAL("Attempt to overread computing length of utf8 string"); - return -1; -} - -char16_t *utf8_to_utf16(const uint8_t *u8str, size_t u8len, char16_t *u16str, size_t u16len) { - // A value > SSIZE_MAX is probably a negative value returned as an error and casted. - LOG_ALWAYS_FATAL_IF(u16len == 0 || u16len > SSIZE_MAX, "u16len is %zu", u16len); - char16_t *end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str, u16len - 1); - *end = 0; - return end; -} - -char16_t *utf8_to_utf16_no_null_terminator( - const uint8_t *src, size_t srcLen, char16_t *dst, size_t dstLen) { - if (src == nullptr || srcLen == 0 || dstLen == 0) { - return dst; - } - // A value > SSIZE_MAX is probably a negative value returned as an error and casted. - LOG_ALWAYS_FATAL_IF(dstLen > SSIZE_MAX, "dstLen is %zu", dstLen); - - const uint8_t *const in_end = src + srcLen; - const uint8_t *in = src; - const char16_t *const out_end = dst + dstLen; - char16_t *out = dst; - uint8_t c, c2, c3, c4; - char32_t w; - - auto err_in = [&c, &out]() { - LOGW("Unended UTF-8 byte: 0x%02x", c); - return out; - }; - - while (in < in_end && out < out_end) { - c = *in++; - if ((c & 0x80) == 0) [[likely]] { - *out++ = (char16_t) (c); - continue; - } - if (c < 0xc0) [[unlikely]] { - ALOGW("Invalid UTF-8 leading byte: 0x%02x", c); - *out++ = (char16_t) (c); - continue; - } - if (c < 0xe0) [[likely]] { - if (in + 1 > in_end) [[unlikely]] { - return err_in(); - } - c2 = *in++; - *out++ = (char16_t) (((c & 0x1f) << 6) | (c2 & 0x3f)); - continue; - } - if (c < 0xf0) [[likely]] { - if (in + 2 > in_end) [[unlikely]] { - return err_in(); - } - c2 = *in++; - c3 = *in++; - *out++ = (char16_t) (((c & 0x0f) << 12) | - ((c2 & 0x3f) << 6) | (c3 & 0x3f)); - continue; - } else { - if (in + 3 > in_end) [[unlikely]] { - return err_in(); - } - if (c >= 0xf8) [[unlikely]] { - LOGW("Invalid UTF-8 leading byte: 0x%02x", c); - } - // Multiple UTF16 characters with surrogates - c2 = *in++; - c3 = *in++; - c4 = *in++; - w = utf8_4b_to_utf32(c, c2, c3, c4); - if (w < 0x10000) [[unlikely]] { - *out++ = (char16_t) (w); - } else { - if (out + 2 > out_end) [[unlikely]] { - // Ooops.... not enough room for this surrogate pair. - return out; - } - *out++ = (char16_t) (((w - 0x10000) >> 10) + 0xd800); - *out++ = (char16_t) (((w - 0x10000) & 0x3ff) + 0xdc00); - } - continue; - } - } - return out; -} - -} diff --git a/sysbridge/src/main/cpp/android/utils/Unicode.h b/sysbridge/src/main/cpp/android/utils/Unicode.h deleted file mode 100644 index d60d5d6ba6..0000000000 --- a/sysbridge/src/main/cpp/android/utils/Unicode.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_UNICODE_H -#define ANDROID_UNICODE_H - -#include -#include - -extern "C" { - -// Standard string functions on char16_t strings. -int strcmp16(const char16_t *, const char16_t *); -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); -size_t strlen16(const char16_t *); -size_t strnlen16(const char16_t *, size_t); -char16_t *strstr16(const char16_t*, const char16_t*); - -// Version of comparison that supports embedded NULs. -// This is different than strncmp() because we don't stop -// at a nul character and consider the strings to be different -// if the lengths are different (thus we need to supply the -// lengths of both strings). This can also be used when -// your string is not nul-terminated as it will have the -// equivalent result as strcmp16 (unlike strncmp16). -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); - -/** - * Measure the length of a UTF-32 string in UTF-8. If the string is invalid - * such as containing a surrogate character, -1 will be returned. - */ -ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len); - -/** - * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not - * large enough to store the string, the part of the "src" string is stored - * into "dst" as much as possible. See the examples for more detail. - * Returns the size actually used for storing the string. - * dst" is not nul-terminated when dst_len is fully used (like strncpy). - * - * \code - * Example 1 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" >= 7 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0 - * (note that "dst" is nul-terminated) - * - * Example 2 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 5 - * -> - * Returned value == 3 - * "dst" becomes \xE3\x81\x82\0 - * (note that "dst" is nul-terminated, but \u3044 is not stored in "dst" - * since "dst" does not have enough size to store the character) - * - * Example 3 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 6 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84 - * (note that "dst" is NOT nul-terminated, like strncpy) - * \endcode - */ -void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len); - -/** - * Returns the unicode value at "index". - * Returns -1 when the index is invalid (equals to or more than "src_len"). - * If returned value is positive, it is able to be converted to char32_t, which - * is unsigned. Then, if "next_index" is not NULL, the next index to be used is - * stored in "next_index". "next_index" can be NULL. - */ -int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index); - - -/** - * Returns the UTF-8 length of UTF-16 string "src". - */ -ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len); - -/** - * Converts a UTF-16 string to UTF-8. The destination buffer must be large - * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added - * NUL terminator. - */ -void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len); - -/** - * Returns the UTF-16 length of UTF-8 string "src". Returns -1 in case - * it's invalid utf8. No buffer over-read occurs because of bound checks. Using overreadIsFatal you - * can ask to log a message and fail in case the invalid utf8 could have caused an override if no - * bound checks were used (otherwise -1 is returned). - */ -ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen, bool overreadIsFatal = false); - -/** - * Convert UTF-8 to UTF-16 including surrogate pairs. - * Returns a pointer to the end of the string (where a NUL terminator might go - * if you wanted to add one). At most dstLen characters are written; it won't emit half a surrogate - * pair. If dstLen == 0 nothing is written and dst is returned. If dstLen > SSIZE_MAX it aborts - * (this being probably a negative number returned as an error and casted to unsigned). - */ -char16_t* utf8_to_utf16_no_null_terminator( - const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen); - -/** - * Convert UTF-8 to UTF-16 including surrogate pairs. At most dstLen - 1 - * characters are written; it won't emit half a surrogate pair; and a NUL terminator is appended - * after. dstLen - 1 can be measured beforehand using utf8_to_utf16_length. Aborts if dstLen == 0 - * (at least one character is needed for the NUL terminator) or dstLen > SSIZE_MAX (the latter - * case being likely a negative number returned as an error and casted to unsigned) . Returns a - * pointer to the NUL terminator. - */ -char16_t *utf8_to_utf16( - const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen); - -} - -#endif diff --git a/sysbridge/src/main/cpp/libevdev_jni.cpp b/sysbridge/src/main/cpp/libevdev_jni.cpp deleted file mode 100644 index 829d5df81a..0000000000 --- a/sysbridge/src/main/cpp/libevdev_jni.cpp +++ /dev/null @@ -1,707 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "libevdev/libevdev.h" -#include "libevdev/libevdev-uinput.h" -#include "logging.h" -#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 -#include -#include -#include -#include -#include -#include - -using aidl::io::github::sds100::keymapper::sysbridge::IEvdevCallback; - -enum CommandType { - STOP -}; - -struct Command { - CommandType type; -}; - -struct DeviceContext { - struct libevdev *evdev; - struct libevdev_uinput *uinputDev; - struct android::KeyLayoutMap keyLayoutMap; - char devicePath[256]; - int fd; -}; - -static int epollFd = -1; -static std::mutex epollMutex; - -static int commandEventFd = -1; -static std::queue commandQueue; -static std::mutex commandMutex; - -// This maps the file descriptor of an evdev device to its context. -static std::map *evdevDevices = new std::map(); -static std::mutex evdevDevicesMutex; -static std::map *fdToDevicePath = new std::map(); - -#define DEBUG_PROBE false - -jint JNI_OnLoad(JavaVM *vm, void *reserved) { - evdevDevices = new std::map(); - return JNI_VERSION_1_6; -} - -extern "C" -JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_grabEvdevDeviceNative(JNIEnv *env, - jobject thiz, - jstring jDevicePath) { - - const char *devicePath = env->GetStringUTFChars(jDevicePath, nullptr); - if (devicePath == nullptr) { - return false; - } - - bool result = false; - - { - std::lock_guard epollLock(epollMutex); - if (epollFd == -1) { - LOGE("Epoll is not initialized. Cannot grab evdev device."); - return false; - } - - // Lock to prevent concurrent grab/ungrab operations on the same device - std::lock_guard lock(evdevDevicesMutex); - - // Check if device is already grabbed - if (evdevDevices->contains(devicePath)) { - LOGW("Device %s is already grabbed. Maybe it is a virtual uinput device.", - devicePath); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - // Perform synchronous grab operation - struct libevdev *dev = nullptr; - - // MUST be NONBLOCK so that the loop reading the evdev events eventually returns - // due to an EAGAIN error. - int fd = open(devicePath, O_RDONLY | O_NONBLOCK); - if (fd == -1) { - LOGE("Failed to open device %s: %s", devicePath, strerror(errno)); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - int rc = libevdev_new_from_fd(fd, &dev); - if (rc != 0) { - LOGE("Failed to create libevdev device from %s: %s", devicePath, strerror(errno)); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - rc = libevdev_grab(dev, LIBEVDEV_GRAB); - if (rc < 0) { - LOGE("Failed to grab evdev device %s: %s", - libevdev_get_name(dev), strerror(-rc)); - libevdev_free(dev); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - // Create a dummy InputDeviceIdentifier for key layout loading - android::InputDeviceIdentifier identifier; - identifier.name = std::string(libevdev_get_name(dev)); - identifier.bus = libevdev_get_id_bustype(dev); - identifier.vendor = libevdev_get_id_vendor(dev); - identifier.product = libevdev_get_id_product(dev); - - std::string klPath = android::getInputDeviceConfigurationFilePathByDeviceIdentifier( - identifier, android::InputDeviceConfigurationFileType::KEY_LAYOUT); - - auto klResult = android::KeyLayoutMap::load(klPath, nullptr); - - if (!klResult.ok()) { - LOGE("key layout map not found for device %s", libevdev_get_name(dev)); - libevdev_grab(dev, LIBEVDEV_UNGRAB); - libevdev_free(dev); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - struct libevdev_uinput *uinputDev = nullptr; - int uinputFd = open("/dev/uinput", O_RDWR); - if (uinputFd < 0) { - LOGE("Failed to open /dev/uinput to clone the device."); - libevdev_grab(dev, LIBEVDEV_UNGRAB); - libevdev_free(dev); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - rc = libevdev_uinput_create_from_device(dev, uinputFd, &uinputDev); - - if (rc < 0) { - LOGE("Failed to create uinput device from evdev device %s: %s", - libevdev_get_name(dev), strerror(-rc)); - close(uinputFd); - libevdev_grab(dev, LIBEVDEV_UNGRAB); - libevdev_free(dev); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - DeviceContext context = { - dev, - uinputDev, - *klResult.value(), - {}, // Initialize devicePath array - fd - }; - - strcpy(context.devicePath, devicePath); - - // Already checked at the start of the method whether epoll was running - struct epoll_event event{}; - event.events = EPOLLIN; - event.data.fd = fd; - - if (epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &event) == -1) { - LOGE("Failed to add new device to epoll: %s", strerror(errno)); - libevdev_uinput_destroy(uinputDev); - libevdev_grab(dev, LIBEVDEV_UNGRAB); - libevdev_free(dev); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - evdevDevices->insert_or_assign(devicePath, context); - fdToDevicePath->insert_or_assign(fd, devicePath); - result = true; - - LOGI("Grabbed device %s, %s", libevdev_get_name(dev), context.devicePath); - } - - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return result; -} - -struct timeval powerButtonDownTime = {0, 0}; - -/** - * @return Whether the events were all handled by the callback. If the callback dies then this - * returns false. - */ -bool onEpollEvdevEvent(DeviceContext *deviceContext, IEvdevCallback *callback) { - struct input_event inputEvent{}; - - int rc = libevdev_next_event(deviceContext->evdev, LIBEVDEV_READ_FLAG_NORMAL, &inputEvent); - - if (rc < 0) { - if (rc != -EAGAIN) { - LOGE("libevdev_next_event failed with error %d: %s", rc, strerror(-rc)); - } - return rc == -EAGAIN; - } - - do { - if (rc == LIBEVDEV_READ_STATUS_SUCCESS) { // rc == 0 - int32_t outKeycode = -1; - uint32_t outFlags = -1; - - deviceContext->keyLayoutMap.mapKey(inputEvent.code, 0, &outKeycode, &outFlags); - - // Kill the system bridge when power button is held down and released after 10+ seconds. - // 26 = KEYCODE_POWER - if (inputEvent.code == KEY_POWER || outKeycode == 26) { - if (inputEvent.value == 1) { - // Down click - powerButtonDownTime = inputEvent.time; - } else if (inputEvent.value == 0) { - // Up click - - // If held down for 10 seconds or more, kill system bridge. - if (inputEvent.time.tv_sec - powerButtonDownTime.tv_sec >= 10) { - callback->onEmergencyKillSystemBridge(); - exit(0); - } - } - } - - bool returnValue; - ndk::ScopedAStatus callbackResult = callback->onEvdevEvent(deviceContext->devicePath, - inputEvent.time.tv_sec, - inputEvent.time.tv_usec, - inputEvent.type, - inputEvent.code, - inputEvent.value, - outKeycode, - &returnValue); - - if (!callbackResult.isOk()) { - return false; - } - - if (!returnValue) { - libevdev_uinput_write_event(deviceContext->uinputDev, - inputEvent.type, - inputEvent.code, - inputEvent.value); - } - - rc = libevdev_next_event(deviceContext->evdev, LIBEVDEV_READ_FLAG_NORMAL, &inputEvent); - - } else if (rc == LIBEVDEV_READ_STATUS_SYNC) { - rc = libevdev_next_event(deviceContext->evdev, - LIBEVDEV_READ_FLAG_NORMAL | LIBEVDEV_READ_FLAG_SYNC, - &inputEvent); - } - - if (rc < 0 && rc != -EAGAIN) { - LOGE("libevdev_next_event failed with error %d: %s", rc, strerror(-rc)); - return false; - } - } while (rc != -EAGAIN); - - return true; -} - -// Set this to some upper limit. It is unlikely that Key Mapper will be polling -// more than a few evdev devices at once. -static int MAX_EPOLL_EVENTS = 100; - -extern "C" -JNIEXPORT void JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_startEvdevEventLoop(JNIEnv *env, - jobject thiz, - jobject jCallbackBinder) { - std::unique_lock epollLock(epollMutex); - - if (epollFd != -1 || commandEventFd != -1) { - LOGE("The evdev event loop has already started."); - return; - } - - epollFd = epoll_create1(EPOLL_CLOEXEC); - if (epollFd == -1) { - LOGE("Failed to create epoll fd: %s", strerror(errno)); - return; - } - - commandEventFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (commandEventFd == -1) { - LOGE("Failed to create command eventfd: %s", strerror(errno)); - close(epollFd); - return; - } - - struct epoll_event event{}; - event.events = EPOLLIN; - event.data.fd = commandEventFd; - - if (epoll_ctl(epollFd, EPOLL_CTL_ADD, commandEventFd, &event) == -1) { - LOGE("Failed to add command eventfd to epoll: %s", strerror(errno)); - close(epollFd); - close(commandEventFd); - epollLock.unlock(); - return; - } - - epollLock.unlock(); - - AIBinder *callbackAIBinder = AIBinder_fromJavaBinder(env, jCallbackBinder); - const ::ndk::SpAIBinder spBinder(callbackAIBinder); - std::shared_ptr callback = IEvdevCallback::fromBinder(spBinder); - - struct epoll_event events[MAX_EPOLL_EVENTS]; - bool running = true; - - LOGI("Start evdev event loop"); - - ndk::ScopedAStatus callbackResult = callback->onEvdevEventLoopStarted(); - - if (!callbackResult.isOk()) { - LOGE("Callback is dead. Not starting evdev loop."); - return; - } - - while (running) { - int n = epoll_wait(epollFd, events, MAX_EPOLL_EVENTS, -1); - - for (int i = 0; i < n; ++i) { - int fd = events[i].data.fd; - if (fd == commandEventFd) { - uint64_t val; - ssize_t s = read(commandEventFd, &val, sizeof(val)); - - if (s < 0) { - LOGE("Error reading from command event fd: %s", strerror(errno)); - } - - std::vector commandsToProcess; - { - std::lock_guard lock(commandMutex); - while (!commandQueue.empty()) { - commandsToProcess.push_back(commandQueue.front()); - commandQueue.pop(); - } - } - - // Process commands without holding the mutex - for (const auto &cmd: commandsToProcess) { - if (cmd.type == STOP) { - running = false; - break; - } - } - } else { - if ((events[i].events & (EPOLLHUP | EPOLLERR))) { - LOGI("Device disconnected, removing from epoll."); - epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, nullptr); - - std::lock_guard lock(evdevDevicesMutex); - auto it = fdToDevicePath->find(fd); - if (it != fdToDevicePath->end()) { - evdevDevices->erase(it->second); - fdToDevicePath->erase(it); - } - } else { - std::lock_guard lock(evdevDevicesMutex); - auto it = fdToDevicePath->find(fd); - if (it != fdToDevicePath->end()) { - DeviceContext *dc = &evdevDevices->at(it->second); - // If handling the evdev event fails then stop the event loop - // and ungrab all the devices. - bool result = onEpollEvdevEvent(dc, callback.get()); - - if (!result) { - running = false; - break; - } - } - } - } - } - } - - // Cleanup - std::lock_guard lock(evdevDevicesMutex); - - for (auto const &[path, dc]: *evdevDevices) { - libevdev_uinput_destroy(dc.uinputDev); - libevdev_grab(dc.evdev, LIBEVDEV_UNGRAB); - libevdev_free(dc.evdev); - } - - evdevDevices->clear(); - close(commandEventFd); - commandEventFd = -1; - close(epollFd); - epollFd = -1; - - LOGI("Stopped evdev event loop"); -} - -extern "C" -JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_ungrabEvdevDeviceNative(JNIEnv *env, - jobject thiz, - jstring jDevicePath) { - const char *devicePath = env->GetStringUTFChars(jDevicePath, nullptr); - if (devicePath == nullptr) { - return false; - } - - bool result = false; - - { - // Lock to prevent concurrent grab/ungrab operations - std::lock_guard lock(evdevDevicesMutex); - - auto it = evdevDevices->find(devicePath); - if (it != evdevDevices->end()) { - // Remove from epoll first (if event loop is running) - if (epollFd != -1) { - if (epoll_ctl(epollFd, EPOLL_CTL_DEL, it->second.fd, nullptr) == -1) { - LOGW("Failed to remove device from epoll: %s", strerror(errno)); - // Continue with ungrab even if epoll removal fails - } - } - - // Do this before freeing the evdev file descriptor - libevdev_uinput_destroy(it->second.uinputDev); - - // Ungrab the device - libevdev_grab(it->second.evdev, LIBEVDEV_UNGRAB); - - // Free resources - libevdev_free(it->second.evdev); - - // Remove from device map - evdevDevices->erase(it); - fdToDevicePath->erase(it->second.fd); - result = true; - - LOGI("Ungrabbed device %s", devicePath); - } - - if (!result) { - LOGW("Device %s was not found in grabbed devices list", devicePath); - } - } - - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return result; -} - - -extern "C" -JNIEXPORT void JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_stopEvdevEventLoop(JNIEnv *env, - jobject thiz) { - if (commandEventFd == -1) { - return; - } - - Command cmd = {STOP}; - - std::lock_guard lock(commandMutex); - commandQueue.push(cmd); - - // Notify the event loop - uint64_t val = 1; - ssize_t written = write(commandEventFd, &val, sizeof(val)); - if (written < 0) { - LOGE("Failed to write to commandEventFd: %s", strerror(errno)); - } -} - -extern "C" -JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_writeEvdevEventNative(JNIEnv *env, - jobject thiz, - jstring jDevicePath, - jint type, - jint code, - jint value) { - const char *devicePath = env->GetStringUTFChars(jDevicePath, nullptr); - if (devicePath == nullptr) { - return false; - } - - bool result = false; - { - auto it = evdevDevices->find(devicePath); - if (it != evdevDevices->end()) { - int rc = libevdev_uinput_write_event(it->second.uinputDev, type, code, value); - if (rc == 0) { - rc = libevdev_uinput_write_event(it->second.uinputDev, EV_SYN, SYN_REPORT, 0); - } - result = (rc == 0); - } - } - - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return result; -} -extern "C" -JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_ungrabAllEvdevDevicesNative( - JNIEnv *env, - jobject thiz) { - - { - // Lock to prevent concurrent grab/ungrab operations - std::lock_guard lock(evdevDevicesMutex); - std::lock_guard epollLock(epollMutex); - - // Create a copy of the iterator to avoid issues with erasing during iteration - auto devicesCopy = *evdevDevices; - - for (const auto &pair: devicesCopy) { - const DeviceContext &context = pair.second; - - // Remove from epoll first (if event loop is running) - if (epollFd != -1) { - if (epoll_ctl(epollFd, EPOLL_CTL_DEL, context.fd, nullptr) == -1) { - LOGW("Failed to remove device %s from epoll: %s", context.devicePath, - strerror(errno)); - // Continue with ungrab even if epoll removal fails - } - } - - // Do this before freeing the evdev file descriptor - libevdev_uinput_destroy(context.uinputDev); - - // Ungrab the device - libevdev_grab(context.evdev, LIBEVDEV_UNGRAB); - - // Free resources - libevdev_free(context.evdev); - - LOGI("Ungrabbed device %s", context.devicePath); - } - - // Clear all devices from the map - evdevDevices->clear(); - fdToDevicePath->clear(); - } - - return true; -} - -// Helper function to create a Java EvdevDeviceHandle object -jobject -createEvdevDeviceHandle(JNIEnv *env, const char *path, const char *name, int bus, int vendor, - int product) { - // Find the EvdevDeviceHandle class - jclass evdevDeviceHandleClass = env->FindClass( - "io/github/sds100/keymapper/common/models/EvdevDeviceHandle"); - if (evdevDeviceHandleClass == nullptr) { - LOGE("Failed to find EvdevDeviceHandle class"); - return nullptr; - } - - // Get the constructor - jmethodID constructor = env->GetMethodID(evdevDeviceHandleClass, "", - "(Ljava/lang/String;Ljava/lang/String;III)V"); - if (constructor == nullptr) { - LOGE("Failed to find EvdevDeviceHandle constructor"); - return nullptr; - } - - // Create Java strings - jstring jPath = env->NewStringUTF(path); - jstring jName = env->NewStringUTF(name); - - // Create the object - jobject evdevDeviceHandle = env->NewObject(evdevDeviceHandleClass, constructor, jPath, jName, - bus, vendor, product); - - // Clean up local references - env->DeleteLocalRef(jPath); - env->DeleteLocalRef(jName); - env->DeleteLocalRef(evdevDeviceHandleClass); - - return evdevDeviceHandle; -} - -extern "C" -JNIEXPORT jobjectArray JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_getEvdevDevicesNative(JNIEnv *env, - jobject thiz) { - DIR *dir = opendir("/dev/input"); - - if (dir == nullptr) { - LOGE("Failed to open /dev/input directory"); - return nullptr; - } - - std::vector deviceHandles; - struct dirent *entry; - - std::lock_guard lock(evdevDevicesMutex); - while ((entry = readdir(dir)) != nullptr) { - // Skip . and .. entries - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { - continue; - } - - char fullPath[256]; - snprintf(fullPath, sizeof(fullPath), "/dev/input/%s", entry->d_name); - - bool ignoreDevice = false; - - { - // Ignore this device if it is a uinput device we created - for (const auto &pair: *evdevDevices) { - DeviceContext context = pair.second; - const char *uinputDevicePath = libevdev_uinput_get_devnode(context.uinputDev); - - if (strcmp(fullPath, uinputDevicePath) == 0) { - LOGW("Ignoring uinput device %s.", uinputDevicePath); - ignoreDevice = true; - break; - } - } - } - - if (ignoreDevice) { - continue; - } - - int fd = open(fullPath, O_RDONLY); - - if (fd == -1) { - continue; - } - - struct libevdev *dev = nullptr; - int status = libevdev_new_from_fd(fd, &dev); - - if (status != 0) { - LOGE("Failed to open libevdev device from path %s: %s", fullPath, strerror(errno)); - close(fd); - continue; - } - - const char *devName = libevdev_get_name(dev); - int devVendor = libevdev_get_id_vendor(dev); - int devProduct = libevdev_get_id_product(dev); - int devBus = libevdev_get_id_bustype(dev); - - if (DEBUG_PROBE) { - LOGD("Evdev device: %s, bus: %d, vendor: %d, product: %d, path: %s", - devName, devBus, devVendor, devProduct, fullPath); - } - - // Create EvdevDeviceHandle object - jobject deviceHandle = createEvdevDeviceHandle(env, fullPath, devName, devBus, devVendor, - devProduct); - if (deviceHandle != nullptr) { - deviceHandles.push_back(deviceHandle); - } - - libevdev_free(dev); - close(fd); - } - - closedir(dir); - - // Create the Java array - jclass evdevDeviceHandleClass = env->FindClass( - "io/github/sds100/keymapper/common/models/EvdevDeviceHandle"); - if (evdevDeviceHandleClass == nullptr) { - LOGE("Failed to find EvdevDeviceHandle class for array creation"); - return nullptr; - } - - jobjectArray result = env->NewObjectArray(deviceHandles.size(), evdevDeviceHandleClass, - nullptr); - if (result == nullptr) { - LOGE("Failed to create EvdevDeviceHandle array"); - env->DeleteLocalRef(evdevDeviceHandleClass); - return nullptr; - } - - // Fill the array - for (size_t i = 0; i < deviceHandles.size(); i++) { - env->SetObjectArrayElement(result, i, deviceHandles[i]); - env->DeleteLocalRef(deviceHandles[i]); // Clean up local reference - } - - env->DeleteLocalRef(evdevDeviceHandleClass); - return result; -} \ No newline at end of file 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 56629c0d9a..2a975d3c60 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 @@ -17,6 +17,7 @@ import androidx.core.content.ContextCompat import dagger.hilt.android.qualifiers.ApplicationContext import io.github.sds100.keymapper.common.BuildConfigProvider import io.github.sds100.keymapper.common.models.isSuccess +import io.github.sds100.keymapper.common.utils.Constants import io.github.sds100.keymapper.common.utils.KMError import io.github.sds100.keymapper.common.utils.KMResult import io.github.sds100.keymapper.common.utils.SettingsUtils @@ -256,7 +257,7 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( } @SuppressLint("ObsoleteSdkInt") -@RequiresApi(Build.VERSION_CODES.Q) +@RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) interface SystemBridgeConnectionManager { val connectionState: StateFlow 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 90% 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 ba92a552d4..d066a7f7e5 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 @@ -40,10 +40,11 @@ import android.util.Log import android.view.InputEvent import androidx.annotation.RequiresApi import com.android.internal.telephony.ITelephony -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo +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.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 @@ -63,23 +64,60 @@ 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 + @Suppress("KotlinJniMissingFunction") + external fun setGrabbedDevicesNative( + devices: Array, + ): Array - external fun ungrabEvdevDeviceNative(devicePath: String): Boolean - external fun ungrabAllEvdevDevicesNative(): Boolean - external fun writeEvdevEventNative( - devicePath: String, + @Suppress("KotlinJniMissingFunction") + external fun writeEvdevEventNative(deviceId: Int, type: Int, code: Int, value: Int): Boolean + + @Suppress("KotlinJniMissingFunction") + external fun getEvdevDevicesNative(): Array + + @Suppress("KotlinJniMissingFunction") + external fun initEvdevManager() + + @Suppress("KotlinJniMissingFunction") + external fun destroyEvdevManager() + + /** + * Called from Rust via JNI when an evdev event occurs. + * Forwards the call to the registered IEvdevCallback and returns whether the event was consumed. + */ + @Suppress("unused") + fun onEvdevEvent( + deviceId: Int, + timeSec: Long, + timeUsec: Long, type: Int, code: Int, value: Int, - ): Boolean - - external fun getEvdevDevicesNative(): Array + androidCode: Int, + ): Boolean { + synchronized(evdevCallbackLock) { + val callback = evdevCallback ?: return false + return try { + callback.onEvdevEvent(deviceId, timeSec, timeUsec, type, code, value, androidCode) + } catch (e: Exception) { + Log.e(TAG, "Error calling evdev callback", e) + false + } + } + } - external fun startEvdevEventLoop(callback: IBinder) - external fun stopEvdevEventLoop() + /** + * 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. + */ + @Suppress("unused") + fun onEmergencyKillSystemBridge() { + synchronized(evdevCallbackLock) { + evdevCallback?.onEmergencyKillSystemBridge() + } + } companion object { private const val TAG: String = "KeyMapperSystemBridge" @@ -95,14 +133,6 @@ internal class SystemBridge : ISystemBridge.Stub() { private const val DATA_ENABLED_REASON_USER: Int = 0 private const val TETHERING_WIFI: Int = 0 - @JvmStatic - fun main(args: Array) { - @Suppress("DEPRECATION") - Looper.prepareMainLooper() - SystemBridge() - Looper.loop() - } - private fun waitSystemService(name: String?) { var count = 0 @@ -167,11 +197,10 @@ internal class SystemBridge : ISystemBridge.Stub() { private val evdevCallbackLock: Any = Any() private var evdevCallback: IEvdevCallback? = null private val evdevCallbackDeathRecipient: IBinder.DeathRecipient = IBinder.DeathRecipient { - Log.i(TAG, "EvdevCallback binder died") - evdevCallback = null + Log.i(TAG, "EvdevCallback binder died. Stopping evdev event loop") - coroutineScope.launch(Dispatchers.Default) { - stopEvdevEventLoop() + synchronized(evdevCallbackLock) { + evdevCallback = null } // Start periodic check for Key Mapper installation @@ -206,7 +235,7 @@ internal class SystemBridge : ISystemBridge.Stub() { val libraryPath = System.getProperty("keymapper_sysbridge.library.path") @SuppressLint("UnsafeDynamicallyLoadedCode") - System.load("$libraryPath/libevdev.so") + System.load("$libraryPath/libevdev_manager.so") Log.i(TAG, "SystemBridge starting... Version code $versionCode") @@ -301,6 +330,8 @@ internal class SystemBridge : ISystemBridge.Stub() { sendBinderToApp() } + initEvdevManager() + Log.i(TAG, "SystemBridge started complete. Version code $versionCode") } @@ -345,6 +376,11 @@ internal class SystemBridge : ISystemBridge.Stub() { } override fun destroy() { + Log.i(TAG, "Destroying system bridge...") + + stopKeyMapperPeriodicCheck() + destroyEvdevManager() + Log.i(TAG, "SystemBridge destroyed") // Must be last line in this method because it halts the JVM. @@ -369,54 +405,26 @@ internal class SystemBridge : ISystemBridge.Stub() { this.evdevCallback = callback binder.linkToDeath(evdevCallbackDeathRecipient, 0) } - - coroutineScope.launch(Dispatchers.IO) { - mainHandler.post { - startEvdevEventLoop(binder) - } - } } override fun unregisterEvdevCallback() { synchronized(evdevCallbackLock) { evdevCallback?.asBinder()?.unlinkToDeath(evdevCallbackDeathRecipient, 0) evdevCallback = null - stopEvdevEventLoop() } } - override fun grabEvdevDevice(devicePath: String?): Boolean { - devicePath ?: return false - return grabEvdevDeviceNative(devicePath) - } - - override fun grabEvdevDeviceArray(devicePath: Array?): Boolean { - devicePath ?: return false - - for (path in devicePath) { - Log.i(TAG, "Grabbing evdev device $path") - grabEvdevDeviceNative(path) - } - - return true - } - - override fun ungrabEvdevDevice(devicePath: String?): Boolean { - devicePath ?: return false - ungrabEvdevDeviceNative(devicePath) - return true - } - - override fun ungrabAllEvdevDevices(): Boolean { - ungrabAllEvdevDevicesNative() - return true + override fun setGrabbedDevices( + devices: Array?, + ): Array? { + return setGrabbedDevicesNative(devices?.filterNotNull()?.toTypedArray() ?: emptyArray()) } override fun injectInputEvent(event: InputEvent?, mode: Int): Boolean { return inputManager.injectInputEvent(event, mode) } - override fun getEvdevInputDevices(): Array? { + override fun getEvdevInputDevices(): Array? { return getEvdevDevicesNative() } @@ -428,9 +436,8 @@ internal class SystemBridge : ISystemBridge.Stub() { return wifiManager.setWifiEnabled(processPackageName, enable) } - override fun writeEvdevEvent(devicePath: String?, type: Int, code: Int, value: Int): Boolean { - devicePath ?: return false - return writeEvdevEventNative(devicePath, type, code, value) + override fun writeEvdevEvent(deviceId: Int, type: Int, code: Int, value: Int): Boolean { + return writeEvdevEventNative(deviceId, type, code, value) } override fun getProcessUid(): Int { diff --git a/system/src/main/java/io/github/sds100/keymapper/system/inputevents/KMEvdevEvent.kt b/system/src/main/java/io/github/sds100/keymapper/system/inputevents/KMEvdevEvent.kt index fa88349920..86fc6e4be6 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/inputevents/KMEvdevEvent.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/inputevents/KMEvdevEvent.kt @@ -1,9 +1,10 @@ package io.github.sds100.keymapper.system.inputevents -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo data class KMEvdevEvent( - val device: EvdevDeviceHandle, + val deviceId: Int, + val deviceInfo: EvdevDeviceInfo, val type: Int, val code: Int, val value: Int, diff --git a/system/src/main/java/io/github/sds100/keymapper/system/permissions/AndroidPermissionAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/permissions/AndroidPermissionAdapter.kt index 6f33664c9e..11956eeadb 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/permissions/AndroidPermissionAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/permissions/AndroidPermissionAdapter.kt @@ -5,6 +5,7 @@ import android.app.NotificationManager import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Context +import android.content.pm.IPackageManager import android.content.pm.PackageManager.PERMISSION_GRANTED import android.os.Build import android.os.PowerManager @@ -71,16 +72,24 @@ class AndroidPermissionAdapter @Inject constructor( const val REQUEST_CODE_SHIZUKU_PERMISSION = 1 } - private val shizukuPermissionManager: IPermissionManager by lazy { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - HiddenApiBypass.addHiddenApiExemptions( - "Landroid/permission", - ) + private val shizukuPermissionManager: IPermissionManager? by lazy { + // Use IPackageManager instead on older versions + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + return@lazy null } + + HiddenApiBypass.addHiddenApiExemptions( + "Landroid/permission", + ) val binder = ShizukuBinderWrapper(SystemServiceHelper.getSystemService("permissionmgr")) IPermissionManager.Stub.asInterface(binder) } + private val shizukuPackageManager: IPackageManager? by lazy { + val binder = ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")) + IPackageManager.Stub.asInterface(binder) + } + private val ctx = context.applicationContext private val powerManager: PowerManager? = ctx.getSystemService() @@ -167,13 +176,22 @@ class AndroidPermissionAdapter @Inject constructor( } else if (shizukuAdapter.isStarted.value && isGranted(Permission.SHIZUKU)) { val userId = Process.myUserHandle()!!.getIdentifier() - PermissionManagerApis.grantPermission( - shizukuPermissionManager, - buildConfigProvider.packageName, - permissionName, - deviceId, - userId, - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + PermissionManagerApis.grantPermission( + shizukuPermissionManager!!, + buildConfigProvider.packageName, + permissionName, + deviceId, + userId, + ) + } else { + PermissionManagerApis.grantPermission( + shizukuPackageManager!!, + buildConfigProvider.packageName, + permissionName, + userId, + ) + } if (ContextCompat.checkSelfPermission(ctx, permissionName) == PERMISSION_GRANTED) { result = success() diff --git a/systemstubs/src/main/java/android/permission/PermissionManagerApis.kt b/systemstubs/src/main/java/android/permission/PermissionManagerApis.kt index 8ef810c186..247bbd02d6 100644 --- a/systemstubs/src/main/java/android/permission/PermissionManagerApis.kt +++ b/systemstubs/src/main/java/android/permission/PermissionManagerApis.kt @@ -1,8 +1,11 @@ package android.permission +import android.content.pm.IPackageManager import android.os.Build +import androidx.annotation.RequiresApi object PermissionManagerApis { + @RequiresApi(Build.VERSION_CODES.R) fun grantPermission( permissionManager: IPermissionManager, packageName: String, @@ -37,12 +40,6 @@ object PermissionManagerApis { } } // In Android 11 this method was moved from IPackageManager to IPermissionManager. - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - permissionManager.grantRuntimePermission( - packageName, - permission, - userId, - ) } else { permissionManager.grantRuntimePermission( packageName, @@ -51,4 +48,14 @@ object PermissionManagerApis { ) } } + + // Used on Android 10 and older. + fun grantPermission( + packageManager: IPackageManager, + packageName: String, + permission: String, + userId: Int, + ) { + packageManager.grantRuntimePermission(packageName, permission, userId) + } }