ref: 7084ab4489ce1ddb6595a8f2624e8fdf23a73d8f
author: Philip Silva <philip.silva@protonmail.com>
date: Mon Mar 28 19:43:43 EDT 2022
First commit
--- /dev/null
+++ b/.gitignore
@@ -1,0 +1,2 @@
+/data
+/wildlife
--- /dev/null
+++ b/README
@@ -1,0 +1,13 @@
+Plan 9 port of MS Dangerous Creatures (very wip)
+
+At the moment it's almost unusable and only the top level menus work. (right click: back)
+
+Compile:
+
+go build .
+
+Usage:
+
+./wildlife [-x scale]
+
+Original iso required, files need to be in ./data/
--- /dev/null
+++ b/go.mod
@@ -1,0 +1,17 @@
+module wildlife
+
+go 1.18
+
+replace 9fans.net/go v0.0.0-00010101000000-000000000000 => github.com/psilva261/go v0.0.0-20210805155101-6b9925e0d807
+
+replace 9fans.net/go v0.0.2 => github.com/psilva261/go v0.0.0-20210805155101-6b9925e0d807
+
+replace github.com/mjl-/duit v0.0.0-20200330125617-580cb0b2843f => github.com/psilva261/duit v0.0.0-20210802155600-7e8fedefa7ba
+
+require (
+ 9fans.net/go v0.0.2
+ github.com/mjl-/duit v0.0.0-20200330125617-580cb0b2843f
+ golang.org/x/image v0.0.0-20220321031419-a8550c1d254a
+)
+
+require github.com/psilva261/szdd v0.0.0-20220328193535-155e45add743 // indirect
--- /dev/null
+++ b/go.sum
@@ -1,0 +1,12 @@
+github.com/psilva261/duit v0.0.0-20210802155600-7e8fedefa7ba h1:p8M5yp22QzefbIU3xGERHYnGxlfkcjWV6dh62xIO0U4=
+github.com/psilva261/duit v0.0.0-20210802155600-7e8fedefa7ba/go.mod h1:Ukumf0oazEttGwdWuN21g81jNap4NVccEgKuAUAqkf0=
+github.com/psilva261/go v0.0.0-20210805155101-6b9925e0d807 h1:JJqdNuwkTdbAe8hxrTaBBbX5TTk88gJrOhGigxoQb7c=
+github.com/psilva261/go v0.0.0-20210805155101-6b9925e0d807/go.mod h1:lfPdxjq9v8pVQXUMBCx5EO5oLXWQFlKRQgs1kEkjoIM=
+github.com/psilva261/szdd v0.0.0-20220328172236-36b7351d8d78 h1:4TlrF1ZxPU0gAEKXyLv39YsYBsC7kVSyAzPh2WFqnrU=
+github.com/psilva261/szdd v0.0.0-20220328172236-36b7351d8d78/go.mod h1:GUNIiWDWdjykRjKAc2I00bfkn6i3P4h2HIUZrEYl3F0=
+github.com/psilva261/szdd v0.0.0-20220328193535-155e45add743 h1:UTvkQdz3N7mxcTdRZM3b5hX6N1SXHdKKjsJdmZTn7rc=
+github.com/psilva261/szdd v0.0.0-20220328193535-155e45add743/go.mod h1:GUNIiWDWdjykRjKAc2I00bfkn6i3P4h2HIUZrEYl3F0=
+golang.org/x/image v0.0.0-20220321031419-a8550c1d254a h1:LnH9RNcpPv5Kzi15lXg42lYMPUf0x8CuPv1YnvBWZAg=
+golang.org/x/image v0.0.0-20220321031419-a8550c1d254a/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
--- /dev/null
+++ b/main.go
@@ -1,0 +1,510 @@
+package main
+
+import (
+ "9fans.net/go/draw"
+ "bytes"
+ "fmt"
+ "image"
+ imagedraw "image/draw"
+ "flag"
+ "github.com/mjl-/duit"
+ "github.com/psilva261/szdd"
+ xdraw "golang.org/x/image/draw"
+ "log"
+ "math"
+ "os"
+
+ _ "golang.org/x/image/bmp"
+ _ "image/gif"
+ _ "image/jpeg"
+ _ "image/png"
+)
+
+var (
+ dui *duit.DUI
+ scale int
+)
+
+type Folder struct {
+ *duit.Image
+
+ Pre string
+ Suf string
+ Name string
+ Items map[string]string
+ Targets []Target
+}
+
+func getFolder(root, pre string) (ff *Folder) {
+ //log.Printf("getFolder(%v, %v)", root, pre)
+ var rf *Folder
+ if root != "" {
+ for _, f := range folders {
+ if f.Pre == root {
+ rf = f
+ }
+ }
+ }
+ for _, f := range folders {
+ if f.Pre == pre {
+ ff = f
+ }
+ }
+ var fn string
+ if ff != nil {
+ fn = ff.Main()
+ } else {
+ ff = rf
+ fn = ff.getItem(pre)
+ }
+ ff.Image = &duit.Image{
+ Image: readImagePath(fn),
+ }
+ return
+}
+
+func (f Folder) Main() string {
+ fnSuf := f.Suf
+ if _, ok := f.Items[f.Suf]; !ok {
+ fnSuf = "0001"
+ }
+ return fmt.Sprintf("./data/about/%v%v/%v%v.dib", f.Pre, f.Suf, f.Pre, fnSuf)
+}
+
+func (f Folder) getItem(k string) string {
+ fnSuf := k
+ return fmt.Sprintf("./data/about/%v%v/%v%v.dib", f.Pre, f.Suf, f.Pre, fnSuf)
+}
+
+func (f *Folder) Mouse(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r duit.Result) {
+ if m.Buttons&1 == 1 {
+ //log.Printf("m=%+v, origM=%+v", m, origM)
+ for _, t := range f.Targets {
+ if t.has(m.Point) {
+ tf := getFolder(f.Pre, t.key)
+ dui.Top.UI = tf
+ dui.MarkLayout(dui.Top.UI)
+ dui.MarkDraw(dui.Top.UI)
+ dui.Render()
+ }
+ }
+ return duit.Result{
+ Consumed: true,
+ }
+ } else if m.Buttons&4 == 4 {
+ tf := getFolder("", "cont")
+ dui.Top.UI = tf
+ dui.MarkLayout(dui.Top.UI)
+ dui.MarkDraw(dui.Top.UI)
+ dui.Render()
+ }
+ return f.Image.Mouse(dui, self, m, origM, orig)
+}
+
+type Target struct {
+ key string
+ xy draw.Point
+ r int
+}
+
+func (t Target) has(p draw.Point) bool {
+ x := p.X * 2 / scale
+ y := p.Y * 2 / scale
+ return math.Abs(float64(x)-float64(t.xy.X)) < float64(t.r) && math.Abs(float64(y)-float64(t.xy.Y)) < float64(t.r)
+}
+
+var folders = []*Folder{
+ &Folder{
+ Pre: "afri",
+ Suf: "00aa",
+ Name: "Africa",
+ Items: map[string]string{
+ "00aa": "Waterhole",
+ "00fb": "Art",
+ },
+ },
+ &Folder{
+ Pre: "atls",
+ Suf: "00nv",
+ Name: "Atlas",
+ Items: map[string]string{
+ "00nv": "Continents",
+ "01p1": "African reptiles",
+ "01p2": "African herbivores",
+ "01pu": "Africa",
+ "02pu": "Asia",
+ "03pu": "Australia",
+ "04pu": "Europe",
+ "05pu": "North America",
+ "06pu": "South America",
+ },
+ Targets: []Target{
+ Target{
+ key: "01pu",
+ xy: draw.Point{
+ X: 659,
+ Y: 461,
+ },
+ r: 100,
+ },
+ Target{
+ key: "02pu",
+ xy: draw.Point{
+ X: 931,
+ Y: 275,
+ },
+ r: 200,
+ },
+ Target{
+ key: "03pu",
+ xy: draw.Point{
+ X: 1117,
+ Y: 603,
+ },
+ r: 50,
+ },
+ Target{
+ key: "04pu",
+ xy: draw.Point{
+ X: 600,
+ Y: 262,
+ },
+ r: 50,
+ },
+ Target{
+ key: "05pu",
+ xy: draw.Point{
+ X: 222,
+ Y: 270,
+ },
+ r: 140,
+ },
+ Target{
+ key: "06pu",
+ xy: draw.Point{
+ X: 350,
+ Y: 552,
+ },
+ r: 120,
+ },
+ },
+ },
+ &Folder{
+ Pre: "aust",
+ Suf: "00aa",
+ Name: "Australia",
+ Items: map[string]string{
+ "00aa": "Oddities",
+ },
+ },
+ &Folder{
+ Pre: "beet",
+ Suf: "00aa",
+ Name: "Beetles",
+ Items: map[string]string{
+ "00aa": "Beetles",
+ },
+ },
+ &Folder{
+ Pre: "frst",
+ Suf: "00aa",
+ Name: "Forest",
+ Items: map[string]string{
+ "00aa": "Forest Environments",
+ },
+ },
+ &Folder{
+ Pre: "cont",
+ Suf: "00nv",
+ Name: "Contents",
+ Items: map[string]string{
+ "00nv": "Contents",
+ },
+ Targets: []Target{
+ Target{
+ key: "atls",
+ xy: draw.Point{
+ X: 242,
+ Y: 190,
+ },
+ r: 150,
+ },
+ Target{
+ key: "habt",
+ xy: draw.Point{
+ X: 256,
+ Y: 588,
+ },
+ r: 200,
+ },
+ Target{
+ key: "indx",
+ xy: draw.Point{
+ X: 920,
+ Y: 614,
+ },
+ r: 200,
+ },
+ Target{
+ key: "weap",
+ xy: draw.Point{
+ X: 997,
+ Y: 200,
+ },
+ r: 200,
+ },
+ },
+ },
+ &Folder{
+ Pre: "guid",
+ Suf: "00nv",
+ Name: "Guides",
+ Items: map[string]string{
+ "00nv": "Guides",
+ },
+ },
+ &Folder{
+ Pre: "habt",
+ Suf: "00nv",
+ Name: "Habitats",
+ Items: map[string]string{
+ "00nv": "Habitats",
+ },
+ },
+ &Folder{
+ Pre: "help",
+ Suf: "00nv",
+ Name: "Help",
+ Items: map[string]string{
+ "00nv": "Help",
+ },
+ },
+ &Folder{
+ Pre: "indx",
+ Suf: "00nv",
+ Name: "Index",
+ Items: map[string]string{
+ "0001": "Index",
+ },
+ Targets: []Target{
+ Target{
+ key: "0001",
+ xy: draw.Point{
+ X: 48,
+ Y: 90,
+ },
+ r: 25,
+ },
+ Target{
+ key: "0002",
+ xy: draw.Point{
+ X: 97,
+ Y: 90,
+ },
+ r: 25,
+ },
+ Target{
+ key: "0003",
+ xy: draw.Point{
+ X: 144,
+ Y: 90,
+ },
+ r: 25,
+ },
+ Target{
+ key: "0003",
+ xy: draw.Point{
+ X: 186,
+ Y: 90,
+ },
+ r: 25,
+ },
+ Target{
+ key: "0004",
+ xy: draw.Point{
+ X: 236,
+ Y: 90,
+ },
+ r: 25,
+ },
+ Target{
+ key: "0005",
+ xy: draw.Point{
+ X: 340,
+ Y: 90,
+ },
+ r: 40,
+ },
+ Target{
+ key: "0006",
+ xy: draw.Point{
+ X: 440,
+ Y: 90,
+ },
+ r: 40,
+ },
+ Target{
+ key: "0007",
+ xy: draw.Point{
+ X: 600,
+ Y: 90,
+ },
+ r: 90,
+ },
+ Target{
+ key: "0008",
+ xy: draw.Point{
+ X: 748,
+ Y: 90,
+ },
+ r: 40,
+ },
+ Target{
+ key: "0009",
+ xy: draw.Point{
+ X: 843,
+ Y: 90,
+ },
+ r: 38,
+ },
+ Target{
+ key: "0010",
+ xy: draw.Point{
+ X: 947,
+ Y: 90,
+ },
+ r: 60,
+ },
+ Target{
+ key: "0011",
+ xy: draw.Point{
+ X: 1112,
+ Y: 90,
+ },
+ r: 85,
+ },
+ },
+ },
+ /*Folder{
+ Pre: "titl",
+ Suf: "00nv",
+ Name: "Title",
+ Items: map[string]string{
+ "00nv": "Title",
+ },
+ },*/
+ &Folder{
+ Pre: "weap",
+ Suf: "00nv",
+ Name: "Index",
+ Items: map[string]string{
+ "00nv": "Index",
+ },
+ Targets: []Target{
+ Target{
+ key: "01pu",
+ xy: draw.Point{
+ X: 250,
+ Y: 400,
+ },
+ r: 150,
+ },
+ Target{
+ key: "03pu",
+ xy: draw.Point{
+ X: 654,
+ Y: 375,
+ },
+ r: 150,
+ },
+ Target{
+ key: "02pu",
+ xy: draw.Point{
+ X: 1047,
+ Y: 400,
+ },
+ r: 150,
+ },
+ },
+ },
+}
+
+func check(err error, msg string) {
+ if err != nil {
+ log.Fatalf("%s: %s\n", msg, err)
+ }
+}
+
+func resize(img image.Image, scale int) image.Image {
+ bounds := img.Bounds()
+ newX := bounds.Dx() * 2
+ newY:= bounds.Dy() * 2
+ dst := image.NewRGBA(image.Rect(0, 0, newX, newY))
+ xdraw.NearestNeighbor.Scale(dst, dst.Rect, img, img.Bounds(), xdraw.Over, nil)
+ return dst
+}
+
+func readImagePath(path string) *draw.Image {
+ bs, err := os.ReadFile(path)
+ check(err, "read file")
+ data, err := szdd.Expand(bs)
+ check(err, "expand")
+ img, _, err := image.Decode(bytes.NewReader(data))
+ check(err, "decode")
+ if scale > 1 {
+ img = resize(img, scale)
+ }
+ bounds := img.Bounds()
+ ni, err := dui.Display.AllocImage(bounds, draw.ABGR32, false, draw.White)
+ check(err, "allocimage")
+ var rgba *image.RGBA
+ switch i := img.(type) {
+ case *image.RGBA:
+ rgba = i
+ default:
+ b := img.Bounds()
+ rgba = image.NewRGBA(image.Rectangle{image.ZP, b.Size()})
+ imagedraw.Draw(rgba, rgba.Bounds(), img, b.Min, imagedraw.Src)
+ }
+ _, err = ni.Load(rgba.Bounds(), rgba.Pix)
+ check(err, "load image")
+ return ni
+ }
+
+func main() {
+ sc := flag.Int("x", 0, "scale")
+ flag.Parse()
+ scale = *sc
+
+ var err error
+ dui, err = duit.NewDUI("wildlife", nil)
+ check(err, "new dui")
+
+ if scale == 0 {
+ if dui.Display.DPI > 100 {
+ scale = 2
+ } else {
+ scale = 1
+ }
+ }
+
+ _=readImagePath
+
+ f := getFolder("", "cont")
+
+ dui.Top.UI = f
+ dui.Render()
+
+ for {
+ select {
+ case e := <-dui.Inputs:
+ dui.Input(e)
+
+ case err, ok := <-dui.Error:
+ if !ok {
+ return
+ }
+ log.Printf("duit: %s\n", err)
+ }
+ }
+}