From 7ef81a0116399a6ce44304148bccf85d6192629d Mon Sep 17 00:00:00 2001 From: hstyi Date: Sun, 26 Jan 2025 14:31:46 +0800 Subject: [PATCH] feat: xterm DCS --- .../terminal/DeviceControlProcessor.kt | 36 --------------- .../terminal/DeviceControlStringProcessor.kt | 46 +++++++++++++++++++ .../terminal/EscapeSequenceProcessor.kt | 4 +- .../OperatingSystemCommandProcessor.kt | 14 ++---- .../termora/terminal/SystemCommandSequence.kt | 37 +++++++++++++++ .../app/termora/terminal/VisualTerminal.kt | 2 +- 6 files changed, 90 insertions(+), 49 deletions(-) delete mode 100644 src/main/kotlin/app/termora/terminal/DeviceControlProcessor.kt create mode 100644 src/main/kotlin/app/termora/terminal/DeviceControlStringProcessor.kt create mode 100644 src/main/kotlin/app/termora/terminal/SystemCommandSequence.kt diff --git a/src/main/kotlin/app/termora/terminal/DeviceControlProcessor.kt b/src/main/kotlin/app/termora/terminal/DeviceControlProcessor.kt deleted file mode 100644 index 0232896..0000000 --- a/src/main/kotlin/app/termora/terminal/DeviceControlProcessor.kt +++ /dev/null @@ -1,36 +0,0 @@ -package app.termora.terminal - -import org.slf4j.LoggerFactory - -class DeviceControlProcessor(private val terminal: Terminal) : Processor { - private val args = StringBuilder() - - companion object { - private val log = LoggerFactory.getLogger(DeviceControlProcessor::class.java) - } - - - override fun process(ch: Char): ProcessorState { - val state = when (ch) { - ControlCharacters.ST -> { - if (log.isWarnEnabled) { - log.warn("Ignore DCS: {}", args) - } - TerminalState.READY - } - - else -> { - args.append(ch) - TerminalState.DCS - } - } - - if (state == TerminalState.READY) { - args.clear() - } - - return state - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/app/termora/terminal/DeviceControlStringProcessor.kt b/src/main/kotlin/app/termora/terminal/DeviceControlStringProcessor.kt new file mode 100644 index 0000000..b70bb97 --- /dev/null +++ b/src/main/kotlin/app/termora/terminal/DeviceControlStringProcessor.kt @@ -0,0 +1,46 @@ +package app.termora.terminal + +import org.slf4j.LoggerFactory + +class DeviceControlStringProcessor(terminal: Terminal, reader: TerminalReader) : AbstractProcessor(terminal, reader) { + + companion object { + private val log = LoggerFactory.getLogger(DeviceControlStringProcessor::class.java) + } + + private val systemCommandSequence = SystemCommandSequence() + + override fun process(ch: Char): ProcessorState { + // 回退回去,然后重新读取出来 + reader.addFirst(ch) + + do { + + if (systemCommandSequence.process(reader.read())) { + break + } + + // 如果没有检测到结束,那么退出重新来 + if (reader.isEmpty()) { + return TerminalState.DCS + } + + } while (reader.isNotEmpty()) + + processCommand(systemCommandSequence.getCommand()) + + systemCommandSequence.reset() + + return TerminalState.READY + } + + + private fun processCommand(command: String) { + if (command.isEmpty()) { + return + } + if (log.isWarnEnabled) { + log.warn("Cannot process command: {}", command) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/termora/terminal/EscapeSequenceProcessor.kt b/src/main/kotlin/app/termora/terminal/EscapeSequenceProcessor.kt index 3f60508..ae35823 100644 --- a/src/main/kotlin/app/termora/terminal/EscapeSequenceProcessor.kt +++ b/src/main/kotlin/app/termora/terminal/EscapeSequenceProcessor.kt @@ -128,9 +128,9 @@ class EscapeSequenceProcessor(terminal: Terminal, reader: TerminalReader) : Abst } - // TODO Device Control String (DCS is 0x90). + // Device Control String (DCS is 0x90). 'P' -> { - + state = TerminalState.DCS } // Start of Guarded Area (SPA is 0x96). diff --git a/src/main/kotlin/app/termora/terminal/OperatingSystemCommandProcessor.kt b/src/main/kotlin/app/termora/terminal/OperatingSystemCommandProcessor.kt index b5316b2..ecde9ff 100644 --- a/src/main/kotlin/app/termora/terminal/OperatingSystemCommandProcessor.kt +++ b/src/main/kotlin/app/termora/terminal/OperatingSystemCommandProcessor.kt @@ -7,7 +7,7 @@ import java.awt.datatransfer.StringSelection class OperatingSystemCommandProcessor(terminal: Terminal, reader: TerminalReader) : AbstractProcessor(terminal, reader) { - private val args = StringBuilder() + private val systemCommandSequence = SystemCommandSequence() private val colorPalette get() = terminal.getTerminalModel().getColorPalette() companion object { @@ -20,14 +20,7 @@ class OperatingSystemCommandProcessor(terminal: Terminal, reader: TerminalReader do { - val c = reader.read() - args.append(c) - if (c == ControlCharacters.BEL || c == ControlCharacters.ST) { - args.deleteAt(args.lastIndex) - break - } else if (c == '\\' && args.length >= 2 && args[args.length - 2] == ControlCharacters.ESC) { - args.deleteAt(args.lastIndex) - args.deleteAt(args.lastIndex) + if (systemCommandSequence.process(reader.read())) { break } @@ -42,7 +35,7 @@ class OperatingSystemCommandProcessor(terminal: Terminal, reader: TerminalReader // process osc processOperatingSystemCommandProcessor() - args.clear() + systemCommandSequence.reset() return TerminalState.READY } @@ -52,6 +45,7 @@ class OperatingSystemCommandProcessor(terminal: Terminal, reader: TerminalReader * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands */ private fun processOperatingSystemCommandProcessor() { + val args = systemCommandSequence.getCommand() val idx = args.indexOfFirst { it == ';' } if (idx == -1) { return diff --git a/src/main/kotlin/app/termora/terminal/SystemCommandSequence.kt b/src/main/kotlin/app/termora/terminal/SystemCommandSequence.kt new file mode 100644 index 0000000..b87a1bb --- /dev/null +++ b/src/main/kotlin/app/termora/terminal/SystemCommandSequence.kt @@ -0,0 +1,37 @@ +package app.termora.terminal + +class SystemCommandSequence { + private var isTerminated = false + private val command = StringBuilder() + + /** + * @return 返回 true 表示处理完毕 + */ + fun process(c: Char): Boolean { + + if (isTerminated) { + throw UnsupportedOperationException("Cannot be processed, call the reset method") + } + + command.append(c) + if (c == ControlCharacters.BEL || c == ControlCharacters.ST) { + command.deleteAt(command.lastIndex) + isTerminated = true + } else if (c == '\\' && command.length >= 2 && command[command.length - 2] == ControlCharacters.ESC) { + command.deleteAt(command.lastIndex) + command.deleteAt(command.lastIndex) + isTerminated = true + } + + return isTerminated + } + + fun getCommand(): String { + return command.toString() + } + + fun reset() { + isTerminated = false + command.clear() + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/termora/terminal/VisualTerminal.kt b/src/main/kotlin/app/termora/terminal/VisualTerminal.kt index d24d89f..07fdaa4 100644 --- a/src/main/kotlin/app/termora/terminal/VisualTerminal.kt +++ b/src/main/kotlin/app/termora/terminal/VisualTerminal.kt @@ -129,7 +129,7 @@ private class MyProcessor(private val terminal: Terminal, reader: TerminalReader TerminalState.CSI to ControlSequenceIntroducerProcessor(terminal, reader), TerminalState.OSC to OperatingSystemCommandProcessor(terminal, reader), TerminalState.ESC_LPAREN to EscapeDesignateCharacterSetProcessor(terminal, reader), - TerminalState.DCS to DeviceControlProcessor(terminal), + TerminalState.DCS to DeviceControlStringProcessor(terminal, reader), TerminalState.Text to TextProcessor(terminal, reader), )