From b7178a30fb061d726bab8fb1acedfe8646b76054 Mon Sep 17 00:00:00 2001 From: hstyi Date: Sun, 6 Jul 2025 10:18:00 +0800 Subject: [PATCH] chore: telnet supports backspace key setting --- .../internal/telnet/TelnetHostOptionsPane.kt | 58 ++++++++++++++----- .../internal/telnet/TelnetTerminalTab.kt | 14 +++++ .../app/termora/terminal/KeyEncoderImpl.kt | 16 ++--- src/main/resources/i18n/messages.properties | 1 + .../resources/i18n/messages_zh_CN.properties | 1 + .../resources/i18n/messages_zh_TW.properties | 1 + 6 files changed, 67 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetHostOptionsPane.kt b/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetHostOptionsPane.kt index 540ffc9..b087510 100644 --- a/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetHostOptionsPane.kt +++ b/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetHostOptionsPane.kt @@ -19,14 +19,13 @@ import java.awt.event.ComponentEvent import java.nio.charset.Charset import javax.swing.* -@Suppress("CascadeIf") -open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPane() { +class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPane() { protected val generalOption = GeneralOption() // telnet 不支持代理密码 - protected val proxyOption = BasicProxyOption(authenticationTypes = listOf()) - protected val terminalOption = TerminalOption() - protected val owner: Window get() = SwingUtilities.getWindowAncestor(this) + private val proxyOption = BasicProxyOption(authenticationTypes = listOf()) + private val terminalOption = TerminalOption() + private val owner: Window get() = SwingUtilities.getWindowAncestor(this) init { addOption(generalOption) @@ -35,7 +34,7 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio } - open fun getHost(): Host { + fun getHost(): Host { val name = generalOption.nameTextField.text val protocol = TelnetProtocolProvider.PROTOCOL val host = generalOption.hostTextField.text @@ -70,6 +69,7 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio env = terminalOption.environmentTextArea.text, startupCommand = terminalOption.startupCommandTextField.text, serialComm = serialComm, + extras = mutableMapOf("backspace" to (terminalOption.backspaceComboBox.selectedItem as Backspace).name) ) return Host( @@ -106,6 +106,8 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio terminalOption.charsetComboBox.selectedItem = host.options.encoding terminalOption.environmentTextArea.text = host.options.env terminalOption.startupCommandTextField.text = host.options.startupCommand + terminalOption.backspaceComboBox.selectedItem = + Backspace.valueOf(host.options.extras["backspace"] ?: Backspace.Delete.name) } @@ -119,20 +121,13 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio return false } - if (StringUtils.equalsIgnoreCase(host.protocol, TelnetProtocolProvider.PROTOCOL)) { + if (host.authentication.type == AuthenticationType.Password) { if (validateField(generalOption.usernameTextField)) { return false } - } - - if (host.authentication.type == AuthenticationType.Password) { if (validateField(generalOption.passwordTextField)) { return false } - } else if (host.authentication.type == AuthenticationType.PublicKey) { - if (validateField(generalOption.publicKeyComboBox)) { - return false - } } // proxy @@ -341,8 +336,9 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio } - protected inner class TerminalOption : JPanel(BorderLayout()), Option { + private inner class TerminalOption : JPanel(BorderLayout()), Option { val charsetComboBox = JComboBox() + val backspaceComboBox = JComboBox() val startupCommandTextField = OutlineTextField() val environmentTextArea = FixedLengthTextArea(2048) @@ -355,6 +351,10 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio private fun initView() { add(getCenterComponent(), BorderLayout.CENTER) + backspaceComboBox.addItem(Backspace.Delete) + backspaceComboBox.addItem(Backspace.Backspace) + backspaceComboBox.addItem(Backspace.VT220) + environmentTextArea.setFocusTraversalKeys( KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, @@ -399,7 +399,7 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio private fun getCenterComponent(): JComponent { val layout = FormLayout( "left:pref, $FORM_MARGIN, default:grow", - "pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref" + "pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref" ) var rows = 1 @@ -407,6 +407,8 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio val panel = FormBuilder.create().layout(layout) .add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows) .add(charsetComboBox).xy(3, rows).apply { rows += step } + .add("${I18n.getString("termora.new-host.terminal.backspace")}:").xy(1, rows) + .add(backspaceComboBox).xy(3, rows).apply { rows += step } .add("${I18n.getString("termora.new-host.terminal.startup-commands")}:").xy(1, rows) .add(startupCommandTextField).xy(3, rows).apply { rows += step } .add("${I18n.getString("termora.new-host.terminal.env")}:").xy(1, rows) @@ -419,4 +421,28 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio } } + enum class Backspace { + /** + * 0x08 + */ + Backspace, + + /** + * 0x7F 默认 + */ + Delete, + + /** + * ESC[3~ + */ + VT220, ; + + override fun toString(): String { + return when (this) { + Backspace -> "ASCII Backspace (0x08)" + Delete -> "ASCII Delete (0x7F)" + VT220 -> "VT220 Delete (ESC[3~)" + } + } + } } \ No newline at end of file diff --git a/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetTerminalTab.kt b/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetTerminalTab.kt index c5f7f41..7f1965f 100644 --- a/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetTerminalTab.kt +++ b/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetTerminalTab.kt @@ -1,8 +1,12 @@ package app.termora.plugin.internal.telnet import app.termora.* +import app.termora.terminal.ControlCharacters +import app.termora.terminal.KeyEncoderImpl import app.termora.terminal.PtyConnector +import app.termora.terminal.TerminalKeyEvent import org.apache.commons.net.telnet.* +import java.awt.event.KeyEvent import java.net.InetSocketAddress import java.net.Proxy import java.nio.charset.Charset @@ -42,6 +46,16 @@ class TelnetTerminalTab( telnet.connect(host.host, host.port) telnet.keepAlive = true + val encoder = terminal.getKeyEncoder() + if (encoder is KeyEncoderImpl) { + val backspace = host.options.extras["backspace"] + if (backspace == TelnetHostOptionsPane.Backspace.Backspace.name) { + encoder.putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_BACK_SPACE), String(byteArrayOf(0x08))) + } else if (backspace == TelnetHostOptionsPane.Backspace.VT220.name) { + encoder.putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_BACK_SPACE), "${ControlCharacters.ESC}[3~") + } + } + return ptyConnectorFactory.decorate(TelnetStreamPtyConnector(telnet, telnet.charset)) } diff --git a/src/main/kotlin/app/termora/terminal/KeyEncoderImpl.kt b/src/main/kotlin/app/termora/terminal/KeyEncoderImpl.kt index 9f5949f..e174021 100644 --- a/src/main/kotlin/app/termora/terminal/KeyEncoderImpl.kt +++ b/src/main/kotlin/app/termora/terminal/KeyEncoderImpl.kt @@ -28,8 +28,8 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList configureLeftRight() - // Ctrl + C - putCode(TerminalKeyEvent(keyCode = 8), String(byteArrayOf(127))) + // Ctrl + C: 0x7F ASCII Delete + putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_BACK_SPACE), String(byteArrayOf(0x7F))) // Enter if (terminalModel.getData(DataKey.AutoNewline, false)) { @@ -113,7 +113,7 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList return terminal } - private fun putCode(event: TerminalKeyEvent, encode: String) { + internal fun putCode(event: TerminalKeyEvent, encode: String) { mapping[event] = encode } @@ -202,7 +202,7 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList || key == KeyEvent.VK_PAGE_UP || key == KeyEvent.VK_PAGE_DOWN } - fun arrowKeysApplicationSequences() { + private fun arrowKeysApplicationSequences() { // Up putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_UP), encode = "${ControlCharacters.ESC}OA") // Down @@ -213,7 +213,7 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_RIGHT), encode = "${ControlCharacters.ESC}OC") } - fun arrowKeysAnsiCursorSequences() { + private fun arrowKeysAnsiCursorSequences() { // Up putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_UP), encode = "${ControlCharacters.ESC}[A") // Down @@ -227,7 +227,7 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList /** * Alt + Left/Right */ - fun configureLeftRight() { + private fun configureLeftRight() { if (SystemInfo.isMacOS) { putCode( TerminalKeyEvent(keyCode = KeyEvent.VK_LEFT, TerminalEvent.ALT_MASK), @@ -262,7 +262,7 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList } - fun keypadApplicationSequences() { + private fun keypadApplicationSequences() { // Up putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_KP_UP), encode = "${ControlCharacters.ESC}OA") // Down @@ -277,7 +277,7 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_END), encode = "${ControlCharacters.ESC}OF") } - fun keypadAnsiSequences() { + private fun keypadAnsiSequences() { // Up putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_KP_UP), encode = "${ControlCharacters.ESC}[A") // Down diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index 96ca7f0..9373a55 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -197,6 +197,7 @@ termora.new-host.proxy=Proxy termora.new-host.terminal=${termora.settings.terminal} termora.new-host.terminal.encoding=Encoding +termora.new-host.terminal.backspace=Backspace termora.new-host.terminal.heartbeat-interval=Heartbeat Interval termora.new-host.terminal.startup-commands=Startup Command termora.new-host.terminal.env=Environment diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties index b550358..03b8f55 100644 --- a/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -188,6 +188,7 @@ termora.new-host.proxy=代理 termora.new-host.terminal=${termora.settings.terminal} termora.new-host.terminal.encoding=编码 +termora.new-host.terminal.backspace=退格键 termora.new-host.terminal.heartbeat-interval=心跳间隔 termora.new-host.terminal.startup-commands=启动命令 termora.new-host.terminal.env=环境 diff --git a/src/main/resources/i18n/messages_zh_TW.properties b/src/main/resources/i18n/messages_zh_TW.properties index 7bf13a2..f4cfa84 100644 --- a/src/main/resources/i18n/messages_zh_TW.properties +++ b/src/main/resources/i18n/messages_zh_TW.properties @@ -187,6 +187,7 @@ termora.new-host.proxy=代理 termora.new-host.terminal=${termora.settings.terminal} termora.new-host.terminal.encoding=編碼 +termora.new-host.terminal.backspace=退格鍵 termora.new-host.terminal.startup-commands=啟動命令 termora.new-host.terminal.heartbeat-interval=心跳間隔 termora.new-host.terminal.env=環境