1// Copyright 2019 The TCell Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use file except in compliance with the License. 5// You may obtain a copy of the license at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package tcell 16 17import ( 18 "bytes" 19 "io" 20 "os" 21 "strconv" 22 "sync" 23 "time" 24 "unicode/utf8" 25 26 "golang.org/x/text/transform" 27 28 "github.com/gdamore/tcell/terminfo" 29 30 // import the stock terminals 31 _ "github.com/gdamore/tcell/terminfo/base" 32) 33 34// NewTerminfoScreen returns a Screen that uses the stock TTY interface 35// and POSIX termios, combined with a terminfo description taken from 36// the $TERM environment variable. It returns an error if the terminal 37// is not supported for any reason. 38// 39// For terminals that do not support dynamic resize events, the $LINES 40// $COLUMNS environment variables can be set to the actual window size, 41// otherwise defaults taken from the terminal database are used. 42func NewTerminfoScreen() (Screen, error) { 43 ti, e := terminfo.LookupTerminfo(os.Getenv("TERM")) 44 if e != nil { 45 ti, e = loadDynamicTerminfo(os.Getenv("TERM")) 46 if e != nil { 47 return nil, e 48 } 49 terminfo.AddTerminfo(ti) 50 } 51 t := &tScreen{ti: ti} 52 53 t.keyexist = make(map[Key]bool) 54 t.keycodes = make(map[string]*tKeyCode) 55 if len(ti.Mouse) > 0 { 56 t.mouse = []byte(ti.Mouse) 57 } 58 t.prepareKeys() 59 t.buildAcsMap() 60 t.sigwinch = make(chan os.Signal, 10) 61 t.fallback = make(map[rune]string) 62 for k, v := range RuneFallbacks { 63 t.fallback[k] = v 64 } 65 66 return t, nil 67} 68 69// tKeyCode represents a combination of a key code and modifiers. 70type tKeyCode struct { 71 key Key 72 mod ModMask 73} 74 75// tScreen represents a screen backed by a terminfo implementation. 76type tScreen struct { 77 ti *terminfo.Terminfo 78 h int 79 w int 80 fini bool 81 cells CellBuffer 82 in *os.File 83 out *os.File 84 buffering bool // true if we are collecting writes to buf instead of sending directly to out 85 buf bytes.Buffer 86 curstyle Style 87 style Style 88 evch chan Event 89 sigwinch chan os.Signal 90 quit chan struct{} 91 indoneq chan struct{} 92 keyexist map[Key]bool 93 keycodes map[string]*tKeyCode 94 keychan chan []byte 95 keytimer *time.Timer 96 keyexpire time.Time 97 cx int 98 cy int 99 mouse []byte 100 clear bool 101 cursorx int 102 cursory int 103 tiosp *termiosPrivate 104 wasbtn bool 105 acs map[rune]string 106 charset string 107 encoder transform.Transformer 108 decoder transform.Transformer 109 fallback map[rune]string 110 colors map[Color]Color 111 palette []Color 112 truecolor bool 113 escaped bool 114 buttondn bool 115 116 sync.Mutex 117} 118 119func (t *tScreen) Init() error { 120 t.evch = make(chan Event, 10) 121 t.indoneq = make(chan struct{}) 122 t.keychan = make(chan []byte, 10) 123 t.keytimer = time.NewTimer(time.Millisecond * 50) 124 t.charset = "UTF-8" 125 126 t.charset = getCharset() 127 if enc := GetEncoding(t.charset); enc != nil { 128 t.encoder = enc.NewEncoder() 129 t.decoder = enc.NewDecoder() 130 } else { 131 return ErrNoCharset 132 } 133 ti := t.ti 134 135 // environment overrides 136 w := ti.Columns 137 h := ti.Lines 138 if i, _ := strconv.Atoi(os.Getenv("LINES")); i != 0 { 139 h = i 140 } 141 if i, _ := strconv.Atoi(os.Getenv("COLUMNS")); i != 0 { 142 w = i 143 } 144 if e := t.termioInit(); e != nil { 145 return e 146 } 147 148 if t.ti.SetFgBgRGB != "" || t.ti.SetFgRGB != "" || t.ti.SetBgRGB != "" { 149 t.truecolor = true 150 } 151 // A user who wants to have his themes honored can 152 // set this environment variable. 153 if os.Getenv("TCELL_TRUECOLOR") == "disable" { 154 t.truecolor = false 155 } 156 if !t.truecolor { 157 t.colors = make(map[Color]Color) 158 t.palette = make([]Color, t.Colors()) 159 for i := 0; i < t.Colors(); i++ { 160 t.palette[i] = Color(i) 161 // identity map for our builtin colors 162 t.colors[Color(i)] = Color(i) 163 } 164 } 165 166 t.TPuts(ti.EnterCA) 167 t.TPuts(ti.HideCursor) 168 t.TPuts(ti.EnableAcs) 169 t.TPuts(ti.Clear) 170 171 t.quit = make(chan struct{}) 172 173 t.Lock() 174 t.cx = -1 175 t.cy = -1 176 t.style = StyleDefault 177 t.cells.Resize(w, h) 178 t.cursorx = -1 179 t.cursory = -1 180 t.resize() 181 t.Unlock() 182 183 go t.mainLoop() 184 go t.inputLoop() 185 186 return nil 187} 188 189func (t *tScreen) prepareKeyMod(key Key, mod ModMask, val string) { 190 if val != "" { 191 // Do not overrride codes that already exist 192 if _, exist := t.keycodes[val]; !exist { 193 t.keyexist[key] = true 194 t.keycodes[val] = &tKeyCode{key: key, mod: mod} 195 } 196 } 197} 198 199func (t *tScreen) prepareKey(key Key, val string) { 200 t.prepareKeyMod(key, ModNone, val) 201} 202 203func (t *tScreen) prepareKeys() { 204 ti := t.ti 205 t.prepareKey(KeyBackspace, ti.KeyBackspace) 206 t.prepareKey(KeyF1, ti.KeyF1) 207 t.prepareKey(KeyF2, ti.KeyF2) 208 t.prepareKey(KeyF3, ti.KeyF3) 209 t.prepareKey(KeyF4, ti.KeyF4) 210 t.prepareKey(KeyF5, ti.KeyF5) 211 t.prepareKey(KeyF6, ti.KeyF6) 212 t.prepareKey(KeyF7, ti.KeyF7) 213 t.prepareKey(KeyF8, ti.KeyF8) 214 t.prepareKey(KeyF9, ti.KeyF9) 215 t.prepareKey(KeyF10, ti.KeyF10) 216 t.prepareKey(KeyF11, ti.KeyF11) 217 t.prepareKey(KeyF12, ti.KeyF12) 218 t.prepareKey(KeyF13, ti.KeyF13) 219 t.prepareKey(KeyF14, ti.KeyF14) 220 t.prepareKey(KeyF15, ti.KeyF15) 221 t.prepareKey(KeyF16, ti.KeyF16) 222 t.prepareKey(KeyF17, ti.KeyF17) 223 t.prepareKey(KeyF18, ti.KeyF18) 224 t.prepareKey(KeyF19, ti.KeyF19) 225 t.prepareKey(KeyF20, ti.KeyF20) 226 t.prepareKey(KeyF21, ti.KeyF21) 227 t.prepareKey(KeyF22, ti.KeyF22) 228 t.prepareKey(KeyF23, ti.KeyF23) 229 t.prepareKey(KeyF24, ti.KeyF24) 230 t.prepareKey(KeyF25, ti.KeyF25) 231 t.prepareKey(KeyF26, ti.KeyF26) 232 t.prepareKey(KeyF27, ti.KeyF27) 233 t.prepareKey(KeyF28, ti.KeyF28) 234 t.prepareKey(KeyF29, ti.KeyF29) 235 t.prepareKey(KeyF30, ti.KeyF30) 236 t.prepareKey(KeyF31, ti.KeyF31) 237 t.prepareKey(KeyF32, ti.KeyF32) 238 t.prepareKey(KeyF33, ti.KeyF33) 239 t.prepareKey(KeyF34, ti.KeyF34) 240 t.prepareKey(KeyF35, ti.KeyF35) 241 t.prepareKey(KeyF36, ti.KeyF36) 242 t.prepareKey(KeyF37, ti.KeyF37) 243 t.prepareKey(KeyF38, ti.KeyF38) 244 t.prepareKey(KeyF39, ti.KeyF39) 245 t.prepareKey(KeyF40, ti.KeyF40) 246 t.prepareKey(KeyF41, ti.KeyF41) 247 t.prepareKey(KeyF42, ti.KeyF42) 248 t.prepareKey(KeyF43, ti.KeyF43) 249 t.prepareKey(KeyF44, ti.KeyF44) 250 t.prepareKey(KeyF45, ti.KeyF45) 251 t.prepareKey(KeyF46, ti.KeyF46) 252 t.prepareKey(KeyF47, ti.KeyF47) 253 t.prepareKey(KeyF48, ti.KeyF48) 254 t.prepareKey(KeyF49, ti.KeyF49) 255 t.prepareKey(KeyF50, ti.KeyF50) 256 t.prepareKey(KeyF51, ti.KeyF51) 257 t.prepareKey(KeyF52, ti.KeyF52) 258 t.prepareKey(KeyF53, ti.KeyF53) 259 t.prepareKey(KeyF54, ti.KeyF54) 260 t.prepareKey(KeyF55, ti.KeyF55) 261 t.prepareKey(KeyF56, ti.KeyF56) 262 t.prepareKey(KeyF57, ti.KeyF57) 263 t.prepareKey(KeyF58, ti.KeyF58) 264 t.prepareKey(KeyF59, ti.KeyF59) 265 t.prepareKey(KeyF60, ti.KeyF60) 266 t.prepareKey(KeyF61, ti.KeyF61) 267 t.prepareKey(KeyF62, ti.KeyF62) 268 t.prepareKey(KeyF63, ti.KeyF63) 269 t.prepareKey(KeyF64, ti.KeyF64) 270 t.prepareKey(KeyInsert, ti.KeyInsert) 271 t.prepareKey(KeyDelete, ti.KeyDelete) 272 t.prepareKey(KeyHome, ti.KeyHome) 273 t.prepareKey(KeyEnd, ti.KeyEnd) 274 t.prepareKey(KeyUp, ti.KeyUp) 275 t.prepareKey(KeyDown, ti.KeyDown) 276 t.prepareKey(KeyLeft, ti.KeyLeft) 277 t.prepareKey(KeyRight, ti.KeyRight) 278 t.prepareKey(KeyPgUp, ti.KeyPgUp) 279 t.prepareKey(KeyPgDn, ti.KeyPgDn) 280 t.prepareKey(KeyHelp, ti.KeyHelp) 281 t.prepareKey(KeyPrint, ti.KeyPrint) 282 t.prepareKey(KeyCancel, ti.KeyCancel) 283 t.prepareKey(KeyExit, ti.KeyExit) 284 t.prepareKey(KeyBacktab, ti.KeyBacktab) 285 286 t.prepareKeyMod(KeyRight, ModShift, ti.KeyShfRight) 287 t.prepareKeyMod(KeyLeft, ModShift, ti.KeyShfLeft) 288 t.prepareKeyMod(KeyUp, ModShift, ti.KeyShfUp) 289 t.prepareKeyMod(KeyDown, ModShift, ti.KeyShfDown) 290 t.prepareKeyMod(KeyHome, ModShift, ti.KeyShfHome) 291 t.prepareKeyMod(KeyEnd, ModShift, ti.KeyShfEnd) 292 293 t.prepareKeyMod(KeyRight, ModCtrl, ti.KeyCtrlRight) 294 t.prepareKeyMod(KeyLeft, ModCtrl, ti.KeyCtrlLeft) 295 t.prepareKeyMod(KeyUp, ModCtrl, ti.KeyCtrlUp) 296 t.prepareKeyMod(KeyDown, ModCtrl, ti.KeyCtrlDown) 297 t.prepareKeyMod(KeyHome, ModCtrl, ti.KeyCtrlHome) 298 t.prepareKeyMod(KeyEnd, ModCtrl, ti.KeyCtrlEnd) 299 300 t.prepareKeyMod(KeyRight, ModAlt, ti.KeyAltRight) 301 t.prepareKeyMod(KeyLeft, ModAlt, ti.KeyAltLeft) 302 t.prepareKeyMod(KeyUp, ModAlt, ti.KeyAltUp) 303 t.prepareKeyMod(KeyDown, ModAlt, ti.KeyAltDown) 304 t.prepareKeyMod(KeyHome, ModAlt, ti.KeyAltHome) 305 t.prepareKeyMod(KeyEnd, ModAlt, ti.KeyAltEnd) 306 307 t.prepareKeyMod(KeyRight, ModAlt, ti.KeyMetaRight) 308 t.prepareKeyMod(KeyLeft, ModAlt, ti.KeyMetaLeft) 309 t.prepareKeyMod(KeyUp, ModAlt, ti.KeyMetaUp) 310 t.prepareKeyMod(KeyDown, ModAlt, ti.KeyMetaDown) 311 t.prepareKeyMod(KeyHome, ModAlt, ti.KeyMetaHome) 312 t.prepareKeyMod(KeyEnd, ModAlt, ti.KeyMetaEnd) 313 314 t.prepareKeyMod(KeyRight, ModAlt|ModShift, ti.KeyAltShfRight) 315 t.prepareKeyMod(KeyLeft, ModAlt|ModShift, ti.KeyAltShfLeft) 316 t.prepareKeyMod(KeyUp, ModAlt|ModShift, ti.KeyAltShfUp) 317 t.prepareKeyMod(KeyDown, ModAlt|ModShift, ti.KeyAltShfDown) 318 t.prepareKeyMod(KeyHome, ModAlt|ModShift, ti.KeyAltShfHome) 319 t.prepareKeyMod(KeyEnd, ModAlt|ModShift, ti.KeyAltShfEnd) 320 321 t.prepareKeyMod(KeyRight, ModAlt|ModShift, ti.KeyMetaShfRight) 322 t.prepareKeyMod(KeyLeft, ModAlt|ModShift, ti.KeyMetaShfLeft) 323 t.prepareKeyMod(KeyUp, ModAlt|ModShift, ti.KeyMetaShfUp) 324 t.prepareKeyMod(KeyDown, ModAlt|ModShift, ti.KeyMetaShfDown) 325 t.prepareKeyMod(KeyHome, ModAlt|ModShift, ti.KeyMetaShfHome) 326 t.prepareKeyMod(KeyEnd, ModAlt|ModShift, ti.KeyMetaShfEnd) 327 328 t.prepareKeyMod(KeyRight, ModCtrl|ModShift, ti.KeyCtrlShfRight) 329 t.prepareKeyMod(KeyLeft, ModCtrl|ModShift, ti.KeyCtrlShfLeft) 330 t.prepareKeyMod(KeyUp, ModCtrl|ModShift, ti.KeyCtrlShfUp) 331 t.prepareKeyMod(KeyDown, ModCtrl|ModShift, ti.KeyCtrlShfDown) 332 t.prepareKeyMod(KeyHome, ModCtrl|ModShift, ti.KeyCtrlShfHome) 333 t.prepareKeyMod(KeyEnd, ModCtrl|ModShift, ti.KeyCtrlShfEnd) 334 335 // Sadly, xterm handling of keycodes is somewhat erratic. In 336 // particular, different codes are sent depending on application 337 // mode is in use or not, and the entries for many of these are 338 // simply absent from terminfo on many systems. So we insert 339 // a number of escape sequences if they are not already used, in 340 // order to have the widest correct usage. Note that prepareKey 341 // will not inject codes if the escape sequence is already known. 342 // We also only do this for terminals that have the application 343 // mode present. 344 345 // Cursor mode 346 if ti.EnterKeypad != "" { 347 t.prepareKey(KeyUp, "\x1b[A") 348 t.prepareKey(KeyDown, "\x1b[B") 349 t.prepareKey(KeyRight, "\x1b[C") 350 t.prepareKey(KeyLeft, "\x1b[D") 351 t.prepareKey(KeyEnd, "\x1b[F") 352 t.prepareKey(KeyHome, "\x1b[H") 353 t.prepareKey(KeyDelete, "\x1b[3~") 354 t.prepareKey(KeyHome, "\x1b[1~") 355 t.prepareKey(KeyEnd, "\x1b[4~") 356 t.prepareKey(KeyPgUp, "\x1b[5~") 357 t.prepareKey(KeyPgDn, "\x1b[6~") 358 359 // Application mode 360 t.prepareKey(KeyUp, "\x1bOA") 361 t.prepareKey(KeyDown, "\x1bOB") 362 t.prepareKey(KeyRight, "\x1bOC") 363 t.prepareKey(KeyLeft, "\x1bOD") 364 t.prepareKey(KeyHome, "\x1bOH") 365 } 366 367outer: 368 // Add key mappings for control keys. 369 for i := 0; i < ' '; i++ { 370 // Do not insert direct key codes for ambiguous keys. 371 // For example, ESC is used for lots of other keys, so 372 // when parsing this we don't want to fast path handling 373 // of it, but instead wait a bit before parsing it as in 374 // isolation. 375 for esc := range t.keycodes { 376 if []byte(esc)[0] == byte(i) { 377 continue outer 378 } 379 } 380 381 t.keyexist[Key(i)] = true 382 383 mod := ModCtrl 384 switch Key(i) { 385 case KeyBS, KeyTAB, KeyESC, KeyCR: 386 // directly typeable- no control sequence 387 mod = ModNone 388 } 389 t.keycodes[string(rune(i))] = &tKeyCode{key: Key(i), mod: mod} 390 } 391} 392 393func (t *tScreen) Fini() { 394 t.Lock() 395 defer t.Unlock() 396 397 ti := t.ti 398 t.cells.Resize(0, 0) 399 t.TPuts(ti.ShowCursor) 400 t.TPuts(ti.AttrOff) 401 t.TPuts(ti.Clear) 402 t.TPuts(ti.ExitCA) 403 t.TPuts(ti.ExitKeypad) 404 t.TPuts(ti.TParm(ti.MouseMode, 0)) 405 t.curstyle = Style(-1) 406 t.clear = false 407 t.fini = true 408 409 select { 410 case <-t.quit: 411 // do nothing, already closed 412 413 default: 414 close(t.quit) 415 } 416 417 t.termioFini() 418} 419 420func (t *tScreen) SetStyle(style Style) { 421 t.Lock() 422 if !t.fini { 423 t.style = style 424 } 425 t.Unlock() 426} 427 428func (t *tScreen) Clear() { 429 t.Fill(' ', t.style) 430} 431 432func (t *tScreen) Fill(r rune, style Style) { 433 t.Lock() 434 if !t.fini { 435 t.cells.Fill(r, style) 436 } 437 t.Unlock() 438} 439 440func (t *tScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) { 441 t.Lock() 442 if !t.fini { 443 t.cells.SetContent(x, y, mainc, combc, style) 444 } 445 t.Unlock() 446} 447 448func (t *tScreen) GetContent(x, y int) (rune, []rune, Style, int) { 449 t.Lock() 450 mainc, combc, style, width := t.cells.GetContent(x, y) 451 t.Unlock() 452 return mainc, combc, style, width 453} 454 455func (t *tScreen) SetCell(x, y int, style Style, ch ...rune) { 456 if len(ch) > 0 { 457 t.SetContent(x, y, ch[0], ch[1:], style) 458 } else { 459 t.SetContent(x, y, ' ', nil, style) 460 } 461} 462 463func (t *tScreen) encodeRune(r rune, buf []byte) []byte { 464 465 nb := make([]byte, 6) 466 ob := make([]byte, 6) 467 num := utf8.EncodeRune(ob, r) 468 ob = ob[:num] 469 dst := 0 470 var err error 471 if enc := t.encoder; enc != nil { 472 enc.Reset() 473 dst, _, err = enc.Transform(nb, ob, true) 474 } 475 if err != nil || dst == 0 || nb[0] == '\x1a' { 476 // Combining characters are elided 477 if len(buf) == 0 { 478 if acs, ok := t.acs[r]; ok { 479 buf = append(buf, []byte(acs)...) 480 } else if fb, ok := t.fallback[r]; ok { 481 buf = append(buf, []byte(fb)...) 482 } else { 483 buf = append(buf, '?') 484 } 485 } 486 } else { 487 buf = append(buf, nb[:dst]...) 488 } 489 490 return buf 491} 492 493func (t *tScreen) sendFgBg(fg Color, bg Color) { 494 ti := t.ti 495 if ti.Colors == 0 { 496 return 497 } 498 if t.truecolor { 499 if ti.SetFgBgRGB != "" && 500 fg != ColorDefault && bg != ColorDefault { 501 r1, g1, b1 := fg.RGB() 502 r2, g2, b2 := bg.RGB() 503 t.TPuts(ti.TParm(ti.SetFgBgRGB, 504 int(r1), int(g1), int(b1), 505 int(r2), int(g2), int(b2))) 506 } else { 507 if fg != ColorDefault && ti.SetFgRGB != "" { 508 r, g, b := fg.RGB() 509 t.TPuts(ti.TParm(ti.SetFgRGB, 510 int(r), int(g), int(b))) 511 } 512 if bg != ColorDefault && ti.SetBgRGB != "" { 513 r, g, b := bg.RGB() 514 t.TPuts(ti.TParm(ti.SetBgRGB, 515 int(r), int(g), int(b))) 516 } 517 } 518 return 519 } 520 521 if fg != ColorDefault { 522 if v, ok := t.colors[fg]; ok { 523 fg = v 524 } else { 525 v = FindColor(fg, t.palette) 526 t.colors[fg] = v 527 fg = v 528 } 529 } 530 531 if bg != ColorDefault { 532 if v, ok := t.colors[bg]; ok { 533 bg = v 534 } else { 535 v = FindColor(bg, t.palette) 536 t.colors[bg] = v 537 bg = v 538 } 539 } 540 541 if ti.SetFgBg != "" && fg != ColorDefault && bg != ColorDefault { 542 t.TPuts(ti.TParm(ti.SetFgBg, int(fg), int(bg))) 543 } else { 544 if fg != ColorDefault && ti.SetFg != "" { 545 t.TPuts(ti.TParm(ti.SetFg, int(fg))) 546 } 547 if bg != ColorDefault && ti.SetBg != "" { 548 t.TPuts(ti.TParm(ti.SetBg, int(bg))) 549 } 550 } 551} 552 553func (t *tScreen) drawCell(x, y int) int { 554 555 ti := t.ti 556 557 mainc, combc, style, width := t.cells.GetContent(x, y) 558 if !t.cells.Dirty(x, y) { 559 return width 560 } 561 562 if t.cy != y || t.cx != x { 563 t.TPuts(ti.TGoto(x, y)) 564 t.cx = x 565 t.cy = y 566 } 567 568 if style == StyleDefault { 569 style = t.style 570 } 571 if style != t.curstyle { 572 fg, bg, attrs := style.Decompose() 573 574 t.TPuts(ti.AttrOff) 575 576 t.sendFgBg(fg, bg) 577 if attrs&AttrBold != 0 { 578 t.TPuts(ti.Bold) 579 } 580 if attrs&AttrUnderline != 0 { 581 t.TPuts(ti.Underline) 582 } 583 if attrs&AttrReverse != 0 { 584 t.TPuts(ti.Reverse) 585 } 586 if attrs&AttrBlink != 0 { 587 t.TPuts(ti.Blink) 588 } 589 if attrs&AttrDim != 0 { 590 t.TPuts(ti.Dim) 591 } 592 t.curstyle = style 593 } 594 // now emit runes - taking care to not overrun width with a 595 // wide character, and to ensure that we emit exactly one regular 596 // character followed up by any residual combing characters 597 598 if width < 1 { 599 width = 1 600 } 601 602 var str string 603 604 buf := make([]byte, 0, 6) 605 606 buf = t.encodeRune(mainc, buf) 607 for _, r := range combc { 608 buf = t.encodeRune(r, buf) 609 } 610 611 str = string(buf) 612 if width > 1 && str == "?" { 613 // No FullWidth character support 614 str = "? " 615 t.cx = -1 616 } 617 618 // XXX: check for hazeltine not being able to display ~ 619 620 if x > t.w-width { 621 // too wide to fit; emit a single space instead 622 width = 1 623 str = " " 624 } 625 t.writeString(str) 626 t.cx += width 627 t.cells.SetDirty(x, y, false) 628 if width > 1 { 629 t.cx = -1 630 } 631 632 return width 633} 634 635func (t *tScreen) ShowCursor(x, y int) { 636 t.Lock() 637 t.cursorx = x 638 t.cursory = y 639 t.Unlock() 640} 641 642func (t *tScreen) HideCursor() { 643 t.ShowCursor(-1, -1) 644} 645 646func (t *tScreen) showCursor() { 647 648 x, y := t.cursorx, t.cursory 649 w, h := t.cells.Size() 650 if x < 0 || y < 0 || x >= w || y >= h { 651 t.hideCursor() 652 return 653 } 654 t.TPuts(t.ti.TGoto(x, y)) 655 t.TPuts(t.ti.ShowCursor) 656 t.cx = x 657 t.cy = y 658} 659 660// writeString sends a string to the terminal. The string is sent as-is and 661// this function does not expand inline padding indications (of the form 662// $<[delay]> where [delay] is msec). In order to have these expanded, use 663// TPuts. If the screen is "buffering", the string is collected in a buffer, 664// with the intention that the entire buffer be sent to the terminal in one 665// write operation at some point later. 666func (t *tScreen) writeString(s string) { 667 if t.buffering { 668 io.WriteString(&t.buf, s) 669 } else { 670 io.WriteString(t.out, s) 671 } 672} 673 674func (t *tScreen) TPuts(s string) { 675 if t.buffering { 676 t.ti.TPuts(&t.buf, s) 677 } else { 678 t.ti.TPuts(t.out, s) 679 } 680} 681 682func (t *tScreen) Show() { 683 t.Lock() 684 if !t.fini { 685 t.resize() 686 t.draw() 687 } 688 t.Unlock() 689} 690 691func (t *tScreen) clearScreen() { 692 fg, bg, _ := t.style.Decompose() 693 t.sendFgBg(fg, bg) 694 t.TPuts(t.ti.Clear) 695 t.clear = false 696} 697 698func (t *tScreen) hideCursor() { 699 // does not update cursor position 700 if t.ti.HideCursor != "" { 701 t.TPuts(t.ti.HideCursor) 702 } else { 703 // No way to hide cursor, stick it 704 // at bottom right of screen 705 t.cx, t.cy = t.cells.Size() 706 t.TPuts(t.ti.TGoto(t.cx, t.cy)) 707 } 708} 709 710func (t *tScreen) draw() { 711 // clobber cursor position, because we're gonna change it all 712 t.cx = -1 713 t.cy = -1 714 715 t.buf.Reset() 716 t.buffering = true 717 defer func() { 718 t.buffering = false 719 }() 720 721 // hide the cursor while we move stuff around 722 t.hideCursor() 723 724 if t.clear { 725 t.clearScreen() 726 } 727 728 for y := 0; y < t.h; y++ { 729 for x := 0; x < t.w; x++ { 730 width := t.drawCell(x, y) 731 if width > 1 { 732 if x+1 < t.w { 733 // this is necessary so that if we ever 734 // go back to drawing that cell, we 735 // actually will *draw* it. 736 t.cells.SetDirty(x+1, y, true) 737 } 738 } 739 x += width - 1 740 } 741 } 742 743 // restore the cursor 744 t.showCursor() 745 746 t.buf.WriteTo(t.out) 747} 748 749func (t *tScreen) EnableMouse() { 750 if len(t.mouse) != 0 { 751 t.TPuts(t.ti.TParm(t.ti.MouseMode, 1)) 752 } 753} 754 755func (t *tScreen) DisableMouse() { 756 if len(t.mouse) != 0 { 757 t.TPuts(t.ti.TParm(t.ti.MouseMode, 0)) 758 } 759} 760 761func (t *tScreen) Size() (int, int) { 762 t.Lock() 763 w, h := t.w, t.h 764 t.Unlock() 765 return w, h 766} 767 768func (t *tScreen) resize() { 769 if w, h, e := t.getWinSize(); e == nil { 770 if w != t.w || h != t.h { 771 t.cx = -1 772 t.cy = -1 773 774 t.cells.Resize(w, h) 775 t.cells.Invalidate() 776 t.h = h 777 t.w = w 778 ev := NewEventResize(w, h) 779 t.PostEvent(ev) 780 } 781 } 782} 783 784func (t *tScreen) Colors() int { 785 // this doesn't change, no need for lock 786 if t.truecolor { 787 return 1 << 24 788 } 789 return t.ti.Colors 790} 791 792func (t *tScreen) PollEvent() Event { 793 select { 794 case <-t.quit: 795 return nil 796 case ev := <-t.evch: 797 return ev 798 } 799} 800 801// vtACSNames is a map of bytes defined by terminfo that are used in 802// the terminals Alternate Character Set to represent other glyphs. 803// For example, the upper left corner of the box drawing set can be 804// displayed by printing "l" while in the alternate character set. 805// Its not quite that simple, since the "l" is the terminfo name, 806// and it may be necessary to use a different character based on 807// the terminal implementation (or the terminal may lack support for 808// this altogether). See buildAcsMap below for detail. 809var vtACSNames = map[byte]rune{ 810 '+': RuneRArrow, 811 ',': RuneLArrow, 812 '-': RuneUArrow, 813 '.': RuneDArrow, 814 '0': RuneBlock, 815 '`': RuneDiamond, 816 'a': RuneCkBoard, 817 'b': '␉', // VT100, Not defined by terminfo 818 'c': '␌', // VT100, Not defined by terminfo 819 'd': '␋', // VT100, Not defined by terminfo 820 'e': '␊', // VT100, Not defined by terminfo 821 'f': RuneDegree, 822 'g': RunePlMinus, 823 'h': RuneBoard, 824 'i': RuneLantern, 825 'j': RuneLRCorner, 826 'k': RuneURCorner, 827 'l': RuneULCorner, 828 'm': RuneLLCorner, 829 'n': RunePlus, 830 'o': RuneS1, 831 'p': RuneS3, 832 'q': RuneHLine, 833 'r': RuneS7, 834 's': RuneS9, 835 't': RuneLTee, 836 'u': RuneRTee, 837 'v': RuneBTee, 838 'w': RuneTTee, 839 'x': RuneVLine, 840 'y': RuneLEqual, 841 'z': RuneGEqual, 842 '{': RunePi, 843 '|': RuneNEqual, 844 '}': RuneSterling, 845 '~': RuneBullet, 846} 847 848// buildAcsMap builds a map of characters that we translate from Unicode to 849// alternate character encodings. To do this, we use the standard VT100 ACS 850// maps. This is only done if the terminal lacks support for Unicode; we 851// always prefer to emit Unicode glyphs when we are able. 852func (t *tScreen) buildAcsMap() { 853 acsstr := t.ti.AltChars 854 t.acs = make(map[rune]string) 855 for len(acsstr) > 2 { 856 srcv := acsstr[0] 857 dstv := string(acsstr[1]) 858 if r, ok := vtACSNames[srcv]; ok { 859 t.acs[r] = t.ti.EnterAcs + dstv + t.ti.ExitAcs 860 } 861 acsstr = acsstr[2:] 862 } 863} 864 865func (t *tScreen) PostEventWait(ev Event) { 866 t.evch <- ev 867} 868 869func (t *tScreen) PostEvent(ev Event) error { 870 select { 871 case t.evch <- ev: 872 return nil 873 default: 874 return ErrEventQFull 875 } 876} 877 878func (t *tScreen) clip(x, y int) (int, int) { 879 w, h := t.cells.Size() 880 if x < 0 { 881 x = 0 882 } 883 if y < 0 { 884 y = 0 885 } 886 if x > w-1 { 887 x = w - 1 888 } 889 if y > h-1 { 890 y = h - 1 891 } 892 return x, y 893} 894 895// buildMouseEvent returns an event based on the supplied coordinates and button 896// state. Note that the screen's mouse button state is updated based on the 897// input to this function (i.e. it mutates the receiver). 898func (t *tScreen) buildMouseEvent(x, y, btn int) *EventMouse { 899 900 // XTerm mouse events only report at most one button at a time, 901 // which may include a wheel button. Wheel motion events are 902 // reported as single impulses, while other button events are reported 903 // as separate press & release events. 904 905 button := ButtonNone 906 mod := ModNone 907 908 // Mouse wheel has bit 6 set, no release events. It should be noted 909 // that wheel events are sometimes misdelivered as mouse button events 910 // during a click-drag, so we debounce these, considering them to be 911 // button press events unless we see an intervening release event. 912 switch btn & 0x43 { 913 case 0: 914 button = Button1 915 t.wasbtn = true 916 case 1: 917 button = Button2 918 t.wasbtn = true 919 case 2: 920 button = Button3 921 t.wasbtn = true 922 case 3: 923 button = ButtonNone 924 t.wasbtn = false 925 case 0x40: 926 if !t.wasbtn { 927 button = WheelUp 928 } else { 929 button = Button1 930 } 931 case 0x41: 932 if !t.wasbtn { 933 button = WheelDown 934 } else { 935 button = Button2 936 } 937 } 938 939 if btn&0x4 != 0 { 940 mod |= ModShift 941 } 942 if btn&0x8 != 0 { 943 mod |= ModAlt 944 } 945 if btn&0x10 != 0 { 946 mod |= ModCtrl 947 } 948 949 // Some terminals will report mouse coordinates outside the 950 // screen, especially with click-drag events. Clip the coordinates 951 // to the screen in that case. 952 x, y = t.clip(x, y) 953 954 return NewEventMouse(x, y, button, mod) 955} 956 957// parseSgrMouse attempts to locate an SGR mouse record at the start of the 958// buffer. It returns true, true if it found one, and the associated bytes 959// be removed from the buffer. It returns true, false if the buffer might 960// contain such an event, but more bytes are necessary (partial match), and 961// false, false if the content is definitely *not* an SGR mouse record. 962func (t *tScreen) parseSgrMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { 963 964 b := buf.Bytes() 965 966 var x, y, btn, state int 967 dig := false 968 neg := false 969 motion := false 970 i := 0 971 val := 0 972 973 for i = range b { 974 switch b[i] { 975 case '\x1b': 976 if state != 0 { 977 return false, false 978 } 979 state = 1 980 981 case '\x9b': 982 if state != 0 { 983 return false, false 984 } 985 state = 2 986 987 case '[': 988 if state != 1 { 989 return false, false 990 } 991 state = 2 992 993 case '<': 994 if state != 2 { 995 return false, false 996 } 997 val = 0 998 dig = false 999 neg = false 1000 state = 3 1001 1002 case '-': 1003 if state != 3 && state != 4 && state != 5 { 1004 return false, false 1005 } 1006 if dig || neg { 1007 return false, false 1008 } 1009 neg = true // stay in state 1010 1011 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 1012 if state != 3 && state != 4 && state != 5 { 1013 return false, false 1014 } 1015 val *= 10 1016 val += int(b[i] - '0') 1017 dig = true // stay in state 1018 1019 case ';': 1020 if neg { 1021 val = -val 1022 } 1023 switch state { 1024 case 3: 1025 btn, val = val, 0 1026 neg, dig, state = false, false, 4 1027 case 4: 1028 x, val = val-1, 0 1029 neg, dig, state = false, false, 5 1030 default: 1031 return false, false 1032 } 1033 1034 case 'm', 'M': 1035 if state != 5 { 1036 return false, false 1037 } 1038 if neg { 1039 val = -val 1040 } 1041 y = val - 1 1042 1043 motion = (btn & 32) != 0 1044 btn &^= 32 1045 if b[i] == 'm' { 1046 // mouse release, clear all buttons 1047 btn |= 3 1048 btn &^= 0x40 1049 t.buttondn = false 1050 } else if motion { 1051 /* 1052 * Some broken terminals appear to send 1053 * mouse button one motion events, instead of 1054 * encoding 35 (no buttons) into these events. 1055 * We resolve these by looking for a non-motion 1056 * event first. 1057 */ 1058 if !t.buttondn { 1059 btn |= 3 1060 btn &^= 0x40 1061 } 1062 } else { 1063 t.buttondn = true 1064 } 1065 // consume the event bytes 1066 for i >= 0 { 1067 buf.ReadByte() 1068 i-- 1069 } 1070 *evs = append(*evs, t.buildMouseEvent(x, y, btn)) 1071 return true, true 1072 } 1073 } 1074 1075 // incomplete & inconclusve at this point 1076 return true, false 1077} 1078 1079// parseXtermMouse is like parseSgrMouse, but it parses a legacy 1080// X11 mouse record. 1081func (t *tScreen) parseXtermMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { 1082 1083 b := buf.Bytes() 1084 1085 state := 0 1086 btn := 0 1087 x := 0 1088 y := 0 1089 1090 for i := range b { 1091 switch state { 1092 case 0: 1093 switch b[i] { 1094 case '\x1b': 1095 state = 1 1096 case '\x9b': 1097 state = 2 1098 default: 1099 return false, false 1100 } 1101 case 1: 1102 if b[i] != '[' { 1103 return false, false 1104 } 1105 state = 2 1106 case 2: 1107 if b[i] != 'M' { 1108 return false, false 1109 } 1110 state++ 1111 case 3: 1112 btn = int(b[i]) 1113 state++ 1114 case 4: 1115 x = int(b[i]) - 32 - 1 1116 state++ 1117 case 5: 1118 y = int(b[i]) - 32 - 1 1119 for i >= 0 { 1120 buf.ReadByte() 1121 i-- 1122 } 1123 *evs = append(*evs, t.buildMouseEvent(x, y, btn)) 1124 return true, true 1125 } 1126 } 1127 return true, false 1128} 1129 1130func (t *tScreen) parseFunctionKey(buf *bytes.Buffer, evs *[]Event) (bool, bool) { 1131 b := buf.Bytes() 1132 partial := false 1133 for e, k := range t.keycodes { 1134 esc := []byte(e) 1135 if (len(esc) == 1) && (esc[0] == '\x1b') { 1136 continue 1137 } 1138 if bytes.HasPrefix(b, esc) { 1139 // matched 1140 var r rune 1141 if len(esc) == 1 { 1142 r = rune(b[0]) 1143 } 1144 mod := k.mod 1145 if t.escaped { 1146 mod |= ModAlt 1147 t.escaped = false 1148 } 1149 *evs = append(*evs, NewEventKey(k.key, r, mod)) 1150 for i := 0; i < len(esc); i++ { 1151 buf.ReadByte() 1152 } 1153 return true, true 1154 } 1155 if bytes.HasPrefix(esc, b) { 1156 partial = true 1157 } 1158 } 1159 return partial, false 1160} 1161 1162func (t *tScreen) parseRune(buf *bytes.Buffer, evs *[]Event) (bool, bool) { 1163 b := buf.Bytes() 1164 if b[0] >= ' ' && b[0] <= 0x7F { 1165 // printable ASCII easy to deal with -- no encodings 1166 mod := ModNone 1167 if t.escaped { 1168 mod = ModAlt 1169 t.escaped = false 1170 } 1171 *evs = append(*evs, NewEventKey(KeyRune, rune(b[0]), mod)) 1172 buf.ReadByte() 1173 return true, true 1174 } 1175 1176 if b[0] < 0x80 { 1177 // Low numbered values are control keys, not runes. 1178 return false, false 1179 } 1180 1181 utfb := make([]byte, 12) 1182 for l := 1; l <= len(b); l++ { 1183 t.decoder.Reset() 1184 nout, nin, e := t.decoder.Transform(utfb, b[:l], true) 1185 if e == transform.ErrShortSrc { 1186 continue 1187 } 1188 if nout != 0 { 1189 r, _ := utf8.DecodeRune(utfb[:nout]) 1190 if r != utf8.RuneError { 1191 mod := ModNone 1192 if t.escaped { 1193 mod = ModAlt 1194 t.escaped = false 1195 } 1196 *evs = append(*evs, NewEventKey(KeyRune, r, mod)) 1197 } 1198 for nin > 0 { 1199 buf.ReadByte() 1200 nin-- 1201 } 1202 return true, true 1203 } 1204 } 1205 // Looks like potential escape 1206 return true, false 1207} 1208 1209func (t *tScreen) scanInput(buf *bytes.Buffer, expire bool) { 1210 evs := t.collectEventsFromInput(buf, expire) 1211 1212 for _, ev := range evs { 1213 t.PostEventWait(ev) 1214 } 1215} 1216 1217// Return an array of Events extracted from the supplied buffer. This is done 1218// while holding the screen's lock - the events can then be queued for 1219// application processing with the lock released. 1220func (t *tScreen) collectEventsFromInput(buf *bytes.Buffer, expire bool) []Event { 1221 1222 res := make([]Event, 0, 20) 1223 1224 t.Lock() 1225 defer t.Unlock() 1226 1227 for { 1228 b := buf.Bytes() 1229 if len(b) == 0 { 1230 buf.Reset() 1231 return res 1232 } 1233 1234 partials := 0 1235 1236 if part, comp := t.parseRune(buf, &res); comp { 1237 continue 1238 } else if part { 1239 partials++ 1240 } 1241 1242 if part, comp := t.parseFunctionKey(buf, &res); comp { 1243 continue 1244 } else if part { 1245 partials++ 1246 } 1247 1248 // Only parse mouse records if this term claims to have 1249 // mouse support 1250 1251 if t.ti.Mouse != "" { 1252 if part, comp := t.parseXtermMouse(buf, &res); comp { 1253 continue 1254 } else if part { 1255 partials++ 1256 } 1257 1258 if part, comp := t.parseSgrMouse(buf, &res); comp { 1259 continue 1260 } else if part { 1261 partials++ 1262 } 1263 } 1264 1265 if partials == 0 || expire { 1266 if b[0] == '\x1b' { 1267 if len(b) == 1 { 1268 res = append(res, NewEventKey(KeyEsc, 0, ModNone)) 1269 t.escaped = false 1270 } else { 1271 t.escaped = true 1272 } 1273 buf.ReadByte() 1274 continue 1275 } 1276 // Nothing was going to match, or we timed out 1277 // waiting for more data -- just deliver the characters 1278 // to the app & let them sort it out. Possibly we 1279 // should only do this for control characters like ESC. 1280 by, _ := buf.ReadByte() 1281 mod := ModNone 1282 if t.escaped { 1283 t.escaped = false 1284 mod = ModAlt 1285 } 1286 res = append(res, NewEventKey(KeyRune, rune(by), mod)) 1287 continue 1288 } 1289 1290 // well we have some partial data, wait until we get 1291 // some more 1292 break 1293 } 1294 1295 return res 1296} 1297 1298func (t *tScreen) mainLoop() { 1299 buf := &bytes.Buffer{} 1300 for { 1301 select { 1302 case <-t.quit: 1303 close(t.indoneq) 1304 return 1305 case <-t.sigwinch: 1306 t.Lock() 1307 t.cx = -1 1308 t.cy = -1 1309 t.resize() 1310 t.cells.Invalidate() 1311 t.draw() 1312 t.Unlock() 1313 continue 1314 case <-t.keytimer.C: 1315 // If the timer fired, and the current time 1316 // is after the expiration of the escape sequence, 1317 // then we assume the escape sequence reached it's 1318 // conclusion, and process the chunk independently. 1319 // This lets us detect conflicts such as a lone ESC. 1320 if buf.Len() > 0 { 1321 if time.Now().After(t.keyexpire) { 1322 t.scanInput(buf, true) 1323 } 1324 } 1325 if buf.Len() > 0 { 1326 if !t.keytimer.Stop() { 1327 select { 1328 case <-t.keytimer.C: 1329 default: 1330 } 1331 } 1332 t.keytimer.Reset(time.Millisecond * 50) 1333 } 1334 case chunk := <-t.keychan: 1335 buf.Write(chunk) 1336 t.keyexpire = time.Now().Add(time.Millisecond * 50) 1337 t.scanInput(buf, false) 1338 if !t.keytimer.Stop() { 1339 select { 1340 case <-t.keytimer.C: 1341 default: 1342 } 1343 } 1344 if buf.Len() > 0 { 1345 t.keytimer.Reset(time.Millisecond * 50) 1346 } 1347 } 1348 } 1349} 1350 1351func (t *tScreen) inputLoop() { 1352 1353 for { 1354 chunk := make([]byte, 128) 1355 n, e := t.in.Read(chunk) 1356 switch e { 1357 case io.EOF: 1358 case nil: 1359 default: 1360 t.PostEvent(NewEventError(e)) 1361 return 1362 } 1363 t.keychan <- chunk[:n] 1364 } 1365} 1366 1367func (t *tScreen) Sync() { 1368 t.Lock() 1369 t.cx = -1 1370 t.cy = -1 1371 if !t.fini { 1372 t.resize() 1373 t.clear = true 1374 t.cells.Invalidate() 1375 t.draw() 1376 } 1377 t.Unlock() 1378} 1379 1380func (t *tScreen) CharacterSet() string { 1381 return t.charset 1382} 1383 1384func (t *tScreen) RegisterRuneFallback(orig rune, fallback string) { 1385 t.Lock() 1386 t.fallback[orig] = fallback 1387 t.Unlock() 1388} 1389 1390func (t *tScreen) UnregisterRuneFallback(orig rune) { 1391 t.Lock() 1392 delete(t.fallback, orig) 1393 t.Unlock() 1394} 1395 1396func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool { 1397 1398 if enc := t.encoder; enc != nil { 1399 nb := make([]byte, 6) 1400 ob := make([]byte, 6) 1401 num := utf8.EncodeRune(ob, r) 1402 1403 enc.Reset() 1404 dst, _, err := enc.Transform(nb, ob[:num], true) 1405 if dst != 0 && err == nil && nb[0] != '\x1A' { 1406 return true 1407 } 1408 } 1409 // Terminal fallbacks always permitted, since we assume they are 1410 // basically nearly perfect renditions. 1411 if _, ok := t.acs[r]; ok { 1412 return true 1413 } 1414 if !checkFallbacks { 1415 return false 1416 } 1417 if _, ok := t.fallback[r]; ok { 1418 return true 1419 } 1420 return false 1421} 1422 1423func (t *tScreen) HasMouse() bool { 1424 return len(t.mouse) != 0 1425} 1426 1427func (t *tScreen) HasKey(k Key) bool { 1428 if k == KeyRune { 1429 return true 1430 } 1431 return t.keyexist[k] 1432} 1433 1434func (t *tScreen) Resize(int, int, int, int) {} 1435