termui/events.go

329 lines
5.7 KiB
Go
Raw Normal View History

2017-01-14 14:07:43 +08:00
// 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
2015-08-20 03:22:53 +08:00
import (
"path"
2015-09-18 23:41:44 +08:00
"strconv"
2015-08-31 11:03:47 +08:00
"sync"
"time"
2015-08-20 03:22:53 +08:00
"github.com/nsf/termbox-go"
)
type Event struct {
2015-08-31 11:03:47 +08:00
Type string
Path string
From string
To string
Data interface{}
Time int64
}
2015-09-18 23:41:44 +08:00
var sysEvtChs []chan Event
2015-09-18 23:41:44 +08:00
type EvtKbd struct {
KeyStr string
2015-08-31 11:03:47 +08:00
}
2015-09-18 23:41:44 +08:00
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",
}
2015-08-31 11:03:47 +08:00
ne := Event{From: "/sys", Time: time.Now().Unix()}
2015-09-18 23:41:44 +08:00
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
}
2015-08-31 11:03:47 +08:00
return ne
}
2015-09-18 23:41:44 +08:00
type EvtWnd struct {
Width int
Height int
}
type EvtMouse struct {
X int
Y int
Press string
}
2015-09-18 23:41:44 +08:00
type EvtErr error
2015-09-18 23:41:44 +08:00
func hookTermboxEvt() {
2015-08-31 11:03:47 +08:00
for {
e := termbox.PollEvent()
2015-09-18 23:41:44 +08:00
for _, c := range sysEvtChs {
func(ch chan Event) {
2015-09-18 23:41:44 +08:00
ch <- crtTermboxEvt(e)
}(c)
}
2015-08-31 11:03:47 +08:00
}
}
2015-08-31 11:03:47 +08:00
func NewSysEvtCh() chan Event {
ec := make(chan Event)
2015-09-18 23:41:44 +08:00
sysEvtChs = append(sysEvtChs, ec)
2015-08-31 11:03:47 +08:00
return ec
}
2015-09-18 23:41:44 +08:00
var DefaultEvtStream = NewEvtStream()
2015-08-31 11:03:47 +08:00
type EvtStream struct {
sync.RWMutex
2015-09-18 23:41:44 +08:00
srcMap map[string]chan Event
stream chan Event
wg sync.WaitGroup
sigStopLoop chan Event
2015-09-18 23:41:44 +08:00
Handlers map[string]func(Event)
2015-10-08 02:25:59 +08:00
hook func(Event)
2015-08-31 11:03:47 +08:00
}
2015-08-31 11:03:47 +08:00
func NewEvtStream() *EvtStream {
return &EvtStream{
2015-09-18 23:41:44 +08:00
srcMap: make(map[string]chan Event),
stream: make(chan Event),
Handlers: make(map[string]func(Event)),
sigStopLoop: make(chan Event),
2015-08-09 07:07:32 +08:00
}
}
2015-08-31 11:03:47 +08:00
func (es *EvtStream) Init() {
es.Merge("internal", es.sigStopLoop)
go func() {
2015-08-31 11:03:47 +08:00
es.wg.Wait()
close(es.stream)
}()
}
func cleanPath(p string) string {
if p == "" {
return "/"
2015-09-18 23:41:44 +08:00
}
if p[0] != '/' {
p = "/" + p
2015-09-18 23:41:44 +08:00
}
return path.Clean(p)
}
2015-09-18 23:41:44 +08:00
func isPathMatch(pattern, path string) bool {
if len(pattern) == 0 {
return false
2015-08-20 03:22:53 +08:00
}
n := len(pattern)
return len(path) >= n && path[0:n] == pattern
2015-08-20 03:22:53 +08:00
}
2015-09-18 23:41:44 +08:00
func (es *EvtStream) Merge(name string, ec chan Event) {
es.Lock()
defer es.Unlock()
2015-08-31 11:03:47 +08:00
es.wg.Add(1)
2015-09-18 23:41:44 +08:00
es.srcMap[name] = ec
2015-08-31 11:03:47 +08:00
go func(a chan Event) {
2015-09-18 23:41:44 +08:00
for n := range a {
n.From = name
2015-08-31 11:03:47 +08:00
es.stream <- n
}
2015-09-18 23:41:44 +08:00
es.wg.Done()
2015-08-31 11:03:47 +08:00
}(ec)
}
2015-09-18 23:41:44 +08:00
func (es *EvtStream) Handle(path string, handler func(Event)) {
es.Handlers[cleanPath(path)] = handler
2015-09-18 23:41:44 +08:00
}
2015-10-08 02:25:59 +08:00
func findMatch(mux map[string]func(Event), path string) string {
n := -1
2015-09-18 23:41:44 +08:00
pattern := ""
2015-10-08 02:25:59 +08:00
for m := range mux {
if !isPathMatch(m, path) {
2015-09-18 23:41:44 +08:00
continue
}
if len(m) > n {
2015-09-18 23:41:44 +08:00
pattern = m
n = len(m)
2015-09-18 23:41:44 +08:00
}
}
return pattern
2015-10-08 02:25:59 +08:00
}
// Remove all existing defined Handlers from the map
func (es *EvtStream) ResetHandlers() {
for Path, _ := range es.Handlers {
delete(es.Handlers, Path)
}
return
}
2015-10-08 02:25:59 +08:00
func (es *EvtStream) match(path string) string {
return findMatch(es.Handlers, path)
}
func (es *EvtStream) Hook(f func(Event)) {
es.hook = f
2015-08-09 07:07:32 +08:00
}
2015-09-18 23:41:44 +08:00
func (es *EvtStream) Loop() {
for e := range es.stream {
2015-10-08 02:25:59 +08:00
switch e.Path {
case "/sig/stoploop":
2015-09-18 23:41:44 +08:00
return
}
func(a Event) {
es.RLock()
defer es.RUnlock()
if pattern := es.match(a.Path); pattern != "" {
2015-09-21 15:11:58 +08:00
es.Handlers[pattern](a)
}
}(e)
2015-10-08 02:25:59 +08:00
if es.hook != nil {
es.hook(e)
}
2015-09-18 23:41:44 +08:00
}
}
func (es *EvtStream) StopLoop() {
go func() {
e := Event{
Path: "/sig/stoploop",
}
es.sigStopLoop <- e
}()
}
2015-09-18 23:41:44 +08:00
func Merge(name string, ec chan Event) {
DefaultEvtStream.Merge(name, ec)
}
func Handle(path string, handler func(Event)) {
DefaultEvtStream.Handle(path, handler)
}
2017-04-08 21:09:47 +08:00
func ResetHandlers() {
DefaultEvtStream.ResetHandlers()
}
2015-09-18 23:41:44 +08:00
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
2015-09-18 23:41:44 +08:00
}
}(t)
return t
}
var DefaultHandler = func(e Event) {
}
2015-10-14 00:45:03 +08:00
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
}