render.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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" //@BUG we probably want that
  8. "sync"
  9. "time"
  10. // "fmt"
  11. // "os"
  12. // "runtime/debug"
  13. // "bytes"
  14. // stack "notabug.org/themusicgod1/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, true) //3rd argument is just a guess @BUG ?
  85. if err != nil {
  86. debug.PrintStack()
  87. os.Exit(1)
  88. }
  89. // io.WriteString(os.Stdout,gs)
  90. //p := &stack.Palette{} @BUG not in our version of panicparse?
  91. // buckets := stack.SortBuckets(stack.Bucketize(gs, stack.AnyValue)) @BUG not in our version of panicparse?
  92. buckets := stack.Aggregate(gs, stack.AnyValue)
  93. srcLen, pkgLen := stack.CalcLengths(buckets, false)
  94. for _, bucket := range buckets {
  95. // io.WriteString(os.Stdout, p.BucketHeader(&bucket, false, len(buckets) > 1))
  96. // io.WriteString(os.Stdout, p.StackLines(&bucket.Signature, srcLen, pkgLen, false))
  97. io.WriteString(os.Stdout,"we are here")
  98. }
  99. os.Exit(1)
  100. }
  101. }()*/ //@BUG
  102. for _, b := range bs {
  103. buf := b.Buffer()
  104. // set cels in buf
  105. for p, c := range buf.CellMap {
  106. if p.In(buf.Area) {
  107. tm.SetCell(p.X, p.Y, c.Ch, toTmAttr(c.Fg), toTmAttr(c.Bg))
  108. }
  109. }
  110. }
  111. renderLock.Lock()
  112. // render
  113. tm.Flush()
  114. renderLock.Unlock()
  115. }
  116. func Clear() {
  117. tm.Clear(tm.ColorDefault, toTmAttr(ThemeAttr("bg")))
  118. }
  119. func clearArea(r image.Rectangle, bg Attribute) {
  120. for i := r.Min.X; i < r.Max.X; i++ {
  121. for j := r.Min.Y; j < r.Max.Y; j++ {
  122. tm.SetCell(i, j, ' ', tm.ColorDefault, toTmAttr(bg))
  123. }
  124. }
  125. }
  126. func ClearArea(r image.Rectangle, bg Attribute) {
  127. clearArea(r, bg)
  128. tm.Flush()
  129. }
  130. var renderJobs chan []Bufferer
  131. func Render(bs ...Bufferer) {
  132. //go func() { renderJobs <- bs }()
  133. renderJobs <- bs
  134. }