From 536ec0c98feff77e99ca58963d454b18d0fa9261 Mon Sep 17 00:00:00 2001 From: Joao Mario Lago Date: Mon, 18 Mar 2024 23:12:19 -0300 Subject: [PATCH] views: ConfigJoystick: Add support for axes remap * Add support for custom axes remapping in jpystick configuration view --- src/views/ConfigurationJoystickView.vue | 108 +++++++++++++++++++++++- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/src/views/ConfigurationJoystickView.vue b/src/views/ConfigurationJoystickView.vue index 764666519..48ef0b618 100644 --- a/src/views/ConfigurationJoystickView.vue +++ b/src/views/ConfigurationJoystickView.vue @@ -250,6 +250,14 @@

Axis mapping

+
+ +

{{ axisRemappingText }}

+
+ + + +
{{ @@ -286,6 +294,13 @@ variant="solo" hide-details /> + + {{ remappingAxisInput && remappingAxisInput === input.id ? 'Remapping' : 'Click to remap' }} +
@@ -320,6 +335,17 @@ import { import BaseConfigurationView from './BaseConfigurationView.vue' +/** + * The time in milliseconds that the remapping process waits for a button press + * before considering it unsuccessful in milliseconds. + */ +const remappingTimeTotalMs = 5000 + +/** + * The minimum value a certain axis must change to be considered a full range move. + */ +const joystickAxisFullRangeThreshold = 3.5 + const controllerStore = useControllerStore() const { globalAddress } = useMainVehicleStore() @@ -348,16 +374,23 @@ const currentJoystick = ref() const currentButtonInputs = ref([]) const currentAxisInputs = ref([]) const remappingInput = ref(false) +const remappingAxisInput = ref(false) const remapTimeProgress = ref() +const remapAxisTimeProgress = ref() const showButtonRemappingText = ref(false) +const showAxisRemappingText = ref(false) const buttonFunctionAssignmentFeedback = ref('') const showButtonFunctionAssignmentFeedback = ref(false) const justRemappedInput = ref() +const justRemappedAxisInput = ref() const inputClickedDialog = ref(false) const currentModifierKey: Ref = ref(modifierKeyActions.regular) const availableModifierKeys: ProtocolAction[] = Object.values(modifierKeyActions) const showJoystickLayout = ref(true) -watch(inputClickedDialog, () => (justRemappedInput.value = undefined)) +watch(inputClickedDialog, () => { + justRemappedInput.value = undefined + justRemappedAxisInput.value = undefined +}) const setCurrentInputs = (joystick: Joystick, inputs: JoystickInput[]): void => { currentJoystick.value = joystick @@ -394,18 +427,17 @@ const remapInput = async (joystick: Joystick, input: JoystickInput): Promise button.value === 1) await new Promise((r) => setTimeout(r, 100)) millisPassed += 100 - remapTimeProgress.value = 100 * (millisPassed / waitingTime) + remapTimeProgress.value = 100 * (millisPassed / remappingTimeTotalMs) } // End the remapping process @@ -423,6 +455,64 @@ const remapInput = async (joystick: Joystick, input: JoystickInput): Promise} A promise that resolves once the remapping process is complete. + */ +const remapAxisInput = async (joystick: Joystick, input: JoystickInput): Promise => { + // Initialize the remapping state + justRemappedAxisInput.value = undefined + remappingAxisInput.value = input.id as JoystickAxis + remapAxisTimeProgress.value = 0 + showAxisRemappingText.value = true + + // Save the initial state of joystick axes values + const lastAxisValues = [...joystick.gamepad.axes] + const totalAxisDiff = new Array(lastAxisValues.length).fill(0) + + // Time tracking could be simplified by calculating the end time first + const endTime = Date.now() + remappingTimeTotalMs + + // Wait for a button press or until the waiting time expires + while (Date.now() < endTime) { + const currentAxisValues = joystick.gamepad.axes + + totalAxisDiff.forEach((_, i) => { + totalAxisDiff[i] += Math.abs(currentAxisValues[i] - lastAxisValues[i]) + }) + + if (totalAxisDiff.some((diff) => diff > joystickAxisFullRangeThreshold)) { + break + } + + // Await a short period before the next check + await new Promise((resolve) => setTimeout(resolve, 100)) + const timePassed = Date.now() - endTime + remappingTimeTotalMs + remapAxisTimeProgress.value = (timePassed / remappingTimeTotalMs) * 100 + } + setTimeout(() => { + if (remappingAxisInput.value === false) { + showAxisRemappingText.value = false + } + }, 5000) + + // Determine success and update the mapping if successful + const remappedIndex = totalAxisDiff.findIndex((diff) => diff > joystickAxisFullRangeThreshold) + const success = remappedIndex !== -1 + + // End the remapping process + justRemappedAxisInput.value = success + remappingAxisInput.value = false + + if (success) { + controllerStore.cockpitStdMappings[joystick.model].axes[input.id] = remappedIndex as JoystickAxis + } +} + const currentButtonActions = computed( () => controllerStore.protocolMapping.buttonsCorrespondencies[currentModifierKey.value.id as CockpitModifierKeyOption] ) @@ -485,6 +575,16 @@ const buttonRemappingText = computed(() => { : 'No input detected.' }) +const axisRemappingText = computed(() => { + return remappingAxisInput.value + ? 'Make a full range move on the axis you want to use for this input.' + : justRemappedAxisInput.value === undefined + ? '' + : justRemappedAxisInput.value + ? 'Axis input remapped.' + : 'No axis detected.' +}) + const buttonActionsToShow = computed(() => controllerStore.availableButtonActions.filter((a) => JSON.stringify(a) !== JSON.stringify(modifierKeyActions.regular)) )