keyboard.go

  1package tea
  2
  3import (
  4	"github.com/charmbracelet/x/ansi"
  5)
  6
  7// KeyboardEnhancements is a type that represents a set of keyboard
  8// enhancements.
  9type KeyboardEnhancements struct {
 10	// Kitty progressive keyboard enhancements protocol. This can be used to
 11	// enable different keyboard features.
 12	//
 13	//  - 0: disable all features
 14	//  - 1: [ansi.KittyDisambiguateEscapeCodes] Disambiguate escape codes such as
 15	//  ctrl+i and tab, ctrl+[ and escape, ctrl+space and ctrl+@, etc.
 16	//  - 2: [ansi.KittyReportEventTypes] Report event types such as key presses,
 17	//  releases, and repeat events.
 18	//  - 4: [ansi.KittyReportAlternateKeys] Report keypresses as though they were
 19	//  on a PC-101 ANSI US keyboard layout regardless of what they layout
 20	//  actually is. Also include information about whether or not is enabled,
 21	//  - 8: [ansi.KittyReportAllKeysAsEscapeCodes] Report all key events as escape
 22	//  codes. This includes simple printable keys like "a" and other Unicode
 23	//  characters.
 24	//  - 16: [ansi.KittyReportAssociatedKeys] Report associated text with key
 25	//  events. This encodes multi-rune key events as escape codes instead of
 26	//  individual runes.
 27	//
 28	kittyFlags int
 29
 30	// Xterm modifyOtherKeys feature.
 31	//
 32	//  - Mode 0 disables modifyOtherKeys.
 33	//  - Mode 1 reports ambiguous keys as escape codes. This is similar to
 34	//  [ansi.KittyDisambiguateEscapeCodes] but uses XTerm escape codes.
 35	//  - Mode 2 reports all key as escape codes including printable keys like "a" and "shift+b".
 36	modifyOtherKeys int
 37
 38	// keyReleases indicates whether we have key release events enabled. This is mainly
 39	// used in Windows to ignore key releases when they are not requested.
 40	keyReleases bool
 41}
 42
 43// KeyboardEnhancementOption is a type that represents a keyboard enhancement.
 44type KeyboardEnhancementOption func(k *KeyboardEnhancements)
 45
 46// withKeyReleases enables support for reporting release key events. This is
 47// useful for terminals that support the Kitty keyboard protocol "Report event
 48// types" progressive enhancement feature.
 49//
 50// Note that not all terminals support this feature.
 51func withKeyReleases(k *KeyboardEnhancements) {
 52	k.kittyFlags |= ansi.KittyReportEventTypes
 53	k.keyReleases = true
 54}
 55
 56// withUniformKeyLayout enables support for reporting key events as though they
 57// were on a PC-101 layout. This is useful for uniform key event reporting
 58// across different keyboard layouts. This is equivalent to the Kitty keyboard
 59// protocol "Report alternate keys" and "Report all keys as escape codes"
 60// progressive enhancement features.
 61//
 62// Note that not all terminals support this feature.
 63func withUniformKeyLayout(k *KeyboardEnhancements) {
 64	k.kittyFlags |= ansi.KittyReportAlternateKeys | ansi.KittyReportAllKeysAsEscapeCodes
 65}
 66
 67// withKeyDisambiguation enables support for disambiguating keyboard escape
 68// codes. This is useful for terminals that support the Kitty keyboard protocol
 69// "Disambiguate escape codes" progressive enhancement feature or the XTerm
 70// modifyOtherKeys mode 1 feature to report ambiguous keys as escape codes.
 71func withKeyDisambiguation(k *KeyboardEnhancements) {
 72	k.kittyFlags |= ansi.KittyDisambiguateEscapeCodes
 73	if k.modifyOtherKeys < 1 {
 74		k.modifyOtherKeys = 1
 75	}
 76}
 77
 78type enableKeyboardEnhancementsMsg []KeyboardEnhancementOption
 79
 80// RequestKeyDisambiguation is a command that enables support for reporting
 81// disambiguous keys as escape codes. This is enabled by default in Bubble Tea
 82// and there's no need to call this function unless you disabled keyboard
 83// enhancements through [DisableKeyboardEnhancements].
 84//
 85// If the terminal supports the requested enhancements, it will send a
 86// [KeyboardEnhancementsMsg] message with the supported enhancements.
 87//
 88// Note that not all terminals support this feature. If the terminal does not
 89// support this feature, the program will not receive disambiguated key
 90// events.
 91func RequestKeyDisambiguation() Msg {
 92	return enableKeyboardEnhancementsMsg{withKeyDisambiguation}
 93}
 94
 95// RequestKeyReleases is a command that enables support for reporting key
 96// release events.
 97//
 98// If the terminal supports the requested enhancements, it will send a
 99// [KeyboardEnhancementsMsg] message with the supported enhancements.
100//
101// Note that not all terminals support all enhancements.
102func RequestKeyReleases() Msg {
103	return enableKeyboardEnhancementsMsg{withKeyReleases}
104}
105
106// RequestUniformKeyLayout is a command that enables support for reporting key
107// events as though they were on a PC-101 layout.
108//
109// If the terminal supports the requested enhancements, it will send a
110// [KeyboardEnhancementsMsg] message with the supported enhancements.
111//
112// Note that not all terminals support all enhancements.
113func RequestUniformKeyLayout() Msg {
114	return enableKeyboardEnhancementsMsg{withUniformKeyLayout}
115}
116
117type disableKeyboardEnhancementsMsg struct{}
118
119// DisableKeyboardEnhancements is a command that disables keyboard enhancements
120// in the terminal.
121func DisableKeyboardEnhancements() Msg {
122	return disableKeyboardEnhancementsMsg{}
123}
124
125// KeyboardEnhancementsMsg is a message that gets sent when the terminal
126// supports keyboard enhancements.
127type KeyboardEnhancementsMsg KeyboardEnhancements
128
129// SupportsKeyDisambiguation returns whether the terminal supports reporting
130// disambiguous keys as escape codes.
131func (k KeyboardEnhancementsMsg) SupportsKeyDisambiguation() bool {
132	if isWindows() {
133		// We use Windows Console API which supports reporting disambiguous keys.
134		return true
135	}
136	return k.kittyFlags&ansi.KittyDisambiguateEscapeCodes != 0 || k.modifyOtherKeys >= 1
137}
138
139// SupportsKeyReleases returns whether the terminal supports key release
140// events.
141func (k KeyboardEnhancementsMsg) SupportsKeyReleases() bool {
142	if isWindows() {
143		// We use Windows Console API which supports key release events.
144		return k.keyReleases
145	}
146	return k.kittyFlags&ansi.KittyReportEventTypes != 0
147}
148
149// SupportsUniformKeyLayout returns whether the terminal supports reporting key
150// events as though they were on a PC-101 layout.
151func (k KeyboardEnhancementsMsg) SupportsUniformKeyLayout() bool {
152	if isWindows() {
153		// We use Windows Console API which supports reporting key events as
154		// though they were on a PC-101 layout.
155		return true
156	}
157	return k.SupportsKeyDisambiguation() &&
158		k.kittyFlags&ansi.KittyReportAlternateKeys != 0 &&
159		k.kittyFlags&ansi.KittyReportAllKeysAsEscapeCodes != 0
160}