Skip to content

Commit

Permalink
fixes #666 cursor color
Browse files Browse the repository at this point in the history
This adds a new optional parameter to screen.SetCursorStyle,
which is a color.  The cursors demo is enhanced to show this.

This ability is supported on screen types, provided the underlying
terminal supports the capability.
  • Loading branch information
gdamore committed Mar 7, 2024
1 parent 652ba11 commit 887cf27
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 29 deletions.
17 changes: 8 additions & 9 deletions _demos/cursors.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// beep makes a beep every second until you press ESC
package main

import (
Expand Down Expand Up @@ -52,6 +51,7 @@ func main() {
style := tcell.StyleDefault
go func() {
for {
s.Show()
ev := s.PollEvent()
switch ev := ev.(type) {
case *tcell.EventKey:
Expand All @@ -60,27 +60,26 @@ func main() {
switch ev.Rune() {
case '0':
s.SetContent(2, 2, '0', nil, style)
s.SetCursorStyle(tcell.CursorStyleDefault)
s.SetCursorStyle(tcell.CursorStyleDefault, tcell.ColorReset)
case '1':
s.SetContent(2, 2, '1', nil, style)
s.SetCursorStyle(tcell.CursorStyleBlinkingBlock)
s.SetCursorStyle(tcell.CursorStyleBlinkingBlock, tcell.ColorGreen)
case '2':
s.SetCell(2, 2, tcell.StyleDefault, '2')
s.SetCursorStyle(tcell.CursorStyleSteadyBlock)
s.SetCursorStyle(tcell.CursorStyleSteadyBlock, tcell.ColorBlue)
case '3':
s.SetCell(2, 2, tcell.StyleDefault, '3')
s.SetCursorStyle(tcell.CursorStyleBlinkingUnderline)
s.SetCursorStyle(tcell.CursorStyleBlinkingUnderline, tcell.ColorRed)
case '4':
s.SetCell(2, 2, tcell.StyleDefault, '4')
s.SetCursorStyle(tcell.CursorStyleSteadyUnderline)
s.SetCursorStyle(tcell.CursorStyleSteadyUnderline, tcell.ColorOrange)
case '5':
s.SetCell(2, 2, tcell.StyleDefault, '5')
s.SetCursorStyle(tcell.CursorStyleBlinkingBar)
s.SetCursorStyle(tcell.CursorStyleBlinkingBar, tcell.ColorYellow)
case '6':
s.SetCell(2, 2, tcell.StyleDefault, '6')
s.SetCursorStyle(tcell.CursorStyleSteadyBar)
s.SetCursorStyle(tcell.CursorStyleSteadyBar, tcell.ColorPink)
}
s.Show()

case tcell.KeyEscape, tcell.KeyEnter, tcell.KeyCtrlC:
close(quit)
Expand Down
14 changes: 12 additions & 2 deletions console_win.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type cScreen struct {
oscreen consoleInfo
ocursor cursorInfo
cursorStyle CursorStyle
cursorColor Color
oimode uint32
oomode uint32
cells CellBuffer
Expand Down Expand Up @@ -173,6 +174,8 @@ const (
vtUnderColorReset = "\x1b[59m"
vtEnterUrl = "\x1b]8;%s;%s\x1b\\" // NB arg 1 is id, arg 2 is url
vtExitUrl = "\x1b]8;;\x1b\\"
vtCursorColorRGB = "\x1b]12;#%02x%02x%02x\007"
vtCursorColorReset = "\x1b]112\007"
)

var vtCursorStyles = map[CursorStyle]string{
Expand Down Expand Up @@ -344,6 +347,7 @@ func (s *cScreen) disengage() {

if s.vten {
s.emitVtString(vtCursorStyles[CursorStyleDefault])
s.emitVtString(vtCursorColorReset)
s.emitVtString(vtEnableAm)
if !s.disableAlt {
s.emitVtString(vtExitCA)
Expand Down Expand Up @@ -435,6 +439,12 @@ func (s *cScreen) showCursor() {
if s.vten {
s.emitVtString(vtShowCursor)
s.emitVtString(vtCursorStyles[s.cursorStyle])
if s.cursorColor == ColorReset {
s.emitVtString(vtCursorColorReset)
} else if s.cursorColor.Valid() {
r, g, b := s.cursorColor.RGB()
s.emitVtString(fmt.Sprintf(vtCursorColorRGB, r, g, b))
}
} else {
s.setCursorInfo(&cursorInfo{size: 100, visible: 1})
}
Expand All @@ -458,11 +468,12 @@ func (s *cScreen) ShowCursor(x, y int) {
s.Unlock()
}

func (s *cScreen) SetCursorStyle(cs CursorStyle) {
func (s *cScreen) SetCursor(cs CursorStyle, cc Color) {
s.Lock()
if !s.fini {
if _, ok := vtCursorStyles[cs]; ok {
s.cursorStyle = cs
s.cursorColor = cc
s.doCursor()
}
}
Expand Down Expand Up @@ -1100,7 +1111,6 @@ func (s *cScreen) setCursorInfo(info *cursorInfo) {
_, _, _ = procSetConsoleCursorInfo.Call(
uintptr(s.out),
uintptr(unsafe.Pointer(info)))

}

func (s *cScreen) setCursorPos(x, y int, vtEnable bool) {
Expand Down
17 changes: 13 additions & 4 deletions screen.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 The TCell Authors
// Copyright 2024 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
Expand Down Expand Up @@ -79,8 +79,9 @@ type Screen interface {

// 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)
// then this will have no effect. Color will be changed if supplied,
// and the terminal supports doing so.
SetCursorStyle(CursorStyle, ...Color)

// Size returns the screen size as width, height. This changes in
// response to a call to Clear or Flush.
Expand Down Expand Up @@ -312,7 +313,7 @@ type screenImpl interface {
SetStyle(style Style)
ShowCursor(x int, y int)
HideCursor()
SetCursorStyle(CursorStyle)
SetCursor(CursorStyle, Color)
Size() (width, height int)
EnableMouse(...MouseFlags)
DisableMouse()
Expand Down Expand Up @@ -464,3 +465,11 @@ func (b *baseScreen) PostEvent(ev Event) error {
return ErrEventQFull
}
}

func (b *baseScreen) SetCursorStyle(cs CursorStyle, ccs ...Color) {
if len(ccs) > 0 {
b.SetCursor(cs, ccs[0])
} else {
b.SetCursor(cs, ColorNone)
}
}
4 changes: 2 additions & 2 deletions simulation.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 The TCell Authors
// Copyright 2024 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
Expand Down Expand Up @@ -239,7 +239,7 @@ func (s *simscreen) hideCursor() {
s.cursorvis = false
}

func (s *simscreen) SetCursorStyle(CursorStyle) {}
func (s *simscreen) SetCursor(CursorStyle, Color) {}

func (s *simscreen) Show() {
s.Lock()
Expand Down
3 changes: 3 additions & 0 deletions terminfo/terminfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ type Terminfo struct {
CursorSteadyUnderline string
CursorBlinkingBar string
CursorSteadyBar string
CursorColor string // nothing uses it yet
CursorColorRGB string // Cs (but not really because Cs uses X11 color string)
CursorColorReset string // Cr
EnterUrl string
ExitUrl string
SetWindowSize string
Expand Down
30 changes: 29 additions & 1 deletion tscreen.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ type tScreen struct {
underFg string
cursorStyles map[CursorStyle]string
cursorStyle CursorStyle
cursorColor Color
cursorRGB string
cursorFg string
saved *term.State
stopQ chan struct{}
eventQ chan Event
Expand Down Expand Up @@ -460,7 +463,20 @@ func (t *tScreen) prepareCursorStyles() {
CursorStyleSteadyBar: "\x1b[6 q",
}
}
if t.ti.CursorColorRGB != "" {
// if it was X11 style with just a single %p1%s, then convert
t.cursorRGB = t.ti.CursorColorRGB
}
if t.ti.CursorColorReset != "" {
t.cursorFg = t.ti.CursorColorReset
}
if t.cursorRGB == "" {
t.cursorRGB = "\x1b]12;%p1%s\007"
t.cursorFg = "\x1b]112\007"
}

// convert XTERM style color names to RGB color code. We have no way to do palette colors
t.cursorRGB = strings.Replace(t.cursorRGB, "%p1%s", "#%p1%02x%p2%02x%p3%02x", 1)
}

func (t *tScreen) prepareKey(key Key, val string) {
Expand Down Expand Up @@ -912,9 +928,10 @@ func (t *tScreen) ShowCursor(x, y int) {
t.Unlock()
}

func (t *tScreen) SetCursorStyle(cs CursorStyle) {
func (t *tScreen) SetCursor(cs CursorStyle, cc Color) {
t.Lock()
t.cursorStyle = cs
t.cursorColor = cc
t.Unlock()
}

Expand All @@ -937,6 +954,14 @@ func (t *tScreen) showCursor() {
t.TPuts(esc)
}
}
if t.cursorRGB != "" {
if t.cursorColor == ColorReset {
t.TPuts(t.cursorFg)
} else if t.cursorColor.Valid() {
r, g, b := t.cursorColor.RGB()
t.TPuts(t.ti.TParm(t.cursorRGB, int(r), int(g), int(b)))
}
}
t.cx = x
t.cy = y
}
Expand Down Expand Up @@ -1954,6 +1979,9 @@ func (t *tScreen) disengage() {
if t.cursorStyles != nil && t.cursorStyle != CursorStyleDefault {
t.TPuts(t.cursorStyles[CursorStyleDefault])
}
if t.cursorFg != "" && t.cursorColor.Valid() {
t.TPuts(t.cursorFg)
}
t.TPuts(ti.ResetFgBg)
t.TPuts(ti.AttrOff)
t.TPuts(ti.ExitKeypad)
Expand Down
12 changes: 10 additions & 2 deletions webfiles/tcell.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const beepAudio = new Audio("beep.wav");
var cx = -1;
var cy = -1;
var cursorClass = "cursor-blinking-block";
var cursorColor = "";

var content; // {data: row[height], dirty: bool}
// row = {data: element[width], previous: span}
Expand Down Expand Up @@ -185,12 +186,18 @@ function displayCursor() {
content.data[cy].data[cx] = span;
}

if (cursorColor != "") {
term.style.setProperty("--cursor-color", cursorColor);
} else {
term.style.setProperty("--cursor-color", "lightgrey");
}

content.data[cy].data[cx].classList.add(cursorClass);
}
}

function setCursorStyle(newClass) {
if (newClass == cursorClass) {
function setCursorStyle(newClass, newColor) {
if (newClass == cursorClass && newColor == cursorColor) {
return;
}

Expand All @@ -207,6 +214,7 @@ function setCursorStyle(newClass) {
}

cursorClass = newClass;
cursorColor = newColor;
}

function beep() {
Expand Down
13 changes: 7 additions & 6 deletions webfiles/termstyle.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
--cursor-color: lightgrey;
}

/* Style attributes */
Expand Down Expand Up @@ -64,26 +65,26 @@
/* Cursor styles */

.cursor-steady-block {
background-color: lightgrey !important;
background-color: var(--cursor-color) !important;
}
.cursor-blinking-block {
animation: blinking-block 1s step-start infinite !important;
}
@keyframes blinking-block {
50% {
background-color: lightgrey;
background-color: var(--cursor-color);
}
}

.cursor-steady-underline {
text-decoration: underline lightgrey !important;
text-decoration: underline var(--cursor-color) !important;
}
.cursor-blinking-underline {
animation: blinking-underline 1s step-start infinite !important;
}
@keyframes blinking-underline {
50% {
text-decoration: underline lightgrey;
text-decoration: underline var(--cursor-color);
}
}

Expand All @@ -93,7 +94,7 @@
.cursor-steady-bar:before {
content: " ";
width: 2px;
background-color: lightgrey !important;
background-color: var(--cursor-color) !important;
display: inline-block;
}
.cursor-blinking-bar {
Expand All @@ -102,7 +103,7 @@
.cursor-blinking-bar:before {
content: " ";
width: 2px;
background-color: lightgrey !important;
background-color: var(--cursor-color) !important;
display: inline-block;
animation: blinker 1s step-start infinite;
}
Expand Down
11 changes: 8 additions & 3 deletions wscreen.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ package tcell

import (
"errors"
"github.com/gdamore/tcell/v2/terminfo"
"fmt"
"strings"
"sync"
"syscall/js"
"unicode/utf8"

"github.com/gdamore/tcell/v2/terminfo"
)

func NewTerminfoScreen() (Screen, error) {
Expand Down Expand Up @@ -158,9 +160,12 @@ func (t *wScreen) ShowCursor(x, y int) {
t.Unlock()
}

func (t *wScreen) SetCursorStyle(cs CursorStyle) {
func (t *wScreen) SetCursor(cs CursorStyle, cc Color) {
if !cc.Valid() {
cc = ColorLightGray
}
t.Lock()
js.Global().Call("setCursorStyle", curStyleClasses[cs])
js.Global().Call("setCursorStyle", curStyleClasses[cs], fmt.Sprintf("#%06x", cc.Hex()))
t.Unlock()
}

Expand Down

0 comments on commit 887cf27

Please sign in to comment.