123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- // Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
- // Use of this source code is governed by a MIT license that can
- // be found in the LICENSE file.
- package termui
- import (
- "path"
- "strconv"
- "sync"
- "time"
- "github.com/nsf/termbox-go"
- )
- type Event struct {
- Type string
- Path string
- From string
- To string
- Data interface{}
- Time int64
- }
- var sysEvtChs []chan Event
- type EvtKbd struct {
- KeyStr string
- }
- func evtKbd(e termbox.Event) EvtKbd {
- ek := EvtKbd{}
- k := string(e.Ch)
- pre := ""
- mod := ""
- if e.Mod == termbox.ModAlt {
- mod = "M-"
- }
- if e.Ch == 0 {
- if e.Key > 0xFFFF-12 {
- k = "<f" + strconv.Itoa(0xFFFF-int(e.Key)+1) + ">"
- } else if e.Key > 0xFFFF-25 {
- ks := []string{"<insert>", "<delete>", "<home>", "<end>", "<previous>", "<next>", "<up>", "<down>", "<left>", "<right>"}
- k = ks[0xFFFF-int(e.Key)-12]
- }
- if e.Key <= 0x7F {
- pre = "C-"
- k = string('a' - 1 + int(e.Key))
- kmap := map[termbox.Key][2]string{
- termbox.KeyCtrlSpace: {"C-", "<space>"},
- termbox.KeyBackspace: {"", "<backspace>"},
- termbox.KeyTab: {"", "<tab>"},
- termbox.KeyEnter: {"", "<enter>"},
- termbox.KeyEsc: {"", "<escape>"},
- termbox.KeyCtrlBackslash: {"C-", "\\"},
- termbox.KeyCtrlSlash: {"C-", "/"},
- termbox.KeySpace: {"", "<space>"},
- termbox.KeyCtrl8: {"C-", "8"},
- }
- if sk, ok := kmap[e.Key]; ok {
- pre = sk[0]
- k = sk[1]
- }
- }
- }
- ek.KeyStr = pre + mod + k
- return ek
- }
- func crtTermboxEvt(e termbox.Event) Event {
- systypemap := map[termbox.EventType]string{
- termbox.EventKey: "keyboard",
- termbox.EventResize: "window",
- termbox.EventMouse: "mouse",
- termbox.EventError: "error",
- termbox.EventInterrupt: "interrupt",
- }
- ne := Event{From: "/sys", Time: time.Now().Unix()}
- typ := e.Type
- ne.Type = systypemap[typ]
- switch typ {
- case termbox.EventKey:
- kbd := evtKbd(e)
- ne.Path = "/sys/kbd/" + kbd.KeyStr
- ne.Data = kbd
- case termbox.EventResize:
- wnd := EvtWnd{}
- wnd.Width = e.Width
- wnd.Height = e.Height
- ne.Path = "/sys/wnd/resize"
- ne.Data = wnd
- case termbox.EventError:
- err := EvtErr(e.Err)
- ne.Path = "/sys/err"
- ne.Data = err
- case termbox.EventMouse:
- m := EvtMouse{}
- m.X = e.MouseX
- m.Y = e.MouseY
- ne.Path = "/sys/mouse"
- ne.Data = m
- }
- return ne
- }
- type EvtWnd struct {
- Width int
- Height int
- }
- type EvtMouse struct {
- X int
- Y int
- Press string
- }
- type EvtErr error
- func hookTermboxEvt() {
- for {
- e := termbox.PollEvent()
- for _, c := range sysEvtChs {
- go func(ch chan Event) {
- ch <- crtTermboxEvt(e)
- }(c)
- }
- }
- }
- func NewSysEvtCh() chan Event {
- ec := make(chan Event)
- sysEvtChs = append(sysEvtChs, ec)
- return ec
- }
- var DefaultEvtStream = NewEvtStream()
- type EvtStream struct {
- sync.RWMutex
- srcMap map[string]chan Event
- stream chan Event
- wg sync.WaitGroup
- sigStopLoop chan Event
- Handlers map[string]func(Event)
- hook func(Event)
- }
- func NewEvtStream() *EvtStream {
- return &EvtStream{
- srcMap: make(map[string]chan Event),
- stream: make(chan Event),
- Handlers: make(map[string]func(Event)),
- sigStopLoop: make(chan Event),
- }
- }
- func (es *EvtStream) Init() {
- es.Merge("internal", es.sigStopLoop)
- go func() {
- es.wg.Wait()
- close(es.stream)
- }()
- }
- func cleanPath(p string) string {
- if p == "" {
- return "/"
- }
- if p[0] != '/' {
- p = "/" + p
- }
- return path.Clean(p)
- }
- func isPathMatch(pattern, path string) bool {
- if len(pattern) == 0 {
- return false
- }
- n := len(pattern)
- return len(path) >= n && path[0:n] == pattern
- }
- func (es *EvtStream) Merge(name string, ec chan Event) {
- es.Lock()
- defer es.Unlock()
- es.wg.Add(1)
- es.srcMap[name] = ec
- go func(a chan Event) {
- for n := range a {
- n.From = name
- es.stream <- n
- }
- es.wg.Done()
- }(ec)
- }
- func (es *EvtStream) Handle(path string, handler func(Event)) {
- es.Handlers[cleanPath(path)] = handler
- }
- func findMatch(mux map[string]func(Event), path string) string {
- n := -1
- pattern := ""
- for m := range mux {
- if !isPathMatch(m, path) {
- continue
- }
- if len(m) > n {
- pattern = m
- n = len(m)
- }
- }
- return pattern
- }
- // Remove all existing defined Handlers from the map
- func (es *EvtStream) ResetHandlers() {
- for Path, _ := range es.Handlers {
- delete(es.Handlers, Path)
- }
- return
- }
- func (es *EvtStream) match(path string) string {
- return findMatch(es.Handlers, path)
- }
- func (es *EvtStream) Hook(f func(Event)) {
- es.hook = f
- }
- func (es *EvtStream) Loop() {
- for e := range es.stream {
- switch e.Path {
- case "/sig/stoploop":
- return
- }
- go func(a Event) {
- es.RLock()
- defer es.RUnlock()
- if pattern := es.match(a.Path); pattern != "" {
- es.Handlers[pattern](a)
- }
- }(e)
- if es.hook != nil {
- es.hook(e)
- }
- }
- }
- func (es *EvtStream) StopLoop() {
- go func() {
- e := Event{
- Path: "/sig/stoploop",
- }
- es.sigStopLoop <- e
- }()
- }
- func Merge(name string, ec chan Event) {
- DefaultEvtStream.Merge(name, ec)
- }
- func Handle(path string, handler func(Event)) {
- DefaultEvtStream.Handle(path, handler)
- }
- func Loop() {
- DefaultEvtStream.Loop()
- }
- func StopLoop() {
- DefaultEvtStream.StopLoop()
- }
- type EvtTimer struct {
- Duration time.Duration
- Count uint64
- }
- func NewTimerCh(du time.Duration) chan Event {
- t := make(chan Event)
- go func(a chan Event) {
- n := uint64(0)
- for {
- n++
- time.Sleep(du)
- e := Event{}
- e.Type = "timer"
- e.Path = "/timer/" + du.String()
- e.Time = time.Now().Unix()
- e.Data = EvtTimer{
- Duration: du,
- Count: n,
- }
- t <- e
- }
- }(t)
- return t
- }
- var DefaultHandler = func(e Event) {
- }
- var usrEvtCh = make(chan Event)
- func SendCustomEvt(path string, data interface{}) {
- e := Event{}
- e.Path = path
- e.Data = data
- e.Time = time.Now().Unix()
- usrEvtCh <- e
- }
|