Improved Cursors

- Cursor position is now float instead of integer, making movement way smoother
- Added a fallback cursor to use in Wayland sessions
This commit is contained in:
Martin Prokoph
2024-07-17 23:23:05 +02:00
parent bdb3c36518
commit 45149af859
16 changed files with 69 additions and 38 deletions

View File

@@ -10,6 +10,7 @@
package eu.midnightdust.midnightcontrols; package eu.midnightdust.midnightcontrols;
import eu.midnightdust.lib.util.PlatformFunctions; import eu.midnightdust.lib.util.PlatformFunctions;
import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -29,6 +30,9 @@ public class MidnightControls {
isExtrasLoaded = PlatformFunctions.isModLoaded("midnightcontrols-extra"); isExtrasLoaded = PlatformFunctions.isModLoaded("midnightcontrols-extra");
log("Initializing MidnightControls..."); log("Initializing MidnightControls...");
} }
public static Identifier id(String path) {
return Identifier.of(MidnightControlsConstants.NAMESPACE, path);
}
/** /**
* Prints a message to the terminal. * Prints a message to the terminal.

View File

@@ -12,7 +12,6 @@ package eu.midnightdust.midnightcontrols.client;
import eu.midnightdust.lib.util.PlatformFunctions; import eu.midnightdust.lib.util.PlatformFunctions;
import eu.midnightdust.midnightcontrols.ControlsMode; import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.MidnightControls; import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import eu.midnightdust.midnightcontrols.MidnightControlsFeature; import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat; import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding; import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
@@ -51,25 +50,27 @@ import java.util.concurrent.atomic.AtomicReference;
*/ */
public class MidnightControlsClient extends MidnightControls { public class MidnightControlsClient extends MidnightControls {
public static boolean lateInitDone = false; public static boolean lateInitDone = false;
public static final KeyBinding BINDING_LOOK_UP = InputManager.makeKeyBinding(Identifier.of(MidnightControlsConstants.NAMESPACE, "look_up"), public static final KeyBinding BINDING_LOOK_UP = InputManager.makeKeyBinding(id("look_up"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, "key.categories.movement"); InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, "key.categories.movement");
public static final KeyBinding BINDING_LOOK_RIGHT = InputManager.makeKeyBinding(Identifier.of(MidnightControlsConstants.NAMESPACE, "look_right"), public static final KeyBinding BINDING_LOOK_RIGHT = InputManager.makeKeyBinding(id("look_right"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, "key.categories.movement"); InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, "key.categories.movement");
public static final KeyBinding BINDING_LOOK_DOWN = InputManager.makeKeyBinding(Identifier.of(MidnightControlsConstants.NAMESPACE, "look_down"), public static final KeyBinding BINDING_LOOK_DOWN = InputManager.makeKeyBinding(id("look_down"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, "key.categories.movement"); InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, "key.categories.movement");
public static final KeyBinding BINDING_LOOK_LEFT = InputManager.makeKeyBinding(Identifier.of(MidnightControlsConstants.NAMESPACE, "look_left"), public static final KeyBinding BINDING_LOOK_LEFT = InputManager.makeKeyBinding(id("look_left"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, "key.categories.movement"); InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, "key.categories.movement");
public static final KeyBinding BINDING_RING = InputManager.makeKeyBinding(Identifier.of(MidnightControlsConstants.NAMESPACE, "ring"), public static final KeyBinding BINDING_RING = InputManager.makeKeyBinding(id("ring"),
InputUtil.Type.KEYSYM, InputUtil.UNKNOWN_KEY.getCode(), "key.categories.misc"); InputUtil.Type.KEYSYM, InputUtil.UNKNOWN_KEY.getCode(), "key.categories.misc");
public static final Identifier CONTROLLER_BUTTONS = Identifier.of(MidnightControlsConstants.NAMESPACE, "textures/gui/controller_buttons.png"); public static final Identifier CONTROLLER_BUTTONS = id("textures/gui/controller_buttons.png");
public static final Identifier CONTROLLER_EXPANDED = Identifier.of(MidnightControlsConstants.NAMESPACE, "textures/gui/controller_expanded.png"); public static final Identifier CONTROLLER_EXPANDED = id("textures/gui/controller_expanded.png");
public static final Identifier CONTROLLER_AXIS = Identifier.of(MidnightControlsConstants.NAMESPACE, "textures/gui/controller_axis.png"); public static final Identifier CONTROLLER_AXIS = id("textures/gui/controller_axis.png");
public static final Identifier CURSOR_TEXTURE = Identifier.of(MidnightControlsConstants.NAMESPACE, "textures/gui/cursor.png"); public static final Identifier WAYLAND_CURSOR_TEXTURE_LIGHT = id("cursor/light/mouse_pointer");
public static final Identifier WAYLAND_CURSOR_TEXTURE_DARK = id("cursor/dark/mouse_pointer");
public static final File MAPPINGS_FILE = new File("config/gamecontrollercustommappings.txt"); public static final File MAPPINGS_FILE = new File("config/gamecontrollercustommappings.txt");
public static final MinecraftClient client = MinecraftClient.getInstance(); public static final MinecraftClient client = MinecraftClient.getInstance();
public static final MidnightInput input = new MidnightInput(); public static final MidnightInput input = new MidnightInput();
public static final MidnightRing ring = new MidnightRing(); public static final MidnightRing ring = new MidnightRing();
public static final MidnightReacharound reacharound = new MidnightReacharound(); public static final MidnightReacharound reacharound = new MidnightReacharound();
public static boolean isWayland;
private static MidnightControlsHud hud; private static MidnightControlsHud hud;
private static ControlsMode previousControlsMode; private static ControlsMode previousControlsMode;
@@ -87,6 +88,7 @@ public class MidnightControlsClient extends MidnightControls {
}, delay, period); }, delay, period);
HudManager.register(hud = new MidnightControlsHud()); HudManager.register(hud = new MidnightControlsHud());
isWayland = GLFW.glfwGetVersionString().contains("Wayland");
} }
/** /**

View File

@@ -81,4 +81,12 @@ public enum VirtualMouseSkin implements Nameable {
public static @NotNull Optional<VirtualMouseSkin> byId(@NotNull String id) { public static @NotNull Optional<VirtualMouseSkin> byId(@NotNull String id) {
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst(); return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
} }
public String getSpritePath() {
return switch (this) {
case DEFAULT_LIGHT -> "cursor/light/default";
case DEFAULT_DARK -> "cursor/dark/default";
case SECOND_LIGHT -> "cursor/light/secondary";
case SECOND_DARK -> "cursor/dark/secondary";
};
}
} }

View File

@@ -10,21 +10,34 @@
package eu.midnightdust.midnightcontrols.client.gui; package eu.midnightdust.midnightcontrols.client.gui;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.client.enums.ControllerType; import eu.midnightdust.midnightcontrols.client.enums.ControllerType;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient; import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.MidnightInput; import eu.midnightdust.midnightcontrols.client.MidnightInput;
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat; import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding; import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.enums.VirtualMouseSkin;
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor; import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer; import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.BufferRenderer;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.resource.language.I18n; import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.texture.Sprite;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.joml.Matrix4f;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
/** /**
* Represents the midnightcontrols renderer. * Represents the midnightcontrols renderer.
* *
@@ -192,14 +205,28 @@ public class MidnightControlsRenderer {
private static int getButtonTipWidth(@NotNull String action, @NotNull TextRenderer textRenderer) { private static int getButtonTipWidth(@NotNull String action, @NotNull TextRenderer textRenderer) {
return 15 + 5 + textRenderer.getWidth(action); return 15 + 5 + textRenderer.getWidth(action);
} }
public static void renderWaylandCursor(@NotNull DrawContext context, @NotNull MinecraftClient client) {
if (MidnightControlsConfig.virtualMouse || client.currentScreen == null || MidnightControlsConfig.controlsMode != ControlsMode.CONTROLLER) return;
float mouseX = (float) client.mouse.getX() * client.getWindow().getScaledWidth() / client.getWindow().getWidth();
float mouseY = (float) client.mouse.getY() * client.getWindow().getScaledHeight() / client.getWindow().getHeight();
try {
Identifier spritePath = MidnightControlsClient.WAYLAND_CURSOR_TEXTURE_LIGHT;
if (MidnightControlsConfig.virtualMouseSkin == VirtualMouseSkin.DEFAULT_DARK || MidnightControlsConfig.virtualMouseSkin == VirtualMouseSkin.SECOND_DARK)
spritePath = MidnightControlsClient.WAYLAND_CURSOR_TEXTURE_DARK;
Sprite sprite = client.getGuiAtlasManager().getSprite(spritePath);
drawUnalignedTexturedQuad(sprite.getAtlasId(), context, mouseX, mouseX + 8, mouseY, mouseY + 8, 999, sprite.getMinU(), sprite.getMaxU(), sprite.getMinV(), sprite.getMaxV());
} catch (IllegalStateException ignored) {}
}
public static void renderVirtualCursor(@NotNull DrawContext context, @NotNull MinecraftClient client) { public static void renderVirtualCursor(@NotNull DrawContext context, @NotNull MinecraftClient client) {
if (!MidnightControlsConfig.virtualMouse || (client.currentScreen == null if (!MidnightControlsConfig.virtualMouse || (client.currentScreen == null
|| MidnightInput.isScreenInteractive(client.currentScreen))) || MidnightInput.isScreenInteractive(client.currentScreen)))
return; return;
int mouseX = (int) (client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth()); float mouseX = (float) client.mouse.getX() * client.getWindow().getScaledWidth() / client.getWindow().getWidth();
int mouseY = (int) (client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight()); float mouseY = (float) client.mouse.getY() * client.getWindow().getScaledHeight() / client.getWindow().getHeight();
boolean hoverSlot = false; boolean hoverSlot = false;
@@ -217,7 +244,7 @@ public class MidnightControlsRenderer {
} }
if (!hoverSlot && client.currentScreen != null) { if (!hoverSlot && client.currentScreen != null) {
var slot = MidnightControlsCompat.getSlotAt(client.currentScreen, mouseX, mouseY); var slot = MidnightControlsCompat.getSlotAt(client.currentScreen, (int) mouseX, (int) mouseY);
if (slot != null) { if (slot != null) {
mouseX = slot.x(); mouseX = slot.x();
@@ -231,32 +258,21 @@ public class MidnightControlsRenderer {
mouseY -= 8; mouseY -= 8;
} }
//context.getMatrices().push(); try {
context.getMatrices().translate(0f, 0f, 999f); Sprite sprite = client.getGuiAtlasManager().getSprite(id(MidnightControlsConfig.virtualMouseSkin.getSpritePath() + (hoverSlot ? "_slot" : "")));
drawCursor(context, mouseX, mouseY, hoverSlot, client); drawUnalignedTexturedQuad(sprite.getAtlasId(), context, mouseX, mouseX + 16, mouseY, mouseY + 16, 999, sprite.getMinU(), sprite.getMaxU(), sprite.getMinV(), sprite.getMaxV());
//context.getMatrices().pop(); } catch (IllegalStateException ignored) {}
} }
private static void drawUnalignedTexturedQuad(Identifier texture, DrawContext context, float x1, float x2, float y1, float y2, float z, float u1, float u2, float v1, float v2) {
/** RenderSystem.setShaderTexture(0, texture);
* Draws the virtual cursor. RenderSystem.setShader(GameRenderer::getPositionTexProgram);
* Matrix4f matrix4f = context.getMatrices().peek().getPositionMatrix();
* @param context the context BufferBuilder bufferBuilder = Tessellator.getInstance().begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE);
* @param x x coordinate bufferBuilder.vertex(matrix4f, x1, y1, z).texture(u1, v1);
* @param y y coordinate bufferBuilder.vertex(matrix4f, x1, y2, z).texture(u1, v2);
* @param hoverSlot true if hovering a slot, else false bufferBuilder.vertex(matrix4f, x2, y2, z).texture(u2, v2);
* @param client the client instance bufferBuilder.vertex(matrix4f, x2, y1, z).texture(u2, v1);
*/ BufferRenderer.drawWithGlobalProgram(bufferBuilder.end());
public static void drawCursor(@NotNull DrawContext context, int x, int y, boolean hoverSlot, @NotNull MinecraftClient client) {
//RenderSystem.disableDepthTest();
//RenderSystem.setShaderColor(1.f, 1.f, 1.f, 1.f);
//RenderSystem.disableBlend();
//RenderSystem.setShaderTexture(0, MidnightControlsClient.CURSOR_TEXTURE);
context.drawTexture(MidnightControlsClient.CURSOR_TEXTURE, x, y,
hoverSlot ? 16.f : 0.f, MidnightControlsConfig.virtualMouseSkin.ordinal() * 16.f,
16, 16, 32, 64);
context.fill(1, 1, x, y, 0xFFFFFF);
context.draw();
//RenderSystem.enableDepthTest();
} }
public record ButtonSize(int length, int height) { public record ButtonSize(int length, int height) {

View File

@@ -42,6 +42,7 @@ public abstract class GameRendererMixin {
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;draw()V", shift = At.Shift.BEFORE)) @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;draw()V", shift = At.Shift.BEFORE))
private void renderVirtualCursor(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci, @Local DrawContext drawContext) { private void renderVirtualCursor(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci, @Local DrawContext drawContext) {
MidnightControlsRenderer.renderVirtualCursor(drawContext, client); MidnightControlsRenderer.renderVirtualCursor(drawContext, client);
if (MidnightControlsClient.isWayland) MidnightControlsRenderer.renderWaylandCursor(drawContext, client);
drawContext.draw(); drawContext.draw();
} }
@Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/GameRenderer;renderHand:Z"), method = "renderWorld") @Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/GameRenderer;renderHand:Z"), method = "renderWorld")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB