Files
kitty/tools/tui/loop/terminal-state.go
Tim Culverhouse 4b354fbd30 tui: explicitly turn off in-band resize mode
Explicitly turn off in-band resize notifications if they were requested
as part of the loop. Kitty implicitly resets this mode when `XTRESTORE`
(`CSI ? r`) is seen, however some terminals implementing mode 2048
(eg foot and Ghostty) follow the xterm behavior of only restoring the
state of explicitly passed modes.

Some of the kittens have become very useful outside of just Kitty
(`show-key`, for example). Add an explicit reset of mode 2048 to support
the behavior of other terminal emulators.
2024-11-18 10:51:14 -06:00

189 lines
5.2 KiB
Go

// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
package loop
import (
"fmt"
"strings"
"kitty"
)
type KeyboardStateBits uint8
const (
LEGACY_KEYS KeyboardStateBits = 0
DISAMBIGUATE_KEYS = 1
REPORT_KEY_EVENT_TYPES = 2
REPORT_ALTERNATE_KEYS = 4
REPORT_ALL_KEYS_AS_ESCAPE_CODES = 8
REPORT_TEXT_WITH_KEYS = 16
FULL_KEYBOARD_PROTOCOL = DISAMBIGUATE_KEYS | REPORT_ALTERNATE_KEYS | REPORT_ALL_KEYS_AS_ESCAPE_CODES | REPORT_TEXT_WITH_KEYS | REPORT_KEY_EVENT_TYPES
NO_KEYBOARD_STATE_CHANGE = 32
)
const (
SAVE_CURSOR = "\0337"
RESTORE_CURSOR = "\0338"
SAVE_PRIVATE_MODE_VALUES = "\033[?s"
RESTORE_PRIVATE_MODE_VALUES = "\033[?r"
SAVE_COLORS = "\033[#P"
RESTORE_COLORS = "\033[#Q"
DECSACE_DEFAULT_REGION_SELECT = "\033[*x"
CLEAR_SCREEN = "\033[H\033[2J"
)
type CursorShapes uint
const (
BLOCK_CURSOR CursorShapes = 1
UNDERLINE_CURSOR CursorShapes = 3
BAR_CURSOR CursorShapes = 5
)
type Mode uint32
const private Mode = 1 << 31
const (
LNM Mode = 20
IRM Mode = 4
DECKM Mode = 1 | private
DECSCNM Mode = 5 | private
DECOM Mode = 6 | private
DECAWM Mode = 7 | private
DECARM Mode = 8 | private
DECTCEM Mode = 25 | private
MOUSE_BUTTON_TRACKING Mode = 1000 | private
MOUSE_MOTION_TRACKING Mode = 1002 | private
MOUSE_MOVE_TRACKING Mode = 1003 | private
FOCUS_TRACKING Mode = 1004 | private
MOUSE_UTF8_MODE Mode = 1005 | private
MOUSE_SGR_MODE Mode = 1006 | private
MOUSE_URXVT_MODE Mode = 1015 | private
MOUSE_SGR_PIXEL_MODE Mode = 1016 | private
ALTERNATE_SCREEN Mode = 1049 | private
BRACKETED_PASTE Mode = 2004 | private
PENDING_UPDATE Mode = 2026 | private
INBAND_RESIZE_NOTIFICATION Mode = 2048 | private
HANDLE_TERMIOS_SIGNALS Mode = kitty.HandleTermiosSignals | private
)
func (self Mode) escape_code(which string) string {
num := self
priv := ""
if num&private > 0 {
priv = "?"
num &^= private
}
return fmt.Sprintf("\033[%s%d%s", priv, uint32(num), which)
}
func (self Mode) EscapeCodeToSet() string {
return self.escape_code("h")
}
func (self Mode) EscapeCodeToReset() string {
return self.escape_code("l")
}
type MouseTracking uint8
const (
NO_MOUSE_TRACKING MouseTracking = iota
BUTTONS_ONLY_MOUSE_TRACKING
BUTTONS_AND_DRAG_MOUSE_TRACKING
FULL_MOUSE_TRACKING
)
type TerminalStateOptions struct {
Alternate_screen, restore_colors bool
mouse_tracking MouseTracking
kitty_keyboard_mode KeyboardStateBits
in_band_resize_notification bool
}
func set_modes(sb *strings.Builder, modes ...Mode) {
for _, m := range modes {
sb.WriteString(m.EscapeCodeToSet())
}
}
func reset_modes(sb *strings.Builder, modes ...Mode) {
for _, m := range modes {
sb.WriteString(m.EscapeCodeToReset())
}
}
func (self *TerminalStateOptions) SetStateEscapeCodes() string {
var sb strings.Builder
sb.Grow(256)
if self.Alternate_screen {
sb.WriteString(SAVE_CURSOR)
}
sb.WriteString(SAVE_PRIVATE_MODE_VALUES)
if self.restore_colors {
sb.WriteString(SAVE_COLORS)
}
sb.WriteString(DECSACE_DEFAULT_REGION_SELECT)
reset_modes(&sb,
IRM, DECKM, DECSCNM, BRACKETED_PASTE, FOCUS_TRACKING,
MOUSE_BUTTON_TRACKING, MOUSE_MOTION_TRACKING, MOUSE_MOVE_TRACKING, MOUSE_UTF8_MODE, MOUSE_SGR_MODE)
set_modes(&sb, DECARM, DECAWM, DECTCEM)
if self.in_band_resize_notification {
set_modes(&sb, INBAND_RESIZE_NOTIFICATION)
}
if self.Alternate_screen {
set_modes(&sb, ALTERNATE_SCREEN)
sb.WriteString(CLEAR_SCREEN)
}
switch self.kitty_keyboard_mode {
case LEGACY_KEYS:
sb.WriteString("\033[>u")
case NO_KEYBOARD_STATE_CHANGE:
default:
sb.WriteString(fmt.Sprintf("\033[>%du", self.kitty_keyboard_mode))
}
if self.mouse_tracking != NO_MOUSE_TRACKING {
sb.WriteString(MOUSE_SGR_PIXEL_MODE.EscapeCodeToSet())
switch self.mouse_tracking {
case BUTTONS_ONLY_MOUSE_TRACKING:
sb.WriteString(MOUSE_BUTTON_TRACKING.EscapeCodeToSet())
case BUTTONS_AND_DRAG_MOUSE_TRACKING:
sb.WriteString(MOUSE_MOTION_TRACKING.EscapeCodeToSet())
case FULL_MOUSE_TRACKING:
sb.WriteString(MOUSE_MOVE_TRACKING.EscapeCodeToSet())
}
}
return sb.String()
}
func (self *TerminalStateOptions) ResetStateEscapeCodes() string {
var sb strings.Builder
sb.Grow(64)
if self.kitty_keyboard_mode != NO_KEYBOARD_STATE_CHANGE {
sb.WriteString("\033[<u")
}
if self.Alternate_screen {
sb.WriteString(ALTERNATE_SCREEN.EscapeCodeToReset())
} else {
sb.WriteString(SAVE_CURSOR)
}
if self.in_band_resize_notification {
reset_modes(&sb, INBAND_RESIZE_NOTIFICATION)
}
sb.WriteString(RESTORE_PRIVATE_MODE_VALUES)
if self.restore_colors {
sb.WriteString(RESTORE_COLORS)
}
sb.WriteString(RESTORE_CURSOR)
return sb.String()
}
func CursorShape(shape CursorShapes, blink bool) string {
if !blink {
shape += 1
}
return fmt.Sprintf("\x1b[%d q", shape)
}