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; }