turbo-editors/turbo-corepublic Fork 0
v1.0.1
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

🛟 Updated. 28d5985 · on v1.0.1 · k33g · 14h ago
keys.go · 111 lines · 3.2 KBGo Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
package terminal

import "github.com/gdamore/tcell/v2"

// Encode turns a key press into the bytes a terminal program expects to read.
//
// applicationCursor is the screen's DECCKM setting: a program that has asked
// for application cursor keys expects ESC O A for the up arrow rather than
// ESC [ A, and gets the arrow keys wrong if it is given the other form.
//
// A key with no terminal meaning encodes to nothing at all, which the caller
// should treat as "nothing to send" rather than as an empty line.
//
//	terminal.Encode(tcell.NewEventKey(tcell.KeyEnter, 0, tcell.ModNone), false)
//	// []byte{'\r'}
func Encode(ev *tcell.EventKey, applicationCursor bool) []byte {
	if sequence, ok := encodeCursor(ev.Key(), applicationCursor); ok {
		return sequence
	}
	if sequence, ok := namedKeys[ev.Key()]; ok {
		return []byte(sequence)
	}
	if ev.Key() == tcell.KeyRune {
		return encodeRune(ev)
	}
	return encodeControl(ev)
}

// cursorKeys are the four arrows and the two keys that travel with them, whose
// encoding depends on the mode the program asked for.
var cursorKeys = map[tcell.Key]byte{
	tcell.KeyUp:    'A',
	tcell.KeyDown:  'B',
	tcell.KeyRight: 'C',
	tcell.KeyLeft:  'D',
	tcell.KeyHome:  'H',
	tcell.KeyEnd:   'F',
}

// encodeCursor encodes an arrow, Home or End in whichever form the program is
// expecting.
func encodeCursor(key tcell.Key, applicationCursor bool) ([]byte, bool) {
	final, ok := cursorKeys[key]
	if !ok {
		return nil, false
	}

	introducer := byte('[')
	if applicationCursor {
		introducer = 'O'
	}
	return []byte{escape, introducer, final}, true
}

// namedKeys are the keys with a fixed sequence of their own.
var namedKeys = map[tcell.Key]string{
	tcell.KeyEnter:      "\r",
	tcell.KeyTab:        "\t",
	tcell.KeyEscape:     "\x1b",
	tcell.KeyBackspace:  "\x7f",
	tcell.KeyBackspace2: "\x7f",
	tcell.KeyBacktab:    "\x1b[Z",
	tcell.KeyInsert:     "\x1b[2~",
	tcell.KeyDelete:     "\x1b[3~",
	tcell.KeyPgUp:       "\x1b[5~",
	tcell.KeyPgDn:       "\x1b[6~",

	tcell.KeyF1:  "\x1bOP",
	tcell.KeyF2:  "\x1bOQ",
	tcell.KeyF3:  "\x1bOR",
	tcell.KeyF4:  "\x1bOS",
	tcell.KeyF5:  "\x1b[15~",
	tcell.KeyF6:  "\x1b[17~",
	tcell.KeyF7:  "\x1b[18~",
	tcell.KeyF8:  "\x1b[19~",
	tcell.KeyF9:  "\x1b[20~",
	tcell.KeyF10: "\x1b[21~",
	tcell.KeyF11: "\x1b[23~",
	tcell.KeyF12: "\x1b[24~",
}

// encodeRune encodes a printable character, with the escape prefix that stands
// for Alt.
func encodeRune(ev *tcell.EventKey) []byte {
	text := []byte(string(ev.Rune()))
	if ev.Modifiers()&tcell.ModAlt != 0 {
		return append([]byte{escape}, text...)
	}
	return text
}

// encodeControl encodes a Ctrl-key press.
//
// tcell does not report the control byte itself: reading 0x03 from the
// terminal, it posts KeyCtrlSpace+3, and KeyCtrlSpace is 64. So the whole
// range 64…95 stands for the control bytes 0…31, and turning a key back into
// what a terminal expects means subtracting that offset again.
//
// Getting this wrong is silent: every Ctrl-key would simply send nothing.
func encodeControl(ev *tcell.EventKey) []byte {
	key := ev.Key()
	if key < tcell.KeyCtrlSpace || key > tcell.KeyCtrlUnderscore {
		return nil
	}

	control := byte(key - tcell.KeyCtrlSpace)
	if ev.Modifiers()&tcell.ModAlt != 0 {
		return []byte{escape, control}
	}
	return []byte{control}
}