From d51840475cfc087b8ba8bbc46bccb045ba166638 Mon Sep 17 00:00:00 2001 From: JacksonAbney Date: Sun, 13 Apr 2025 18:19:32 -0800 Subject: [PATCH] Re-enable alt buffer, currently mouse support works in btop, htop, nano, vim, and nextvi. It does not work in Midnight Commander, but this seems to be an issue with our build of MC. --- .../oc2/client/gui/MachineTerminalWidget.java | 104 ++++++++++++++---- .../vm/terminal/ImplementedPrivateModes.java | 7 +- .../oc2/common/vm/terminal/PrivateMode.java | 1 + .../common/vm/terminal/PrivateModeState.java | 2 + .../cil/oc2/common/vm/terminal/Terminal.java | 40 ++----- .../common/vm/terminal/escapes/CSI/RM.java | 1 + .../common/vm/terminal/escapes/CSI/SM.java | 43 +++++--- .../oc2/common/vm/terminal/escapes/DECRC.java | 1 - .../oc2/common/vm/terminal/escapes/DECSC.java | 1 - 9 files changed, 126 insertions(+), 74 deletions(-) diff --git a/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java b/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java index 30f42e7c..2c0c8663 100644 --- a/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java +++ b/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java @@ -21,8 +21,8 @@ import org.joml.Vector2i; import org.lwjgl.glfw.GLFW; import javax.annotation.Nullable; -import java.io.Console; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; @OnlyIn(Dist.CLIENT) public final class MachineTerminalWidget { @@ -151,17 +151,44 @@ public final class MachineTerminalWidget { Vector2i position = getMousePosition(x, y); boolean overTerminal = isMouseOverTerminal((int)x, (int)y); if (overTerminal && shouldCaptureInput()) { - if (currentMouseMode.PrimaryMode == PrivateMode.X11MM && !currentMouseMode.isSecondaryModeEnabled(PrivateMode.SGR_MOUSE)) { - terminal.putInput('\033'); - terminal.putInput('['); - terminal.putInput('M'); - terminal.putInput((byte) (button + 32)); - terminal.putInput((byte) (position.x + 32)); - terminal.putInput((byte) (position.y + 32)); - return true; - } else if (currentMouseMode.PrimaryMode == PrivateMode.X11MM) { - terminal.putInput("\033[<" + button + ";" + position.x + ";" + position.y + "M"); - return true; + switch(currentMouseMode.PrimaryMode) { + case PrivateMode.X11MM, PrivateMode.CELL_MOTION_MOUSE -> { + if (currentMouseMode.isSecondaryModeEnabled(PrivateMode.SGR_MOUSE)) { + terminal.putInput("\033[<" + button + ";" + position.x + ";" + position.y + "M"); + return true; + } + else if (currentMouseMode.isSecondaryModeEnabled(PrivateMode.UTF8_MOUSE)) + { + byte[] csiMBytes = "\033[M".getBytes(StandardCharsets.UTF_8); + byte[] buttonBytes = utf8(button + 32); + byte[] colBytes = utf8(position.x + 32); + byte[] rowBytes = utf8(position.y + 32); + byte[] finalBytes = new byte[csiMBytes.length + buttonBytes.length + colBytes.length + rowBytes.length]; + + System.arraycopy(csiMBytes, 0, finalBytes, 0, csiMBytes.length); + System.arraycopy(buttonBytes, 0, finalBytes, csiMBytes.length, buttonBytes.length); + System.arraycopy(colBytes, 0, finalBytes, csiMBytes.length + buttonBytes.length, colBytes.length); + System.arraycopy(rowBytes, 0, finalBytes, csiMBytes.length + buttonBytes.length + colBytes.length, rowBytes.length); + + terminal.putInput(ByteBuffer.wrap(finalBytes)); + return true; + } + else if (currentMouseMode.isSecondaryModeEnabled(PrivateMode.URXVT_MOUSE)) + { + terminal.putInput("\033[" + (button + 32) + ";" + position.x + ";" + position.y + "M"); + } + else + { + terminal.putInput('\033'); + terminal.putInput('['); + terminal.putInput('M'); + terminal.putInput((byte) (button + 32)); + terminal.putInput((byte) (position.x + 32)); + terminal.putInput((byte) (position.y + 32)); + return true; + } + } + default -> System.out.println("ERR: Unsupported primary mode"); } } return false; @@ -173,22 +200,53 @@ public final class MachineTerminalWidget { Vector2i position = getMousePosition(x, y); boolean overTerminal = isMouseOverTerminal((int)x, (int)y); if (overTerminal && shouldCaptureInput()) { - if (currentMouseMode.PrimaryMode == PrivateMode.X11MM && !currentMouseMode.isSecondaryModeEnabled(PrivateMode.SGR_MOUSE)) { - terminal.putInput('\033'); - terminal.putInput('['); - terminal.putInput('M'); - terminal.putInput((byte) 35); - terminal.putInput((byte) (position.x + 32)); - terminal.putInput((byte) (position.y + 32)); - return true; - } else if (currentMouseMode.PrimaryMode == PrivateMode.X11MM) { - terminal.putInput("\033[<" + button + ";" + position.x + ";" + position.y + "m"); - return true; + switch(currentMouseMode.PrimaryMode) { + case PrivateMode.X11MM, PrivateMode.CELL_MOTION_MOUSE -> { + if (currentMouseMode.isSecondaryModeEnabled(PrivateMode.SGR_MOUSE)) { + terminal.putInput("\033[<" + button + ";" + position.x + ";" + position.y + "m"); + return true; + } + else if (currentMouseMode.isSecondaryModeEnabled(PrivateMode.UTF8_MOUSE)) + { + byte[] csiMBytes = "\033[M".getBytes(StandardCharsets.UTF_8); + byte[] buttonBytes = utf8(35); + byte[] colBytes = utf8(position.x + 32); + byte[] rowBytes = utf8(position.y + 32); + byte[] finalBytes = new byte[csiMBytes.length + buttonBytes.length + colBytes.length + rowBytes.length]; + + System.arraycopy(csiMBytes, 0, finalBytes, 0, csiMBytes.length); + System.arraycopy(buttonBytes, 0, finalBytes, csiMBytes.length, buttonBytes.length); + System.arraycopy(colBytes, 0, finalBytes, csiMBytes.length + buttonBytes.length, colBytes.length); + System.arraycopy(rowBytes, 0, finalBytes, csiMBytes.length + buttonBytes.length + colBytes.length, rowBytes.length); + + terminal.putInput(ByteBuffer.wrap(finalBytes)); + return true; + } + else if (currentMouseMode.isSecondaryModeEnabled(PrivateMode.URXVT_MOUSE)) + { + terminal.putInput("\033[" + 35 + ";" + position.x + ";" + position.y + "M"); + } + else + { + terminal.putInput('\033'); + terminal.putInput('['); + terminal.putInput('M'); + terminal.putInput((byte) 35); + terminal.putInput((byte) (position.x + 32)); + terminal.putInput((byte) (position.y + 32)); + return true; + } + } + default -> System.out.println("ERR: Unsupported primary mode"); } } return false; } + private byte[] utf8(int value) { + return new String(new int[]{value}, 0, 1).getBytes(StandardCharsets.UTF_8); + } + private Vector2i getMousePosition(double x, double y) { int tx = TERMINAL_WIDTH / Terminal.WIDTH; int ty = TERMINAL_HEIGHT / Terminal.HEIGHT; diff --git a/src/main/java/li/cil/oc2/common/vm/terminal/ImplementedPrivateModes.java b/src/main/java/li/cil/oc2/common/vm/terminal/ImplementedPrivateModes.java index 536d6330..43619bb6 100644 --- a/src/main/java/li/cil/oc2/common/vm/terminal/ImplementedPrivateModes.java +++ b/src/main/java/li/cil/oc2/common/vm/terminal/ImplementedPrivateModes.java @@ -42,16 +42,16 @@ public class ImplementedPrivateModes { modeStatus.put(96, false); // DECNCSM; modeStatus.put(1000, true); // X11MM; modeStatus.put(1001, false); // HILITE_MOUSE; - modeStatus.put(1002, false); // CELL_MOTION_MOUSE; + modeStatus.put(1002, true); // CELL_MOTION_MOUSE; -- PARTIAL SUPPORT: Treated as X11MM currently, no motion events are sent. This actually seems to match Window Terminal's level of implementation. modeStatus.put(1003, false); // ALL_MOTION_MOUSE_TRACKING; modeStatus.put(1004, true); // FOCUS_IN_FOCUS_OUT; - modeStatus.put(1005, false); // UTF8_MOUSE; + modeStatus.put(1005, true); // UTF8_MOUSE; modeStatus.put(1006, true); // SGR_MOUSE; modeStatus.put(1007, false); // ALTERNATE_SCROLL_MODE; modeStatus.put(1010, false); // SCROLL_BOTTOM_ON_OUTPUT; modeStatus.put(1011, false); // SCROLL_BOTTOM_ON_KEY_PRESS; modeStatus.put(1014, false); // FAST_SCROLL; - modeStatus.put(1015, false); // URXVT_MOUSE; + modeStatus.put(1015, true); // URXVT_MOUSE; modeStatus.put(1016, false); // SGR_MOUSE_PIXEL; modeStatus.put(1034, false); // META_KEY; modeStatus.put(1035, false); // SPECIAL_MODIFIERS; @@ -80,6 +80,7 @@ public class ImplementedPrivateModes { modeStatus.put(2004, true); // SET_BRACKETED_PASTE; modeStatus.put(2005, false); // ENABLE_READLINE_CHAR_QUOTE; modeStatus.put(2006, false); // ENABLE_READLINE_NEWLINE_PASTE; + modeStatus.put(2026, true); // APPLICATION_SYNC; } public void modeUsed(int mode, boolean state) { diff --git a/src/main/java/li/cil/oc2/common/vm/terminal/PrivateMode.java b/src/main/java/li/cil/oc2/common/vm/terminal/PrivateMode.java index 6d963d57..c2597f82 100644 --- a/src/main/java/li/cil/oc2/common/vm/terminal/PrivateMode.java +++ b/src/main/java/li/cil/oc2/common/vm/terminal/PrivateMode.java @@ -74,4 +74,5 @@ public final class PrivateMode { public static final int SET_BRACKETED_PASTE = 2004; // Set bracketed paste mode public static final int ENABLE_READLINE_CHAR_QUOTE = 2005; // Enable readline character quoting public static final int ENABLE_READLINE_NEWLINE_PASTE = 2006; // Enable readline newline pasting + public static final int APPLICATION_SYNC = 2026; // Wait to render until application is ready } diff --git a/src/main/java/li/cil/oc2/common/vm/terminal/PrivateModeState.java b/src/main/java/li/cil/oc2/common/vm/terminal/PrivateModeState.java index 06a5f41c..20184002 100644 --- a/src/main/java/li/cil/oc2/common/vm/terminal/PrivateModeState.java +++ b/src/main/java/li/cil/oc2/common/vm/terminal/PrivateModeState.java @@ -79,6 +79,7 @@ public class PrivateModeState { public boolean SET_BRACKETED_PASTE = false; public boolean ENABLE_READLINE_CHAR_QUOTE = false; public boolean ENABLE_READLINE_NEWLINE_PASTE = false; + public boolean APPLICATION_SYNC = false; public boolean getMode(int mode) { return switch (mode) { @@ -154,6 +155,7 @@ public class PrivateModeState { case 2004 -> SET_BRACKETED_PASTE; case 2005 -> ENABLE_READLINE_CHAR_QUOTE; case 2006 -> ENABLE_READLINE_NEWLINE_PASTE; + case 2026 -> APPLICATION_SYNC; default -> throw new IndexOutOfBoundsException(); }; } diff --git a/src/main/java/li/cil/oc2/common/vm/terminal/Terminal.java b/src/main/java/li/cil/oc2/common/vm/terminal/Terminal.java index 58b7be2c..fce23e9c 100644 --- a/src/main/java/li/cil/oc2/common/vm/terminal/Terminal.java +++ b/src/main/java/li/cil/oc2/common/vm/terminal/Terminal.java @@ -425,23 +425,17 @@ public class Terminal { } } - public static boolean isPrintableCharacter(final int ch) { - return ch == 0 || - (ch > ' ' && ch <= '~') || - ch >= 177; - } - public void putOutput(final byte value) { final char ch = (char) value; if (!continuationByte && (ch & (1 << 7)) != 0) { continuationByte = true; + bytesToRead = 0; + bytesRead = 0; unicode = 0; if ((ch & (1 << 6)) != 0) { bytesToRead++; } else { continuationByte = false; - bytesToRead = 0; - bytesRead = 0; System.out.println("ERR: Invalid first byte received"); return; } @@ -862,12 +856,6 @@ public class Terminal { // Renderer @OnlyIn(Dist.CLIENT) public static final class Renderer implements RendererModel, RendererView { - public static final ResourceLocation LOCATION_FONT_TEXTURE = new ResourceLocation(API.MOD_ID, "textures/font/terminus.png"); - public static final int TEXTURE_RESOLUTION = 256; - public static final float ONE_OVER_TEXTURE_RESOLUTION = 1.0f / (float) TEXTURE_RESOLUTION; - public static final int TEXTURE_COLUMNS = 16; - public static final int TEXTURE_BOLD_SHIFT = TEXTURE_COLUMNS; // Bold chars are in right half of texture. - public static final int[] COLORS = { 0x555555, // Black 0xEE3322, // Red @@ -942,15 +930,16 @@ public class Terminal { @Override public void render(final PoseStack stack, final Matrix4f projectionMatrix) { + if (terminal.currentPrivateModeState.APPLICATION_SYNC) return; validateLineCache(); renderBuffer(stack, projectionMatrix); - boolean blink = switch (terminal.cursorMode) { - case 2, 4, 6 -> false; - default -> true; + boolean steady = switch (terminal.cursorMode) { + case 2, 4, 6 -> true; + default -> false; }; - if (!blink || (System.currentTimeMillis() + terminal.hashCode()) % 1000 > 500) { + if (steady || (System.currentTimeMillis() + terminal.hashCode()) % 1000 > 500) { renderCursor(stack); } } @@ -1062,9 +1051,7 @@ public class Terminal { final int[] palette = (style & STYLE_DIM_MASK) != 0 ? DIM_COLORS : COLORS; int background = switch (color.Mode) { case SIXTEEN_COLOR -> palette[!invertBackground ? color.G : color.R]; - case TWO_FIFTY_SIX_COLOR -> { - yield COLORS_256[!invertBackground ? color.G : color.R]; - } + case TWO_FIFTY_SIX_COLOR -> COLORS_256[!invertBackground ? color.G : color.R]; case TRUE_COLOR -> color.ToInt(); }; @@ -1143,13 +1130,10 @@ public class Terminal { } if ((style & STYLE_UNDERLINE_MASK) != 0) { - final float ulu = (TEXTURE_RESOLUTION - 1) / (float) TEXTURE_RESOLUTION; - final float ulv = 1 / (float) TEXTURE_RESOLUTION; - - buffer.vertex(matrix, offset, CHAR_HEIGHT - 3, 0).color(r, g, b, 1).uv(ulu, ulv).endVertex(); - buffer.vertex(matrix, offset + CHAR_WIDTH, CHAR_HEIGHT - 3, 0).color(r, g, b, 1).uv(ulu, ulv).endVertex(); - buffer.vertex(matrix, offset + CHAR_WIDTH, CHAR_HEIGHT - 2, 0).color(r, g, b, 1).uv(ulu, ulv).endVertex(); - buffer.vertex(matrix, offset, CHAR_HEIGHT - 2, 0).color(r, g, b, 1).uv(ulu, ulv).endVertex(); + buffer.vertex(matrix, offset, CHAR_HEIGHT - 3, 0).color(r, g, b, 1).uv(0, 0).endVertex(); + buffer.vertex(matrix, offset + CHAR_WIDTH, CHAR_HEIGHT - 3, 0).color(r, g, b, 1).uv(0, 0).endVertex(); + buffer.vertex(matrix, offset + CHAR_WIDTH, CHAR_HEIGHT - 2, 0).color(r, g, b, 1).uv(0, 0).endVertex(); + buffer.vertex(matrix, offset, CHAR_HEIGHT - 2, 0).color(r, g, b, 1).uv(0, 0).endVertex(); } } diff --git a/src/main/java/li/cil/oc2/common/vm/terminal/escapes/CSI/RM.java b/src/main/java/li/cil/oc2/common/vm/terminal/escapes/CSI/RM.java index 14782bd7..13e2b66a 100644 --- a/src/main/java/li/cil/oc2/common/vm/terminal/escapes/CSI/RM.java +++ b/src/main/java/li/cil/oc2/common/vm/terminal/escapes/CSI/RM.java @@ -124,6 +124,7 @@ public class RM { case 2004 -> terminal.currentPrivateModeState.SET_BRACKETED_PASTE = false; case 2005 -> terminal.currentPrivateModeState.ENABLE_READLINE_CHAR_QUOTE = false; case 2006 -> terminal.currentPrivateModeState.ENABLE_READLINE_NEWLINE_PASTE = false; + case 2026 -> terminal.currentPrivateModeState.APPLICATION_SYNC = false; } ImplementedPrivateModes.instance.modeUsed(args[i], false); diff --git a/src/main/java/li/cil/oc2/common/vm/terminal/escapes/CSI/SM.java b/src/main/java/li/cil/oc2/common/vm/terminal/escapes/CSI/SM.java index a6fba141..fefbf548 100644 --- a/src/main/java/li/cil/oc2/common/vm/terminal/escapes/CSI/SM.java +++ b/src/main/java/li/cil/oc2/common/vm/terminal/escapes/CSI/SM.java @@ -18,10 +18,7 @@ public class SM { public static void executeDECSET(Terminal terminal, int[] args, int argCount) { for (int i = 0; i < argCount; i++) { switch (args[i]) { - case 1 -> { - System.out.println("Enabled DECCKM"); - terminal.currentPrivateModeState.DECCKM = true; - } + case 1 -> terminal.currentPrivateModeState.DECCKM = true; case 2 -> terminal.currentPrivateModeState.DECANM = true; case 3 -> terminal.currentPrivateModeState.DECCOLM = true; case 4 -> terminal.currentPrivateModeState.DECSCLM = true; @@ -64,9 +61,6 @@ public class SM { case 45 -> terminal.currentPrivateModeState.XTREVWRAP = true; case 46 -> terminal.currentPrivateModeState.XTLOGGING = true; case 47 -> { - if (true) { - break; - } terminal.clearAlt(); terminal.setCursorPos(0, 0); terminal.currentPrivateModeState.ALT_BUFFER = true; @@ -102,14 +96,34 @@ public class SM { terminal.currentPrivateModeState.ALL_MOTION_MOUSE_TRACKING = true; } case 1004 -> terminal.currentPrivateModeState.FOCUS_IN_FOCUS_OUT = true; - case 1005 -> terminal.currentPrivateModeState.UTF8_MOUSE = true; - case 1006 -> terminal.currentPrivateModeState.SGR_MOUSE = true; + case 1005 -> { + terminal.currentPrivateModeState.SGR_MOUSE = false; + terminal.currentPrivateModeState.URXVT_MOUSE = false; + terminal.currentPrivateModeState.SGR_MOUSE_PIXEL = false; + terminal.currentPrivateModeState.UTF8_MOUSE = true; + } + case 1006 -> { + terminal.currentPrivateModeState.UTF8_MOUSE = false; + terminal.currentPrivateModeState.URXVT_MOUSE = false; + terminal.currentPrivateModeState.SGR_MOUSE_PIXEL = false; + terminal.currentPrivateModeState.SGR_MOUSE = true; + } case 1007 -> terminal.currentPrivateModeState.ALTERNATE_SCROLL_MODE = true; case 1010 -> terminal.currentPrivateModeState.SCROLL_BOTTOM_ON_OUTPUT = true; case 1011 -> terminal.currentPrivateModeState.SCROLL_BOTTOM_ON_KEY_PRESS = true; case 1014 -> terminal.currentPrivateModeState.FAST_SCROLL = true; - case 1015 -> terminal.currentPrivateModeState.URXVT_MOUSE = true; - case 1016 -> terminal.currentPrivateModeState.SGR_MOUSE_PIXEL = true; + case 1015 -> { + terminal.currentPrivateModeState.UTF8_MOUSE = false; + terminal.currentPrivateModeState.SGR_MOUSE = false; + terminal.currentPrivateModeState.SGR_MOUSE_PIXEL = false; + terminal.currentPrivateModeState.URXVT_MOUSE = true; + } + case 1016 -> { + terminal.currentPrivateModeState.UTF8_MOUSE = false; + terminal.currentPrivateModeState.SGR_MOUSE = false; + terminal.currentPrivateModeState.URXVT_MOUSE = false; + terminal.currentPrivateModeState.SGR_MOUSE_PIXEL = true; + } case 1034 -> terminal.currentPrivateModeState.META_KEY = true; case 1035 -> terminal.currentPrivateModeState.SPECIAL_MODIFIERS = true; case 1036 -> terminal.currentPrivateModeState.META_SENDS_ESCAPE = true; @@ -123,9 +137,6 @@ public class SM { case 1045 -> terminal.currentPrivateModeState.EXT_REV_WRAP = true; case 1046 -> terminal.currentPrivateModeState.ALLOW_ALT_BUFFER = true; case 1047 -> { - if (true) { - break; - } terminal.clearAlt(); terminal.setCursorPos(0, 0); terminal.currentPrivateModeState.SWITCH_ALT_BUFFER = true; @@ -137,15 +148,11 @@ public class SM { terminal.renderers.forEach(model -> model.getDirtyMask().accumulateAndGet(finalDirtyLinesMask, (left, right) -> left | right)); } case 1048 -> { - System.out.println("SAVE"); terminal.savedX = terminal.x; terminal.savedY = terminal.y; terminal.currentPrivateModeState.SAVE_CURSOR = true; } case 1049 -> { - if (true) { - break; - } terminal.savedX = terminal.x; terminal.savedY = terminal.y; terminal.clearAlt(); diff --git a/src/main/java/li/cil/oc2/common/vm/terminal/escapes/DECRC.java b/src/main/java/li/cil/oc2/common/vm/terminal/escapes/DECRC.java index 7ffb8ff8..225f3407 100644 --- a/src/main/java/li/cil/oc2/common/vm/terminal/escapes/DECRC.java +++ b/src/main/java/li/cil/oc2/common/vm/terminal/escapes/DECRC.java @@ -4,7 +4,6 @@ import li.cil.oc2.common.vm.terminal.Terminal; public class DECRC { public static void execute(Terminal terminal) { - System.out.print("Restored cursor"); terminal.x = terminal.savedX; terminal.y = terminal.savedY; } diff --git a/src/main/java/li/cil/oc2/common/vm/terminal/escapes/DECSC.java b/src/main/java/li/cil/oc2/common/vm/terminal/escapes/DECSC.java index 33e49c2f..4b4f450c 100644 --- a/src/main/java/li/cil/oc2/common/vm/terminal/escapes/DECSC.java +++ b/src/main/java/li/cil/oc2/common/vm/terminal/escapes/DECSC.java @@ -4,7 +4,6 @@ import li.cil.oc2.common.vm.terminal.Terminal; public class DECSC { public static void execute(Terminal terminal) { - System.out.print("Saved cursor"); terminal.savedX = terminal.x; terminal.savedY = terminal.y; }