Skip to content

Commit

Permalink
fixes #356 Vim cursors shapes?
Browse files Browse the repository at this point in the history
This adds a new method, SetCursorStyle() to the screen API.
It also automatically restores the cursor when disengaging to
the default cursor.  Modern terminals (and Windows console) support
this.
  • Loading branch information
gdamore committed Sep 26, 2021
1 parent beb254a commit a7f7853
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 71 deletions.
67 changes: 50 additions & 17 deletions console_win.go
Expand Up @@ -46,11 +46,12 @@ type cScreen struct {
w int
h int

oscreen consoleInfo
ocursor cursorInfo
oimode uint32
oomode uint32
cells CellBuffer
oscreen consoleInfo
ocursor cursorInfo
cursorStyle CursorStyle
oimode uint32
oomode uint32
cells CellBuffer

finiOnce sync.Once

Expand Down Expand Up @@ -138,20 +139,37 @@ const (

const (
// VT100/XTerm escapes understood by the console
vtShowCursor = "\x1b[?25h"
vtHideCursor = "\x1b[?25l"
vtCursorPos = "\x1b[%d;%dH" // Note that it is Y then X
vtSgr0 = "\x1b[0m"
vtBold = "\x1b[1m"
vtUnderline = "\x1b[4m"
vtBlink = "\x1b[5m" // Not sure this is processed
vtReverse = "\x1b[7m"
vtSetFg = "\x1b[38;5;%dm"
vtSetBg = "\x1b[48;5;%dm"
vtSetFgRGB = "\x1b[38;2;%d;%d;%dm" // RGB
vtSetBgRGB = "\x1b[48;2;%d;%d;%dm" // RGB
vtShowCursor = "\x1b[?25h"
vtHideCursor = "\x1b[?25l"
vtCursorPos = "\x1b[%d;%dH" // Note that it is Y then X
vtSgr0 = "\x1b[0m"
vtBold = "\x1b[1m"
vtUnderline = "\x1b[4m"
vtBlink = "\x1b[5m" // Not sure this is processed
vtReverse = "\x1b[7m"
vtSetFg = "\x1b[38;5;%dm"
vtSetBg = "\x1b[48;5;%dm"
vtSetFgRGB = "\x1b[38;2;%d;%d;%dm" // RGB
vtSetBgRGB = "\x1b[48;2;%d;%d;%dm" // RGB
vtCursorDefault = "\x1b[0 q"
vtCursorBlinkingBlock = "\x1b[1 q"
vtCursorSteadyBlock = "\x1b[2 q"
vtCursorBlinkingUnderline = "\x1b[3 q"
vtCursorSteadyUnderline = "\x1b[4 q"
vtCursorBlinkingBar = "\x1b[5 q"
vtCursorSteadyBar = "\x1b[6 q"
)

var vtCursorStyles = map[CursorStyle]string{
CursorStyleDefault: vtCursorDefault,
CursorStyleBlinkingBlock: vtCursorBlinkingBlock,
CursorStyleSteadyBlock: vtCursorSteadyBlock,
CursorStyleBlinkingUnderline: vtCursorBlinkingUnderline,
CursorStyleSteadyUnderline: vtCursorSteadyUnderline,
CursorStyleBlinkingBar: vtCursorBlinkingBar,
CursorStyleSteadyBar: vtCursorSteadyBar,
}

// NewConsoleScreen returns a Screen for the Windows console associated
// with the current process. The Screen makes use of the Windows Console
// API to display content and read events.
Expand Down Expand Up @@ -280,6 +298,9 @@ func (s *cScreen) disengage() {

s.wg.Wait()

if s.vten {
s.emitVtString(vtCursorStyles[CursorStyleDefault])
}
s.setInMode(s.oimode)
s.setOutMode(s.oomode)
s.setBufferSize(int(s.oscreen.size.x), int(s.oscreen.size.y))
Expand Down Expand Up @@ -406,6 +427,7 @@ func (s *cScreen) emitVtString(vs string) {
func (s *cScreen) showCursor() {
if s.vten {
s.emitVtString(vtShowCursor)
s.emitVtString(vtCursorStyles[s.cursorStyle])
} else {
s.setCursorInfo(&cursorInfo{size: 100, visible: 1})
}
Expand All @@ -429,6 +451,17 @@ func (s *cScreen) ShowCursor(x, y int) {
s.Unlock()
}

func (s *cScreen) SetCursorStyle(cs CursorStyle) {
s.Lock()
if !s.fini {
if _, ok := vtCursorStyles[cs]; ok {
s.cursorStyle = cs
s.doCursor()
}
}
s.Unlock()
}

func (s *cScreen) doCursor() {
x, y := s.curx, s.cury

Expand Down
21 changes: 20 additions & 1 deletion screen.go
Expand Up @@ -71,9 +71,14 @@ type Screen interface {
ShowCursor(x int, y int)

// HideCursor is used to hide the cursor. Its an alias for
// ShowCursor(-1, -1).
// ShowCursor(-1, -1).sim
HideCursor()

// SetCursorStyle is used to set the cursor style. If the style
// is not supported (or cursor styles are not supported at all),
// then this will have no effect.
SetCursorStyle(CursorStyle)

// Size returns the screen size as width, height. This changes in
// response to a call to Clear or Flush.
Size() (width, height int)
Expand Down Expand Up @@ -258,3 +263,17 @@ const (
MouseDragEvents = MouseFlags(2) // Click-drag events (includes button events)
MouseMotionEvents = MouseFlags(4) // All mouse events (includes click and drag events)
)

// CursorStyle represents a given cursor style, which can include the shape and
// whether the cursor blinks or is solid. Support for changing these is not universal.
type CursorStyle int

const (
CursorStyleDefault = CursorStyle(iota) // The default
CursorStyleBlinkingBlock
CursorStyleSteadyBlock
CursorStyleBlinkingUnderline
CursorStyleSteadyUnderline
CursorStyleBlinkingBar
CursorStyleSteadyBar
)
2 changes: 2 additions & 0 deletions simulation.go
Expand Up @@ -281,6 +281,8 @@ func (s *simscreen) hideCursor() {
s.cursorvis = false
}

func (s *simscreen) SetCursorStyle(CursorStyle) {}

func (s *simscreen) Show() {
s.Lock()
s.resize()
Expand Down
7 changes: 7 additions & 0 deletions terminfo/mkinfo.go
Expand Up @@ -609,6 +609,13 @@ func dotGoInfo(w io.Writer, terms []*TData) {
dotGoAddFlag(w, "TrueColor", t.TrueColor)
dotGoAddFlag(w, "AutoMargin", t.AutoMargin)
dotGoAddStr(w, "InsertChar", t.InsertChar)
dotGoAddStr(w, "CursorDefault", t.CursorDefault)
dotGoAddStr(w, "CursorBlinkingBlock", t.CursorBlinkingBlock)
dotGoAddStr(w, "CursorSteadyBlock", t.CursorSteadyBlock)
dotGoAddStr(w, "CursorBlinkingUnderline", t.CursorBlinkingUnderline)
dotGoAddStr(w, "CursorSteadyUnderline", t.CursorSteadyUnderline)
dotGoAddStr(w, "CursorBlinkingBar", t.CursorBlinkingBar)
dotGoAddStr(w, "CursorSteadyBar", t.CursorSteadyBar)
fmt.Fprintln(w, "\t})")
}
fmt.Fprintln(w, "}")
Expand Down
113 changes: 60 additions & 53 deletions terminfo/terminfo.go
Expand Up @@ -167,59 +167,66 @@ type Terminfo struct {
// Terminal support for these are going to vary amongst XTerm
// emulations, so don't depend too much on them in your application.

StrikeThrough string // smxx
SetFgBg string // setfgbg
SetFgBgRGB string // setfgbgrgb
SetFgRGB string // setfrgb
SetBgRGB string // setbrgb
KeyShfUp string // shift-up
KeyShfDown string // shift-down
KeyShfPgUp string // shift-kpp
KeyShfPgDn string // shift-knp
KeyCtrlUp string // ctrl-up
KeyCtrlDown string // ctrl-left
KeyCtrlRight string // ctrl-right
KeyCtrlLeft string // ctrl-left
KeyMetaUp string // meta-up
KeyMetaDown string // meta-left
KeyMetaRight string // meta-right
KeyMetaLeft string // meta-left
KeyAltUp string // alt-up
KeyAltDown string // alt-left
KeyAltRight string // alt-right
KeyAltLeft string // alt-left
KeyCtrlHome string
KeyCtrlEnd string
KeyMetaHome string
KeyMetaEnd string
KeyAltHome string
KeyAltEnd string
KeyAltShfUp string
KeyAltShfDown string
KeyAltShfLeft string
KeyAltShfRight string
KeyMetaShfUp string
KeyMetaShfDown string
KeyMetaShfLeft string
KeyMetaShfRight string
KeyCtrlShfUp string
KeyCtrlShfDown string
KeyCtrlShfLeft string
KeyCtrlShfRight string
KeyCtrlShfHome string
KeyCtrlShfEnd string
KeyAltShfHome string
KeyAltShfEnd string
KeyMetaShfHome string
KeyMetaShfEnd string
EnablePaste string // bracketed paste mode
DisablePaste string
PasteStart string
PasteEnd string
Modifiers int
InsertChar string // string to insert a character (ich1)
AutoMargin bool // true if writing to last cell in line advances
TrueColor bool // true if the terminal supports direct color
StrikeThrough string // smxx
SetFgBg string // setfgbg
SetFgBgRGB string // setfgbgrgb
SetFgRGB string // setfrgb
SetBgRGB string // setbrgb
KeyShfUp string // shift-up
KeyShfDown string // shift-down
KeyShfPgUp string // shift-kpp
KeyShfPgDn string // shift-knp
KeyCtrlUp string // ctrl-up
KeyCtrlDown string // ctrl-left
KeyCtrlRight string // ctrl-right
KeyCtrlLeft string // ctrl-left
KeyMetaUp string // meta-up
KeyMetaDown string // meta-left
KeyMetaRight string // meta-right
KeyMetaLeft string // meta-left
KeyAltUp string // alt-up
KeyAltDown string // alt-left
KeyAltRight string // alt-right
KeyAltLeft string // alt-left
KeyCtrlHome string
KeyCtrlEnd string
KeyMetaHome string
KeyMetaEnd string
KeyAltHome string
KeyAltEnd string
KeyAltShfUp string
KeyAltShfDown string
KeyAltShfLeft string
KeyAltShfRight string
KeyMetaShfUp string
KeyMetaShfDown string
KeyMetaShfLeft string
KeyMetaShfRight string
KeyCtrlShfUp string
KeyCtrlShfDown string
KeyCtrlShfLeft string
KeyCtrlShfRight string
KeyCtrlShfHome string
KeyCtrlShfEnd string
KeyAltShfHome string
KeyAltShfEnd string
KeyMetaShfHome string
KeyMetaShfEnd string
EnablePaste string // bracketed paste mode
DisablePaste string
PasteStart string
PasteEnd string
Modifiers int
InsertChar string // string to insert a character (ich1)
AutoMargin bool // true if writing to last cell in line advances
TrueColor bool // true if the terminal supports direct color
CursorDefault string
CursorBlinkingBlock string
CursorSteadyBlock string
CursorBlinkingUnderline string
CursorSteadyUnderline string
CursorBlinkingBar string
CursorSteadyBar string
}

const (
Expand Down
46 changes: 46 additions & 0 deletions tscreen.go
Expand Up @@ -148,6 +148,8 @@ type tScreen struct {
finiOnce sync.Once
enablePaste string
disablePaste string
cursorStyles map[CursorStyle]string
cursorStyle CursorStyle
saved *term.State
stopQ chan struct{}
running bool
Expand Down Expand Up @@ -332,6 +334,35 @@ func (t *tScreen) prepareBracketedPaste() {
}
}

func (t *tScreen) prepareCursorStyles() {
// Another workaround for lack of reporting in terminfo.
// We assume if the terminal has a mouse entry, that it
// offers bracketed paste. But we allow specific overrides
// via our terminal database.
if t.ti.CursorDefault != "" {
t.cursorStyles = map[CursorStyle]string{
CursorStyleDefault: t.ti.CursorDefault,
CursorStyleBlinkingBlock: t.ti.CursorBlinkingBlock,
CursorStyleSteadyBlock: t.ti.CursorSteadyBlock,
CursorStyleBlinkingUnderline: t.ti.CursorBlinkingUnderline,
CursorStyleSteadyUnderline: t.ti.CursorSteadyUnderline,
CursorStyleBlinkingBar: t.ti.CursorBlinkingBar,
CursorStyleSteadyBar: t.ti.CursorSteadyBar,
}
} else if t.ti.Mouse != "" {
t.cursorStyles = map[CursorStyle]string{
CursorStyleDefault: "\x1b[0 q",
CursorStyleBlinkingBlock: "\x1b[1 q",
CursorStyleSteadyBlock: "\x1b[2 q",
CursorStyleBlinkingUnderline: "\x1b[3 q",
CursorStyleSteadyUnderline: "\x1b[4 q",
CursorStyleBlinkingBar: "\x1b[5 q",
CursorStyleSteadyBar: "\x1b[6 q",

}
}
}

func (t *tScreen) prepareKey(key Key, val string) {
t.prepareKeyMod(key, ModNone, val)
}
Expand Down Expand Up @@ -471,6 +502,7 @@ func (t *tScreen) prepareKeys() {
t.prepareKey(keyPasteEnd, ti.PasteEnd)
t.prepareXtermModifiers()
t.prepareBracketedPaste()
t.prepareCursorStyles()

outer:
// Add key mappings for control keys.
Expand Down Expand Up @@ -754,6 +786,12 @@ func (t *tScreen) ShowCursor(x, y int) {
t.Unlock()
}

func (t *tScreen) SetCursorStyle(cs CursorStyle) {
t.Lock()
t.cursorStyle = cs
t.Unlock()
}

func (t *tScreen) HideCursor() {
t.ShowCursor(-1, -1)
}
Expand All @@ -768,6 +806,11 @@ func (t *tScreen) showCursor() {
}
t.TPuts(t.ti.TGoto(x, y))
t.TPuts(t.ti.ShowCursor)
if t.cursorStyles != nil {
if esc, ok := t.cursorStyles[t.cursorStyle]; ok {
t.TPuts(esc)
}
}
t.cx = x
t.cy = y
}
Expand Down Expand Up @@ -1737,6 +1780,9 @@ func (t *tScreen) disengage() {
ti := t.ti
t.cells.Resize(0, 0)
t.TPuts(ti.ShowCursor)
if t.cursorStyles != nil && t.cursorStyle != CursorStyleDefault {
t.TPuts(t.cursorStyles[t.cursorStyle])
}
t.TPuts(ti.ResetFgBg)
t.TPuts(ti.AttrOff)
t.TPuts(ti.Clear)
Expand Down

0 comments on commit a7f7853

Please sign in to comment.