diff options
Diffstat (limited to 'gui/draw/mouse.go')
| -rw-r--r-- | gui/draw/mouse.go | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/gui/draw/mouse.go b/gui/draw/mouse.go new file mode 100644 index 0000000..a4ddf63 --- /dev/null +++ b/gui/draw/mouse.go @@ -0,0 +1,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() } |
