1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
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 = make(chan Event, 1)
once sync.Once
)
func Events() <-chan Event {
once.Do(func() {
ch := terminfo.FindKeys(bufio.NewReader(os.Stdin))
go readKeys(ch, evChan)
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGWINCH)
go func() {
for _ = range sigs {
evChan <- Event {Resize: true}
}
}()
})
return evChan
}
func readKeys(ch <-chan termfo.Event, evChan chan<- Event) {
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
}
if kev.Key == keys.Tab {
ev.TextInput = '\t'
}
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
}
}
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
}
|