From bcd3aacd6f33494e6318b31fc134c8716f2b4199 Mon Sep 17 00:00:00 2001 From: hstyi Date: Fri, 14 Mar 2025 14:54:56 +0800 Subject: [PATCH] fix: emacs alt x --- .../app/termora/TerminalPanelFactory.kt | 10 ++++ .../app/termora/terminal/ColorPaletteImpl.kt | 11 ++-- .../terminal/panel/TerminalPanelKeyAdapter.kt | 55 ++++++++++++++++--- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/app/termora/TerminalPanelFactory.kt b/src/main/kotlin/app/termora/TerminalPanelFactory.kt index 3999032..9feec3a 100644 --- a/src/main/kotlin/app/termora/TerminalPanelFactory.kt +++ b/src/main/kotlin/app/termora/TerminalPanelFactory.kt @@ -11,6 +11,7 @@ import app.termora.terminal.panel.TerminalPanel import app.termora.terminal.panel.TerminalWriter import kotlinx.coroutines.* import org.apache.commons.lang3.StringUtils +import org.slf4j.LoggerFactory import java.awt.event.ComponentEvent import java.awt.event.ComponentListener import java.nio.charset.Charset @@ -105,6 +106,10 @@ class TerminalPanelFactory : Disposable { } private class MyTerminalWriter(private val ptyConnector: PtyConnector) : TerminalWriter { + companion object { + private val log = LoggerFactory.getLogger(MyTerminalWriter::class.java) + } + private lateinit var evt: AnActionEvent override fun onMounted(c: JComponent) { @@ -112,6 +117,11 @@ class TerminalPanelFactory : Disposable { } override fun write(request: TerminalWriter.WriteRequest) { + + if (log.isDebugEnabled) { + log.debug("write: ${String(request.buffer, getCharset())}") + } + val windowScope = evt.getData(DataProviders.WindowScope) if (windowScope == null) { ptyConnector.write(request.buffer) diff --git a/src/main/kotlin/app/termora/terminal/ColorPaletteImpl.kt b/src/main/kotlin/app/termora/terminal/ColorPaletteImpl.kt index e023dd2..6237d81 100644 --- a/src/main/kotlin/app/termora/terminal/ColorPaletteImpl.kt +++ b/src/main/kotlin/app/termora/terminal/ColorPaletteImpl.kt @@ -58,11 +58,10 @@ open class ColorPaletteImpl(private val terminal: Terminal) : ColorPalette { for (g in 0 until 6) { for (b in 0 until 6) { val idx = 36 * r + 6 * g + b - xterm256Colors[idx] = Color( - getCubeColorValue(r), - getCubeColorValue(g), - getCubeColorValue(b), - ).rgb + val x = getCubeColorValue(r) + val y = getCubeColorValue(g) + val z = getCubeColorValue(b) + xterm256Colors[idx] = 65536 * x + 256 * y + z } } } @@ -70,7 +69,7 @@ open class ColorPaletteImpl(private val terminal: Terminal) : ColorPalette { for (gray in 0..23) { val a = 10 * gray + 8 val idx = 216 + gray - xterm256Colors[idx] = Color(a, a, a).rgb + xterm256Colors[idx] = 65536 * a + 256 * a + a } } diff --git a/src/main/kotlin/app/termora/terminal/panel/TerminalPanelKeyAdapter.kt b/src/main/kotlin/app/termora/terminal/panel/TerminalPanelKeyAdapter.kt index c248512..2bebfb9 100644 --- a/src/main/kotlin/app/termora/terminal/panel/TerminalPanelKeyAdapter.kt +++ b/src/main/kotlin/app/termora/terminal/panel/TerminalPanelKeyAdapter.kt @@ -15,26 +15,39 @@ class TerminalPanelKeyAdapter( private val writer: TerminalWriter ) : KeyAdapter() { + companion object { + private const val ASCII_ESC = 27.toChar() + } + private val activeKeymap get() = KeymapManager.getInstance().getActiveKeymap() + private var isIgnoreKeyTyped = false override fun keyTyped(e: KeyEvent) { - if (Character.isISOControl(e.keyChar)) { + // 如果忽略并且不是正常字符 + if (isIgnoreKeyTyped || Character.isISOControl(e.keyChar)) { return } terminal.getSelectionModel().clearSelection() - - writer.write( - TerminalWriter.WriteRequest.fromBytes( - "${e.keyChar}".toByteArray(writer.getCharset()) - ) - ) - + writer.write(TerminalWriter.WriteRequest.fromBytes("${e.keyChar}".toByteArray(writer.getCharset()))) terminal.getScrollingModel().scrollTo(Int.MAX_VALUE) } override fun keyPressed(e: KeyEvent) { + // 重置 + isIgnoreKeyTyped = false + + // 处理 + doKeyPressed(e) + + // 如果已经处理,那么忽略 keyTyped 事件 + if (e.isConsumed) { + isIgnoreKeyTyped = true + } + } + + private fun doKeyPressed(e: KeyEvent) { if (e.isConsumed) return // remove all toast @@ -53,6 +66,7 @@ class TerminalPanelKeyAdapter( val encode = terminal.getKeyEncoder().encode(AWTTerminalKeyEvent(e)) if (encode.isNotEmpty()) { writer.write(TerminalWriter.WriteRequest.fromBytes(encode.toByteArray(writer.getCharset()))) + e.consume() } // https://github.com/TermoraDev/termora/issues/52 @@ -60,6 +74,14 @@ class TerminalPanelKeyAdapter( return } + // https://github.com/TermoraDev/termora/issues/331 + if (isAltPressedOnly(e) && Character.isDefined(e.keyChar)) { + val c = String(charArrayOf(ASCII_ESC, simpleMapKeyCodeToChar(e))) + writer.write(TerminalWriter.WriteRequest.fromBytes(c.toByteArray(writer.getCharset()))) + e.consume() + return + } + // 如果命中了全局快捷键,那么不处理 if (keyStroke.modifiers != 0 && activeKeymap.getActionIds(KeyShortcut(keyStroke)).isNotEmpty()) { return @@ -70,6 +92,7 @@ class TerminalPanelKeyAdapter( // 如果不为空表示已经发送过了,所以这里为空的时候再发送 if (encode.isEmpty()) { writer.write(TerminalWriter.WriteRequest.fromBytes("${e.keyChar}".toByteArray(writer.getCharset()))) + e.consume() } terminal.getScrollingModel().scrollTo(Int.MAX_VALUE) } @@ -83,4 +106,20 @@ class TerminalPanelKeyAdapter( && (modifiersEx and InputEvent.CTRL_DOWN_MASK) != 0 && (modifiersEx and InputEvent.SHIFT_DOWN_MASK) == 0 } + + private fun isAltPressedOnly(e: KeyEvent): Boolean { + val modifiersEx = e.modifiersEx + return (modifiersEx and InputEvent.ALT_DOWN_MASK) != 0 + && (modifiersEx and InputEvent.ALT_GRAPH_DOWN_MASK) == 0 + && (modifiersEx and InputEvent.CTRL_DOWN_MASK) == 0 + && (modifiersEx and InputEvent.SHIFT_DOWN_MASK) == 0 + } + + + private fun simpleMapKeyCodeToChar(e: KeyEvent): Char { + // zsh requires proper case of letter + if (e.isShiftDown) return Character.toUpperCase(e.keyCode.toChar()) + return Character.toLowerCase(e.keyCode.toChar()); + } + } \ No newline at end of file