Finish Event
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 443 KiB After Width: | Height: | Size: 443 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 782 KiB After Width: | Height: | Size: 782 KiB |
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
113
debug/debuger.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package debug
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"golang.org/x/net/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
Port string
|
||||||
|
Addr string
|
||||||
|
Path string
|
||||||
|
Msg chan string
|
||||||
|
chs []chan string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
Port string
|
||||||
|
Addr string
|
||||||
|
Path string
|
||||||
|
ws *websocket.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultPort = ":8080"
|
||||||
|
|
||||||
|
func NewServer() *Server {
|
||||||
|
return &Server{
|
||||||
|
Port: defaultPort,
|
||||||
|
Addr: "localhost",
|
||||||
|
Path: "/echo",
|
||||||
|
Msg: make(chan string),
|
||||||
|
chs: make([]chan string, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient() Client {
|
||||||
|
return Client{
|
||||||
|
Port: defaultPort,
|
||||||
|
Addr: "localhost",
|
||||||
|
Path: "/echo",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) ConnectAndListen() error {
|
||||||
|
ws, err := websocket.Dial("ws://"+c.Addr+c.Port+c.Path, "", "http://"+c.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer ws.Close()
|
||||||
|
|
||||||
|
var m string
|
||||||
|
for {
|
||||||
|
err := websocket.Message.Receive(ws, &m)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Print(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ListenAndServe() error {
|
||||||
|
http.Handle(s.Path, websocket.Handler(func(ws *websocket.Conn) {
|
||||||
|
defer ws.Close()
|
||||||
|
|
||||||
|
mc := make(chan string)
|
||||||
|
s.chs = append(s.chs, mc)
|
||||||
|
|
||||||
|
for m := range mc {
|
||||||
|
websocket.Message.Send(ws, m)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for msg := range s.Msg {
|
||||||
|
for _, c := range s.chs {
|
||||||
|
go func(a chan string) {
|
||||||
|
a <- msg
|
||||||
|
}(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return http.ListenAndServe(s.Port, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Log(msg string) {
|
||||||
|
go func() { s.Msg <- msg }()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Logf(format string, a ...interface{}) {
|
||||||
|
s.Log(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultServer = NewServer()
|
||||||
|
var DefaultClient = NewClient()
|
||||||
|
|
||||||
|
func ListenAndServe() error {
|
||||||
|
return DefaultServer.ListenAndServe()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConnectAndListen() error {
|
||||||
|
return DefaultClient.ConnectAndListen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Log(msg string) {
|
||||||
|
DefaultServer.Log(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Logf(format string, a ...interface{}) {
|
||||||
|
DefaultServer.Logf(format, a...)
|
||||||
|
}
|
251
events.go
@ -9,6 +9,7 @@
|
|||||||
package termui
|
package termui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -25,32 +26,125 @@ type Event struct {
|
|||||||
Time int64
|
Time int64
|
||||||
}
|
}
|
||||||
|
|
||||||
var sysevt struct {
|
var sysEvtChs []chan Event
|
||||||
chs []chan Event
|
|
||||||
|
type EvtKbd struct {
|
||||||
|
KeyStr string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSysEvtFromTb(e termbox.Event) Event {
|
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()}
|
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
|
return ne
|
||||||
}
|
}
|
||||||
|
|
||||||
func hookSysEvt() {
|
type EvtWnd struct {
|
||||||
sysevt.chs = make([]chan Event, 0)
|
Width int
|
||||||
|
Height int
|
||||||
|
}
|
||||||
|
|
||||||
|
type EvtMouse struct {
|
||||||
|
X int
|
||||||
|
Y int
|
||||||
|
Press string
|
||||||
|
}
|
||||||
|
|
||||||
|
type EvtErr error
|
||||||
|
|
||||||
|
func hookTermboxEvt() {
|
||||||
for {
|
for {
|
||||||
e := termbox.PollEvent()
|
e := termbox.PollEvent()
|
||||||
for _, c := range sysevt.chs {
|
|
||||||
// shorten?
|
for _, c := range sysEvtChs {
|
||||||
go func(ch chan Event, ev Event) { ch <- ev }(c, newSysEvtFromTb(e))
|
go func(ch chan Event) {
|
||||||
|
ch <- crtTermboxEvt(e)
|
||||||
|
}(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSysEvtCh() chan Event {
|
func NewSysEvtCh() chan Event {
|
||||||
ec := make(chan Event)
|
ec := make(chan Event)
|
||||||
sysevt.chs = append(sysevt.chs, ec)
|
sysEvtChs = append(sysEvtChs, ec)
|
||||||
return ec
|
return ec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DefaultEvtStream = NewEvtStream()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
type evtCtl struct {
|
type evtCtl struct {
|
||||||
in chan Event
|
in chan Event
|
||||||
@ -73,22 +167,23 @@ func newEvtCtl() evtCtl {
|
|||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
type EvtStream struct {
|
type EvtStream struct {
|
||||||
srcMap map[string]chan Event
|
srcMap map[string]chan Event
|
||||||
stream chan Event
|
stream chan Event
|
||||||
cache map[string][]func(Event)
|
wg sync.WaitGroup
|
||||||
wg sync.WaitGroup
|
sigStopLoop chan int
|
||||||
Handlers map[string]func(Event)
|
Handlers map[string]func(Event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEvtStream() *EvtStream {
|
func NewEvtStream() *EvtStream {
|
||||||
return &EvtStream{
|
return &EvtStream{
|
||||||
srcMap: make(map[string]chan Event),
|
srcMap: make(map[string]chan Event),
|
||||||
stream: make(chan Event),
|
stream: make(chan Event),
|
||||||
|
Handlers: make(map[string]func(Event)),
|
||||||
|
sigStopLoop: make(chan int),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *EvtStream) Init() {
|
func (es *EvtStream) Init() {
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
es.wg.Wait()
|
es.wg.Wait()
|
||||||
close(es.stream)
|
close(es.stream)
|
||||||
@ -107,17 +202,38 @@ func (es *EvtStream) Init() {
|
|||||||
// b: /
|
// b: /
|
||||||
// score: 0
|
// score: 0
|
||||||
func MatchScore(a, b string) int {
|
func MatchScore(a, b string) int {
|
||||||
sa := strings.Split(a, "/")
|
|
||||||
sb := strings.Split(b, "/")
|
|
||||||
|
|
||||||
score := -1
|
// divide by "" and rm heading ""
|
||||||
|
sliced := func(s string) []string {
|
||||||
|
ss := strings.Split(s, "/")
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for j := range ss {
|
||||||
|
if ss[j] == "" {
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss[i:]
|
||||||
|
}
|
||||||
|
|
||||||
|
sa := sliced(a)
|
||||||
|
sb := sliced(b)
|
||||||
|
|
||||||
|
score := 0
|
||||||
|
if len(sb) > len(sa) {
|
||||||
|
return -1 // sb couldnt be more deeper than sa
|
||||||
|
}
|
||||||
|
|
||||||
for i, s := range sa {
|
for i, s := range sa {
|
||||||
if i >= len(sb) {
|
if i >= len(sb) {
|
||||||
break
|
break // exhaust b
|
||||||
}
|
}
|
||||||
|
|
||||||
if s != sb[i] {
|
if s != sb[i] {
|
||||||
return -1
|
return -1 // mismatch
|
||||||
}
|
}
|
||||||
score++
|
score++
|
||||||
}
|
}
|
||||||
@ -125,23 +241,98 @@ func MatchScore(a, b string) int {
|
|||||||
return score
|
return score
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *EvtStream) Merge(ec chan Event) {
|
func (es *EvtStream) Merge(name string, ec chan Event) {
|
||||||
es.wg.Add(1)
|
es.wg.Add(1)
|
||||||
|
es.srcMap[name] = ec
|
||||||
|
|
||||||
go func(a chan Event) {
|
go func(a chan Event) {
|
||||||
for n := range ec {
|
for n := range a {
|
||||||
|
n.From = name
|
||||||
es.stream <- n
|
es.stream <- n
|
||||||
}
|
}
|
||||||
wg.Done()
|
es.wg.Done()
|
||||||
}(ec)
|
}(ec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
func (es *EvtStream) Handle(path string, handler func(Event)) {
|
||||||
func (es *EvtStream) hookup() {
|
es.Handlers[path] = handler
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es EvtStream) Subscribe(uri string) chan Event {
|
func (es *EvtStream) match(path string) string {
|
||||||
|
n := 0
|
||||||
|
pattern := ""
|
||||||
|
for m := range es.Handlers {
|
||||||
|
if MatchScore(path, m) < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if pattern == "" || len(m) > n {
|
||||||
|
pattern = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es *EvtStream) Loop() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case e := <-es.stream:
|
||||||
|
if pattern := es.match(e.Path); pattern != "" {
|
||||||
|
es.Handlers[pattern](e)
|
||||||
|
}
|
||||||
|
case <-es.sigStopLoop:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es *EvtStream) StopLoop() {
|
||||||
|
go func() { es.sigStopLoop <- 1 }()
|
||||||
|
}
|
||||||
|
|
||||||
|
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.From = "timer"
|
||||||
|
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 DefualtHandler = func(e Event) {
|
||||||
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -8,21 +8,34 @@
|
|||||||
|
|
||||||
package termui
|
package termui
|
||||||
|
|
||||||
import (
|
import "testing"
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
termbox "github.com/nsf/termbox-go"
|
var ps = []string{
|
||||||
"github.com/stretchr/testify/assert"
|
"",
|
||||||
)
|
"/",
|
||||||
|
"/a",
|
||||||
|
"/b",
|
||||||
|
"/a/c",
|
||||||
|
"/a/b",
|
||||||
|
"/a/b/c",
|
||||||
|
"/a/b/c/d",
|
||||||
|
"/a/b/c/d/"}
|
||||||
|
|
||||||
type boxEvent termbox.Event
|
func TestMatchScore(t *testing.T) {
|
||||||
|
chk := func(a, b string, s int) {
|
||||||
|
if c := MatchScore(a, b); c != s {
|
||||||
|
t.Errorf("\na:%s\nb:%s\nshould:%d\nscore:%d", a, b, s, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chk(ps[1], ps[1], 0)
|
||||||
|
chk(ps[1], ps[2], -1)
|
||||||
|
chk(ps[2], ps[1], 0)
|
||||||
|
chk(ps[4], ps[1], 0)
|
||||||
|
chk(ps[6], ps[2], 1)
|
||||||
|
chk(ps[4], ps[5], -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCrtEvt(t *testing.T) {
|
||||||
|
|
||||||
func TestUiEvt(t *testing.T) {
|
|
||||||
err := errors.New("This is a mock error")
|
|
||||||
event := boxEvent{3, 5, 2, 'H', 200, 500, err, 50, 30, 2}
|
|
||||||
expetced := Event{3, 5, 2, 'H', 200, 500, err, 50, 30, 2}
|
|
||||||
|
|
||||||
// We need to do that ugly casting so that vet does not complain
|
|
||||||
assert.Equal(t, uiEvt(termbox.Event(event)), expetced)
|
|
||||||
}
|
}
|
||||||
|
3
grid.go
@ -275,6 +275,3 @@ func (g Grid) Buffer() Buffer {
|
|||||||
}
|
}
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
// Body corresponds to the entire terminal display region.
|
|
||||||
var Body *Grid
|
|
||||||
|
34
render.go
@ -4,7 +4,11 @@
|
|||||||
|
|
||||||
package termui
|
package termui
|
||||||
|
|
||||||
import tm "github.com/nsf/termbox-go"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
tm "github.com/nsf/termbox-go"
|
||||||
|
)
|
||||||
|
|
||||||
// Bufferer should be implemented by all renderable components.
|
// Bufferer should be implemented by all renderable components.
|
||||||
type Bufferer interface {
|
type Bufferer interface {
|
||||||
@ -14,16 +18,24 @@ type Bufferer interface {
|
|||||||
// Init initializes termui library. This function should be called before any others.
|
// Init initializes termui library. This function should be called before any others.
|
||||||
// After initialization, the library must be finalized by 'Close' function.
|
// After initialization, the library must be finalized by 'Close' function.
|
||||||
func Init() error {
|
func Init() error {
|
||||||
Body = NewGrid()
|
|
||||||
Body.X = 0
|
|
||||||
Body.Y = 0
|
|
||||||
Body.BgColor = theme.BodyBg
|
|
||||||
if err := tm.Init(); err != nil {
|
if err := tm.Init(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w, _ := tm.Size()
|
|
||||||
Body.Width = w
|
sysEvtChs = make([]chan Event, 0)
|
||||||
evtListen()
|
go hookTermboxEvt()
|
||||||
|
renderJobs = make(chan []Bufferer)
|
||||||
|
go func() {
|
||||||
|
for bs := range renderJobs {
|
||||||
|
Render(bs...)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
DefaultEvtStream.Init()
|
||||||
|
DefaultEvtStream.Merge("termbox", NewSysEvtCh())
|
||||||
|
DefaultEvtStream.Merge("timer", NewTimerCh(time.Second))
|
||||||
|
DefaultEvtStream.Handle("/", DefualtHandler)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,3 +76,9 @@ func Render(bs ...Bufferer) {
|
|||||||
// render
|
// render
|
||||||
tm.Flush()
|
tm.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var renderJobs chan []Bufferer
|
||||||
|
|
||||||
|
func SendBufferToRender(bs ...Bufferer) {
|
||||||
|
go func() { renderJobs <- bs }()
|
||||||
|
}
|
||||||
|
38
test/runtest.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gizak/termui"
|
||||||
|
"github.com/gizak/termui/debug"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// run as client
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
fmt.Print(debug.ConnectAndListen())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// run as server
|
||||||
|
go func() { panic(debug.ListenAndServe()) }()
|
||||||
|
|
||||||
|
if err := termui.Init(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer termui.Close()
|
||||||
|
|
||||||
|
termui.Handle("/sys", func(e termui.Event) {
|
||||||
|
k, ok := e.Data.(termui.EvtKbd)
|
||||||
|
debug.Logf("-->%v\n", e)
|
||||||
|
if ok && k.KeyStr == "q" {
|
||||||
|
termui.StopLoop()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
termui.Handle("/timer", func(e termui.Event) {
|
||||||
|
//debug.Logf("-->%v\n", e)
|
||||||
|
})
|
||||||
|
termui.Loop()
|
||||||
|
}
|