summaryrefslogtreecommitdiff
path: root/tui/event.go
diff options
context:
space:
mode:
Diffstat (limited to 'tui/event.go')
-rw-r--r--tui/event.go136
1 files changed, 136 insertions, 0 deletions
diff --git a/tui/event.go b/tui/event.go
new file mode 100644
index 0000000..4d0a535
--- /dev/null
+++ b/tui/event.go
@@ -0,0 +1,136 @@
+package tui
+
+import (
+ "os"
+ "os/signal"
+ "zgo.at/termfo"
+ "zgo.at/termfo/keys"
+ "strings"
+ "strconv"
+ "bufio"
+ "syscall"
+ "sync"
+)
+
+type Event struct {
+ TextInput rune
+ Key keys.Key
+ Mouse struct {
+ Button int
+ Pressed bool
+ Released bool
+ X, Y int
+ Scroll int
+ }
+ Err error
+ Resize bool
+}
+
+var evChan chan Event
+var once sync.Once
+func Events() <-chan Event {
+ once.Do(func() {
+ evChan = make(chan Event, 2)
+
+ // buffer FindKeys
+ ch := make(chan termfo.Event, 16)
+ go func() {
+ r := bufio.NewReader(os.Stdin)
+ for e := range terminfo.FindKeys(r) {
+ ch <- e
+ if e.Err != nil {
+ return
+ }
+ }
+ }()
+
+ go func() {
+ for kev := range ch {
+ var ev Event
+ if kev.Err != nil {
+ ev.Err = kev.Err
+ evChan <- ev
+ return
+ }
+
+ noMods := kev.Key & (keys.Ctrl | keys.Alt) == 0
+ notSpecial := kev.Key.WithoutMods() < (1 << 32)
+ if noMods && notSpecial {
+ r := rune(kev.Key.WithoutMods())
+ if kev.Key & keys.Shift != 0 && r >= 'a' && r <= 'z' {
+ r -= 0x20
+ }
+ ev.TextInput = r
+ }
+
+ switch kev.Key {
+ case keys.Mouse:
+ if err := parseMouse(&ev, ch); err != nil {
+ ev.Err = err
+ evChan <- ev
+ return
+ }
+ default:
+ ev.Key = kev.Key
+ }
+
+ evChan <- ev
+ }
+ }()
+
+ sigs := make(chan os.Signal, 1)
+ signal.Notify(sigs, syscall.SIGWINCH)
+ go func() {
+ for _ = range sigs {
+ evChan <- Event {Resize: true}
+ }
+ }()
+ })
+
+ return evChan
+}
+
+func parseMouse(ev *Event, ch <-chan termfo.Event) error {
+ var (
+ arg strings.Builder
+ args []int
+ down bool
+ )
+ parse: for kev := range ch {
+ if kev.Err != nil {
+ return kev.Err
+ }
+ switch {
+ case kev.Key >= '0' && kev.Key <= '9':
+ arg.WriteRune(rune(kev.Key))
+ case kev.Key == ';':
+ i, _ := strconv.Atoi(arg.String())
+ args = append(args, i)
+ arg.Reset()
+ case kev.Key == 'm' | keys.Shift:
+ down = true
+ fallthrough
+ default:
+ i, _ := strconv.Atoi(arg.String())
+ args = append(args, i)
+ break parse
+ }
+ }
+ if len(args) != 3 {
+ return nil
+ }
+ ev.Mouse.X = args[1] - 1
+ ev.Mouse.Y = args[2] - 1
+ switch args[0] {
+ case 64:
+ ev.Mouse.Scroll = -1
+ case 65:
+ ev.Mouse.Scroll = 1
+ default:
+ ev.Mouse.Button = args[0]
+ ev.Mouse.Pressed = down
+ ev.Mouse.Released = !down
+ }
+
+ return nil
+}