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/keyboard.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/keyboard.go')
| -rw-r--r-- | gui/draw/keyboard.go | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/gui/draw/keyboard.go b/gui/draw/keyboard.go new file mode 100644 index 0000000..3885364 --- /dev/null +++ b/gui/draw/keyboard.go @@ -0,0 +1,65 @@ +package draw + +import ( + "bufio" + "os" + "unicode/utf8" +) + +// Keyboardctl delivers runes typed at the window. +// +// Opening it also turns the console raw. Without that, rio keeps its own +// line editor on the window and echoes what you type into the text layer, +// painting over whatever the program has drawn. Holding consctl open is +// what keeps raw mode; closing it puts the window back as it was. +type Keyboardctl struct { + C <-chan rune + ctl *os.File + cons *os.File +} + +func OpenKeyboard(dev string) (*Keyboardctl, error) { + ctl, err := os.OpenFile(dev+"/consctl", os.O_WRONLY, 0) + if err != nil { + return nil, err + } + if _, err := ctl.WriteString("rawon"); err != nil { + ctl.Close() + return nil, err + } + cons, err := os.Open(dev + "/cons") + if err != nil { + ctl.Close() + return nil, err + } + + ch := make(chan rune, 32) + k := &Keyboardctl{C: ch, ctl: ctl, cons: cons} + go func() { + defer close(ch) + br := bufio.NewReader(cons) + var buf []byte + b := make([]byte, 1) + for { + n, err := br.Read(b) + if n == 0 || err != nil { + return + } + buf = append(buf, b[0]) + // a rune may arrive a byte at a time + if r, sz := utf8.DecodeRune(buf); r != utf8.RuneError || sz > 1 { + ch <- r + buf = buf[:0] + } else if len(buf) >= utf8.UTFMax { + buf = buf[:0] + } + } + }() + return k, nil +} + +func (k *Keyboardctl) Close() error { + k.cons.Close() + // dropping consctl is what restores rio's own line editing + return k.ctl.Close() +} |
