From bb03bca05144330e776ef0792131eee1e320465a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 10 Jul 2021 12:22:48 +0200 Subject: [PATCH 1/2] Add support for hyperlink OSC --- style.go | 58 +++++++++++++------- terminfo/f/foot/foot.go | 116 ++++++++++++++++++++-------------------- terminfo/terminfo.go | 108 +++++++++++++++++++------------------ tscreen.go | 5 ++ 4 files changed, 157 insertions(+), 130 deletions(-) diff --git a/style.go b/style.go index 8359e28c..e63fdffc 100644 --- a/style.go +++ b/style.go @@ -23,9 +23,10 @@ package tcell // // To use Style, just declare a variable of its type. type Style struct { - fg Color - bg Color - attrs AttrMask + fg Color + bg Color + attrs AttrMask + hyperlink string } // StyleDefault represents a default style, based upon the context. @@ -39,9 +40,10 @@ var styleInvalid = Style{attrs: AttrInvalid} // as requested. ColorDefault can be used to select the global default. func (s Style) Foreground(c Color) Style { return Style{ - fg: c, - bg: s.bg, - attrs: s.attrs, + fg: c, + bg: s.bg, + attrs: s.attrs, + hyperlink: s.hyperlink, } } @@ -49,9 +51,10 @@ func (s Style) Foreground(c Color) Style { // as requested. ColorDefault can be used to select the global default. func (s Style) Background(c Color) Style { return Style{ - fg: s.fg, - bg: c, - attrs: s.attrs, + fg: s.fg, + bg: c, + attrs: s.attrs, + hyperlink: s.hyperlink, } } @@ -64,23 +67,26 @@ func (s Style) Decompose() (fg Color, bg Color, attr AttrMask) { func (s Style) setAttrs(attrs AttrMask, on bool) Style { if on { return Style{ - fg: s.fg, - bg: s.bg, - attrs: s.attrs | attrs, + fg: s.fg, + bg: s.bg, + attrs: s.attrs | attrs, + hyperlink: s.hyperlink, } } return Style{ - fg: s.fg, - bg: s.bg, - attrs: s.attrs &^ attrs, + fg: s.fg, + bg: s.bg, + attrs: s.attrs &^ attrs, + hyperlink: s.hyperlink, } } // Normal returns the style with all attributes disabled. func (s Style) Normal() Style { return Style{ - fg: s.fg, - bg: s.bg, + fg: s.fg, + bg: s.bg, + hyperlink: s.hyperlink, } } @@ -130,8 +136,20 @@ func (s Style) StrikeThrough(on bool) Style { // specified. func (s Style) Attributes(attrs AttrMask) Style { return Style{ - fg: s.fg, - bg: s.bg, - attrs: attrs, + fg: s.fg, + bg: s.bg, + attrs: attrs, + hyperlink: s.hyperlink, + } +} + +// Hyperlink returns a new style based on s, with its hyperlink set to the +// specified URL. An empty string disables the hyperlink. +func (s Style) Hyperlink(url string) Style { + return Style{ + fg: s.fg, + bg: s.bg, + attrs: s.attrs, + hyperlink: url, } } diff --git a/terminfo/f/foot/foot.go b/terminfo/f/foot/foot.go index fb734cbd..217c6956 100644 --- a/terminfo/f/foot/foot.go +++ b/terminfo/f/foot/foot.go @@ -8,62 +8,64 @@ func init() { // foot terminal emulator terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "foot", - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h\x1b[22;0;0t", - ExitCA: "\x1b[?1049l\x1b[23;0;0t", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b(B\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Italic: "\x1b[3m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48:5:%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48:5:%p2%d%;m", - ResetFgBg: "\x1b[39;49m", - AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - StrikeThrough: "\x1b[9m", - Mouse: "\x1b[M", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\u007f", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyBacktab: "\x1b[Z", - Modifiers: 1, - AutoMargin: true, + Name: "foot", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h\x1b[22;0;0t", + ExitCA: "\x1b[?1049l\x1b[23;0;0t", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b(B\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48:5:%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48:5:%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Hyperlink: "\x1b]8;;", + StringTerminator: "\x1b\\", + Modifiers: 1, + AutoMargin: true, }) } diff --git a/terminfo/terminfo.go b/terminfo/terminfo.go index 7e17352c..0e2c9a61 100644 --- a/terminfo/terminfo.go +++ b/terminfo/terminfo.go @@ -167,59 +167,61 @@ 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 + StringTerminator string + Hyperlink string } const ( diff --git a/tscreen.go b/tscreen.go index ebd61a25..90b99b8f 100644 --- a/tscreen.go +++ b/tscreen.go @@ -681,6 +681,11 @@ func (t *tScreen) drawCell(x, y int) int { if attrs&AttrStrikeThrough != 0 { t.TPuts(ti.StrikeThrough) } + if style.hyperlink != "" && ti.Hyperlink != "" { + t.TPuts(ti.Hyperlink + style.hyperlink + ti.StringTerminator) + } else if t.curstyle.hyperlink != "" && ti.Hyperlink != "" { + t.TPuts(ti.Hyperlink + ti.StringTerminator) + } t.curstyle = style } // now emit runes - taking care to not overrun width with a From 2362f49a2b6c12251248754ab0ed89d69916e457 Mon Sep 17 00:00:00 2001 From: delthas Date: Wed, 23 Feb 2022 14:14:37 +0100 Subject: [PATCH 2/2] Enable OSC 8 hyperlink on VTE-based terminals >= 0.50.0 Some terminals will not support it even when on VTE >= 0.50.0, but these terminals will ignore the sequence, so this is harmless. See: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda --- tscreen.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tscreen.go b/tscreen.go index 90b99b8f..03252055 100644 --- a/tscreen.go +++ b/tscreen.go @@ -59,6 +59,16 @@ func NewTerminfoScreenFromTty(tty Tty) (Screen, error) { } terminfo.AddTerminfo(ti) } + if vteVersion := os.Getenv("VTE_VERSION"); vteVersion != "" && ti.Hyperlink == "" && ti.StringTerminator == "" { + v, err := strconv.Atoi(vteVersion) + if err == nil && v > 5000 { + // hyperlinks are supported in VTE-based terminal on VTE >= 0.50.0. + // some terminals do not support it even when VTE does, but they will ignore the escape code in those cases. + // see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda#terminal-emulators + ti.Hyperlink = "\x1b]8;;" + ti.StringTerminator = "\x1b\\" + } + } t := &tScreen{ti: ti, tty: tty} t.keyexist = make(map[Key]bool)