summaryrefslogtreecommitdiff
path: root/tui
diff options
context:
space:
mode:
authorcitrons <citrons@mondecitronne.com>2025-01-29 21:57:36 -0600
committercitrons <citrons@mondecitronne.com>2025-01-29 21:57:36 -0600
commitdcc8a3a4f93eb5b7dcc965e63ad2ea6057a8ee6b (patch)
tree02d7fa114a86322d92322533b2ee70874f01b121 /tui
parente3a5cdb759ba98ebcdf43e38efaa9d33aa29f466 (diff)
keyboard and mouse handling
Diffstat (limited to 'tui')
-rw-r--r--tui/event.go136
-rw-r--r--tui/input.go57
2 files changed, 136 insertions, 57 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
+}
diff --git a/tui/input.go b/tui/input.go
deleted file mode 100644
index a200e53..0000000
--- a/tui/input.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package tui
-
-import (
- "os"
- "os/signal"
- "golang.org/x/term"
- "unicode"
- "syscall"
- "bufio"
-)
-
-type Event interface {}
-
-type TextInput rune
-type Paste string
-type Resize Dims
-
-var evChan chan Event
-func Events() <-chan Event {
- if evChan != nil {
- return evChan
- }
- evChan = make(chan Event, 1)
-
- go func() {
- rd := bufio.NewReader(os.Stdin)
- for {
- r, _, err := rd.ReadRune()
- if err != nil {
- evChan <- err
- return
- }
- switch {
- case r == '\033':
- // todo
- case unicode.IsControl(r):
- default:
- evChan <- TextInput(r)
- }
- }
- }()
-
- sigs := make(chan os.Signal, 1)
- signal.Notify(sigs, syscall.SIGWINCH)
- go func() {
- for _ = range sigs {
- w, h, err := term.GetSize(0)
- if err == nil {
- evChan <- Resize {w, h}
- } else {
- evChan <- err
- }
- }
- }()
-
- return evChan
-} \ No newline at end of file