diff --git a/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java b/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java index 8f780c2..4a4dde3 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/MinecraftMixin.java @@ -3,12 +3,16 @@ package dev.isxander.controlify.mixins.core; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.controlify.Controlify; import dev.isxander.controlify.ControllerManager; +import dev.isxander.controlify.api.ControlifyApi; import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.utils.Animator; +import dev.isxander.controlify.utils.MouseMinecraftCallNotifier; import net.minecraft.client.Minecraft; +import net.minecraft.client.MouseHandler; import net.minecraft.client.gui.screens.Screen; import net.minecraft.server.packs.resources.ReloadInstance; import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -21,8 +25,30 @@ public abstract class MinecraftMixin { @Shadow public abstract void setScreen(@Nullable Screen screen); @Shadow public abstract float getDeltaFrameTime(); + @Shadow @Final public MouseHandler mouseHandler; @Unique private boolean initNextTick = false; + // Ideally, this would be done in MouseHandler#releaseMouse, but moving + // the mouse before the screen init is bad, because some mods (e.g. PuzzleLib) + // have custom mouse events that call into screens, events that have not been + // initialised yet in Screen#init. Causing NPEs and many strange issues. + @Inject(method = "setScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;releaseMouse()V", shift = At.Shift.BEFORE)) + private void notifyInjectionToNotRun(Screen screen, CallbackInfo ci) { + ((MouseMinecraftCallNotifier) mouseHandler).imFromMinecraftSetScreen(); + } + + /** + * Without this, the mouse would be left in the middle of the + * screen, hovering over whatever is there which would look wrong + * as there is a focus as well. + */ + @Inject(method = "setScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/Screen;init(Lnet/minecraft/client/Minecraft;II)V", shift = At.Shift.AFTER)) + private void hideMouseAfterRelease(Screen screen, CallbackInfo ci) { + if (ControlifyApi.get().currentInputMode().isController()) { + Controlify.instance().hideMouse(true, true); + } + } + @ModifyExpressionValue(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/ReloadableResourceManager;createReload(Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/concurrent/CompletableFuture;Ljava/util/List;)Lnet/minecraft/server/packs/resources/ReloadInstance;")) private ReloadInstance onInputInitialized(ReloadInstance resourceReload) { // Controllers need to be initialized extremely late due to the data-driven nature of controllers. 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 28721d9..297e6a6 100644 --- a/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/core/MouseHandlerMixin.java @@ -3,19 +3,23 @@ package dev.isxander.controlify.mixins.core; import dev.isxander.controlify.Controlify; import dev.isxander.controlify.InputMode; import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.utils.MouseMinecraftCallNotifier; import net.minecraft.client.Minecraft; import net.minecraft.client.MouseHandler; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(MouseHandler.class) -public class MouseHandlerMixin { +public class MouseHandlerMixin implements MouseMinecraftCallNotifier { @Shadow @Final private Minecraft minecraft; + @Unique private boolean calledFromMinecraftSetScreen = false; + // 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) { @@ -34,6 +38,7 @@ public class MouseHandlerMixin { onMouse(window); } + @Unique private void onMouse(long window) { if (window == minecraft.getWindow().getWindow()) { if (Controlify.instance().currentInputMode() != InputMode.MIXED) { @@ -49,8 +54,19 @@ 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().isController()) { + if (!calledFromMinecraftSetScreen && ControlifyApi.get().currentInputMode().isController()) { Controlify.instance().hideMouse(true, true); } } + + // shift after RETURN to escape the if statement scope + @Inject(method = "releaseMouse", at = @At(value = "RETURN")) + private void resetCalledFromMinecraftSetScreen(CallbackInfo ci) { + calledFromMinecraftSetScreen = false; + } + + @Override + public void imFromMinecraftSetScreen() { + calledFromMinecraftSetScreen = true; + } } diff --git a/src/main/java/dev/isxander/controlify/utils/MouseMinecraftCallNotifier.java b/src/main/java/dev/isxander/controlify/utils/MouseMinecraftCallNotifier.java new file mode 100644 index 0000000..bdca3bf --- /dev/null +++ b/src/main/java/dev/isxander/controlify/utils/MouseMinecraftCallNotifier.java @@ -0,0 +1,5 @@ +package dev.isxander.controlify.utils; + +public interface MouseMinecraftCallNotifier { + void imFromMinecraftSetScreen(); +}