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 b087510..d0d5bdd 100644 --- a/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetHostOptionsPane.kt +++ b/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetHostOptionsPane.kt @@ -2,18 +2,15 @@ package app.termora.plugin.internal.telnet import app.termora.* import app.termora.account.AccountOwner -import app.termora.keymgr.KeyManager import app.termora.plugin.internal.BasicProxyOption import com.formdev.flatlaf.FlatClientProperties import com.formdev.flatlaf.extras.components.FlatComboBox import com.formdev.flatlaf.ui.FlatTextBorder import com.jgoodies.forms.builder.FormBuilder import com.jgoodies.forms.layout.FormLayout -import org.apache.commons.lang3.StringUtils import java.awt.BorderLayout import java.awt.Component import java.awt.KeyboardFocusManager -import java.awt.Window import java.awt.event.ComponentAdapter import java.awt.event.ComponentEvent import java.nio.charset.Charset @@ -25,7 +22,6 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan // telnet 不支持代理密码 private val proxyOption = BasicProxyOption(authenticationTypes = listOf()) private val terminalOption = TerminalOption() - private val owner: Window get() = SwingUtilities.getWindowAncestor(this) init { addOption(generalOption) @@ -69,7 +65,10 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan env = terminalOption.environmentTextArea.text, startupCommand = terminalOption.startupCommandTextField.text, serialComm = serialComm, - extras = mutableMapOf("backspace" to (terminalOption.backspaceComboBox.selectedItem as Backspace).name) + extras = mutableMapOf( + "backspace" to (terminalOption.backspaceComboBox.selectedItem as Backspace).name, + "character-at-a-time" to (terminalOption.characterAtATimeTextField.selectedItem?.toString() ?: "false") + ) ) return Host( @@ -108,6 +107,8 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan terminalOption.startupCommandTextField.text = host.options.startupCommand terminalOption.backspaceComboBox.selectedItem = Backspace.valueOf(host.options.extras["backspace"] ?: Backspace.Delete.name) + terminalOption.characterAtATimeTextField.selectedItem = + host.options.extras["character-at-a-time"]?.toBooleanStrictOrNull() ?: false } @@ -186,7 +187,6 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan val usernameTextField = OutlineTextField(128) val hostTextField = OutlineTextField(255) val passwordTextField = OutlinePasswordField(255) - val publicKeyComboBox = OutlineComboBox() val remarkTextArea = FixedLengthTextArea(512) val authenticationTypeComboBox = FlatComboBox() @@ -198,30 +198,6 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan private fun initView() { add(getCenterComponent(), BorderLayout.CENTER) - publicKeyComboBox.isEditable = false - - publicKeyComboBox.renderer = object : DefaultListCellRenderer() { - override fun getListCellRendererComponent( - list: JList<*>?, - value: Any?, - index: Int, - isSelected: Boolean, - cellHasFocus: Boolean - ): Component { - var text = StringUtils.EMPTY - if (value is String) { - text = KeyManager.getInstance().getOhKeyPair(value)?.name ?: text - } - return super.getListCellRendererComponent( - list, - text, - index, - isSelected, - cellHasFocus - ) - } - } - authenticationTypeComboBox.renderer = object : DefaultListCellRenderer() { override fun getListCellRendererComponent( list: JList<*>?, @@ -340,6 +316,7 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan val charsetComboBox = JComboBox() val backspaceComboBox = JComboBox() val startupCommandTextField = OutlineTextField() + val characterAtATimeTextField = YesOrNoComboBox() val environmentTextArea = FixedLengthTextArea(2048) @@ -355,6 +332,7 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan backspaceComboBox.addItem(Backspace.Backspace) backspaceComboBox.addItem(Backspace.VT220) + characterAtATimeTextField.selectedItem = false environmentTextArea.setFocusTraversalKeys( KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, @@ -409,6 +387,8 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan .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.character-mode")}:").xy(1, rows) + .add(characterAtATimeTextField).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) diff --git a/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetStreamPtyConnector.kt b/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetStreamPtyConnector.kt index ca845d1..0c26380 100644 --- a/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetStreamPtyConnector.kt +++ b/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetStreamPtyConnector.kt @@ -1,6 +1,7 @@ package app.termora.plugin.internal.telnet import app.termora.terminal.StreamPtyConnector +import org.apache.commons.io.IOUtils import org.apache.commons.net.telnet.TelnetClient import org.apache.commons.net.telnet.TelnetOption import org.apache.commons.net.telnet.WindowSizeOptionHandler @@ -9,18 +10,27 @@ import java.nio.charset.Charset class TelnetStreamPtyConnector( private val telnet: TelnetClient, - private val charset: Charset -) : - StreamPtyConnector(telnet.inputStream, telnet.outputStream) { - private val reader = InputStreamReader(telnet.inputStream, getCharset()) + private val charset: Charset, + private val characterMode: Boolean, +) : StreamPtyConnector(telnet.inputStream, telnet.outputStream) { + + private val reader = InputStreamReader(telnet.inputStream, charset) + override fun read(buffer: CharArray): Int { return reader.read(buffer) } override fun write(buffer: ByteArray, offset: Int, len: Int) { - output.write(buffer, offset, len) - output.flush() + if (characterMode) { + for (i in offset until len + offset) { + output.write(byteArrayOf(buffer[i])) + output.flush() + } + } else { + output.write(buffer, offset, len) + output.flush() + } } override fun resize(rows: Int, cols: Int) { @@ -33,10 +43,13 @@ class TelnetStreamPtyConnector( } override fun close() { + IOUtils.closeQuietly(input) + IOUtils.closeQuietly(output) telnet.disconnect() } override fun getCharset(): Charset { return charset } + } \ 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 7f1965f..c4fe128 100644 --- a/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetTerminalTab.kt +++ b/src/main/kotlin/app/termora/plugin/internal/telnet/TelnetTerminalTab.kt @@ -11,6 +11,7 @@ import java.net.InetSocketAddress import java.net.Proxy import java.nio.charset.Charset + class TelnetTerminalTab( windowScope: WindowScope, host: Host, ) : PtyHostTerminalTab(windowScope, host) { @@ -32,12 +33,18 @@ class TelnetTerminalTab( ) } + val characterMode = host.options.extras["character-at-a-time"]?.toBooleanStrictOrNull() ?: false + val termtype = host.options.envs()["TERM"] ?: "xterm-256color" val ttopt = TerminalTypeOptionHandler(termtype, false, false, true, false) val echoopt = EchoOptionHandler(false, true, false, true) val gaopt = SuppressGAOptionHandler(true, true, true, true) val wsopt = WindowSizeOptionHandler(winSize.cols, winSize.rows, true, false, true, false) + val bopt = SimpleOptionHandler(TelnetOption.BINARY, true, false, true, false) + val fcopt = SimpleOptionHandler(TelnetOption.REMOTE_FLOW_CONTROL, true, true, false, false) + telnet.addOptionHandler(bopt) + telnet.addOptionHandler(fcopt) telnet.addOptionHandler(ttopt) telnet.addOptionHandler(echoopt) telnet.addOptionHandler(gaopt) @@ -45,6 +52,7 @@ class TelnetTerminalTab( telnet.connect(host.host, host.port) telnet.keepAlive = true + telnet.tcpNoDelay = characterMode val encoder = terminal.getKeyEncoder() if (encoder is KeyEncoderImpl) { @@ -56,7 +64,8 @@ class TelnetTerminalTab( } } - return ptyConnectorFactory.decorate(TelnetStreamPtyConnector(telnet, telnet.charset)) + + return ptyConnectorFactory.decorate(TelnetStreamPtyConnector(telnet, telnet.charset, characterMode)) } diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index 06e99d1..4e4d26e 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -182,6 +182,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.character-mode=Character-at-a-time 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 1409cf4..d75760d 100644 --- a/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -174,6 +174,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.character-mode=单字符模式 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 c2efc11..47d626d 100644 --- a/src/main/resources/i18n/messages_zh_TW.properties +++ b/src/main/resources/i18n/messages_zh_TW.properties @@ -172,6 +172,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.character-mode=單字元模式 termora.new-host.terminal.startup-commands=啟動命令 termora.new-host.terminal.heartbeat-interval=心跳間隔 termora.new-host.terminal.env=環境