diff options
| author | citrons <citrons@mondecitronne.com> | 2025-01-29 21:57:36 -0600 |
|---|---|---|
| committer | citrons <citrons@mondecitronne.com> | 2025-01-29 21:57:36 -0600 |
| commit | dcc8a3a4f93eb5b7dcc965e63ad2ea6057a8ee6b (patch) | |
| tree | 02d7fa114a86322d92322533b2ee70874f01b121 /tui/event.go | |
| parent | e3a5cdb759ba98ebcdf43e38efaa9d33aa29f466 (diff) | |
keyboard and mouse handling
Diffstat (limited to 'tui/event.go')
| -rw-r--r-- | tui/event.go | 136 |
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 +} |
