Skip to content

Commit

Permalink
fixes #710 Add support for setting the window title
Browse files Browse the repository at this point in the history
  • Loading branch information
gdamore committed Mar 9, 2024
1 parent 887cf27 commit c9ba0cf
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 4 deletions.
1 change: 1 addition & 0 deletions _demos/mouse.go
Expand Up @@ -123,6 +123,7 @@ func main() {
fmt.Fprintf(os.Stderr, "%v\n", e)
os.Exit(1)
}
s.SetTitle("Tcell Mouse Demonstration")
defStyle = tcell.StyleDefault.
Background(tcell.ColorReset).
Foreground(tcell.ColorReset)
Expand Down
3 changes: 3 additions & 0 deletions _demos/unicode.go
Expand Up @@ -109,6 +109,9 @@ func main() {
Background(tcell.ColorWhite))
s.Clear()

// we can even try to use unicode window titles!
s.SetTitle("Unicode Demonstration -- 🤯")

quit := make(chan struct{})

style = bold
Expand Down
18 changes: 18 additions & 0 deletions console_win.go
Expand Up @@ -42,6 +42,7 @@ type cScreen struct {
truecolor bool
running bool
disableAlt bool // disable the alternate screen
title string

w int
h int
Expand Down Expand Up @@ -176,6 +177,9 @@ const (
vtExitUrl = "\x1b]8;;\x1b\\"
vtCursorColorRGB = "\x1b]12;#%02x%02x%02x\007"
vtCursorColorReset = "\x1b]112\007"
vtSaveTitle = "\x1b[22;2t"
vtRestoreTitle = "\x1b[23;2t"
vtSetTitle = "\x1b]2;%s\x1b\\"
)

var vtCursorStyles = map[CursorStyle]string{
Expand Down Expand Up @@ -350,6 +354,7 @@ func (s *cScreen) disengage() {
s.emitVtString(vtCursorColorReset)
s.emitVtString(vtEnableAm)
if !s.disableAlt {
s.emitVtString(vtRestoreTitle)
s.emitVtString(vtExitCA)
}
} else if !s.disableAlt {
Expand Down Expand Up @@ -387,9 +392,13 @@ func (s *cScreen) engage() error {
if s.vten {
s.setOutMode(modeVtOutput | modeNoAutoNL | modeCookedOut | modeUnderline)
if !s.disableAlt {
s.emitVtString(vtSaveTitle)
s.emitVtString(vtEnterCA)
}
s.emitVtString(vtDisableAm)
if s.title != "" {
s.emitVtString(fmt.Sprintf(vtSetTitle, s.title))
}
} else {
s.setOutMode(0)
}
Expand Down Expand Up @@ -1275,6 +1284,15 @@ func (s *cScreen) SetStyle(style Style) {
s.Unlock()
}

func (s *cScreen) SetTitle(title string) {
s.Lock()
s.title = title
if s.vten {
s.emitVtString(fmt.Sprintf(vtSetTitle, title))
}
s.Unlock()
}

// No fallback rune support, since we have Unicode. Yay!

func (s *cScreen) RegisterRuneFallback(_ rune, _ string) {
Expand Down
7 changes: 7 additions & 0 deletions screen.go
Expand Up @@ -266,6 +266,12 @@ type Screen interface {
// Tty returns the underlying Tty. If the screen is not a terminal, the
// returned bool will be false
Tty() (Tty, bool)

// SetTitle sets a window title on the screen.
// Terminals may be configured to ignore this, or unable to.
// Tcell may attempt to save and restore the window title on entry and exit, but
// the results may vary. Use of unicode characters may not be supported.
SetTitle(string)
}

// NewScreen returns a default Screen suitable for the user's terminal
Expand Down Expand Up @@ -335,6 +341,7 @@ type screenImpl interface {
Resume() error
Beep() error
SetSize(int, int)
SetTitle(string)
Tty() (Tty, bool)

// Following methods are not part of the Screen api, but are used for interaction with
Expand Down
10 changes: 10 additions & 0 deletions sim_test.go
Expand Up @@ -150,3 +150,13 @@ func TestBeep(t *testing.T) {
}
}
}

func TestTitle(t *testing.T) {
s := mkTestScreen(t, "")
defer s.Fini()
s.SetTitle("My Title")
s.Show()
if s.GetTitle() != "My Title" {
t.Errorf("Title mismatched")
}
}
12 changes: 12 additions & 0 deletions simulation.go
Expand Up @@ -60,6 +60,9 @@ type SimulationScreen interface {

// GetCursor returns the cursor details.
GetCursor() (x int, y int, visible bool)

// GetTitle gets the set title
GetTitle() string
}

// SimCell represents a simulated screen cell. The purpose of this
Expand Down Expand Up @@ -98,6 +101,7 @@ type simscreen struct {
fillchar rune
fillstyle Style
fallback map[rune]string
title string

Screen
sync.Mutex
Expand Down Expand Up @@ -495,3 +499,11 @@ func (s *simscreen) EventQ() chan Event {
func (s *simscreen) StopQ() <-chan struct{} {
return s.quit
}

func (s *simscreen) SetTitle(title string) {
s.title = title
}

func (s *simscreen) GetTitle() string {
return s.title
}
1 change: 1 addition & 0 deletions terminfo/terminfo.go
Expand Up @@ -233,6 +233,7 @@ type Terminfo struct {
EnterUrl string
ExitUrl string
SetWindowSize string
SetWindowTitle string // no terminfo extension
EnableFocusReporting string
DisableFocusReporting string
DisableAutoMargin string // smam
Expand Down
40 changes: 36 additions & 4 deletions tscreen.go
Expand Up @@ -171,6 +171,10 @@ type tScreen struct {
mouseFlags MouseFlags
pasteEnabled bool
focusEnabled bool
setTitle string
saveTitle string
restoreTitle string
title string

sync.Mutex
}
Expand Down Expand Up @@ -414,27 +418,37 @@ func (t *tScreen) prepareExtendedOSC() {
if t.ti.EnterUrl != "" {
t.enterUrl = t.ti.EnterUrl
t.exitUrl = t.ti.ExitUrl
} else if t.ti.Mouse != "" {
} else if t.ti.Mouse != "" || t.ti.XTermLike {
t.enterUrl = "\x1b]8;%p2%s;%p1%s\x1b\\"
t.exitUrl = "\x1b]8;;\x1b\\"
}

if t.ti.SetWindowSize != "" {
t.setWinSize = t.ti.SetWindowSize
} else if t.ti.Mouse != "" {
} else if t.ti.Mouse != "" || t.ti.XTermLike {
t.setWinSize = "\x1b[8;%p1%p2%d;%dt"
}

if t.ti.EnableFocusReporting != "" {
t.enableFocus = t.ti.EnableFocusReporting
} else if t.ti.Mouse != "" {
} else if t.ti.Mouse != "" || t.ti.XTermLike {
t.enableFocus = "\x1b[?1004h"
}
if t.ti.DisableFocusReporting != "" {
t.disableFocus = t.ti.DisableFocusReporting
} else if t.ti.Mouse != "" {
} else if t.ti.Mouse != "" || t.ti.XTermLike {
t.disableFocus = "\x1b[?1004l"
}

if t.ti.SetWindowTitle != "" {
t.setTitle = t.ti.SetWindowTitle
} else if t.ti.XTermLike {
t.saveTitle = "\x1b[22;2t"
t.restoreTitle = "\x1b[23;2t"
// this also tries to request that UTF-8 is allowed in the title
t.setTitle = "\x1b[>2t\x1b]2;%p1%s\x1b\\"

}
}

func (t *tScreen) prepareCursorStyles() {
Expand Down Expand Up @@ -1938,12 +1952,18 @@ func (t *tScreen) engage() error {
// (In theory there could be terminals that don't support X,Y cursor
// positions without a setup command, but we don't support them.)
t.TPuts(ti.EnterCA)
if t.saveTitle != "" {
t.TPuts(t.saveTitle)
}
}
t.TPuts(ti.EnterKeypad)
t.TPuts(ti.HideCursor)
t.TPuts(ti.EnableAcs)
t.TPuts(ti.DisableAutoMargin)
t.TPuts(ti.Clear)
if t.title != "" && t.setTitle != "" {
t.TPuts(t.ti.TParm(t.setTitle, t.title))
}

t.wg.Add(2)
go t.inputLoop(stopQ)
Expand Down Expand Up @@ -1987,6 +2007,9 @@ func (t *tScreen) disengage() {
t.TPuts(ti.ExitKeypad)
t.TPuts(ti.EnableAutoMargin)
if os.Getenv("TCELL_ALTSCREEN") != "disable" {
if t.restoreTitle != "" {
t.TPuts(t.restoreTitle)
}
t.TPuts(ti.Clear) // only needed if ExitCA is empty
t.TPuts(ti.ExitCA)
}
Expand Down Expand Up @@ -2021,3 +2044,12 @@ func (t *tScreen) EventQ() chan Event {
func (t *tScreen) GetCells() *CellBuffer {
return &t.cells
}

func (t *tScreen) SetTitle(title string) {
t.Lock()
t.title = title
if t.setTitle != "" && t.running {
t.TPuts(t.ti.TParm(t.setTitle, title))
}
t.Unlock()
}
4 changes: 4 additions & 0 deletions webfiles/tcell.js
Expand Up @@ -222,6 +222,10 @@ function beep() {
beepAudio.play();
}

function setTitle(title) {
document.title = title;
}

function intToHex(n) {
return "#" + n.toString(16).padStart(6, "0");
}
Expand Down
4 changes: 4 additions & 0 deletions wscreen.go
Expand Up @@ -523,6 +523,10 @@ func (t *wScreen) StopQ() <-chan struct{} {
return t.quit
}

func (t *wScreen) SetTitle(title string) {
js.Global().Call("setTitle", title)
}

// WebKeyNames maps string names reported from HTML
// (KeyboardEvent.key) to tcell accepted keys.
var WebKeyNames = map[string]Key{
Expand Down

0 comments on commit c9ba0cf

Please sign in to comment.