Skip to content

Commit c1d1a93

Browse files
committed
Merge branch '1918-fix-grabbing-key-codes' into develop
2 parents 96c6e04 + d8bfd39 commit c1d1a93

File tree

22 files changed

+627
-168
lines changed

22 files changed

+627
-168
lines changed

base/src/main/java/io/github/sds100/keymapper/base/actions/PerformKeyEventActionDelegate.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class PerformKeyEventActionDelegate(
3838
keyMetaState: Int,
3939
triggerDevice: PerformActionTriggerDevice,
4040
): KMResult<Unit> {
41+
// Only input evdev event if the device is grabbed. It may not be grabbed
42+
// if the device is disconnected.
4143
if (injectKeyEventsWithSystemBridge.value &&
4244
triggerDevice is PerformActionTriggerDevice.Evdev &&
4345
(action.device == null || isActionDeviceGrabbed(action.device))

base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapAlgorithm.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ class KeyMapAlgorithm(
9898
*/
9999
private var doublePressTimeoutTimes = longArrayOf()
100100

101-
private var actionMap: SparseArrayCompat<Action> = SparseArrayCompat()
101+
var actionMap: SparseArrayCompat<Action> = SparseArrayCompat()
102+
private set
102103
var triggers: Array<Trigger> = emptyArray()
103104
private set
104105

@@ -141,7 +142,8 @@ class KeyMapAlgorithm(
141142
* The actions to perform when each trigger is detected. The order matches with
142143
* [triggers].
143144
*/
144-
private var triggerActions: Array<IntArray> = arrayOf()
145+
var triggerActions: Array<IntArray> = arrayOf()
146+
private set
145147

146148
/**
147149
* Stores whether each event in each parallel trigger need to be released after being held down.

base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.github.sds100.keymapper.base.detection
22

3+
import io.github.sds100.keymapper.base.actions.ActionData
34
import io.github.sds100.keymapper.base.actions.PerformActionsUseCase
45
import io.github.sds100.keymapper.base.constraints.DetectConstraintsUseCase
56
import io.github.sds100.keymapper.base.input.InputEventDetectionSource
@@ -11,7 +12,7 @@ import io.github.sds100.keymapper.base.trigger.AssistantTriggerType
1112
import io.github.sds100.keymapper.base.trigger.EvdevTriggerKey
1213
import io.github.sds100.keymapper.base.trigger.RecordTriggerController
1314
import io.github.sds100.keymapper.base.trigger.RecordTriggerState
14-
import io.github.sds100.keymapper.base.trigger.Trigger
15+
import io.github.sds100.keymapper.common.models.GrabDeviceRequest
1516
import io.github.sds100.keymapper.system.inputevents.KMEvdevEvent
1617
import io.github.sds100.keymapper.system.inputevents.KMInputEvent
1718
import kotlinx.coroutines.CoroutineScope
@@ -34,6 +35,37 @@ class KeyMapDetectionController(
3435
) : InputEventHubCallback {
3536
companion object {
3637
private const val INPUT_EVENT_HUB_ID = "key_map_controller"
38+
39+
fun getEvdevGrabRequests(algorithm: KeyMapAlgorithm): List<GrabDeviceRequest> {
40+
val requests = mutableListOf<GrabDeviceRequest>()
41+
42+
for ((index, trigger) in algorithm.triggers.withIndex()) {
43+
val evdevDevices = trigger.keys.filterIsInstance<EvdevTriggerKey>()
44+
.map { it.device }
45+
.distinct()
46+
.toList()
47+
48+
if (evdevDevices.isEmpty()) {
49+
continue
50+
}
51+
52+
val actions: List<ActionData> = algorithm.triggerActions[index]
53+
.map { actionIndex -> algorithm.actionMap[actionIndex]?.data }
54+
.filterNotNull()
55+
56+
val extraKeyCodes = actions
57+
.filterIsInstance<ActionData.InputKeyEvent>()
58+
.map { it.keyCode }
59+
.distinct()
60+
.toIntArray()
61+
62+
for (device in evdevDevices) {
63+
requests.add(GrabDeviceRequest(device, extraKeyCodes))
64+
}
65+
}
66+
67+
return requests
68+
}
3769
}
3870

3971
private val algorithm: KeyMapAlgorithm =
@@ -43,25 +75,40 @@ class KeyMapDetectionController(
4375
pauseKeyMapsUseCase.isPaused.stateIn(coroutineScope, SharingStarted.Eagerly, false)
4476

4577
init {
46-
// Must first register before collecting anything that may call reset()
78+
// Must first register before collecting anything that may change the grabbed
79+
// evdev devices.
4780
inputEventHub.registerClient(INPUT_EVENT_HUB_ID, this, listOf(KMEvdevEvent.TYPE_KEY_EVENT))
4881

4982
coroutineScope.launch {
5083
combine(detectUseCase.allKeyMapList, isPaused) { keyMapList, isPaused ->
51-
algorithm.reset()
52-
53-
if (isPaused) {
54-
algorithm.loadKeyMaps(emptyList())
55-
inputEventHub.setGrabbedEvdevDevices(INPUT_EVENT_HUB_ID, emptyList())
56-
} else {
57-
algorithm.loadKeyMaps(keyMapList)
58-
// Only grab the triggers that are actually being listened to by the algorithm
59-
grabEvdevDevicesForTriggers(algorithm.triggers)
60-
}
84+
invalidateState(keyMapList, isPaused)
6185
}.launchIn(coroutineScope)
6286
}
6387
}
6488

89+
private fun invalidateState(keyMapList: List<DetectKeyMapModel>, isPaused: Boolean) {
90+
algorithm.reset()
91+
92+
if (isPaused) {
93+
algorithm.loadKeyMaps(emptyList())
94+
inputEventHub.setGrabbedEvdevDevices(INPUT_EVENT_HUB_ID, emptyList())
95+
} else {
96+
algorithm.loadKeyMaps(keyMapList)
97+
// Determine which evdev devices need to be grabbed depending on the state
98+
// of the algorithm.
99+
val grabRequests = getEvdevGrabRequests(algorithm)
100+
101+
Timber.i(
102+
"Grab evdev devices for key map detection: ${grabRequests.joinToString()}",
103+
)
104+
105+
inputEventHub.setGrabbedEvdevDevices(
106+
INPUT_EVENT_HUB_ID,
107+
grabRequests,
108+
)
109+
}
110+
}
111+
65112
override fun onInputEvent(
66113
event: KMInputEvent,
67114
detectionSource: InputEventDetectionSource,
@@ -94,26 +141,8 @@ class KeyMapDetectionController(
94141
}
95142

96143
fun teardown() {
97-
reset()
98-
inputEventHub.unregisterClient(INPUT_EVENT_HUB_ID)
99-
}
100-
101-
private fun grabEvdevDevicesForTriggers(triggers: Array<Trigger>) {
102-
val evdevDevices = triggers
103-
.flatMap { trigger -> trigger.keys.filterIsInstance<EvdevTriggerKey>() }
104-
.map { it.device }
105-
.distinct()
106-
.toList()
107-
108-
Timber.i("Grab evdev devices for key map detection: ${evdevDevices.joinToString()}")
109-
inputEventHub.setGrabbedEvdevDevices(
110-
INPUT_EVENT_HUB_ID,
111-
evdevDevices,
112-
)
113-
}
114-
115-
private fun reset() {
116144
algorithm.reset()
117145
inputEventHub.setGrabbedEvdevDevices(INPUT_EVENT_HUB_ID, emptyList())
146+
inputEventHub.unregisterClient(INPUT_EVENT_HUB_ID)
118147
}
119148
}

base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package io.github.sds100.keymapper.base.input
22

33
import androidx.annotation.RequiresApi
44
import io.github.sds100.keymapper.common.models.EvdevDeviceInfo
5+
import io.github.sds100.keymapper.common.models.GrabDeviceRequest
56
import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle
67
import io.github.sds100.keymapper.common.utils.Constants
78
import io.github.sds100.keymapper.common.utils.onFailure
@@ -18,6 +19,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
1819
import kotlinx.coroutines.channels.Channel
1920
import kotlinx.coroutines.flow.MutableStateFlow
2021
import kotlinx.coroutines.flow.collect
22+
import kotlinx.coroutines.flow.combine
2123
import kotlinx.coroutines.flow.emptyFlow
2224
import kotlinx.coroutines.flow.flatMapLatest
2325
import kotlinx.coroutines.flow.onEach
@@ -44,7 +46,7 @@ class EvdevDevicesDelegate @Inject constructor(
4446

4547
// Use a channel so there are no race conditions when grabbing and that all
4648
// grab operations finish in the correct order to completion.
47-
private val grabDevicesChannel: Channel<List<EvdevDeviceInfo>> = Channel(capacity = 16)
49+
private val grabDevicesChannel: Channel<List<GrabDeviceRequest>> = Channel(capacity = 16)
4850

4951
// All the evdev devices on the device, regardless of whether they are grabbed.
5052
val allDevices: MutableStateFlow<List<EvdevDeviceInfo>> = MutableStateFlow(emptyList())
@@ -69,23 +71,32 @@ class EvdevDevicesDelegate @Inject constructor(
6971
}.collect()
7072
}
7173

72-
// Process on another thread because system bridge grabbing calls are blocking
73-
coroutineScope.launch(Dispatchers.IO) {
74-
grabDevicesChannel.receiveAsFlow().collect { devices ->
75-
systemBridgeConnectionManager
76-
.run { bridge -> bridge.setGrabbedDevices(devices.toTypedArray()) }
77-
.onSuccess { grabbedDevices ->
78-
onGrabbedDevicesChanged(grabbedDevices?.filterNotNull() ?: emptyList())
79-
}.onFailure { error ->
80-
Timber.w(
81-
"Grabbing devices failed in system bridge: $error",
82-
)
74+
coroutineScope.launch {
75+
combine(
76+
grabDevicesChannel.receiveAsFlow(),
77+
devicesAdapter.connectedInputDevices,
78+
) { devicesToGrab, _ -> devicesToGrab }
79+
.collect { devices ->
80+
withContext(Dispatchers.IO) {
81+
invalidateGrabbedDevices(devices)
8382
}
84-
}
83+
}
8584
}
8685
}
8786

88-
fun setGrabbedDevices(devices: List<EvdevDeviceInfo>) {
87+
private fun invalidateGrabbedDevices(devices: List<GrabDeviceRequest>) {
88+
systemBridgeConnectionManager
89+
.run { bridge -> bridge.setGrabbedDevices(devices.toTypedArray()) }
90+
.onSuccess { grabbedDevices ->
91+
onGrabbedDevicesChanged(grabbedDevices?.filterNotNull() ?: emptyList())
92+
}.onFailure { error ->
93+
Timber.w(
94+
"Grabbing devices failed in system bridge: $error",
95+
)
96+
}
97+
}
98+
99+
fun setGrabbedDevices(devices: List<GrabDeviceRequest>) {
89100
grabDevicesChannel.trySend(devices)
90101
}
91102

base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import androidx.annotation.RequiresApi
66
import io.github.sds100.keymapper.base.BuildConfig
77
import io.github.sds100.keymapper.base.system.inputmethod.ImeInputEventInjector
88
import io.github.sds100.keymapper.common.models.EvdevDeviceInfo
9+
import io.github.sds100.keymapper.common.models.GrabDeviceRequest
910
import io.github.sds100.keymapper.common.utils.Constants
1011
import io.github.sds100.keymapper.common.utils.KMError
1112
import io.github.sds100.keymapper.common.utils.KMResult
@@ -136,13 +137,13 @@ class InputEventHubImpl @Inject constructor(
136137
for (clientContext in clients.values) {
137138
if (event is KMEvdevEvent) {
138139
if (!clientContext.evdevEventTypes.contains(event.type) ||
139-
clientContext.devicesToGrab.isEmpty()
140+
clientContext.grabRequests.isEmpty()
140141
) {
141142
continue
142143
}
143144

144145
// Only send events from evdev devices to the client if they grabbed it
145-
if (!clientContext.devicesToGrab.contains(event.deviceInfo)) {
146+
if (!clientContext.grabbedDevice(event.deviceInfo)) {
146147
continue
147148
}
148149

@@ -228,14 +229,14 @@ class InputEventHubImpl @Inject constructor(
228229
}
229230

230231
@RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API)
231-
override fun setGrabbedEvdevDevices(clientId: String, devices: List<EvdevDeviceInfo>) {
232+
override fun setGrabbedEvdevDevices(clientId: String, devices: List<GrabDeviceRequest>) {
232233
if (!clients.containsKey(clientId)) {
233234
throw IllegalArgumentException(
234235
"This client $clientId is not registered when trying to grab devices!",
235236
)
236237
}
237238

238-
clients[clientId] = clients[clientId]!!.copy(devicesToGrab = devices.toSet())
239+
clients[clientId] = clients[clientId]!!.copy(grabRequests = devices.toSet())
239240
invalidateGrabbedDevices()
240241
}
241242

@@ -252,8 +253,12 @@ class InputEventHubImpl @Inject constructor(
252253
)
253254
}
254255

255-
val devices = evdevDevicesDelegate.allDevices.value.toSet()
256-
clients[clientId] = clients[clientId]!!.copy(devicesToGrab = devices)
256+
val devices = evdevDevicesDelegate.allDevices.value
257+
val grabRequests = devices.map {
258+
GrabDeviceRequest(device = it, extraKeyCodes = intArrayOf())
259+
}.toSet()
260+
clients[clientId] = clients[clientId]!!.copy(grabRequests = grabRequests)
261+
257262
invalidateGrabbedDevices()
258263
}
259264

@@ -356,7 +361,7 @@ class InputEventHubImpl @Inject constructor(
356361

357362
@RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API)
358363
private fun invalidateGrabbedDevices() {
359-
val devicesToGrab = clients.values.flatMap { it.devicesToGrab }.toSet()
364+
val devicesToGrab = clients.values.flatMap { it.grabRequests }.toSet()
360365
evdevDevicesDelegate.setGrabbedDevices(devicesToGrab.toList())
361366
}
362367

@@ -365,9 +370,15 @@ class InputEventHubImpl @Inject constructor(
365370
/**
366371
* The evdev devices that this client wants to grab.
367372
*/
368-
val devicesToGrab: Set<EvdevDeviceInfo>,
373+
val grabRequests: Set<GrabDeviceRequest>,
369374
val evdevEventTypes: Set<Int>,
370-
)
375+
) {
376+
private val devicesSet: Set<EvdevDeviceInfo> = grabRequests.map { it.device }.toSet()
377+
378+
fun grabbedDevice(device: EvdevDeviceInfo): Boolean {
379+
return devicesSet.contains(device)
380+
}
381+
}
371382
}
372383

373384
interface InputEventHub {
@@ -386,7 +397,7 @@ interface InputEventHub {
386397
fun unregisterClient(clientId: String)
387398

388399
fun getGrabbedDevices(): List<EvdevDeviceInfo>
389-
fun setGrabbedEvdevDevices(clientId: String, devices: List<EvdevDeviceInfo>)
400+
fun setGrabbedEvdevDevices(clientId: String, devices: List<GrabDeviceRequest>)
390401
fun grabAllEvdevDevices(clientId: String)
391402

392403
/**

0 commit comments

Comments
 (0)