Skip to content

Commit

Permalink
feat(input): support kitty keyboard protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
aymanbagabas committed Feb 26, 2024
1 parent c1c98d8 commit e81d88b
Show file tree
Hide file tree
Showing 14 changed files with 189 additions and 114 deletions.
15 changes: 3 additions & 12 deletions da1.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,16 @@ package tea

import (
"fmt"

"github.com/charmbracelet/x/exp/term/input"
)

// PrimaryDeviceAttributesEvent represents a primary device attributes event.
type PrimaryDeviceAttributesEvent []uint

var _ input.Event = PrimaryDeviceAttributesEvent{}
// PrimaryDeviceAttrsMsg represents a primary device attributes event.
type PrimaryDeviceAttrsMsg []uint

// String implements input.Event.
func (e PrimaryDeviceAttributesEvent) String() string {
func (e PrimaryDeviceAttrsMsg) String() string {
s := "DA1"
if len(e) > 0 {
s += fmt.Sprintf(": %v", []uint(e))
}
return s
}

// Type implements input.Event.
func (PrimaryDeviceAttributesEvent) Type() string {
return "PrimaryDeviceAttributesEvent"
}
6 changes: 3 additions & 3 deletions driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ type driver struct {
flags int
}

// NewDriver returns a new ANSI input driver.
// newInputDriver returns a new ANSI input driver.
// This driver uses ANSI control codes compatible with VT100/VT200 terminals,
// and XTerm. It supports reading Terminfo databases to overwrite the default
// key sequences.
func NewDriver(r io.Reader, term string, flags int) *driver {
func newInputDriver(r io.Reader, term string, flags int) *driver {
d := &driver{
rd: bufio.NewReaderSize(r, 256),
flags: flags,
Expand Down Expand Up @@ -344,7 +344,7 @@ func (d *driver) parseCsi(i int, p []byte, alt bool) (int, Msg) {
for i, p := range params {
da1[i] = p[0]
}
return len(seq), PrimaryDeviceAttributesEvent(da1)
return len(seq), PrimaryDeviceAttrsMsg(da1)
case string(seq) == "\x1b[200~" || string(seq) == "\x1b[201~":
// bracketed-paste
return len(seq), parseBracketedPaste(seq, &d.paste)
Expand Down
51 changes: 14 additions & 37 deletions examples/echo-key/main.go → examples/echo-msg/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.KeyMsg:
if msg.Action != tea.KeyPress {
break
}
switch m.prevKey.String() {
case "q":
if msg.String() == "q" {
Expand All @@ -33,53 +36,27 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg.String() {
case "b":
execute(sys.RequestBackgroundColor)
case "k":
cmd = tea.RequestKittyFlags
}
case "k":
switch msg.String() {
case "0":
m.kittyFlags = 0
execute(kitty.Disable(m.kittyFlags))
case "1":
if m.kittyFlags&kitty.DisambiguateEscapeCodes == 0 {
cmd = tea.DisableKittyKeyboard
default:
switch msg.String() {
case "1":
m.kittyFlags |= kitty.DisambiguateEscapeCodes
execute(kitty.Enable(m.kittyFlags))
} else {
m.kittyFlags &^= kitty.DisambiguateEscapeCodes
execute(kitty.Disable(m.kittyFlags))
}
case "2":
if m.kittyFlags&kitty.ReportEventTypes == 0 {
case "2":
m.kittyFlags |= kitty.ReportEventTypes
execute(kitty.Enable(m.kittyFlags))
} else {
m.kittyFlags &^= kitty.ReportEventTypes
execute(kitty.Disable(m.kittyFlags))
}
case "3":
if m.kittyFlags&kitty.ReportAlternateKeys == 0 {
case "3":
m.kittyFlags |= kitty.ReportAlternateKeys
execute(kitty.Enable(m.kittyFlags))
} else {
m.kittyFlags &^= kitty.ReportAlternateKeys
execute(kitty.Disable(m.kittyFlags))
}
case "4":
if m.kittyFlags&kitty.ReportAllKeys == 0 {
case "4":
m.kittyFlags |= kitty.ReportAllKeys
execute(kitty.Enable(m.kittyFlags))
} else {
m.kittyFlags &^= kitty.ReportAllKeys
execute(kitty.Disable(m.kittyFlags))
}
case "5":
if m.kittyFlags&kitty.ReportAssociatedKeys == 0 {
case "5":
m.kittyFlags |= kitty.ReportAssociatedKeys
execute(kitty.Enable(m.kittyFlags))
} else {
m.kittyFlags &^= kitty.ReportAssociatedKeys
execute(kitty.Disable(m.kittyFlags))
}

cmd = tea.EnableKittyKeyboard(m.kittyFlags)
}
}
m.prevKey = msg
Expand Down
2 changes: 1 addition & 1 deletion examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/charmbracelet/harmonica v0.2.0
github.com/charmbracelet/lipgloss v0.9.1
github.com/charmbracelet/x/exp/teatest v0.0.0-20230508155401-2bd6fa14c46a
github.com/charmbracelet/x/exp/term v0.0.0-20240226163258-9e64273902b7
github.com/fogleman/ease v0.0.0-20170301025033-8da417bf1776
github.com/lucasb-eyer/go-colorful v1.2.0
github.com/mattn/go-isatty v0.0.20
Expand All @@ -22,7 +23,6 @@ require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymanbagabas/go-udiff v0.1.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/charmbracelet/x/exp/term v0.0.0-20240224162640-3d670a517d3e // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gorilla/css v1.0.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
github.com/charmbracelet/x/exp/teatest v0.0.0-20230508155401-2bd6fa14c46a h1:GpNt24LKE8sH5G0SZUpu4Tg15sP5XSt1mnfIqE7fW34=
github.com/charmbracelet/x/exp/teatest v0.0.0-20230508155401-2bd6fa14c46a/go.mod h1:dpMo6PfGlhavn+ofggWhfFQmK9sZB2Ewljiz9bZtKVI=
github.com/charmbracelet/x/exp/term v0.0.0-20240224162640-3d670a517d3e h1:pBfk4IXd/ZLTc95hFZ1azczbFANZJ0EMd8qu0MxXVqI=
github.com/charmbracelet/x/exp/term v0.0.0-20240224162640-3d670a517d3e/go.mod h1:ZPy8qL+1IzKxGHoaeKbXTbCB5u/WGFelf/N6O+KGA+w=
github.com/charmbracelet/x/exp/term v0.0.0-20240226163258-9e64273902b7 h1:pO9Cp1eVKjnFJfLjc8ZgRnztlQ5dOsahfL/QburBSSw=
github.com/charmbracelet/x/exp/term v0.0.0-20240226163258-9e64273902b7/go.mod h1:ZPy8qL+1IzKxGHoaeKbXTbCB5u/WGFelf/N6O+KGA+w=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/charmbracelet/bubbletea
go 1.18

require (
github.com/charmbracelet/x/exp/term v0.0.0-20240224162640-3d670a517d3e
github.com/charmbracelet/x/exp/term v0.0.0-20240226163258-9e64273902b7
github.com/mattn/go-localereader v0.0.1
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b
github.com/muesli/cancelreader v0.2.2
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/x/exp/term v0.0.0-20240224162640-3d670a517d3e h1:pBfk4IXd/ZLTc95hFZ1azczbFANZJ0EMd8qu0MxXVqI=
github.com/charmbracelet/x/exp/term v0.0.0-20240224162640-3d670a517d3e/go.mod h1:ZPy8qL+1IzKxGHoaeKbXTbCB5u/WGFelf/N6O+KGA+w=
github.com/charmbracelet/x/exp/term v0.0.0-20240225222649-13567539c901 h1:HWeWoPkCOWzZ3Xnyi1aGMmPJ5hhTjGl7x2eQoG6rP2M=
github.com/charmbracelet/x/exp/term v0.0.0-20240225222649-13567539c901/go.mod h1:ZPy8qL+1IzKxGHoaeKbXTbCB5u/WGFelf/N6O+KGA+w=
github.com/charmbracelet/x/exp/term v0.0.0-20240226163258-9e64273902b7 h1:pO9Cp1eVKjnFJfLjc8ZgRnztlQ5dOsahfL/QburBSSw=
github.com/charmbracelet/x/exp/term v0.0.0-20240226163258-9e64273902b7/go.mod h1:ZPy8qL+1IzKxGHoaeKbXTbCB5u/WGFelf/N6O+KGA+w=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
Expand Down
4 changes: 4 additions & 0 deletions nil_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package tea

type nilRenderer struct{}

var _ renderer = nilRenderer{}

func (n nilRenderer) start() {}
func (n nilRenderer) stop() {}
func (n nilRenderer) kill() {}
Expand All @@ -22,3 +24,5 @@ func (n nilRenderer) disableBracketedPaste() {}
func (n nilRenderer) enableMouseSGRMode() {}
func (n nilRenderer) disableMouseSGRMode() {}
func (n nilRenderer) bracketedPasteActive() bool { return false }
func (n nilRenderer) pushKitty(int) {}
func (n nilRenderer) requestKittyFlags() {}
28 changes: 28 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"sync/atomic"

"github.com/charmbracelet/x/exp/term/ansi/kitty"
"github.com/muesli/termenv"
)

Expand Down Expand Up @@ -223,3 +224,30 @@ func WithFPS(fps int) ProgramOption {
p.fps = fps
}
}

// Kitty keyboard flags.
const (
KittyEventTypesFlag = kitty.ReportEventTypes
KittyAlternateKeysFlag = kitty.ReportAlternateKeys
KittyAllKeysFlag = kitty.ReportAllKeys
KittyAssociatedKeysFlag = kitty.ReportAssociatedKeys
)

// WithKittyKeyboard enables kitty keyboard support. This will enable the
// program to recognize and handle kitty keyboard sequences. This is useful
// for terminals that support kitty keyboard sequences.
//
// This option can be combined with the following flags:
// - KittyEventTypesFlag
// - KittyAlternateKeysFlag
// - KittyAllKeysFlag
// - KittyAssociatedKeysFlag
func WithKittyKeyboard(flags ...int) ProgramOption {
return func(p *Program) {
var f int
for _, flag := range flags {
f |= flag
}
p.kittyFlags = kitty.DisambiguateEscapeCodes | f
}
}
7 changes: 7 additions & 0 deletions renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ type renderer interface {
// bracketedPasteActive reports whether bracketed paste mode is
// currently enabled.
bracketedPasteActive() bool

// pushKitty sends a command to the terminal to enable kitty keyboard
// mode flags.
pushKitty(flags int)

// requestKittyFlags requests the current kitty keyboard mode flags.
requestKittyFlags()
}

// repaintMsg forces a full repaint.
Expand Down
33 changes: 33 additions & 0 deletions screen.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package tea

import "github.com/charmbracelet/x/exp/term/ansi/kitty"

// WindowSizeMsg is used to report the terminal size. It's sent to Update once
// initially and then on every terminal resize. Note that Windows does not
// have support for reporting when resizes occur as it does not support the
Expand Down Expand Up @@ -144,6 +146,37 @@ func DisableBracketedPaste() Msg {
// disableBracketedPasteMsg with DisableBracketedPaste.
type disableBracketedPasteMsg struct{}

type requestKittyFlagsMsg struct{}

// RequestKittyFlags is a special command that tells the Bubble Tea program to
// request the current kitty keyboard flags.
// The flags will be sent to the program as a KittyKeyboardMsg.
func RequestKittyFlags() Msg {
return requestKittyFlagsMsg{}
}

type enableKittyKeyboardMsg int

// EnableKittyKeyboard is a special command that tells the Bubble
// Tea program to enable kitty keyboard disambiguation.
func EnableKittyKeyboard(flags ...int) Cmd {
return func() Msg {
f := kitty.DisambiguateEscapeCodes
for _, flag := range flags {
f |= flag
}
return enableKittyKeyboardMsg(f)
}
}

type disableKittyKeyboardMsg struct{}

// DisableKittyKeyboard is a special command that tells the Bubble
// Tea program to disable kitty keyboard disambiguation.
func DisableKittyKeyboard() Msg {
return disableKittyKeyboardMsg{}
}

// EnterAltScreen enters the alternate screen buffer, which consumes the entire
// terminal window. ExitAltScreen will return the terminal to its former state.
//
Expand Down
Loading

0 comments on commit e81d88b

Please sign in to comment.