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
|
package draw
import (
"fmt"
"os"
"strconv"
)
// Mouse is one record from /dev/mouse.
type Mouse struct {
Point
Buttons int
Msec uint32
}
// Mousectl delivers mouse and resize events on channels. This is the shape
// libdraw's Mousectl already has -- it is hand-rolled CSP in C because there
// were no goroutines. Here it is what the language does natively.
type Mousectl struct {
C <-chan Mouse
Resize <-chan struct{}
f *os.File
}
func OpenMouse(dev string) (*Mousectl, error) {
if dev == "" {
dev = "/dev"
}
f, err := os.OpenFile(dev+"/mouse", os.O_RDWR, 0)
if err != nil {
return nil, fmt.Errorf("open mouse: %w", err)
}
c := make(chan Mouse)
rc := make(chan struct{}, 1)
mc := &Mousectl{C: c, Resize: rc, f: f}
// One goroutine parked in read(2). Plan 9 has no netpoll in the Go
// runtime, so this pins an OS thread for as long as it blocks -- fine
// for the two or three event sources a UI has, not for hundreds.
go func() {
defer close(c)
buf := make([]byte, 1+5*12)
for {
n, err := f.Read(buf)
if err != nil {
return
}
if n != 1+4*12 {
continue
}
fld := func(i int) int {
v, _ := strconv.Atoi(trim(string(buf[1+i*12 : 1+i*12+12])))
return v
}
switch buf[0] {
case 'r':
select {
case rc <- struct{}{}:
default:
}
fallthrough
case 'm':
c <- Mouse{
Point: Point{int32(fld(0)), int32(fld(1))},
Buttons: fld(2),
Msec: uint32(fld(3)),
}
}
}
}()
return mc, nil
}
func (mc *Mousectl) Close() error { return mc.f.Close() }
|