render.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
  2. // Use of this source code is governed by a MIT license that can
  3. // be found in the LICENSE file.
  4. package termui
  5. import (
  6. "image"
  7. "io"
  8. "sync"
  9. "time"
  10. "fmt"
  11. "os"
  12. "runtime/debug"
  13. "bytes"
  14. "github.com/maruel/panicparse/stack"
  15. tm "github.com/nsf/termbox-go"
  16. )
  17. // Bufferer should be implemented by all renderable components.
  18. type Bufferer interface {
  19. Buffer() Buffer
  20. }
  21. // Init initializes termui library. This function should be called before any others.
  22. // After initialization, the library must be finalized by 'Close' function.
  23. func Init() error {
  24. if err := tm.Init(); err != nil {
  25. return err
  26. }
  27. sysEvtChs = make([]chan Event, 0)
  28. go hookTermboxEvt()
  29. renderJobs = make(chan []Bufferer)
  30. //renderLock = new(sync.RWMutex)
  31. Body = NewGrid()
  32. Body.X = 0
  33. Body.Y = 0
  34. Body.BgColor = ThemeAttr("bg")
  35. Body.Width = TermWidth()
  36. DefaultEvtStream.Init()
  37. DefaultEvtStream.Merge("termbox", NewSysEvtCh())
  38. DefaultEvtStream.Merge("timer", NewTimerCh(time.Second))
  39. DefaultEvtStream.Merge("custom", usrEvtCh)
  40. DefaultEvtStream.Handle("/", DefaultHandler)
  41. DefaultEvtStream.Handle("/sys/wnd/resize", func(e Event) {
  42. w := e.Data.(EvtWnd)
  43. Body.Width = w.Width
  44. })
  45. DefaultWgtMgr = NewWgtMgr()
  46. DefaultEvtStream.Hook(DefaultWgtMgr.WgtHandlersHook())
  47. go func() {
  48. for bs := range renderJobs {
  49. render(bs...)
  50. }
  51. }()
  52. return nil
  53. }
  54. // Close finalizes termui library,
  55. // should be called after successful initialization when termui's functionality isn't required anymore.
  56. func Close() {
  57. tm.Close()
  58. }
  59. var renderLock sync.Mutex
  60. func termSync() {
  61. renderLock.Lock()
  62. tm.Sync()
  63. termWidth, termHeight = tm.Size()
  64. renderLock.Unlock()
  65. }
  66. // TermWidth returns the current terminal's width.
  67. func TermWidth() int {
  68. termSync()
  69. return termWidth
  70. }
  71. // TermHeight returns the current terminal's height.
  72. func TermHeight() int {
  73. termSync()
  74. return termHeight
  75. }
  76. // Render renders all Bufferer in the given order from left to right,
  77. // right could overlap on left ones.
  78. func render(bs ...Bufferer) {
  79. defer func() {
  80. if e := recover(); e != nil {
  81. Close()
  82. fmt.Fprintf(os.Stderr, "Captured a panic(value=%v) when rendering Bufferer. Exit termui and clean terminal...\nPrint stack trace:\n\n", e)
  83. //debug.PrintStack()
  84. gs, err := stack.ParseDump(bytes.NewReader(debug.Stack()), os.Stderr)
  85. if err != nil {
  86. debug.PrintStack()
  87. os.Exit(1)
  88. }
  89. p := &stack.Palette{}
  90. buckets := stack.SortBuckets(stack.Bucketize(gs, stack.AnyValue))
  91. srcLen, pkgLen := stack.CalcLengths(buckets, false)
  92. for _, bucket := range buckets {
  93. io.WriteString(os.Stdout, p.BucketHeader(&bucket, false, len(buckets) > 1))
  94. io.WriteString(os.Stdout, p.StackLines(&bucket.Signature, srcLen, pkgLen, false))
  95. }
  96. os.Exit(1)
  97. }
  98. }()
  99. for _, b := range bs {
  100. buf := b.Buffer()
  101. // set cels in buf
  102. for p, c := range buf.CellMap {
  103. if p.In(buf.Area) {
  104. tm.SetCell(p.X, p.Y, c.Ch, toTmAttr(c.Fg), toTmAttr(c.Bg))
  105. }
  106. }
  107. }
  108. renderLock.Lock()
  109. // render
  110. tm.Flush()
  111. renderLock.Unlock()
  112. }
  113. func Clear() {
  114. tm.Clear(tm.ColorDefault, toTmAttr(ThemeAttr("bg")))
  115. }
  116. func clearArea(r image.Rectangle, bg Attribute) {
  117. for i := r.Min.X; i < r.Max.X; i++ {
  118. for j := r.Min.Y; j < r.Max.Y; j++ {
  119. tm.SetCell(i, j, ' ', tm.ColorDefault, toTmAttr(bg))
  120. }
  121. }
  122. }
  123. func ClearArea(r image.Rectangle, bg Attribute) {
  124. clearArea(r, bg)
  125. tm.Flush()
  126. }
  127. var renderJobs chan []Bufferer
  128. func Render(bs ...Bufferer) {
  129. //go func() { renderJobs <- bs }()
  130. renderJobs <- bs
  131. }