From 0f123b5efdca5f277f2c15208b9241d3fb9ca8fa Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Wed, 12 Feb 2025 18:41:45 +0000 Subject: [PATCH] Ignore unrepresentable characters when typing In 94ad6dab0e5b8d9eb65467dd1b635d708ce58b53, we changed it so typing characters outside of CC's codepage were replaced with '?' rather than ignored. This can be quite annoying for non-European users (where latin1 isn't very helpful!), so it makes sense to revert this change. See discussion in #860 for more context. --- .../client/gui/widgets/TerminalWidget.java | 2 +- .../computercraft/core/util/StringUtil.java | 26 ++++++++++--------- .../cc/tweaked/standalone/InputState.java | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/TerminalWidget.java b/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/TerminalWidget.java index f57206205..2bf9a4494 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/TerminalWidget.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/widgets/TerminalWidget.java @@ -72,7 +72,7 @@ public class TerminalWidget extends AbstractWidget { @Override public boolean charTyped(char ch, int modifiers) { var terminalChar = StringUtil.unicodeToTerminal(ch); - if (StringUtil.isTypableChar(terminalChar)) computer.charTyped(terminalChar); + if (StringUtil.isTypableChar(terminalChar)) computer.charTyped((byte) terminalChar); return true; } diff --git a/projects/core/src/main/java/dan200/computercraft/core/util/StringUtil.java b/projects/core/src/main/java/dan200/computercraft/core/util/StringUtil.java index 27e443bc6..a3af9376c 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/util/StringUtil.java +++ b/projects/core/src/main/java/dan200/computercraft/core/util/StringUtil.java @@ -18,24 +18,25 @@ public final class StringUtil { * Convert a Unicode character to a terminal one. * * @param chr The Unicode character. - * @return The terminal character. + * @return The terminal character. This is either in the range [0, 255] (if a valid character) or {@code -1} if + * it cannot be mapped to CC's charset. */ - public static byte unicodeToTerminal(int chr) { + public static int unicodeToTerminal(int chr) { // ASCII and latin1 map to themselves if (chr == 0 || chr == '\t' || chr == '\n' || chr == '\r' || (chr >= ' ' && chr <= '~') || (chr >= 160 && chr <= 255)) { - return (byte) chr; + return chr; } // Teletext block mosaics are *fairly* contiguous. - if (chr >= 0x1FB00 && chr <= 0x1FB13) return (byte) (chr + (129 - 0x1fb00)); - if (chr >= 0x1FB14 && chr <= 0x1FB1D) return (byte) (chr + (150 - 0x1fb14)); + if (chr >= 0x1FB00 && chr <= 0x1FB13) return chr + (129 - 0x1fb00); + if (chr >= 0x1FB14 && chr <= 0x1FB1D) return chr + (150 - 0x1fb14); // Everything else is just a manual lookup. For now, we just use a big switch statement, which we spin into a // separate function to hopefully avoid inlining it here. return unicodeToCraftOsFallback(chr); } - private static byte unicodeToCraftOsFallback(int c) { + private static int unicodeToCraftOsFallback(int c) { return switch (c) { case 0x263A -> 1; case 0x263B -> 2; @@ -64,8 +65,8 @@ public final class StringUtil { case 0x25B2 -> 30; case 0x25BC -> 31; case 0x1FB99 -> 127; - case 0x258C -> (byte) 149; - default -> '?'; + case 0x258C -> 149; + default -> -1; }; } @@ -76,8 +77,8 @@ public final class StringUtil { * @param chr The character to check. * @return Whether this character can be typed. */ - public static boolean isTypableChar(byte chr) { - return chr != 0 && chr != '\r' && chr != '\n'; + public static boolean isTypableChar(int chr) { + return chr >= 0 && chr <= 255 && chr != 0 && chr != '\r' && chr != '\n'; } private static boolean isAllowedInLabel(char c) { @@ -110,8 +111,9 @@ public final class StringUtil { var iterator = clipboard.codePoints().iterator(); while (iterator.hasNext() && idx <= output.length) { var chr = unicodeToTerminal(iterator.next()); - if (!isTypableChar(chr)) break; - output[idx++] = chr; + if (chr < 0) continue; // Strip out unconvertible characters + if (!isTypableChar(chr)) break; // Stop at untypable ones. + output[idx++] = (byte) chr; } return ByteBuffer.wrap(output, 0, idx).asReadOnlyBuffer(); diff --git a/projects/standalone/src/main/java/cc/tweaked/standalone/InputState.java b/projects/standalone/src/main/java/cc/tweaked/standalone/InputState.java index 2013a1fb3..4adbfd3a9 100644 --- a/projects/standalone/src/main/java/cc/tweaked/standalone/InputState.java +++ b/projects/standalone/src/main/java/cc/tweaked/standalone/InputState.java @@ -51,7 +51,7 @@ public class InputState { public void onCharEvent(int codepoint) { var terminalChar = StringUtil.unicodeToTerminal(codepoint); - if (StringUtil.isTypableChar(terminalChar)) ComputerEvents.charTyped(computer, terminalChar); + if (StringUtil.isTypableChar(terminalChar)) ComputerEvents.charTyped(computer, (byte) terminalChar); } public void onKeyEvent(long window, int key, int action, int modifiers) {