Rework event system
* Timers should now be done through Go tickers * Reworked event names used in Handle() * Reworked Event type and payloads
This commit is contained in:
parent
609f0e3c48
commit
b227bd5277
@ -24,7 +24,7 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(bc)
|
ui.Render(bc)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", "<Insert>", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
)
|
)
|
||||||
@ -22,14 +23,21 @@ func main() {
|
|||||||
p.TextFgColor = ui.ColorWhite
|
p.TextFgColor = ui.ColorWhite
|
||||||
p.BorderLabel = "Text Box"
|
p.BorderLabel = "Text Box"
|
||||||
p.BorderFg = ui.ColorCyan
|
p.BorderFg = ui.ColorCyan
|
||||||
p.Handle("/timer/1s", func(e ui.Event) {
|
|
||||||
cnt := e.Data.(ui.EvtTimer)
|
pTicker := time.NewTicker(time.Second)
|
||||||
if cnt.Count%2 == 0 {
|
pTickerCount := 1
|
||||||
p.TextFgColor = ui.ColorRed
|
go func() {
|
||||||
} else {
|
for {
|
||||||
p.TextFgColor = ui.ColorWhite
|
if pTickerCount%2 == 0 {
|
||||||
|
p.TextFgColor = ui.ColorRed
|
||||||
|
} else {
|
||||||
|
p.TextFgColor = ui.ColorWhite
|
||||||
|
}
|
||||||
|
|
||||||
|
pTickerCount++
|
||||||
|
<-pTicker.C
|
||||||
}
|
}
|
||||||
})
|
}()
|
||||||
|
|
||||||
listData := []string{"[0] gizak/termui", "[1] editbox.go", "[2] interrupt.go", "[3] keyboard.go", "[4] output.go", "[5] random_out.go", "[6] dashboard.go", "[7] nsf/termbox-go"}
|
listData := []string{"[0] gizak/termui", "[1] editbox.go", "[2] interrupt.go", "[3] keyboard.go", "[4] output.go", "[5] random_out.go", "[6] dashboard.go", "[7] nsf/termbox-go"}
|
||||||
|
|
||||||
@ -136,14 +144,20 @@ func main() {
|
|||||||
ui.Render(p, l, g, sls, lc, bc, lc2, p2)
|
ui.Render(p, l, g, sls, lc, bc, lc2, p2)
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/timer/1s", func(e ui.Event) {
|
drawTicker := time.NewTicker(time.Second)
|
||||||
t := e.Data.(ui.EvtTimer)
|
drawTickerCount := 1
|
||||||
draw(int(t.Count))
|
go func() {
|
||||||
})
|
for {
|
||||||
|
draw(drawTickerCount)
|
||||||
|
|
||||||
|
drawTickerCount++
|
||||||
|
<-drawTicker.C
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
ui.Loop()
|
ui.Loop()
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(g0, g1, g2, g3, g4)
|
ui.Render(g0, g1, g2, g3, g4)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
)
|
)
|
||||||
@ -91,29 +92,33 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(ui.Body)
|
ui.Render(ui.Body)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/timer/1s", func(e ui.Event) {
|
drawTicker := time.NewTicker(time.Second)
|
||||||
t := e.Data.(ui.EvtTimer)
|
drawTickerCount := 1
|
||||||
i := t.Count
|
go func() {
|
||||||
if i > 103 {
|
for {
|
||||||
ui.StopLoop()
|
if drawTickerCount > 103 {
|
||||||
return
|
ui.StopLoop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, g := range gs {
|
||||||
|
g.Percent = (g.Percent + 3) % 100
|
||||||
|
}
|
||||||
|
sp.Lines[0].Data = spdata[:100+drawTickerCount]
|
||||||
|
lc.Data["default"] = sinps[2*drawTickerCount:]
|
||||||
|
ui.Render(ui.Body)
|
||||||
|
|
||||||
|
drawTickerCount++
|
||||||
|
<-drawTicker.C
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
for _, g := range gs {
|
ui.Handle("<Resize>", func(e ui.Event) {
|
||||||
g.Percent = (g.Percent + 3) % 100
|
payload := e.Payload.(ui.Resize)
|
||||||
}
|
ui.Body.Width = payload.Width
|
||||||
|
|
||||||
sp.Lines[0].Data = spdata[:100+i]
|
|
||||||
lc.Data["default"] = sinps[2*i:]
|
|
||||||
ui.Render(ui.Body)
|
|
||||||
})
|
|
||||||
|
|
||||||
ui.Handle("/sys/wnd/resize", func(e ui.Event) {
|
|
||||||
ui.Body.Width = ui.TermWidth()
|
|
||||||
ui.Body.Align()
|
ui.Body.Align()
|
||||||
ui.Clear()
|
ui.Clear()
|
||||||
ui.Render(ui.Body)
|
ui.Render(ui.Body)
|
||||||
|
@ -63,7 +63,7 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(lc0, lc1, lc2)
|
ui.Render(lc0, lc1, lc2)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(ls)
|
ui.Render(ls)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(bc)
|
ui.Render(bc)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(par0, par1, par2, par3)
|
ui.Render(par0, par1, par2, par3)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -37,14 +37,21 @@ func main() {
|
|||||||
return fmt.Sprintf("%.02f", v)
|
return fmt.Sprintf("%.02f", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Handle("/timer/1s", func(e ui.Event) {
|
drawTicker := time.NewTicker(time.Second)
|
||||||
if run {
|
drawTickerCount := 1
|
||||||
pc.Data, pc.Offset = randomDataAndOffset()
|
go func() {
|
||||||
ui.Render(pc)
|
for {
|
||||||
}
|
if run {
|
||||||
})
|
pc.Data, pc.Offset = randomDataAndOffset()
|
||||||
|
ui.Render(pc)
|
||||||
|
}
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/s", func(ui.Event) {
|
drawTickerCount++
|
||||||
|
<-drawTicker.C
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
ui.Handle("s", func(ui.Event) {
|
||||||
run = !run
|
run = !run
|
||||||
if run {
|
if run {
|
||||||
pc.BorderLabel = "Pie Chart"
|
pc.BorderLabel = "Pie Chart"
|
||||||
@ -54,7 +61,7 @@ func main() {
|
|||||||
ui.Render(pc)
|
ui.Render(pc)
|
||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(spls0, spls1, spls2)
|
ui.Render(spls0, spls1, spls2)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(table2)
|
ui.Render(table2)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -58,17 +58,17 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(header, tabpane)
|
ui.Render(header, tabpane)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/j", func(ui.Event) {
|
ui.Handle("j", func(ui.Event) {
|
||||||
tabpane.SetActiveLeft()
|
tabpane.SetActiveLeft()
|
||||||
ui.Clear()
|
ui.Clear()
|
||||||
ui.Render(header, tabpane)
|
ui.Render(header, tabpane)
|
||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/k", func(ui.Event) {
|
ui.Handle("k", func(ui.Event) {
|
||||||
tabpane.SetActiveRight()
|
tabpane.SetActiveRight()
|
||||||
ui.Clear()
|
ui.Clear()
|
||||||
ui.Render(header, tabpane)
|
ui.Render(header, tabpane)
|
||||||
|
@ -8,6 +8,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
)
|
)
|
||||||
@ -134,14 +135,20 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(p, list, g, sp, lc, bc, lc1, p1)
|
ui.Render(p, list, g, sp, lc, bc, lc1, p1)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/timer/1s", func(e ui.Event) {
|
drawTicker := time.NewTicker(time.Second)
|
||||||
t := e.Data.(ui.EvtTimer)
|
drawTickerCount := 1
|
||||||
draw(int(t.Count))
|
go func() {
|
||||||
})
|
for {
|
||||||
|
draw(drawTickerCount)
|
||||||
|
|
||||||
|
drawTickerCount++
|
||||||
|
<-drawTicker.C
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
ui.Loop()
|
ui.Loop()
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
"github.com/gizak/termui/extra"
|
"github.com/gizak/termui/extra"
|
||||||
@ -330,35 +331,42 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(header, tabpane)
|
ui.Render(header, tabpane)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/j", func(ui.Event) {
|
ui.Handle("j", func(ui.Event) {
|
||||||
tabpane.SetActiveLeft()
|
tabpane.SetActiveLeft()
|
||||||
ui.Render(header, tabpane)
|
ui.Render(header, tabpane)
|
||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/k", func(ui.Event) {
|
ui.Handle("k", func(ui.Event) {
|
||||||
tabpane.SetActiveRight()
|
tabpane.SetActiveRight()
|
||||||
ui.Render(header, tabpane)
|
ui.Render(header, tabpane)
|
||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/timer/1s", func(e ui.Event) {
|
drawTicker := time.NewTicker(time.Second)
|
||||||
cs, errcs := getCpusStatsMap()
|
drawTickerCount := 1
|
||||||
if errcs != nil {
|
go func() {
|
||||||
panic(errcs)
|
for {
|
||||||
}
|
cs, errcs := getCpusStatsMap()
|
||||||
cpusStats.tick(cs)
|
if errcs != nil {
|
||||||
cpuTabElems.Update(*cpusStats)
|
panic(errcs)
|
||||||
|
}
|
||||||
|
cpusStats.tick(cs)
|
||||||
|
cpuTabElems.Update(*cpusStats)
|
||||||
|
|
||||||
ms, errm := getMemStats()
|
ms, errm := getMemStats()
|
||||||
if errm != nil {
|
if errm != nil {
|
||||||
panic(errm)
|
panic(errm)
|
||||||
|
}
|
||||||
|
memTabElems.Update(ms)
|
||||||
|
ui.Render(header, tabpane)
|
||||||
|
|
||||||
|
drawTickerCount++
|
||||||
|
<-drawTicker.C
|
||||||
}
|
}
|
||||||
memTabElems.Update(ms)
|
}()
|
||||||
ui.Render(header, tabpane)
|
|
||||||
})
|
|
||||||
|
|
||||||
ui.Loop()
|
ui.Loop()
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ func main() {
|
|||||||
|
|
||||||
ui.Render(p)
|
ui.Render(p)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) {
|
ui.Handle("q", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
494
events.go
494
events.go
@ -5,60 +5,163 @@
|
|||||||
package termui
|
package termui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/nsf/termbox-go"
|
tb "github.com/nsf/termbox-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Here's the list of events which can be assigned handlers using Handle():
|
||||||
|
mouse events:
|
||||||
|
<MouseLeft> <MouseRight> <MouseMiddle>
|
||||||
|
<MouseWheelUp> <MouseWheelDown>
|
||||||
|
keyboard events:
|
||||||
|
any uppercase or lowercase letter or a set of two letters like j or jj or J or JJ
|
||||||
|
<C-d> etc
|
||||||
|
<M-d> etc
|
||||||
|
<Up> <Down> <Left> <Right>
|
||||||
|
<Insert> <Delete> <Home> <End> <Previous> <Next>
|
||||||
|
<Backspace> <Tab> <Enter> <Escape> <Space>
|
||||||
|
<C-<Space>> etc
|
||||||
|
terminal events:
|
||||||
|
<Resize>
|
||||||
|
*/
|
||||||
|
|
||||||
|
type EventType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
KeyboardEvent EventType = iota
|
||||||
|
MouseEvent
|
||||||
|
ResizeEvent
|
||||||
|
)
|
||||||
|
|
||||||
|
type eventStream struct {
|
||||||
|
sync.RWMutex
|
||||||
|
handlers map[string]func(Event)
|
||||||
|
stopLoop chan bool
|
||||||
|
eventQueue chan tb.Event // list of events from termbox
|
||||||
|
hook func(Event)
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultES = eventStream{
|
||||||
|
handlers: make(map[string]func(Event)),
|
||||||
|
stopLoop: make(chan bool, 1),
|
||||||
|
eventQueue: make(chan tb.Event),
|
||||||
|
hook: DefaultHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event contains an ID used for Handle() and an optional payload.
|
||||||
type Event struct {
|
type Event struct {
|
||||||
Type string
|
Type EventType
|
||||||
Path string
|
ID string
|
||||||
From string
|
Payload interface{}
|
||||||
To string
|
|
||||||
Data interface{}
|
|
||||||
Time int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var sysEvtChs []chan Event
|
// Mouse payload.
|
||||||
|
type Mouse struct {
|
||||||
type EvtKbd struct {
|
Drag bool
|
||||||
KeyStr string
|
X int
|
||||||
|
Y int
|
||||||
}
|
}
|
||||||
|
|
||||||
func evtKbd(e termbox.Event) EvtKbd {
|
// Resize payload.
|
||||||
ek := EvtKbd{}
|
type Resize struct {
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleEvent calls the approriate callback function if there is one.
|
||||||
|
func handleEvent(e Event) {
|
||||||
|
if val, ok := defaultES.handlers[e.ID]; ok {
|
||||||
|
val(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop gets events from termbox and passes them off to handleEvent.
|
||||||
|
// Stops when StopLoop is called.
|
||||||
|
func Loop() {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
defaultES.eventQueue <- tb.PollEvent()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-defaultES.stopLoop:
|
||||||
|
return
|
||||||
|
case e := <-defaultES.eventQueue:
|
||||||
|
ne := convertTermboxEvent(e)
|
||||||
|
defaultES.RLock()
|
||||||
|
handleEvent(ne)
|
||||||
|
defaultES.hook(ne)
|
||||||
|
defaultES.RUnlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopLoop stops the event loop.
|
||||||
|
func StopLoop() {
|
||||||
|
defaultES.stopLoop <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle assigns event names to their handlers. Takes a string, strings, or a slice of strings, and a function.
|
||||||
|
func Handle(things ...interface{}) {
|
||||||
|
function := things[len(things)-1].(func(Event))
|
||||||
|
for _, thing := range things {
|
||||||
|
if value, ok := thing.(string); ok {
|
||||||
|
defaultES.Lock()
|
||||||
|
defaultES.handlers[value] = function
|
||||||
|
defaultES.Unlock()
|
||||||
|
}
|
||||||
|
if value, ok := thing.([]string); ok {
|
||||||
|
defaultES.Lock()
|
||||||
|
for _, name := range value {
|
||||||
|
defaultES.handlers[name] = function
|
||||||
|
}
|
||||||
|
defaultES.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func EventHook(f func(Event)) {
|
||||||
|
defaultES.Lock()
|
||||||
|
defaultES.hook = f
|
||||||
|
defaultES.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertTermboxKeyboardEvent converts a termbox keyboard event to a more friendly string format.
|
||||||
|
// Combines modifiers into the string instead of having them as additional fields in an event.
|
||||||
|
func convertTermboxKeyboardEvent(e tb.Event) Event {
|
||||||
k := string(e.Ch)
|
k := string(e.Ch)
|
||||||
pre := ""
|
pre := ""
|
||||||
mod := ""
|
mod := ""
|
||||||
|
|
||||||
if e.Mod == termbox.ModAlt {
|
if e.Mod == tb.ModAlt {
|
||||||
mod = "M-"
|
mod = "<M-"
|
||||||
}
|
}
|
||||||
if e.Ch == 0 {
|
if e.Ch == 0 {
|
||||||
if e.Key > 0xFFFF-12 {
|
if e.Key > 0xFFFF-12 {
|
||||||
k = "<f" + strconv.Itoa(0xFFFF-int(e.Key)+1) + ">"
|
k = "<f" + strconv.Itoa(0xFFFF-int(e.Key)+1) + ">"
|
||||||
} else if e.Key > 0xFFFF-25 {
|
} else if e.Key > 0xFFFF-25 {
|
||||||
ks := []string{"<insert>", "<delete>", "<home>", "<end>", "<previous>", "<next>", "<up>", "<down>", "<left>", "<right>"}
|
ks := []string{"<Insert>", "<Delete>", "<Home>", "<End>", "<Previous>", "<Next>", "<Up>", "<Down>", "<Left>", "<Right>"}
|
||||||
k = ks[0xFFFF-int(e.Key)-12]
|
k = ks[0xFFFF-int(e.Key)-12]
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Key <= 0x7F {
|
if e.Key <= 0x7F {
|
||||||
pre = "C-"
|
pre = "<C-"
|
||||||
k = string('a' - 1 + int(e.Key))
|
k = string('a' - 1 + int(e.Key))
|
||||||
kmap := map[termbox.Key][2]string{
|
kmap := map[tb.Key][2]string{
|
||||||
termbox.KeyCtrlSpace: {"C-", "<space>"},
|
tb.KeyCtrlSpace: {"C-", "<Space>"},
|
||||||
termbox.KeyBackspace: {"", "<backspace>"},
|
tb.KeyBackspace: {"", "<Backspace>"},
|
||||||
termbox.KeyTab: {"", "<tab>"},
|
tb.KeyTab: {"", "<Tab>"},
|
||||||
termbox.KeyEnter: {"", "<enter>"},
|
tb.KeyEnter: {"", "<Enter>"},
|
||||||
termbox.KeyEsc: {"", "<escape>"},
|
tb.KeyEsc: {"", "<Escape>"},
|
||||||
termbox.KeyCtrlBackslash: {"C-", "\\"},
|
tb.KeyCtrlBackslash: {"C-", "\\"},
|
||||||
termbox.KeyCtrlSlash: {"C-", "/"},
|
tb.KeyCtrlSlash: {"C-", "/"},
|
||||||
termbox.KeySpace: {"", "<space>"},
|
tb.KeySpace: {"", "<Space>"},
|
||||||
termbox.KeyCtrl8: {"C-", "8"},
|
tb.KeyCtrl8: {"C-", "8"},
|
||||||
}
|
}
|
||||||
if sk, ok := kmap[e.Key]; ok {
|
if sk, ok := kmap[e.Key]; ok {
|
||||||
pre = sk[0]
|
pre = sk[0]
|
||||||
@ -67,288 +170,85 @@ func evtKbd(e termbox.Event) EvtKbd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ek.KeyStr = pre + mod + k
|
if pre != "" {
|
||||||
return ek
|
k += ">"
|
||||||
}
|
|
||||||
|
|
||||||
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(e)
|
|
||||||
ne.Path = "/sys/mouse/" + m.Press
|
|
||||||
ne.Data = m
|
|
||||||
}
|
|
||||||
return ne
|
|
||||||
}
|
|
||||||
|
|
||||||
type EvtWnd struct {
|
|
||||||
Width int
|
|
||||||
Height int
|
|
||||||
}
|
|
||||||
|
|
||||||
type EvtMouse struct {
|
|
||||||
X int
|
|
||||||
Y int
|
|
||||||
Drag bool
|
|
||||||
Press string
|
|
||||||
}
|
|
||||||
|
|
||||||
func evtMouse(e termbox.Event) EvtMouse {
|
|
||||||
em := EvtMouse{}
|
|
||||||
em.X = e.MouseX
|
|
||||||
em.Y = e.MouseY
|
|
||||||
|
|
||||||
if e.Mod == termbox.ModMotion {
|
|
||||||
em.Drag = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch e.Key {
|
id := pre + mod + k
|
||||||
case termbox.MouseLeft:
|
|
||||||
em.Press = "MouseLeft"
|
|
||||||
case termbox.MouseMiddle:
|
|
||||||
em.Press = "MouseMiddle"
|
|
||||||
case termbox.MouseRight:
|
|
||||||
em.Press = "MouseRight"
|
|
||||||
|
|
||||||
case termbox.MouseRelease:
|
return Event{
|
||||||
em.Press = "MouseRelease"
|
Type: KeyboardEvent,
|
||||||
|
ID: id,
|
||||||
case termbox.MouseWheelUp:
|
|
||||||
em.Press = "MouseWheelUp"
|
|
||||||
case termbox.MouseWheelDown:
|
|
||||||
em.Press = "MouseWheelDown"
|
|
||||||
default:
|
|
||||||
em.Press = "Unknown_Mouse_Button"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return em
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type EvtErr error
|
func convertTermboxMouseEvent(e tb.Event) Event {
|
||||||
|
mouseButtonMap := map[tb.Key]string{
|
||||||
|
tb.MouseLeft: "<MouseLeft>",
|
||||||
|
tb.MouseMiddle: "<MouseMiddle>",
|
||||||
|
tb.MouseRight: "<MouseRight>",
|
||||||
|
tb.MouseRelease: "<MouseRelease>",
|
||||||
|
tb.MouseWheelUp: "<MouseWheelUp>",
|
||||||
|
tb.MouseWheelDown: "<MouseWheelDown>",
|
||||||
|
}
|
||||||
|
|
||||||
func hookTermboxEvt() {
|
converted, ok := mouseButtonMap[e.Key]
|
||||||
for {
|
if !ok {
|
||||||
e := termbox.PollEvent()
|
converted = "Unknown_Mouse_Button"
|
||||||
|
}
|
||||||
|
|
||||||
for _, c := range sysEvtChs {
|
Drag := false
|
||||||
go func(ch chan Event) {
|
if e.Mod == tb.ModMotion {
|
||||||
ch <- crtTermboxEvt(e)
|
Drag = true
|
||||||
}(c)
|
}
|
||||||
|
|
||||||
|
return Event{
|
||||||
|
Type: MouseEvent,
|
||||||
|
ID: converted,
|
||||||
|
Payload: Mouse{
|
||||||
|
X: e.MouseX,
|
||||||
|
Y: e.MouseY,
|
||||||
|
Drag: Drag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertTermboxEvent turns a termbox event into a termui event.
|
||||||
|
func convertTermboxEvent(e tb.Event) Event {
|
||||||
|
if e.Type == tb.EventError {
|
||||||
|
panic(e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.Type {
|
||||||
|
case tb.EventKey:
|
||||||
|
return convertTermboxKeyboardEvent(e)
|
||||||
|
case tb.EventMouse:
|
||||||
|
return convertTermboxMouseEvent(e)
|
||||||
|
case tb.EventResize:
|
||||||
|
return Event{
|
||||||
|
Type: ResizeEvent,
|
||||||
|
ID: "<Resize>",
|
||||||
|
Payload: Resize{
|
||||||
|
Width: e.Width,
|
||||||
|
Height: e.Height,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func NewSysEvtCh() chan Event {
|
return Event{}
|
||||||
ec := make(chan Event)
|
|
||||||
sysEvtChs = append(sysEvtChs, ec)
|
|
||||||
return ec
|
|
||||||
}
|
|
||||||
|
|
||||||
var DefaultEvtStream *EvtStream
|
|
||||||
|
|
||||||
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 DefaultHandler = func(e Event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var usrEvtCh = make(chan Event)
|
func ResetHandlers() {
|
||||||
|
defaultES.Lock()
|
||||||
func SendCustomEvt(path string, data interface{}) {
|
defaultES.handlers = make(map[string]func(Event))
|
||||||
e := Event{}
|
defaultES.Unlock()
|
||||||
e.Path = path
|
}
|
||||||
e.Data = data
|
|
||||||
e.Time = time.Now().Unix()
|
func ResetHandler(handle string) {
|
||||||
usrEvtCh <- e
|
defaultES.Lock()
|
||||||
|
delete(defaultES.handlers, handle)
|
||||||
|
defaultES.Unlock()
|
||||||
}
|
}
|
||||||
|
21
render.go
21
render.go
@ -8,7 +8,6 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
tb "github.com/nsf/termbox-go"
|
tb "github.com/nsf/termbox-go"
|
||||||
)
|
)
|
||||||
@ -25,10 +24,10 @@ func Init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tb.SetInputMode(tb.InputEsc | tb.InputMouse)
|
tb.SetInputMode(tb.InputEsc | tb.InputMouse)
|
||||||
DefaultEvtStream = NewEvtStream()
|
// DefaultEvtStream = NewEvtStream()
|
||||||
|
|
||||||
sysEvtChs = make([]chan Event, 0)
|
// sysEvtChs = make([]chan Event, 0)
|
||||||
go hookTermboxEvt()
|
// go hookTermboxEvt()
|
||||||
|
|
||||||
renderJobs = make(chan []Bufferer)
|
renderJobs = make(chan []Bufferer)
|
||||||
//renderLock = new(sync.RWMutex)
|
//renderLock = new(sync.RWMutex)
|
||||||
@ -39,19 +38,13 @@ func Init() error {
|
|||||||
Body.BgColor = ThemeAttr("bg")
|
Body.BgColor = ThemeAttr("bg")
|
||||||
Body.Width = TermWidth()
|
Body.Width = TermWidth()
|
||||||
|
|
||||||
DefaultEvtStream.Init()
|
Handle("<Resize>", func(e Event) {
|
||||||
DefaultEvtStream.Merge("termbox", NewSysEvtCh())
|
payload := e.Payload.(Resize)
|
||||||
DefaultEvtStream.Merge("timer", NewTimerCh(time.Second))
|
Body.Width = payload.Width
|
||||||
DefaultEvtStream.Merge("custom", usrEvtCh)
|
|
||||||
|
|
||||||
DefaultEvtStream.Handle("/", DefaultHandler)
|
|
||||||
DefaultEvtStream.Handle("/sys/wnd/resize", func(e Event) {
|
|
||||||
w := e.Data.(EvtWnd)
|
|
||||||
Body.Width = w.Width
|
|
||||||
})
|
})
|
||||||
|
|
||||||
DefaultWgtMgr = NewWgtMgr()
|
DefaultWgtMgr = NewWgtMgr()
|
||||||
DefaultEvtStream.Hook(DefaultWgtMgr.WgtHandlersHook())
|
EventHook(DefaultWgtMgr.WgtHandlersHook())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for bs := range renderJobs {
|
for bs := range renderJobs {
|
||||||
|
@ -75,8 +75,8 @@ func GenId() string {
|
|||||||
func (wm WgtMgr) WgtHandlersHook() func(Event) {
|
func (wm WgtMgr) WgtHandlersHook() func(Event) {
|
||||||
return func(e Event) {
|
return func(e Event) {
|
||||||
for _, v := range wm {
|
for _, v := range wm {
|
||||||
if k := findMatch(v.Handlers, e.Path); k != "" {
|
if val, ok := v.Handlers[e.ID]; ok {
|
||||||
v.Handlers[k](e)
|
val(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user