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/window.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/window.go')
| -rw-r--r-- | gui/draw/window.go | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/gui/draw/window.go b/gui/draw/window.go new file mode 100644 index 0000000..0de8070 --- /dev/null +++ b/gui/draw/window.go @@ -0,0 +1,86 @@ +package draw + +import ( + "fmt" + "os" + "strconv" + "strings" +) + +// NamedImage looks up an image the window system published by name and +// installs it under a fresh id of ours. The 'n' message also sets the +// client's infoid, so the image's geometry comes back from a plain read of +// the ctl file -- the same 12-field block that draw/new returns. +func (d *Display) NamedImage(name string) (*Image, error) { + if name == "" || len(name) >= 256 { + return nil, fmt.Errorf("namedimage: bad name %q", name) + } + d.mu.Lock() + d.nextid++ + id := d.nextid + d.mu.Unlock() + + b := make([]byte, 6+len(name)) + b[0] = 'n' + put32(b, 1, id) + b[5] = byte(len(name)) + copy(b[6:], name) + if err := d.write(b); err != nil { + return nil, fmt.Errorf("namedimage %q: %w", name, err) + } + + var info [12 * 12]byte + n, err := d.ctl.ReadAt(info[:], 0) + if err != nil && n < len(info) { + return nil, fmt.Errorf("namedimage %q: ctl read: %w", name, err) + } + fld := func(i int) int32 { + v, _ := strconv.Atoi(trim(string(info[i*12 : i*12+12]))) + return int32(v) + } + return &Image{ + d: d, + id: id, + chn: strToChan(trim(string(info[2*12 : 3*12]))), + repl: fld(3) != 0, + R: Rect(fld(4), fld(5), fld(6), fld(7)), + Clipr: Rect(fld(8), fld(9), fld(10), fld(11)), + }, nil +} + +// Window returns the image to draw into: the rio window named by +// <dev>/winname if there is one, otherwise the raw screen. +// +// Without this a program draws to image 0 -- the screen itself -- straight +// over the window system. Nothing stops it: /dev/draw is the kernel's +// device, not rio's, and rio publishes the window by name rather than +// proxying the device. Being a good citizen is opt-in, and this is the +// opt-in. +func (d *Display) Window(dev string) (*Image, error) { + if dev == "" { + dev = "/dev" + } + buf, err := os.ReadFile(dev + "/winname") + if err != nil { + return d.Screen, nil // not under a window system + } + name := strings.TrimRight(string(buf), "\x00\n ") + if name == "" { + return d.Screen, nil + } + win, err := d.NamedImage(name) + if err != nil { + return nil, err + } + return win, nil +} + +// Reattach re-reads winname and looks the window up again. rio replaces the +// window's image on resize rather than resizing it in place, so a resize +// event means "throw the old one away and ask again". +func (d *Display) Reattach(dev string, old *Image) (*Image, error) { + if old != nil && old.id != 0 { + old.Free() // ignore errors: the old image may already be gone + } + return d.Window(dev) +} |
