mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
fix: emacs shift key
This commit is contained in:
@@ -384,7 +384,7 @@ class ControlSequenceIntroducerProcessor(terminal: Terminal, reader: TerminalRea
|
|||||||
val mode = args.toInt(0)
|
val mode = args.toInt(0)
|
||||||
if (mode == 0) {
|
if (mode == 0) {
|
||||||
val x = terminal.getCursorModel().getPosition().x
|
val x = terminal.getCursorModel().getPosition().x
|
||||||
terminal.getTabulator().clearTabStop(x)
|
terminal.getTabulator().clearTabStop(x - 1)
|
||||||
if (log.isDebugEnabled) {
|
if (log.isDebugEnabled) {
|
||||||
log.debug("Tab Clear (TBC). clearTabStop($x)")
|
log.debug("Tab Clear (TBC). clearTabStop($x)")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ class EscapeSequenceProcessor(terminal: Terminal, reader: TerminalReader) : Abst
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val x = terminal.getCursorModel().getPosition().x
|
val x = terminal.getCursorModel().getPosition().x
|
||||||
terminal.getTabulator().setTabStop(x)
|
terminal.getTabulator().setTabStop(x - 1)
|
||||||
if (log.isDebugEnabled) {
|
if (log.isDebugEnabled) {
|
||||||
log.debug("Horizontal Tab Set (HTS). col: $x")
|
log.debug("Horizontal Tab Set (HTS). col: $x")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,14 +55,14 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList
|
|||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F2), encode = "${ControlCharacters.ESC}OQ")
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F2), encode = "${ControlCharacters.ESC}OQ")
|
||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F3), encode = "${ControlCharacters.ESC}OR")
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F3), encode = "${ControlCharacters.ESC}OR")
|
||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F4), encode = "${ControlCharacters.ESC}OS")
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F4), encode = "${ControlCharacters.ESC}OS")
|
||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F5), encode = "${ControlCharacters.ESC}[15~");
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F5), encode = "${ControlCharacters.ESC}[15~")
|
||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F6), encode = "${ControlCharacters.ESC}[17~");
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F6), encode = "${ControlCharacters.ESC}[17~")
|
||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F7), encode = "${ControlCharacters.ESC}[18~");
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F7), encode = "${ControlCharacters.ESC}[18~")
|
||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F8), encode = "${ControlCharacters.ESC}[19~");
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F8), encode = "${ControlCharacters.ESC}[19~")
|
||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F9), encode = "${ControlCharacters.ESC}[20~");
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F9), encode = "${ControlCharacters.ESC}[20~")
|
||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F10), encode = "${ControlCharacters.ESC}[21~");
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F10), encode = "${ControlCharacters.ESC}[21~")
|
||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F11), encode = "${ControlCharacters.ESC}[23~");
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F11), encode = "${ControlCharacters.ESC}[23~")
|
||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F12), encode = "${ControlCharacters.ESC}[24~");
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_F12), encode = "${ControlCharacters.ESC}[24~")
|
||||||
|
|
||||||
terminal.getTerminalModel().addDataListener(object : DataListener {
|
terminal.getTerminalModel().addDataListener(object : DataListener {
|
||||||
override fun onChanged(key: DataKey<*>, data: Any) {
|
override fun onChanged(key: DataKey<*>, data: Any) {
|
||||||
@@ -73,7 +73,40 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun encode(event: TerminalKeyEvent): String {
|
override fun encode(event: TerminalKeyEvent): String {
|
||||||
return mapping[event] ?: nothing
|
if (mapping.containsKey(event)) {
|
||||||
|
return mapping.getValue(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes = (mapping[TerminalKeyEvent(event.keyCode, 0)] ?: return nothing).toByteArray()
|
||||||
|
|
||||||
|
if (alwaysSendEsc(event.keyCode) && (event.modifiers and TerminalEvent.ALT_MASK) != 0) {
|
||||||
|
bytes = insertCodeAt(bytes, makeCode(ControlCharacters.ESC.code), 0)
|
||||||
|
return String(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alwaysSendEsc(event.keyCode) && (event.modifiers and TerminalEvent.META_MASK) != 0) {
|
||||||
|
bytes = insertCodeAt(bytes, makeCode(ControlCharacters.ESC.code), 0)
|
||||||
|
return String(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCursorKey(event.keyCode) || isFunctionKey(event.keyCode)) {
|
||||||
|
bytes = getCodeWithModifiers(bytes, event.modifiers)
|
||||||
|
return String(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeCode(vararg bytesAsInt: Int): ByteArray {
|
||||||
|
val bytes = ByteArray(bytesAsInt.size)
|
||||||
|
for ((i, byteAsInt) in bytesAsInt.withIndex()) {
|
||||||
|
bytes[i] = byteAsInt.toByte()
|
||||||
|
}
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun alwaysSendEsc(key: Int): Boolean {
|
||||||
|
return isCursorKey(key) || key == '\b'.code
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTerminal(): Terminal {
|
override fun getTerminal(): Terminal {
|
||||||
@@ -84,6 +117,91 @@ open class KeyEncoderImpl(private val terminal: Terminal) : KeyEncoder, DataList
|
|||||||
mapping[event] = encode
|
mapping[event] = encode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refer to section PC-Style Function Keys in http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||||
|
*/
|
||||||
|
private fun getCodeWithModifiers(bytes: ByteArray, modifiers: Int): ByteArray {
|
||||||
|
val code = modifiersToCode(modifiers)
|
||||||
|
|
||||||
|
if (code > 0 && bytes.size > 2) {
|
||||||
|
// SS3 needs to become CSI.
|
||||||
|
if (bytes[0].toInt() == ControlCharacters.ESC.code && bytes[1] == 'O'.code.toByte()) {
|
||||||
|
bytes[1] = '['.code.toByte()
|
||||||
|
}
|
||||||
|
// If the control sequence has no parameters, it needs a default parameter.
|
||||||
|
// Either way it also needs a semicolon separator.
|
||||||
|
val prefix = if (bytes.size == 3) "1;" else ";"
|
||||||
|
return insertCodeAt(
|
||||||
|
bytes,
|
||||||
|
(prefix + code).toByteArray(),
|
||||||
|
bytes.size - 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun insertCodeAt(bytes: ByteArray, code: ByteArray, at: Int): ByteArray {
|
||||||
|
val res = ByteArray(bytes.size + code.size)
|
||||||
|
System.arraycopy(bytes, 0, res, 0, bytes.size)
|
||||||
|
System.arraycopy(bytes, at, res, at + code.size, bytes.size - at)
|
||||||
|
System.arraycopy(code, 0, res, at, code.size)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Code Modifiers
|
||||||
|
* ------+--------------------------
|
||||||
|
* 2 | Shift
|
||||||
|
* 3 | Alt
|
||||||
|
* 4 | Shift + Alt
|
||||||
|
* 5 | Control
|
||||||
|
* 6 | Shift + Control
|
||||||
|
* 7 | Alt + Control
|
||||||
|
* 8 | Shift + Alt + Control
|
||||||
|
* 9 | Meta
|
||||||
|
* 10 | Meta + Shift
|
||||||
|
* 11 | Meta + Alt
|
||||||
|
* 12 | Meta + Alt + Shift
|
||||||
|
* 13 | Meta + Ctrl
|
||||||
|
* 14 | Meta + Ctrl + Shift
|
||||||
|
* 15 | Meta + Ctrl + Alt
|
||||||
|
* 16 | Meta + Ctrl + Alt + Shift
|
||||||
|
* ------+--------------------------
|
||||||
|
* @param modifiers
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private fun modifiersToCode(modifiers: Int): Int {
|
||||||
|
var code = 0
|
||||||
|
if ((modifiers and TerminalEvent.SHIFT_MASK) != 0) {
|
||||||
|
code = code or 1
|
||||||
|
}
|
||||||
|
if ((modifiers and TerminalEvent.ALT_MASK) != 0) {
|
||||||
|
code = code or 2
|
||||||
|
}
|
||||||
|
if ((modifiers and TerminalEvent.CTRL_MASK) != 0) {
|
||||||
|
code = code or 4
|
||||||
|
}
|
||||||
|
if ((modifiers and TerminalEvent.META_MASK) != 0) {
|
||||||
|
code = code or 8
|
||||||
|
}
|
||||||
|
return if (code != 0) code + 1 else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isCursorKey(key: Int): Boolean {
|
||||||
|
return key == KeyEvent.VK_DOWN || key == KeyEvent.VK_UP
|
||||||
|
|| key == KeyEvent.VK_LEFT || key == KeyEvent.VK_RIGHT
|
||||||
|
|| key == KeyEvent.VK_HOME || key == KeyEvent.VK_END
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isFunctionKey(key: Int): Boolean {
|
||||||
|
return key >= KeyEvent.VK_F1 && key <= KeyEvent.VK_F12
|
||||||
|
|| key == KeyEvent.VK_INSERT || key == KeyEvent.VK_DELETE
|
||||||
|
|| key == KeyEvent.VK_PAGE_UP || key == KeyEvent.VK_PAGE_DOWN
|
||||||
|
}
|
||||||
|
|
||||||
fun arrowKeysApplicationSequences() {
|
fun arrowKeysApplicationSequences() {
|
||||||
// Up
|
// Up
|
||||||
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_UP), encode = "${ControlCharacters.ESC}OA")
|
putCode(TerminalKeyEvent(keyCode = KeyEvent.VK_UP), encode = "${ControlCharacters.ESC}OA")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.termora.terminal
|
package app.termora.terminal
|
||||||
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import kotlin.math.min
|
import kotlin.math.max
|
||||||
|
|
||||||
open class VisualTerminal : Terminal {
|
open class VisualTerminal : Terminal {
|
||||||
|
|
||||||
@@ -119,6 +119,9 @@ open class VisualTerminal : Terminal {
|
|||||||
|
|
||||||
private class MyProcessor(private val terminal: Terminal, reader: TerminalReader) {
|
private class MyProcessor(private val terminal: Terminal, reader: TerminalReader) {
|
||||||
private var state: ProcessorState = TerminalState.READY
|
private var state: ProcessorState = TerminalState.READY
|
||||||
|
private val document get() = terminal.getDocument()
|
||||||
|
private val cursorModel get() = terminal.getCursorModel()
|
||||||
|
private val terminalModel get() = terminal.getTerminalModel()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LoggerFactory.getLogger(MyProcessor::class.java)
|
private val log = LoggerFactory.getLogger(MyProcessor::class.java)
|
||||||
@@ -135,7 +138,7 @@ private class MyProcessor(private val terminal: Terminal, reader: TerminalReader
|
|||||||
|
|
||||||
fun process(ch: Char) {
|
fun process(ch: Char) {
|
||||||
if (log.isTraceEnabled) {
|
if (log.isTraceEnabled) {
|
||||||
val position = terminal.getCursorModel().getPosition()
|
val position = cursorModel.getPosition()
|
||||||
log.trace("process [${printChar(ch)}] , state: $state , position: $position")
|
log.trace("process [${printChar(ch)}] , state: $state , position: $position")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,16 +158,29 @@ private class MyProcessor(private val terminal: Terminal, reader: TerminalReader
|
|||||||
}
|
}
|
||||||
|
|
||||||
ControlCharacters.CR -> {
|
ControlCharacters.CR -> {
|
||||||
terminal.getCursorModel().move(CursorMove.RowHome)
|
cursorModel.move(CursorMove.RowHome)
|
||||||
TerminalState.READY
|
TerminalState.READY
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlCharacters.TAB -> {
|
ControlCharacters.TAB -> {
|
||||||
val position = terminal.getCursorModel().getPosition()
|
val position = cursorModel.getPosition()
|
||||||
// Next tab + 1,如果当前 x = 11,那么下一个就是 16,因为在 TerminalLineBuffer#writeTerminalLineChar 的时候会 - 1 会导致错乱一位
|
// Next tab + 1,如果当前 x = 11,那么下一个就是 16,因为在 TerminalLineBuffer#writeTerminalLineChar 的时候会 - 1 会导致错乱一位
|
||||||
var nextTab = terminal.getTabulator().nextTab(position.x) + 1
|
val nextTab = terminal.getTabulator().nextTab(position.x - 1) + 1
|
||||||
nextTab = min(terminal.getTerminalModel().getCols(), nextTab)
|
val length = if (terminalModel.isAlternateScreenBuffer()) {
|
||||||
terminal.getCursorModel().move(row = position.y, col = nextTab)
|
document.getCurrentTerminalLineBuffer()
|
||||||
|
.getLineAt(position.y - 1).getText().length
|
||||||
|
} else {
|
||||||
|
document.getCurrentTerminalLineBuffer()
|
||||||
|
.getScreenLineAt(position.y - 1)
|
||||||
|
.getText().length
|
||||||
|
}
|
||||||
|
|
||||||
|
val x = max(position.x - 1, length)
|
||||||
|
if (x < nextTab) {
|
||||||
|
cursorModel.move(row = position.y, col = (position.x - 1) + (nextTab - x))
|
||||||
|
} else {
|
||||||
|
cursorModel.move(row = position.y, col = nextTab)
|
||||||
|
}
|
||||||
TerminalState.READY
|
TerminalState.READY
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,12 +192,12 @@ private class MyProcessor(private val terminal: Terminal, reader: TerminalReader
|
|||||||
}
|
}
|
||||||
|
|
||||||
ControlCharacters.BS -> {
|
ControlCharacters.BS -> {
|
||||||
terminal.getCursorModel().move(CursorMove.Left)
|
cursorModel.move(CursorMove.Left)
|
||||||
TerminalState.READY
|
TerminalState.READY
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlCharacters.SI -> {
|
ControlCharacters.SI -> {
|
||||||
terminal.getTerminalModel().getData(DataKey.GraphicCharacterSet).use(Graphic.G0)
|
terminalModel.getData(DataKey.GraphicCharacterSet).use(Graphic.G0)
|
||||||
if (log.isDebugEnabled) {
|
if (log.isDebugEnabled) {
|
||||||
log.debug("Use Graphic.G0")
|
log.debug("Use Graphic.G0")
|
||||||
}
|
}
|
||||||
@@ -189,7 +205,7 @@ private class MyProcessor(private val terminal: Terminal, reader: TerminalReader
|
|||||||
}
|
}
|
||||||
|
|
||||||
ControlCharacters.SO -> {
|
ControlCharacters.SO -> {
|
||||||
terminal.getTerminalModel().getData(DataKey.GraphicCharacterSet).use(Graphic.G1)
|
terminalModel.getData(DataKey.GraphicCharacterSet).use(Graphic.G1)
|
||||||
if (log.isDebugEnabled) {
|
if (log.isDebugEnabled) {
|
||||||
log.debug("Use Graphic.G1")
|
log.debug("Use Graphic.G1")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user