chore: telnet supports backspace key setting

This commit is contained in:
hstyi
2025-07-06 10:18:00 +08:00
committed by hstyi
parent 939d6a1fd7
commit b7178a30fb
6 changed files with 67 additions and 24 deletions

View File

@@ -19,14 +19,13 @@ import java.awt.event.ComponentEvent
import java.nio.charset.Charset import java.nio.charset.Charset
import javax.swing.* import javax.swing.*
@Suppress("CascadeIf") class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPane() {
open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPane() {
protected val generalOption = GeneralOption() protected val generalOption = GeneralOption()
// telnet 不支持代理密码 // telnet 不支持代理密码
protected val proxyOption = BasicProxyOption(authenticationTypes = listOf()) private val proxyOption = BasicProxyOption(authenticationTypes = listOf())
protected val terminalOption = TerminalOption() private val terminalOption = TerminalOption()
protected val owner: Window get() = SwingUtilities.getWindowAncestor(this) private val owner: Window get() = SwingUtilities.getWindowAncestor(this)
init { init {
addOption(generalOption) 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 name = generalOption.nameTextField.text
val protocol = TelnetProtocolProvider.PROTOCOL val protocol = TelnetProtocolProvider.PROTOCOL
val host = generalOption.hostTextField.text val host = generalOption.hostTextField.text
@@ -70,6 +69,7 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio
env = terminalOption.environmentTextArea.text, env = terminalOption.environmentTextArea.text,
startupCommand = terminalOption.startupCommandTextField.text, startupCommand = terminalOption.startupCommandTextField.text,
serialComm = serialComm, serialComm = serialComm,
extras = mutableMapOf("backspace" to (terminalOption.backspaceComboBox.selectedItem as Backspace).name)
) )
return Host( return Host(
@@ -106,6 +106,8 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio
terminalOption.charsetComboBox.selectedItem = host.options.encoding terminalOption.charsetComboBox.selectedItem = host.options.encoding
terminalOption.environmentTextArea.text = host.options.env terminalOption.environmentTextArea.text = host.options.env
terminalOption.startupCommandTextField.text = host.options.startupCommand 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 return false
} }
if (StringUtils.equalsIgnoreCase(host.protocol, TelnetProtocolProvider.PROTOCOL)) { if (host.authentication.type == AuthenticationType.Password) {
if (validateField(generalOption.usernameTextField)) { if (validateField(generalOption.usernameTextField)) {
return false return false
} }
}
if (host.authentication.type == AuthenticationType.Password) {
if (validateField(generalOption.passwordTextField)) { if (validateField(generalOption.passwordTextField)) {
return false return false
} }
} else if (host.authentication.type == AuthenticationType.PublicKey) {
if (validateField(generalOption.publicKeyComboBox)) {
return false
}
} }
// proxy // 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<String>() val charsetComboBox = JComboBox<String>()
val backspaceComboBox = JComboBox<Backspace>()
val startupCommandTextField = OutlineTextField() val startupCommandTextField = OutlineTextField()
val environmentTextArea = FixedLengthTextArea(2048) val environmentTextArea = FixedLengthTextArea(2048)
@@ -355,6 +351,10 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio
private fun initView() { private fun initView() {
add(getCenterComponent(), BorderLayout.CENTER) add(getCenterComponent(), BorderLayout.CENTER)
backspaceComboBox.addItem(Backspace.Delete)
backspaceComboBox.addItem(Backspace.Backspace)
backspaceComboBox.addItem(Backspace.VT220)
environmentTextArea.setFocusTraversalKeys( environmentTextArea.setFocusTraversalKeys(
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
@@ -399,7 +399,7 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio
private fun getCenterComponent(): JComponent { private fun getCenterComponent(): JComponent {
val layout = FormLayout( val layout = FormLayout(
"left:pref, $FORM_MARGIN, default:grow", "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 var rows = 1
@@ -407,6 +407,8 @@ open class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : Optio
val panel = FormBuilder.create().layout(layout) val panel = FormBuilder.create().layout(layout)
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows) .add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
.add(charsetComboBox).xy(3, rows).apply { rows += step } .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("${I18n.getString("termora.new-host.terminal.startup-commands")}:").xy(1, rows)
.add(startupCommandTextField).xy(3, rows).apply { rows += step } .add(startupCommandTextField).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.env")}:").xy(1, rows) .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~)"
}
}
}
} }

View File

@@ -1,8 +1,12 @@
package app.termora.plugin.internal.telnet package app.termora.plugin.internal.telnet
import app.termora.* import app.termora.*
import app.termora.terminal.ControlCharacters
import app.termora.terminal.KeyEncoderImpl
import app.termora.terminal.PtyConnector import app.termora.terminal.PtyConnector
import app.termora.terminal.TerminalKeyEvent
import org.apache.commons.net.telnet.* import org.apache.commons.net.telnet.*
import java.awt.event.KeyEvent
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.net.Proxy import java.net.Proxy
import java.nio.charset.Charset import java.nio.charset.Charset
@@ -42,6 +46,16 @@ class TelnetTerminalTab(
telnet.connect(host.host, host.port) telnet.connect(host.host, host.port)
telnet.keepAlive = true 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)) return ptyConnectorFactory.decorate(TelnetStreamPtyConnector(telnet, telnet.charset))
} }

View File

@@ -28,8 +28,8 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList
configureLeftRight() configureLeftRight()
// Ctrl + C // Ctrl + C: 0x7F ASCII Delete
putCode(TerminalKeyEvent(keyCode = 8), String(byteArrayOf(127))) putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_BACK_SPACE), String(byteArrayOf(0x7F)))
// Enter // Enter
if (terminalModel.getData(DataKey.AutoNewline, false)) { if (terminalModel.getData(DataKey.AutoNewline, false)) {
@@ -113,7 +113,7 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList
return terminal return terminal
} }
private fun putCode(event: TerminalKeyEvent, encode: String) { internal fun putCode(event: TerminalKeyEvent, encode: String) {
mapping[event] = encode 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 || key == KeyEvent.VK_PAGE_UP || key == KeyEvent.VK_PAGE_DOWN
} }
fun arrowKeysApplicationSequences() { private fun arrowKeysApplicationSequences() {
// Up // Up
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_UP), encode = "${ControlCharacters.ESC}OA") putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_UP), encode = "${ControlCharacters.ESC}OA")
// Down // Down
@@ -213,7 +213,7 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_RIGHT), encode = "${ControlCharacters.ESC}OC") putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_RIGHT), encode = "${ControlCharacters.ESC}OC")
} }
fun arrowKeysAnsiCursorSequences() { private fun arrowKeysAnsiCursorSequences() {
// Up // Up
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_UP), encode = "${ControlCharacters.ESC}[A") putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_UP), encode = "${ControlCharacters.ESC}[A")
// Down // Down
@@ -227,7 +227,7 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList
/** /**
* Alt + Left/Right * Alt + Left/Right
*/ */
fun configureLeftRight() { private fun configureLeftRight() {
if (SystemInfo.isMacOS) { if (SystemInfo.isMacOS) {
putCode( putCode(
TerminalKeyEvent(keyCode = KeyEvent.VK_LEFT, TerminalEvent.ALT_MASK), 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 // Up
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_KP_UP), encode = "${ControlCharacters.ESC}OA") putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_KP_UP), encode = "${ControlCharacters.ESC}OA")
// Down // Down
@@ -277,7 +277,7 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_END), encode = "${ControlCharacters.ESC}OF") putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_END), encode = "${ControlCharacters.ESC}OF")
} }
fun keypadAnsiSequences() { private fun keypadAnsiSequences() {
// Up // Up
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_KP_UP), encode = "${ControlCharacters.ESC}[A") putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_KP_UP), encode = "${ControlCharacters.ESC}[A")
// Down // Down

View File

@@ -197,6 +197,7 @@ termora.new-host.proxy=Proxy
termora.new-host.terminal=${termora.settings.terminal} termora.new-host.terminal=${termora.settings.terminal}
termora.new-host.terminal.encoding=Encoding termora.new-host.terminal.encoding=Encoding
termora.new-host.terminal.backspace=Backspace
termora.new-host.terminal.heartbeat-interval=Heartbeat Interval termora.new-host.terminal.heartbeat-interval=Heartbeat Interval
termora.new-host.terminal.startup-commands=Startup Command termora.new-host.terminal.startup-commands=Startup Command
termora.new-host.terminal.env=Environment termora.new-host.terminal.env=Environment

View File

@@ -188,6 +188,7 @@ termora.new-host.proxy=代理
termora.new-host.terminal=${termora.settings.terminal} termora.new-host.terminal=${termora.settings.terminal}
termora.new-host.terminal.encoding=编码 termora.new-host.terminal.encoding=编码
termora.new-host.terminal.backspace=退格键
termora.new-host.terminal.heartbeat-interval=心跳间隔 termora.new-host.terminal.heartbeat-interval=心跳间隔
termora.new-host.terminal.startup-commands=启动命令 termora.new-host.terminal.startup-commands=启动命令
termora.new-host.terminal.env=环境 termora.new-host.terminal.env=环境

View File

@@ -187,6 +187,7 @@ termora.new-host.proxy=代理
termora.new-host.terminal=${termora.settings.terminal} termora.new-host.terminal=${termora.settings.terminal}
termora.new-host.terminal.encoding=編碼 termora.new-host.terminal.encoding=編碼
termora.new-host.terminal.backspace=退格鍵
termora.new-host.terminal.startup-commands=啟動命令 termora.new-host.terminal.startup-commands=啟動命令
termora.new-host.terminal.heartbeat-interval=心跳間隔 termora.new-host.terminal.heartbeat-interval=心跳間隔
termora.new-host.terminal.env=環境 termora.new-host.terminal.env=環境