events.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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. "path"
  7. "strconv"
  8. "sync"
  9. "time"
  10. "github.com/nsf/termbox-go"
  11. )
  12. type Event struct {
  13. Type string
  14. Path string
  15. From string
  16. To string
  17. Data interface{}
  18. Time int64
  19. }
  20. var sysEvtChs []chan Event
  21. type EvtKbd struct {
  22. KeyStr string
  23. }
  24. func evtKbd(e termbox.Event) EvtKbd {
  25. ek := EvtKbd{}
  26. k := string(e.Ch)
  27. pre := ""
  28. mod := ""
  29. if e.Mod == termbox.ModAlt {
  30. mod = "M-"
  31. }
  32. if e.Ch == 0 {
  33. if e.Key > 0xFFFF-12 {
  34. k = "<f" + strconv.Itoa(0xFFFF-int(e.Key)+1) + ">"
  35. } else if e.Key > 0xFFFF-25 {
  36. ks := []string{"<insert>", "<delete>", "<home>", "<end>", "<previous>", "<next>", "<up>", "<down>", "<left>", "<right>"}
  37. k = ks[0xFFFF-int(e.Key)-12]
  38. }
  39. if e.Key <= 0x7F {
  40. pre = "C-"
  41. k = string('a' - 1 + int(e.Key))
  42. kmap := map[termbox.Key][2]string{
  43. termbox.KeyCtrlSpace: {"C-", "<space>"},
  44. termbox.KeyBackspace: {"", "<backspace>"},
  45. termbox.KeyTab: {"", "<tab>"},
  46. termbox.KeyEnter: {"", "<enter>"},
  47. termbox.KeyEsc: {"", "<escape>"},
  48. termbox.KeyCtrlBackslash: {"C-", "\\"},
  49. termbox.KeyCtrlSlash: {"C-", "/"},
  50. termbox.KeySpace: {"", "<space>"},
  51. termbox.KeyCtrl8: {"C-", "8"},
  52. }
  53. if sk, ok := kmap[e.Key]; ok {
  54. pre = sk[0]
  55. k = sk[1]
  56. }
  57. }
  58. }
  59. ek.KeyStr = pre + mod + k
  60. return ek
  61. }
  62. func crtTermboxEvt(e termbox.Event) Event {
  63. systypemap := map[termbox.EventType]string{
  64. termbox.EventKey: "keyboard",
  65. termbox.EventResize: "window",
  66. termbox.EventMouse: "mouse",
  67. termbox.EventError: "error",
  68. termbox.EventInterrupt: "interrupt",
  69. }
  70. ne := Event{From: "/sys", Time: time.Now().Unix()}
  71. typ := e.Type
  72. ne.Type = systypemap[typ]
  73. switch typ {
  74. case termbox.EventKey:
  75. kbd := evtKbd(e)
  76. ne.Path = "/sys/kbd/" + kbd.KeyStr
  77. ne.Data = kbd
  78. case termbox.EventResize:
  79. wnd := EvtWnd{}
  80. wnd.Width = e.Width
  81. wnd.Height = e.Height
  82. ne.Path = "/sys/wnd/resize"
  83. ne.Data = wnd
  84. case termbox.EventError:
  85. err := EvtErr(e.Err)
  86. ne.Path = "/sys/err"
  87. ne.Data = err
  88. case termbox.EventMouse:
  89. m := EvtMouse{}
  90. m.X = e.MouseX
  91. m.Y = e.MouseY
  92. ne.Path = "/sys/mouse"
  93. ne.Data = m
  94. }
  95. return ne
  96. }
  97. type EvtWnd struct {
  98. Width int
  99. Height int
  100. }
  101. type EvtMouse struct {
  102. X int
  103. Y int
  104. Press string
  105. }
  106. type EvtErr error
  107. func hookTermboxEvt() {
  108. for {
  109. e := termbox.PollEvent()
  110. for _, c := range sysEvtChs {
  111. func(ch chan Event) {
  112. ch <- crtTermboxEvt(e)
  113. }(c)
  114. }
  115. }
  116. }
  117. func NewSysEvtCh() chan Event {
  118. ec := make(chan Event)
  119. sysEvtChs = append(sysEvtChs, ec)
  120. return ec
  121. }
  122. var DefaultEvtStream = NewEvtStream()
  123. type EvtStream struct {
  124. sync.RWMutex
  125. srcMap map[string]chan Event
  126. stream chan Event
  127. wg sync.WaitGroup
  128. sigStopLoop chan Event
  129. Handlers map[string]func(Event)
  130. hook func(Event)
  131. }
  132. func NewEvtStream() *EvtStream {
  133. return &EvtStream{
  134. srcMap: make(map[string]chan Event),
  135. stream: make(chan Event),
  136. Handlers: make(map[string]func(Event)),
  137. sigStopLoop: make(chan Event),
  138. }
  139. }
  140. func (es *EvtStream) Init() {
  141. es.Merge("internal", es.sigStopLoop)
  142. go func() {
  143. es.wg.Wait()
  144. close(es.stream)
  145. }()
  146. }
  147. func cleanPath(p string) string {
  148. if p == "" {
  149. return "/"
  150. }
  151. if p[0] != '/' {
  152. p = "/" + p
  153. }
  154. return path.Clean(p)
  155. }
  156. func isPathMatch(pattern, path string) bool {
  157. if len(pattern) == 0 {
  158. return false
  159. }
  160. n := len(pattern)
  161. return len(path) >= n && path[0:n] == pattern
  162. }
  163. func (es *EvtStream) Merge(name string, ec chan Event) {
  164. es.Lock()
  165. defer es.Unlock()
  166. es.wg.Add(1)
  167. es.srcMap[name] = ec
  168. go func(a chan Event) {
  169. for n := range a {
  170. n.From = name
  171. es.stream <- n
  172. }
  173. es.wg.Done()
  174. }(ec)
  175. }
  176. func (es *EvtStream) Handle(path string, handler func(Event)) {
  177. es.Handlers[cleanPath(path)] = handler
  178. }
  179. func findMatch(mux map[string]func(Event), path string) string {
  180. n := -1
  181. pattern := ""
  182. for m := range mux {
  183. if !isPathMatch(m, path) {
  184. continue
  185. }
  186. if len(m) > n {
  187. pattern = m
  188. n = len(m)
  189. }
  190. }
  191. return pattern
  192. }
  193. // Remove all existing defined Handlers from the map
  194. func (es *EvtStream) ResetHandlers() {
  195. for Path, _ := range es.Handlers {
  196. delete(es.Handlers, Path)
  197. }
  198. return
  199. }
  200. func (es *EvtStream) match(path string) string {
  201. return findMatch(es.Handlers, path)
  202. }
  203. func (es *EvtStream) Hook(f func(Event)) {
  204. es.hook = f
  205. }
  206. func (es *EvtStream) Loop() {
  207. for e := range es.stream {
  208. switch e.Path {
  209. case "/sig/stoploop":
  210. return
  211. }
  212. func(a Event) {
  213. es.RLock()
  214. defer es.RUnlock()
  215. if pattern := es.match(a.Path); pattern != "" {
  216. es.Handlers[pattern](a)
  217. }
  218. }(e)
  219. if es.hook != nil {
  220. es.hook(e)
  221. }
  222. }
  223. }
  224. func (es *EvtStream) StopLoop() {
  225. go func() {
  226. e := Event{
  227. Path: "/sig/stoploop",
  228. }
  229. es.sigStopLoop <- e
  230. }()
  231. }
  232. func Merge(name string, ec chan Event) {
  233. DefaultEvtStream.Merge(name, ec)
  234. }
  235. func Handle(path string, handler func(Event)) {
  236. DefaultEvtStream.Handle(path, handler)
  237. }
  238. func ResetHandlers() {
  239. DefaultEvtStream.ResetHandlers()
  240. }
  241. func Loop() {
  242. DefaultEvtStream.Loop()
  243. }
  244. func StopLoop() {
  245. DefaultEvtStream.StopLoop()
  246. }
  247. type EvtTimer struct {
  248. Duration time.Duration
  249. Count uint64
  250. }
  251. func NewTimerCh(du time.Duration) chan Event {
  252. t := make(chan Event)
  253. go func(a chan Event) {
  254. n := uint64(0)
  255. for {
  256. n++
  257. time.Sleep(du)
  258. e := Event{}
  259. e.Type = "timer"
  260. e.Path = "/timer/" + du.String()
  261. e.Time = time.Now().Unix()
  262. e.Data = EvtTimer{
  263. Duration: du,
  264. Count: n,
  265. }
  266. t <- e
  267. }
  268. }(t)
  269. return t
  270. }
  271. var DefaultHandler = func(e Event) {
  272. }
  273. var usrEvtCh = make(chan Event)
  274. func SendCustomEvt(path string, data interface{}) {
  275. e := Event{}
  276. e.Path = path
  277. e.Data = data
  278. e.Time = time.Now().Unix()
  279. usrEvtCh <- e
  280. }