diff options
| author | Calvin Morrison <calvin@pobox.com> | 2026-08-22 13:16:35 -0400 |
|---|---|---|
| committer | Calvin Morrison <calvin@pobox.com> | 2026-08-22 13:16:35 -0400 |
| commit | 9aacce8b3b54060d0037eca897856d7273e6e5e8 (patch) | |
| tree | a98ea0cb151a7ded56fe681d6782986b2807dfcf /gui/draw/mouse.go | |
| parent | 5ef4699d05bc919255449a9af780f216a0589a72 (diff) | |
gui: a /dev/draw layer in go, with no cgo and no devdraw
9fans.net/go/draw builds for plan9 but shells out to plan9port's
devdraw, which 9front does not have. This talks to /dev/draw itself:
the protocol is file i/o, so a pure-go client is a few hundred lines
under an already-complete idea.
draw/keyboard.go opens /dev/cons and turns the console raw, as
libdraw's initkeyboard does. Without it rio keeps its line editor on
the window and paints what you type over the drawing.
draw/snarf.go writes /dev/snarf, which is what every other program on
the system means by copy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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() } |
