From c5c7a3775f475c8c5b750aa6d8c14adf3a2f75f6 Mon Sep 17 00:00:00 2001 From: isXander Date: Fri, 16 Jun 2023 17:26:42 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9E=95=20Mixed=20input=20mode=20for=20Steam?= =?UTF-8?q?=20Deck?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/isxander/controlify/Controlify.java | 29 +++++++++++++++++-- .../dev/isxander/controlify/InputMode.java | 11 ++++++- .../controlify/driver/GamepadDrivers.java | 2 +- .../controlify/driver/SteamDeckDriver.java | 5 ++-- .../controlify/ingame/InGameInputHandler.java | 2 +- .../core/ClientPacketListenerMixin.java | 2 +- .../mixins/core/KeyboardHandlerMixin.java | 2 +- .../mixins/core/MouseHandlerMixin.java | 27 ++++++++++------- .../accessibility/LocalPlayerMixin.java | 2 +- .../fixes/boatfix/LocalPlayerMixin.java | 25 +++++++--------- .../ingame/ClientPacketListenerMixin.java | 2 +- .../guide/screen/AbstractButtonMixin.java | 2 +- .../guide/screen/TabNavigationBarMixin.java | 2 +- .../vanilla/AbstractSelectionListMixin.java | 2 +- .../vanilla/AbstractSliderButtonMixin.java | 2 +- .../controlify/screenop/ScreenProcessor.java | 2 +- .../AbstractContainerScreenProcessor.java | 4 +-- .../virtualmouse/VirtualMouseHandler.java | 6 ++-- 18 files changed, 82 insertions(+), 47 deletions(-) diff --git a/src/main/java/dev/isxander/controlify/Controlify.java b/src/main/java/dev/isxander/controlify/Controlify.java index 8c6a605..2b61d0c 100644 --- a/src/main/java/dev/isxander/controlify/Controlify.java +++ b/src/main/java/dev/isxander/controlify/Controlify.java @@ -3,6 +3,7 @@ package dev.isxander.controlify; import com.mojang.blaze3d.Blaze3D; import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.api.entrypoint.ControlifyEntrypoint; +import dev.isxander.controlify.driver.SteamDeckDriver; import dev.isxander.controlify.gui.controllers.ControllerBindHandler; import dev.isxander.controlify.gui.screen.ControllerCarouselScreen; import dev.isxander.controlify.controller.Controller; @@ -72,6 +73,8 @@ public class Controlify implements ControlifyApi { private int consecutiveInputSwitches = 0; private double lastInputSwitchTime = 0; + private int showMouseTicks = 0; + private @Nullable Controller switchableController = null; private double askSwitchTime = 0; private ToastUtils.ControlifyToast askSwitchToast = null; @@ -278,6 +281,15 @@ public class Controlify implements ControlifyApi { } } + if (minecraft.mouseHandler.isMouseGrabbed()) + showMouseTicks = 0; + if (currentInputMode() == InputMode.MIXED && showMouseTicks > 0) { + showMouseTicks--; + if (showMouseTicks == 0) { + hideMouse(true, false); + } + } + LowBatteryNotifier.tick(); getCurrentController().ifPresent(currentController -> { @@ -300,7 +312,11 @@ public class Controlify implements ControlifyApi { } if (state.hasAnyInput()) { - this.setInputMode(InputMode.CONTROLLER); + // use MIXED input mode to support Steam Input for things like gyro mouse and touchpads + // this is only temporary until the Steam Deck driver is finished + boolean isSteamDeck = controller.hidInfo().map(info -> info.hidDevice().map(device -> SteamDeckDriver.isSteamDeck(device.getVendorId(), device.getProductId())).orElse(false)).orElse(false); + + this.setInputMode(isSteamDeck ? InputMode.MIXED : InputMode.CONTROLLER); } if (consecutiveInputSwitches > 100) { @@ -483,7 +499,7 @@ public class Controlify implements ControlifyApi { this.currentInputMode = currentInputMode; if (!minecraft.mouseHandler.isMouseGrabbed()) - hideMouse(currentInputMode == InputMode.CONTROLLER, true); + hideMouse(currentInputMode.isController(), true); if (minecraft.screen != null) { ScreenProcessorProvider.provide(minecraft.screen).onInputModeChanged(currentInputMode); } @@ -501,7 +517,7 @@ public class Controlify implements ControlifyApi { } lastInputSwitchTime = Blaze3D.getTime(); - if (currentInputMode == InputMode.CONTROLLER) + if (this.currentInputMode == InputMode.CONTROLLER) getCurrentController().ifPresent(Controller::clearState); ControlifyEvents.INPUT_MODE_CHANGED.invoker().onInputModeChanged(currentInputMode); @@ -527,6 +543,13 @@ public class Controlify implements ControlifyApi { } } + public void showCursorTemporarily() { + if (currentInputMode() == InputMode.MIXED && !minecraft.mouseHandler.isMouseGrabbed()) { + hideMouse(false, false); + showMouseTicks = 20 * 3; + } + } + public static Controlify instance() { if (instance == null) instance = new Controlify(); return instance; diff --git a/src/main/java/dev/isxander/controlify/InputMode.java b/src/main/java/dev/isxander/controlify/InputMode.java index 0bee16c..12f051a 100644 --- a/src/main/java/dev/isxander/controlify/InputMode.java +++ b/src/main/java/dev/isxander/controlify/InputMode.java @@ -2,5 +2,14 @@ package dev.isxander.controlify; public enum InputMode { KEYBOARD_MOUSE, - CONTROLLER; + CONTROLLER, + MIXED; + + public boolean isKeyboardMouse() { + return this != CONTROLLER; + } + + public boolean isController() { + return this != KEYBOARD_MOUSE; + } } diff --git a/src/main/java/dev/isxander/controlify/driver/GamepadDrivers.java b/src/main/java/dev/isxander/controlify/driver/GamepadDrivers.java index 4d608c8..67b688a 100644 --- a/src/main/java/dev/isxander/controlify/driver/GamepadDrivers.java +++ b/src/main/java/dev/isxander/controlify/driver/GamepadDrivers.java @@ -50,7 +50,7 @@ public record GamepadDrivers(BasicGamepadInputDriver basicGamepadInputDriver, Gy } // TODO: Fix Steam Deck driver - if (hid.isPresent() && SteamDeckDriver.isSteamDeck(hid.get()) && false) { + if (hid.isPresent() && SteamDeckDriver.isSteamDeck(hid.get().getVendorId(), hid.get().getProductId()) && false) { gyroDriver = new SteamDeckDriver(hid.get()); } diff --git a/src/main/java/dev/isxander/controlify/driver/SteamDeckDriver.java b/src/main/java/dev/isxander/controlify/driver/SteamDeckDriver.java index 9866f38..627e729 100644 --- a/src/main/java/dev/isxander/controlify/driver/SteamDeckDriver.java +++ b/src/main/java/dev/isxander/controlify/driver/SteamDeckDriver.java @@ -1,6 +1,7 @@ package dev.isxander.controlify.driver; import dev.isxander.controlify.controller.gamepad.GamepadState; +import dev.isxander.controlify.controller.hid.HIDIdentifier; import dev.isxander.controlify.utils.Log; import org.hid4java.HidDevice; @@ -243,7 +244,7 @@ public class SteamDeckDriver implements GyroDriver, BasicGamepadInputDriver { } } - public static boolean isSteamDeck(HidDevice hid) { - return hid.getVendorId() == 0x28DE && hid.getProductId() == 0x1205; + public static boolean isSteamDeck(int vendorId, int productId) { + return vendorId == 0x28DE && productId == 0x1205; } } diff --git a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java index f1c15c2..20d78b0 100644 --- a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java @@ -44,7 +44,7 @@ public class InGameInputHandler { ControlifyEvents.INPUT_MODE_CHANGED.register(mode -> { if (minecraft.player != null) { - minecraft.player.input = mode == InputMode.CONTROLLER + minecraft.player.input = mode.isController() ? new ControllerPlayerMovement(controller, minecraft.player) : new KeyboardInput(minecraft.options); } diff --git a/src/main/java/dev/isxander/controlify/mixins/core/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/ClientPacketListenerMixin.java index 6af2663..5b6b2ab 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/ClientPacketListenerMixin.java @@ -35,7 +35,7 @@ public class ClientPacketListenerMixin { } private void overrideInput(LocalPlayer player) { - if (Controlify.instance().currentInputMode() == InputMode.CONTROLLER && player != null) + if (Controlify.instance().currentInputMode().isController() && player != null) player.input = new ControllerPlayerMovement(Controlify.instance().currentController(), player); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java index 916424b..c7db9ca 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/KeyboardHandlerMixin.java @@ -18,7 +18,7 @@ public class KeyboardHandlerMixin { // m_unngxkoe is lambda for GLFW keypress hook - do it outside of the `keyPress` method due to fake inputs @Inject(method = "method_22678", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/KeyboardHandler;keyPress(JIIII)V")) private void onKeyboardInput(long window, int i, int j, int k, int m, CallbackInfo ci) { - if (window == minecraft.getWindow().getWindow()) + if (window == minecraft.getWindow().getWindow() && Controlify.instance().currentInputMode() != InputMode.MIXED) Controlify.instance().setInputMode(InputMode.KEYBOARD_MOUSE); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java index a99d09a..28721d9 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java @@ -16,25 +16,32 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; public class MouseHandlerMixin { @Shadow @Final private Minecraft minecraft; - // m_sljgmtqm is lambda for GLFW mouse click hook - do it outside of the `onPress` method due to fake inputs + // method_22686 is lambda for GLFW mouse click hook - do it outside of the `onPress` method due to fake inputs @Inject(method = "method_22686", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;onPress(JIII)V")) private void onMouseClickInput(long window, int button, int action, int modifiers, CallbackInfo ci) { - if (window == minecraft.getWindow().getWindow()) - Controlify.instance().setInputMode(InputMode.KEYBOARD_MOUSE); + onMouse(window); } - // m_swhlgdws is lambda for GLFW mouse move hook - do it outside of the `onMove` method due to fake inputs + // method_22689 is lambda for GLFW mouse move hook - do it outside of the `onMove` method due to fake inputs @Inject(method = "method_22689", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;onMove(JDD)V")) private void onMouseMoveInput(long window, double x, double y, CallbackInfo ci) { - if (window == minecraft.getWindow().getWindow()) - Controlify.instance().setInputMode(InputMode.KEYBOARD_MOUSE); + onMouse(window); } - // m_qoshpwkl is lambda for GLFW mouse scroll hook - do it outside of the `onScroll` method due to fake inputs + // method_22687 is lambda for GLFW mouse scroll hook - do it outside of the `onScroll` method due to fake inputs @Inject(method = "method_22687", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;onScroll(JDD)V")) private void onMouseScrollInput(long window, double scrollDeltaX, double scrollDeltaY, CallbackInfo ci) { - if (window == minecraft.getWindow().getWindow()) - Controlify.instance().setInputMode(InputMode.KEYBOARD_MOUSE); + onMouse(window); + } + + private void onMouse(long window) { + if (window == minecraft.getWindow().getWindow()) { + if (Controlify.instance().currentInputMode() != InputMode.MIXED) { + Controlify.instance().setInputMode(InputMode.KEYBOARD_MOUSE); + } else { + Controlify.instance().showCursorTemporarily(); + } + } } /** @@ -42,7 +49,7 @@ public class MouseHandlerMixin { */ @Inject(method = "releaseMouse", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/InputConstants;grabOrReleaseMouse(JIDD)V")) private void moveMouseIfNecessary(CallbackInfo ci) { - if (ControlifyApi.get().currentInputMode() == InputMode.CONTROLLER) { + if (ControlifyApi.get().currentInputMode().isController()) { Controlify.instance().hideMouse(true, true); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/accessibility/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/accessibility/LocalPlayerMixin.java index ca4f0d8..173a79a 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/accessibility/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/accessibility/LocalPlayerMixin.java @@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.At; public class LocalPlayerMixin { @ModifyExpressionValue(method = "sendPosition", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/OptionInstance;get()Ljava/lang/Object;")) private Object shouldUseAutoJump(Object keyboardAutoJump) { - if (ControlifyApi.get().currentInputMode() == InputMode.CONTROLLER) { + if (ControlifyApi.get().currentInputMode().isController()) { return ControlifyApi.get().getCurrentController() .map(controller -> controller.config().autoJump) .orElse(false); diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/fixes/boatfix/LocalPlayerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/fixes/boatfix/LocalPlayerMixin.java index f669ac5..a679c0d 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/fixes/boatfix/LocalPlayerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/fixes/boatfix/LocalPlayerMixin.java @@ -3,33 +3,28 @@ package dev.isxander.controlify.mixins.feature.fixes.boatfix; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.InputMode; import dev.isxander.controlify.api.ControlifyApi; -import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.fixes.boatfix.AnalogBoatInput; +import net.minecraft.client.player.Input; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.vehicle.Boat; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import java.util.Optional; - @Mixin(LocalPlayer.class) public class LocalPlayerMixin { + @Shadow public Input input; + @WrapOperation(method = "rideTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/vehicle/Boat;setInput(ZZZZ)V")) private void useAnalogInput(Boat boat, boolean pressingLeft, boolean pressingRight, boolean pressingForward, boolean pressingBack, Operation original) { - if (ControlifyApi.get().currentInputMode() == InputMode.CONTROLLER && !Controlify.instance().config().globalSettings().keyboardMovement) { - Optional> controllerOpt = ControlifyApi.get().getCurrentController(); - if (controllerOpt.isPresent()) { - var controller = controllerOpt.get(); + if (ControlifyApi.get().currentInputMode().isController() && !Controlify.instance().config().globalSettings().keyboardMovement) { + ((AnalogBoatInput) boat).setAnalogInput( + input.forwardImpulse, + -input.leftImpulse + ); - ((AnalogBoatInput) boat).setAnalogInput( - controller.bindings().WALK_FORWARD.state() - controller.bindings().WALK_BACKWARD.state(), - controller.bindings().WALK_RIGHT.state() - controller.bindings().WALK_LEFT.state() - ); - - return; - } + return; } original.call(boat, pressingLeft, pressingRight, pressingForward, pressingBack); diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java index c9848fa..15b24d4 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java @@ -30,7 +30,7 @@ public class ClientPacketListenerMixin { } private void initButtonGuide() { - if (Controlify.instance().currentInputMode() == InputMode.CONTROLLER && minecraft.player != null) + if (Controlify.instance().currentInputMode().isController() && minecraft.player != null) Controlify.instance().inGameButtonGuide = new InGameButtonGuide(Controlify.instance().getCurrentController().orElseThrow(), minecraft.player); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java index 94f99fa..22512a4 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractButtonMixin.java @@ -66,7 +66,7 @@ public abstract class AbstractButtonMixin extends AbstractWidgetMixin implements private boolean shouldRender() { return renderData != null && this.isActive() - && Controlify.instance().currentInputMode() == InputMode.CONTROLLER + && Controlify.instance().currentInputMode().isController() && Controlify.instance().getCurrentController().map(c -> c.config().showScreenGuide).orElse(false) && !renderData.binding().apply(Controlify.instance().currentController().bindings()).isUnbound() && renderData.renderPredicate().shouldDisplay((AbstractButton) (Object) this); diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java index 8046fa5..9d0a743 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/TabNavigationBarMixin.java @@ -25,7 +25,7 @@ public class TabNavigationBarMixin { @Inject(method = "render", at = @At("RETURN")) private void renderControllerButtonOverlay(GuiGraphics graphics, int mouseX, int mouseY, float delta, CallbackInfo ci) { - if (Controlify.instance().currentInputMode() == InputMode.CONTROLLER) { + if (Controlify.instance().currentInputMode().isController()) { Controlify.instance().getCurrentController().ifPresent(c -> { if (c.config().showScreenGuide) { this.renderControllerButtonOverlay(graphics, c); diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/vanilla/AbstractSelectionListMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/vanilla/AbstractSelectionListMixin.java index d81cae1..9225c3b 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/vanilla/AbstractSelectionListMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/vanilla/AbstractSelectionListMixin.java @@ -11,6 +11,6 @@ import org.spongepowered.asm.mixin.injection.At; public class AbstractSelectionListMixin { @ModifyExpressionValue(method = "setFocused", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/InputType;isKeyboard()Z")) private boolean shouldEnsureEntryVisible(boolean keyboard) { - return keyboard || Controlify.instance().currentInputMode() == InputMode.CONTROLLER; + return keyboard || Controlify.instance().currentInputMode().isController(); } } diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/vanilla/AbstractSliderButtonMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/vanilla/AbstractSliderButtonMixin.java index ebd4d2e..fa64f3e 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/screenop/vanilla/AbstractSliderButtonMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/screenop/vanilla/AbstractSliderButtonMixin.java @@ -29,7 +29,7 @@ public class AbstractSliderButtonMixin implements ComponentProcessorProvider { @ModifyExpressionValue(method = "setFocused", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getLastInputType()Lnet/minecraft/client/InputType;")) private InputType shouldChangeValue(InputType type) { - if (Controlify.instance().currentInputMode() == InputMode.CONTROLLER) + if (Controlify.instance().currentInputMode().isController()) return InputType.NONE; // none doesn't pass condition return type; } diff --git a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java b/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java index 551e04c..90d5995 100644 --- a/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/screenop/ScreenProcessor.java @@ -199,7 +199,7 @@ public class ScreenProcessor { } protected void setInitialFocus() { - if (screen.getFocused() == null && Controlify.instance().currentInputMode() == InputMode.CONTROLLER && !Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) { + if (screen.getFocused() == null && Controlify.instance().currentInputMode().isController() && !Controlify.instance().virtualMouseHandler().isVirtualMouseEnabled()) { var accessor = (ScreenAccessor) screen; ComponentPath path = screen.nextFocusPath(accessor.invokeCreateArrowEvent(ScreenDirection.DOWN)); if (path != null) { diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractContainerScreenProcessor.java b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractContainerScreenProcessor.java index 5b8b5fa..f5a11c3 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractContainerScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractContainerScreenProcessor.java @@ -184,14 +184,14 @@ public class AbstractContainerScreenProcessor