diff options
Diffstat (limited to 'gui/cmd/spike')
| -rw-r--r-- | gui/cmd/spike/main.go | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/gui/cmd/spike/main.go b/gui/cmd/spike/main.go new file mode 100644 index 0000000..442df52 --- /dev/null +++ b/gui/cmd/spike/main.go @@ -0,0 +1,106 @@ +// Spike: prove that a Plan 9 GUI can be written in Go with nothing but the +// standard library -- no C, no cgo, no libdraw, no window system. +// +// Draws a panel layout and a cursor block that tracks the mouse. +// Button 3 quits; so does the deadline, so a forgotten instance on a VM +// does not sit on /dev/draw forever. +package main + +import ( + "fmt" + "os" + "time" + + "9front/gui/draw" +) + +func main() { + if err := run(); err != nil { + fmt.Fprintf(os.Stderr, "spike: %v\n", err) + os.Exit(1) + } +} + +func run() error { + d, err := draw.Init("/dev") + if err != nil { + return err + } + defer d.Close() + if d.Screen == nil { + return fmt.Errorf("draw device gave no screen image") + } + scr := d.Screen + r := scr.Rect() + fmt.Fprintf(os.Stderr, "spike: screen %dx%d\n", r.Dx(), r.Dy()) + + col := map[string]uint32{} + img := map[string]*draw.Image{} + for name, v := range map[string]uint32{ + "bg": 0x2B3A42FF, + "panel": 0x3F5765FF, + "accent": 0xBDD4DEFF, + "hot": 0xEFA00BFF, + } { + col[name] = v + if img[name], err = d.Color(v); err != nil { + return err + } + } + + // Static layout: a header bar and three panels, laid out by arithmetic + // rather than by hand-placed constants. This is the seam where a real + // layout pass would go. + repaint := func() { + draw.Draw(scr, r, img["bg"], nil, draw.ZP) + hdr := draw.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+40) + draw.Draw(scr, hdr, img["accent"], nil, draw.ZP) + + const gap = 12 + top := r.Min.Y + 40 + gap + w := (r.Dx() - 4*gap) / 3 + for i := int32(0); i < 3; i++ { + x := r.Min.X + gap + i*(w+gap) + p := draw.Rect(x, top, x+w, r.Max.Y-gap) + draw.Draw(scr, p, img["panel"], nil, draw.ZP) + } + } + repaint() + d.Flush() + + mc, err := draw.OpenMouse("/dev") + if err != nil { + return err + } + defer mc.Close() + + deadline := time.After(10 * time.Minute) + cursor := draw.Rect(0, 0, 28, 28) + var last draw.Rectangle + for { + select { + case m, ok := <-mc.C: + if !ok { + return nil + } + if m.Buttons&4 != 0 { + return nil + } + // Damage repair without a full repaint: erase where the + // block was, then draw it where the mouse is now. + if last != (draw.Rectangle{}) { + repaint() + } + now := cursor.Add(draw.Point{X: m.X - 14, Y: m.Y - 14}) + draw.Draw(scr, now, img["hot"], nil, draw.ZP) + d.Flush() + last = now + case <-mc.Resize: + r = scr.Rect() + repaint() + d.Flush() + case <-deadline: + return nil + } + } +} |
