├── LICENSE ├── README.md ├── acme ├── Dict │ ├── Dict.go │ ├── go.mod │ └── go.sum ├── Watch │ ├── main.go │ ├── sig_generic.go │ └── sig_unix.go ├── acme.go ├── acme_p9p.go ├── acme_plan9.go ├── acmego │ ├── main.go │ └── read.go └── editinacme │ └── main.go ├── cmd ├── acme │ ├── acme.go │ ├── dat.h.go │ ├── fsys1.go │ ├── internal │ │ ├── addr │ │ │ └── addr.go │ │ ├── adraw │ │ │ ├── draw.go │ │ │ └── font.go │ │ ├── alog │ │ │ └── alog.go │ │ ├── bufs │ │ │ └── buf.go │ │ ├── complete │ │ │ └── complete.go │ │ ├── disk │ │ │ ├── buff.go │ │ │ └── disk.go │ │ ├── dump │ │ │ └── rows1.go │ │ ├── edit │ │ │ ├── ecmd.go │ │ │ ├── edit.go │ │ │ ├── edit.h.go │ │ │ └── elog.go │ │ ├── exec │ │ │ └── exec.go │ │ ├── file │ │ │ └── file.go │ │ ├── fileload │ │ │ ├── buff.go │ │ │ └── text1.go │ │ ├── regx │ │ │ └── regx.go │ │ ├── runes │ │ │ ├── runes.go │ │ │ └── util.go │ │ ├── ui │ │ │ ├── arg.go │ │ │ ├── cols1.go │ │ │ ├── look.go │ │ │ ├── look1.go │ │ │ ├── rows.go │ │ │ ├── scrl1.go │ │ │ ├── text.go │ │ │ ├── ui.go │ │ │ └── wind1.go │ │ ├── util │ │ │ ├── cols1.go │ │ │ └── util.go │ │ └── wind │ │ │ ├── cols.go │ │ │ ├── rows.go │ │ │ ├── scrl.go │ │ │ ├── text.go │ │ │ └── wind.go │ ├── logf.go │ ├── look1.go │ ├── post.go │ ├── util.go │ └── xfid.go ├── devdraw │ ├── devdraw.go │ ├── devdraw.h.go │ ├── drawclient.go │ ├── latin1.go │ ├── latinkbd.go │ ├── mklatinkbd.go │ ├── mouseswap.go │ ├── screen.go │ ├── srv.go │ └── winsize.go ├── internal │ └── base │ │ └── dat.h.go ├── sam │ ├── address.go │ ├── buff.go │ ├── cmd.go │ ├── conv │ ├── disk.go │ ├── error.go │ ├── errors.h.go │ ├── file.go │ ├── io.go │ ├── list.go │ ├── mesg.go │ ├── mesg.h.go │ ├── moveto.go │ ├── multi.go │ ├── parse.h.go │ ├── plumb.h.go │ ├── rasp.go │ ├── regexp.go │ ├── rules.txt │ ├── sam.go │ ├── sam.h.go │ ├── shell.go │ ├── string.go │ ├── sys.go │ ├── test.sh │ ├── testdata │ │ ├── 000.in │ │ ├── 000.txt │ │ ├── 001.txt │ │ ├── 002.txt │ │ ├── 003.txt │ │ ├── 004.txt │ │ ├── 005.txt │ │ ├── 006.txt │ │ ├── 007.txt │ │ ├── 008.txt │ │ ├── 009.txt │ │ ├── 011.txt │ │ ├── 012.txt │ │ ├── 013.txt │ │ ├── 014.txt │ │ ├── 015.txt │ │ └── gettysburg │ ├── unix.go │ ├── util.go │ └── xec.go └── samterm │ ├── conv │ ├── flayer.go │ ├── flayer.h.go │ ├── icons.go │ ├── io.go │ ├── main.go │ ├── menu.go │ ├── mesg.go │ ├── mesg.h.go │ ├── notunix.go │ ├── rasp.go │ ├── rules.txt │ ├── samterm.h.go │ ├── scroll.go │ ├── sys.go │ └── unix.go ├── draw ├── alloc.go ├── allocimagemix.go ├── arith.go ├── bench_test.go ├── bezier.go ├── border.go ├── buildfont.go ├── bytesperline.go ├── cloadimage.go ├── color.go ├── color_test.go ├── computil.go ├── creadimage.go ├── cursor.go ├── debug.go ├── defont.go ├── doc.go ├── draw.go ├── drawfcall │ ├── bit.go │ ├── msg.go │ ├── mux.go │ └── mux_plan9.go ├── drawrepl.go ├── egetrect.go ├── ellipse.go ├── emenuhit.go ├── event.go ├── fixbp ├── font.go ├── frame │ ├── frame.go │ ├── frbox.go │ ├── frdelete.go │ ├── frdraw.go │ ├── frinit.go │ ├── frinsert.go │ ├── frptofchar.go │ ├── frselect.go │ ├── frutil.go │ └── internal │ │ └── frametest │ │ └── main.go ├── freesubfont.go ├── getdefont.go ├── getrect.go ├── getsubfont.go ├── icossin.go ├── icossin2.go ├── init.go ├── init_test.go ├── keyboard.go ├── line.go ├── loadimage.go ├── memdraw │ ├── alloc.go │ ├── arc.go │ ├── arctest.go │ ├── cload.go │ ├── cmap.go │ ├── conv │ ├── cread.go │ ├── draw-stub.go │ ├── draw.go │ ├── drawtest.go │ ├── ellipse.go │ ├── fillpoly.go │ ├── hwdraw.go │ ├── iprint.go │ ├── lalloc.go │ ├── layerop.go │ ├── ldelete.go │ ├── ldraw.go │ ├── lhide.go │ ├── line.go │ ├── lline.go │ ├── lload.go │ ├── load.go │ ├── lorigin.go │ ├── lsetrefresh.go │ ├── ltofront.go │ ├── ltorear.go │ ├── lunload.go │ ├── memdraw.h.go │ ├── memlayer.h.go │ ├── mkcmap.go │ ├── openmemsubfont.go │ ├── poly.go │ ├── read.go │ ├── rules.txt │ ├── string.go │ ├── subfont.go │ ├── unload.go │ └── write.go ├── menuhit.go ├── mkfont.go ├── mouse.go ├── openfont.go ├── pix.go ├── poly.go ├── readimage.go ├── readsubfont.go ├── rectclip.go ├── replclipr.go ├── rgb.go ├── scroll.go ├── snarf.go ├── string.go ├── stringsubfont.go ├── stringwidth.go ├── subfont.go ├── subfontcache.go ├── subfontname.go ├── unloadimage.go ├── window.go ├── writeimage.go ├── writesubfont.go └── wsys.go ├── games ├── 4s │ ├── 4s.go │ ├── data.go │ └── xs.go └── spacewar │ ├── code.go │ ├── pdp1 │ └── pdp1.go │ └── spacewar.go ├── go.mod ├── go.sum ├── p9trace ├── deps.go ├── go.mod ├── go.sum ├── reorder.go ├── super.go ├── super2.go └── trace.go ├── plan9 ├── bit.go ├── client │ ├── cat │ │ └── cat.go │ ├── conn.go │ ├── conn_plan9.go │ ├── dial.go │ ├── dial_plan9.go │ ├── fid.go │ ├── fid_p9p.go │ ├── fid_plan9.go │ ├── fsys.go │ └── fsys_plan9.go ├── const.go ├── dir.go ├── fcall.go └── srv9p │ ├── example │ ├── netshell │ │ └── main.go │ ├── ramfs │ │ └── main.go │ ├── rot13fs │ │ └── main.go │ └── roundtrip │ │ └── main.go │ ├── fid.go │ ├── file.go │ ├── help.go │ ├── post.go │ ├── refmap.go │ ├── req.go │ ├── srv.go │ ├── srv_test.go │ ├── testdata │ ├── basic.txt │ ├── flush.txt │ ├── pipe.txt │ └── ramfs.txt │ └── uid.go └── plumb ├── plumb.go ├── plumb_p9p.go ├── plumb_plan9.go └── plumb_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Plan 9 Foundation 2 | Portions Copyright © 2001-2008 Russ Cox 3 | Portions Copyright © 2008-2009 Google LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Go Reference](https://pkg.go.dev/badge/9fans.net/go.svg)](https://pkg.go.dev/9fans.net/go) 2 | 3 | This repository contains packages for interacting with Plan 9 4 | as well as ports of common Plan 9 libraries and tools. 5 | 6 | ``` 7 | $ go install 9fans.net/go/...@latest 8 | -------------------------------------------------------------------------------- /acme/Dict/Dict.go: -------------------------------------------------------------------------------- 1 | package main // import "9fans.net/go/acme/Dict" 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | 7 | "9fans.net/go/acme" 8 | "golang.org/x/net/dict" 9 | ) 10 | 11 | var dictx = flag.String("d", "", "dictionary") 12 | var server = flag.String("s", "dict.org:dict", "server") 13 | var lookc = make(chan string) 14 | var d *dict.Client 15 | var dicts []dict.Dict 16 | 17 | func main() { 18 | flag.Parse() 19 | w, err := acme.New() 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | w.Name("/dict/") 24 | d, err = dict.Dial("tcp", *server) 25 | if err != nil { 26 | w.Write("body", []byte(err.Error())) 27 | return 28 | } 29 | w.Ctl("clean") 30 | go func() { 31 | dicts, err = d.Dicts() 32 | if err != nil { 33 | w.Write("body", []byte(err.Error())) 34 | return 35 | } 36 | for _, dict := range dicts { 37 | w.Fprintf("body", "%s\t%s\n", dict.Name, dict.Desc) 38 | } 39 | }() 40 | for word := range events(w) { 41 | go lookup(word) 42 | } 43 | } 44 | 45 | func lookup(word string) { 46 | defs, err := d.Define("!", word) 47 | if err != nil { 48 | log.Print(err) 49 | return 50 | } 51 | for _, def := range defs { 52 | go wordwin(def) 53 | } 54 | } 55 | 56 | func wordwin(def *dict.Defn) { 57 | w, err := acme.New() 58 | if err != nil { 59 | log.Fatal(err) 60 | } 61 | w.Name("/dict/%s/%s", def.Dict.Name, def.Word) 62 | w.Write("body", def.Text) 63 | w.Ctl("clean") 64 | for word := range events(w) { 65 | go lookup(word) 66 | } 67 | } 68 | 69 | func events(w *acme.Win) <-chan string { 70 | c := make(chan string, 10) 71 | go func() { 72 | for e := range w.EventChan() { 73 | switch e.C2 { 74 | case 'x', 'X': // execute 75 | if string(e.Text) == "Del" { 76 | w.Ctl("delete") 77 | } 78 | w.WriteEvent(e) 79 | case 'l', 'L': // look 80 | w.Ctl("clean") 81 | c <- string(e.Text) 82 | } 83 | } 84 | w.CloseFiles() 85 | close(c) 86 | }() 87 | return c 88 | } 89 | -------------------------------------------------------------------------------- /acme/Dict/go.mod: -------------------------------------------------------------------------------- 1 | module 9fans.net/go/acme/Dict 2 | 3 | go 1.13 4 | 5 | require ( 6 | 9fans.net/go v0.0.1 7 | golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b 8 | ) 9 | -------------------------------------------------------------------------------- /acme/Dict/go.sum: -------------------------------------------------------------------------------- 1 | 9fans.net/go v0.0.1 h1:PLKE9jnKK5I/hnQ4hZ0kM92946us4DClpcrzS+RTQZ0= 2 | 9fans.net/go v0.0.1/go.mod h1:lfPdxjq9v8pVQXUMBCx5EO5oLXWQFlKRQgs1kEkjoIM= 3 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 4 | golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b h1:lkjdUzSyJ5P1+eal9fxXX9Xg2BTfswsonKUse48C0uE= 5 | golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 6 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 7 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 8 | -------------------------------------------------------------------------------- /acme/Watch/sig_generic.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !linux,!darwin,!freebsd,!netbsd,!openbsd,!solaris 6 | 7 | package main 8 | 9 | import ( 10 | "os" 11 | "os/exec" 12 | "time" 13 | ) 14 | 15 | func isolate(cmd *exec.Cmd) { 16 | } 17 | 18 | func quit(cmd *exec.Cmd) { 19 | } 20 | 21 | func kill(cmd *exec.Cmd) { 22 | cmd.Process.Signal(os.Interrupt) 23 | time.Sleep(100 * time.Millisecond) 24 | cmd.Process.Kill() 25 | } 26 | -------------------------------------------------------------------------------- /acme/Watch/sig_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux darwin freebsd netbsd openbsd solaris 6 | 7 | package main 8 | 9 | import ( 10 | "os/exec" 11 | "syscall" 12 | "time" 13 | ) 14 | 15 | func isolate(cmd *exec.Cmd) { 16 | cmd.SysProcAttr = &syscall.SysProcAttr{ 17 | Setpgid: true, 18 | } 19 | } 20 | 21 | func quit(cmd *exec.Cmd) { 22 | pid := cmd.Process.Pid 23 | if pid <= 0 { 24 | return 25 | } 26 | syscall.Kill(-pid, syscall.SIGQUIT) 27 | } 28 | 29 | func kill(cmd *exec.Cmd) { 30 | pid := cmd.Process.Pid 31 | if pid <= 0 { 32 | return 33 | } 34 | syscall.Kill(-pid, syscall.SIGINT) 35 | time.Sleep(100 * time.Millisecond) 36 | syscall.Kill(-pid, syscall.SIGTERM) 37 | time.Sleep(100 * time.Millisecond) 38 | syscall.Kill(-pid, syscall.SIGKILL) 39 | } 40 | -------------------------------------------------------------------------------- /acme/acme_p9p.go: -------------------------------------------------------------------------------- 1 | // +build !plan9 2 | 3 | package acme 4 | 5 | import "9fans.net/go/plan9/client" 6 | 7 | func mountAcme() { 8 | fs, err := client.MountService("acme") 9 | fsys = fs 10 | fsysErr = err 11 | } 12 | -------------------------------------------------------------------------------- /acme/acme_plan9.go: -------------------------------------------------------------------------------- 1 | package acme 2 | 3 | import ( 4 | "9fans.net/go/plan9/client" 5 | ) 6 | 7 | func mountAcme() { 8 | // Already mounted at /mnt/acme 9 | fsys = &client.Fsys{Mtpt: "/mnt/acme"} 10 | fsysErr = nil 11 | } 12 | -------------------------------------------------------------------------------- /acme/editinacme/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Editinacme can be used as $EDITOR in a Unix environment. 6 | // 7 | // Usage: 8 | // 9 | // editinacme 10 | // 11 | // Editinacme uses the plumber to ask acme to open the file, 12 | // waits until the file's acme window is deleted, and exits. 13 | package main 14 | 15 | import ( 16 | "flag" 17 | "fmt" 18 | "log" 19 | "os" 20 | "os/exec" 21 | "path/filepath" 22 | 23 | "9fans.net/go/acme" 24 | ) 25 | 26 | func main() { 27 | log.SetFlags(0) 28 | log.SetPrefix("editinacme: ") 29 | flag.Usage = func() { 30 | fmt.Fprintf(os.Stderr, "usage: editinacme file\n") 31 | os.Exit(2) 32 | } 33 | flag.Parse() 34 | if flag.NArg() != 1 { 35 | flag.Usage() 36 | } 37 | 38 | file := flag.Arg(0) 39 | 40 | fullpath, err := filepath.Abs(file) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | file = fullpath 45 | 46 | r, err := acme.Log() 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | 51 | log.Printf("editing %s", file) 52 | 53 | out, err := exec.Command("plumb", "-d", "edit", file).CombinedOutput() 54 | if err != nil { 55 | log.Fatalf("executing plumb: %v\n%s", err, out) 56 | } 57 | 58 | for { 59 | ev, err := r.Read() 60 | if err != nil { 61 | log.Fatalf("reading acme log: %v", err) 62 | } 63 | if ev.Op == "del" && ev.Name == file { 64 | break 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /cmd/acme/dat.h.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "9fans.net/go/cmd/acme/internal/wind" 5 | "9fans.net/go/cmd/internal/base" 6 | "9fans.net/go/draw" 7 | "9fans.net/go/plan9" 8 | "9fans.net/go/plumb" 9 | ) 10 | 11 | const ( 12 | Qdir = iota 13 | Qacme 14 | Qcons 15 | Qconsctl 16 | Qdraw 17 | Qeditout 18 | Qindex 19 | Qlabel 20 | Qlog 21 | Qnew 22 | QWaddr 23 | QWbody 24 | QWctl 25 | QWdata 26 | QWeditout 27 | QWerrors 28 | QWevent 29 | QWrdsel 30 | QWwrsel 31 | QWtag 32 | QWxdata 33 | QMAX 34 | ) 35 | 36 | type Timer struct { 37 | dt int 38 | cancel int 39 | c chan int 40 | next *Timer 41 | } 42 | 43 | type Dirtab struct { 44 | name string 45 | typ uint8 46 | qid int 47 | perm int 48 | } 49 | 50 | type Fid struct { 51 | fid int 52 | busy bool 53 | open bool 54 | qid plan9.Qid 55 | w *wind.Window 56 | dir []Dirtab 57 | next *Fid 58 | mntdir *base.Mntdir 59 | rpart []byte 60 | logoff int64 61 | } 62 | 63 | type Xfid struct { 64 | arg interface{} 65 | fcall *plan9.Fcall 66 | next *Xfid 67 | c chan func(*Xfid) 68 | f *Fid 69 | // buf *uint8 70 | flushed bool 71 | } 72 | 73 | var screen *draw.Image 74 | var keyboardctl *draw.Keyboardctl 75 | var timerpid int 76 | var fsyspid int 77 | var cputype string 78 | var home string 79 | var dodollarsigns bool 80 | 81 | type Waitmsg struct { 82 | pid int 83 | msg string 84 | } 85 | 86 | var ( 87 | cplumb = make(chan *plumb.Message) 88 | cxfidalloc = make(chan *Xfid) // TODO bidi 89 | cxfidfree = make(chan *Xfid) 90 | cnewwindow = make(chan *wind.Window) // TODO bidi 91 | mouseexit0 chan int 92 | mouseexit1 chan int 93 | cerr = make(chan []byte) 94 | cwarn = make(chan int, 1) 95 | ) 96 | 97 | // #define STACK 65536 98 | -------------------------------------------------------------------------------- /cmd/acme/internal/adraw/font.go: -------------------------------------------------------------------------------- 1 | package adraw 2 | 3 | import ( 4 | "sync" 5 | 6 | "9fans.net/go/cmd/acme/internal/alog" 7 | "9fans.net/go/cmd/acme/internal/util" 8 | "9fans.net/go/draw" 9 | ) 10 | 11 | var Font *draw.Font 12 | 13 | type RefFont struct { 14 | lk sync.Mutex 15 | Ref uint32 16 | F *draw.Font 17 | } 18 | 19 | var RefFont1 RefFont 20 | 21 | var RefFonts [2]*RefFont 22 | 23 | var nfix int 24 | 25 | func FindFont(fix, save, setfont bool, name string) *RefFont { 26 | var r *RefFont 27 | fixi := 0 28 | if fix { 29 | fixi = 1 30 | if nfix++; nfix > 1 { 31 | panic("fixi") 32 | } 33 | } 34 | if name == "" { 35 | name = FontNames[fixi] 36 | r = RefFonts[fixi] 37 | } 38 | if r == nil { 39 | for _, r = range FontCache { 40 | if r.F.Name == name { 41 | goto Found 42 | } 43 | } 44 | f, err := Display.OpenFont(name) 45 | if err != nil { 46 | alog.Printf("can't open font file %s: %v\n", name, err) 47 | return nil 48 | } 49 | r = new(RefFont) 50 | r.F = f 51 | FontCache = append(FontCache, r) 52 | } 53 | Found: 54 | if save { 55 | util.Incref(&r.Ref) 56 | if RefFonts[fixi] != nil { 57 | CloseFont(RefFonts[fixi]) 58 | } 59 | RefFonts[fixi] = r 60 | if name != FontNames[fixi] { 61 | FontNames[fixi] = name 62 | } 63 | } 64 | if setfont { 65 | RefFont1.F = r.F 66 | util.Incref(&r.Ref) 67 | CloseFont(RefFonts[0]) 68 | Font = r.F 69 | RefFonts[0] = r 70 | util.Incref(&r.Ref) 71 | Init() 72 | } 73 | util.Incref(&r.Ref) 74 | return r 75 | } 76 | 77 | func CloseFont(r *RefFont) { 78 | if util.Decref(&r.Ref) == 0 { 79 | for i := range FontCache { 80 | if FontCache[i] == r { 81 | copy(FontCache[i:], FontCache[i+1:]) 82 | FontCache = FontCache[:len(FontCache)-1] 83 | goto Found 84 | } 85 | } 86 | alog.Printf("internal error: can't find font in cache\n") 87 | Found: 88 | r.F.Free() 89 | } 90 | } 91 | 92 | var FontNames = []string{ 93 | "/lib/font/bit/lucsans/euro.8.font", 94 | "/lib/font/bit/lucm/unicode.9.font", 95 | } 96 | 97 | var FontCache []*RefFont 98 | -------------------------------------------------------------------------------- /cmd/acme/internal/alog/alog.go: -------------------------------------------------------------------------------- 1 | package alog 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | var warn = func(msg string) { 9 | fmt.Fprintf(os.Stderr, "acme: %s", msg) // msg has final newline 10 | } 11 | 12 | func Init(w func(string)) { 13 | warn = w 14 | } 15 | 16 | func Printf(format string, args ...interface{}) { 17 | s := fmt.Sprintf(format, args...) 18 | if s != "" && s[len(s)-1] != '\n' { 19 | s += "\n" 20 | } 21 | warn(s) 22 | } 23 | -------------------------------------------------------------------------------- /cmd/acme/internal/bufs/buf.go: -------------------------------------------------------------------------------- 1 | package bufs 2 | 3 | import ( 4 | "sync" 5 | 6 | "9fans.net/go/cmd/acme/internal/runes" 7 | ) 8 | 9 | const Len = 32 * 1024 10 | const RuneLen = Len / runes.RuneSize 11 | 12 | var runesPool = sync.Pool{ 13 | New: func() interface{} { return make([]rune, RuneLen) }, 14 | } 15 | 16 | func AllocRunes() []rune { 17 | return runesPool.Get().([]rune) 18 | } 19 | 20 | func FreeRunes(buf []rune) { 21 | if cap(buf) != RuneLen { 22 | panic("FreeRunes: wrong size") 23 | } 24 | runesPool.Put(buf[:RuneLen]) 25 | } 26 | 27 | var bytesPool = sync.Pool{ 28 | New: func() interface{} { return make([]byte, Len) }, 29 | } 30 | 31 | func AllocBytes() []byte { 32 | return bytesPool.Get().([]byte) 33 | } 34 | 35 | func FreeBytes(buf []byte) { 36 | if cap(buf) != Len { 37 | panic("FreeRunes: wrong size") 38 | } 39 | bytesPool.Put(buf[:Len]) 40 | } 41 | -------------------------------------------------------------------------------- /cmd/acme/internal/complete/complete.go: -------------------------------------------------------------------------------- 1 | // libcomplete/complete.c 2 | 3 | package complete 4 | 5 | import ( 6 | "fmt" 7 | "io/ioutil" 8 | "strings" 9 | "unicode/utf8" 10 | ) 11 | 12 | type Completion struct { 13 | Progress bool /* whether forward progress has been made */ 14 | Done bool /* whether the completion now represents a file or directory */ 15 | Text string /* the string to advance, suffixed " " or "/" for file or directory */ 16 | NumMatch int /* number of files that matched */ 17 | Files []string /* files returned */ 18 | } 19 | 20 | func Complete(dir, s string) (*Completion, error) { 21 | if strings.Contains(s, "/") { 22 | return nil, fmt.Errorf("slash character in name argument to complete") 23 | } 24 | 25 | // Note: ioutil.ReadDir sorts, so no sort below. 26 | dirs, err := ioutil.ReadDir(dir) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | // find the matches 32 | c := new(Completion) 33 | for _, info := range dirs { 34 | if name := info.Name(); strings.HasPrefix(name, s) { 35 | suffix := "" 36 | if info.IsDir() { 37 | suffix = "/" 38 | } 39 | c.Files = append(c.Files, name+suffix) 40 | } 41 | } 42 | 43 | if len(c.Files) > 0 { 44 | /* report interesting results */ 45 | /* trim length back to longest common initial string */ 46 | minlen := len(c.Files[0]) 47 | for i := 1; i < len(c.Files); i++ { 48 | minlen = longestprefixlength(c.Files[0], c.Files[i], minlen) 49 | } 50 | 51 | /* build the answer */ 52 | c.Done = len(c.Files) == 1 53 | c.Progress = c.Done || minlen > len(s) 54 | c.Text = c.Files[0][len(s):minlen] 55 | if c.Done && !strings.HasSuffix(c.Text, "/") { 56 | c.Text += " " 57 | } 58 | c.NumMatch = len(c.Files) 59 | } else { 60 | /* no match, so return all possible strings */ 61 | for _, info := range dirs { 62 | suffix := "" 63 | if info.IsDir() { 64 | suffix = "/" 65 | } 66 | c.Files = append(c.Files, info.Name()+suffix) 67 | } 68 | } 69 | return c, nil 70 | } 71 | 72 | func longestprefixlength(a, b string, n int) int { 73 | var i int 74 | for i = 0; i < n && i < len(a) && i < len(b); { 75 | ra, wa := utf8.DecodeRuneInString(a) 76 | rb, wb := utf8.DecodeRuneInString(b) 77 | if ra != rb || wa != wb { 78 | break 79 | } 80 | i += wa 81 | } 82 | return i 83 | } 84 | -------------------------------------------------------------------------------- /cmd/acme/internal/edit/edit.h.go: -------------------------------------------------------------------------------- 1 | //#pragma varargck argpos editerror 1 2 | 3 | package edit 4 | 5 | import ( 6 | "9fans.net/go/cmd/acme/internal/runes" 7 | "9fans.net/go/cmd/acme/internal/wind" 8 | ) 9 | 10 | type String struct { 11 | r []rune 12 | } 13 | 14 | type Addr struct { 15 | typ rune 16 | u struct { 17 | re *String 18 | left *Addr 19 | } 20 | num int 21 | next *Addr 22 | } 23 | 24 | type Address struct { 25 | r runes.Range 26 | f *wind.File 27 | } 28 | 29 | type Cmd struct { 30 | addr *Addr 31 | re *String 32 | u struct { 33 | cmd *Cmd 34 | text *String 35 | mtaddr *Addr 36 | } 37 | next *Cmd 38 | num int 39 | flag bool 40 | cmdc rune 41 | } 42 | 43 | // extern var cmdtab [unknown]cmdtab 44 | 45 | // #define INCR 25 // delta when growing list 46 | 47 | type List struct { 48 | nalloc int 49 | nused int 50 | u struct { 51 | listptr *[0]byte 52 | ptr **[0]byte 53 | ucharptr **uint8 54 | stringptr **String 55 | } 56 | } 57 | 58 | type Defaddr int 59 | 60 | const ( 61 | aNo Defaddr = iota 62 | aDot 63 | aAll 64 | ) 65 | -------------------------------------------------------------------------------- /cmd/acme/internal/fileload/buff.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | // #include 11 | // #include 12 | // #include "dat.h" 13 | // #include "fns.h" 14 | 15 | package fileload 16 | 17 | import ( 18 | "io" 19 | "os" 20 | "unicode/utf8" 21 | 22 | "9fans.net/go/cmd/acme/internal/alog" 23 | "9fans.net/go/cmd/acme/internal/bufs" 24 | "9fans.net/go/cmd/acme/internal/runes" 25 | "9fans.net/go/cmd/acme/internal/util" 26 | "9fans.net/go/cmd/acme/internal/wind" 27 | ) 28 | 29 | func Loadfile(fd *os.File, q0 int, nulls *bool, f func(int, []rune) int, h io.Writer) int { 30 | p := make([]byte, bufs.Len+utf8.UTFMax+1) 31 | r := make([]rune, bufs.Len) 32 | m := 0 33 | n := 1 34 | q1 := q0 35 | /* 36 | * At top of loop, may have m bytes left over from 37 | * last pass, possibly representing a partial rune. 38 | */ 39 | for n > 0 { 40 | var err error 41 | n, err = fd.Read(p[m : m+bufs.Len]) 42 | if err != nil && err != io.EOF { 43 | alog.Printf("read error in Buffer.load: %v", err) 44 | break 45 | } 46 | if h != nil { 47 | h.Write(p[m : m+n]) 48 | } 49 | m += n 50 | nb, nr, nulls1 := runes.Convert(p[:m], r, err == io.EOF) 51 | if nulls1 { 52 | *nulls = true 53 | } 54 | copy(p, p[nb:m]) 55 | m -= nb 56 | q1 += f(q1, r[:nr]) 57 | } 58 | return q1 - q0 59 | } 60 | 61 | func fileloader(f *wind.File) func(pos int, data []rune) int { 62 | return func(pos int, data []rune) int { 63 | f.Insert(pos, data) 64 | return len(data) 65 | } 66 | } 67 | 68 | func fileload1(f *wind.File, pos int, fd *os.File, nulls *bool, h io.Writer) int { 69 | if pos > f.Len() { 70 | util.Fatal("internal error: fileload1") 71 | } 72 | return Loadfile(fd, pos, nulls, fileloader(f), h) 73 | } 74 | -------------------------------------------------------------------------------- /cmd/acme/internal/runes/util.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | // #include 11 | // #include 12 | // #include "dat.h" 13 | // #include "fns.h" 14 | 15 | package runes 16 | 17 | type Text interface { 18 | Len() int 19 | RuneAt(pos int) rune 20 | } 21 | -------------------------------------------------------------------------------- /cmd/acme/internal/ui/cols1.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | // #include 11 | // #include 12 | // #include "dat.h" 13 | // #include "fns.h" 14 | 15 | package ui 16 | 17 | import ( 18 | "9fans.net/go/cmd/acme/internal/adraw" 19 | "9fans.net/go/cmd/acme/internal/wind" 20 | "9fans.net/go/draw" 21 | ) 22 | 23 | func ColaddAndMouse(c *wind.Column, w *wind.Window, clone *wind.Window, y int) *wind.Window { 24 | w = wind.Coladd(c, w, clone, y) 25 | savemouse(w) 26 | // near the button, but in the body 27 | adraw.Display.MoveCursor(w.Tag.ScrollR.Max.Add(draw.Pt(3, 3))) 28 | wind.Barttext = &w.Body 29 | return w 30 | } 31 | 32 | func ColcloseAndMouse(c *wind.Column, w *wind.Window, dofree bool) { 33 | didmouse := restoremouse(w) != 0 34 | wr := w.R 35 | w = wind.Colclose(c, w, dofree) 36 | if !didmouse && w != nil && w.R.Min.Y == wr.Min.Y { 37 | w.Showdel = true 38 | wind.Winresize(w, w.R, false, true) 39 | movetodel(w) 40 | } 41 | } 42 | 43 | func Colmousebut(c *wind.Column) { 44 | adraw.Display.MoveCursor(c.Tag.ScrollR.Min.Add(c.Tag.ScrollR.Max).Div(2)) 45 | } 46 | 47 | func Coldragwin(c *wind.Column, w *wind.Window, but int) { 48 | Clearmouse() 49 | adraw.Display.SwitchCursor2(&adraw.BoxCursor, &adraw.BoxCursor2) 50 | b := Mouse.Buttons 51 | op := Mouse.Point 52 | for Mouse.Buttons == b { 53 | Mousectl.Read() 54 | } 55 | adraw.Display.SwitchCursor(nil) 56 | if Mouse.Buttons != 0 { 57 | for Mouse.Buttons != 0 { 58 | Mousectl.Read() 59 | } 60 | return 61 | } 62 | wind.Coldragwin1(c, w, but, op, Mouse.Point) 63 | Winmousebut(w) 64 | } 65 | -------------------------------------------------------------------------------- /cmd/acme/internal/ui/look1.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | // #include 11 | // #include <9pclient.h> 12 | // #include 13 | // #include 14 | // #include "dat.h" 15 | // #include "fns.h" 16 | 17 | package ui 18 | 19 | import ( 20 | "9fans.net/go/cmd/acme/internal/runes" 21 | "9fans.net/go/cmd/acme/internal/wind" 22 | ) 23 | 24 | func New(et, t, argt *wind.Text, flag1, flag2 bool, arg []rune) { 25 | var a []rune 26 | Getarg(argt, false, true, &a) 27 | if a != nil { 28 | New(et, t, nil, flag1, flag2, a) 29 | if len(arg) == 0 { 30 | return 31 | } 32 | } 33 | // loop condition: *arg is not a blank 34 | for ndone := 0; ; ndone++ { 35 | a = runes.SkipNonBlank(arg) 36 | if len(a) == len(arg) { 37 | if ndone == 0 && et.Col != nil { 38 | w := ColaddAndMouse(et.Col, nil, nil, -1) 39 | wind.Winsettag(w) 40 | OnNewWindow(w) 41 | } 42 | break 43 | } 44 | nf := len(arg) - len(a) 45 | f := runes.Clone(arg[:nf]) 46 | rs := wind.Dirname(et, f) 47 | var e Expand 48 | e.Name = rs 49 | e.Bname = string(rs) 50 | e.Jump = true 51 | Openfile(et, &e) 52 | arg = runes.SkipBlank(a) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /cmd/acme/internal/ui/rows.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | // #include 11 | // #include 12 | // #include 13 | // #include "dat.h" 14 | // #include "fns.h" 15 | 16 | package ui 17 | 18 | import ( 19 | "unicode/utf8" 20 | 21 | "9fans.net/go/cmd/acme/internal/adraw" 22 | "9fans.net/go/cmd/acme/internal/wind" 23 | "9fans.net/go/draw" 24 | ) 25 | 26 | func Rowdragcol(row *wind.Row, c *wind.Column, _0 int) { 27 | Clearmouse() 28 | adraw.Display.SwitchCursor2(&adraw.BoxCursor, &adraw.BoxCursor2) 29 | b := Mouse.Buttons 30 | op := Mouse.Point 31 | for Mouse.Buttons == b { 32 | Mousectl.Read() 33 | } 34 | adraw.Display.SwitchCursor(nil) 35 | if Mouse.Buttons != 0 { 36 | for Mouse.Buttons != 0 { 37 | Mousectl.Read() 38 | } 39 | return 40 | } 41 | 42 | wind.Rowdragcol1(row, c, op, Mouse.Point) 43 | Clearmouse() 44 | Colmousebut(c) 45 | } 46 | 47 | func Rowtype(row *wind.Row, r rune, p draw.Point) *wind.Text { 48 | if r == 0 { 49 | r = utf8.RuneError 50 | } 51 | 52 | Clearmouse() 53 | BigUnlock() 54 | row.Lk.Lock() 55 | BigLock() 56 | var t *wind.Text 57 | if Bartflag { 58 | t = wind.Barttext 59 | } else { 60 | t = wind.Rowwhich(row, p) 61 | } 62 | if t != nil && (t.What != wind.Tag || !p.In(t.ScrollR)) { 63 | w := t.W 64 | if w == nil { 65 | Texttype(t, r) 66 | } else { 67 | wind.Winlock(w, 'K') 68 | Wintype(w, t, r) 69 | // Expand tag if necessary 70 | if t.What == wind.Tag { 71 | t.W.Tagsafe = false 72 | if r == '\n' { 73 | t.W.Tagexpand = true 74 | } 75 | WinresizeAndMouse(w, w.R, true, true) 76 | } 77 | wind.Winunlock(w) 78 | } 79 | } 80 | row.Lk.Unlock() 81 | return t 82 | } 83 | 84 | var Bartflag bool 85 | -------------------------------------------------------------------------------- /cmd/acme/internal/ui/scrl1.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | // #include 11 | // #include 12 | // #include "dat.h" 13 | // #include "fns.h" 14 | 15 | package ui 16 | 17 | import ( 18 | "time" 19 | 20 | "9fans.net/go/cmd/acme/internal/adraw" 21 | "9fans.net/go/cmd/acme/internal/wind" 22 | "9fans.net/go/draw" 23 | ) 24 | 25 | func scrsleep(dt time.Duration) { 26 | timer := time.NewTimer(dt) 27 | defer timer.Stop() 28 | select { 29 | case <-timer.C: 30 | case Mousectl.Mouse = <-Mousectl.C: 31 | } 32 | } 33 | 34 | func Textscroll(t *wind.Text, but int) { 35 | s := t.ScrollR.Inset(1) 36 | h := s.Max.Y - s.Min.Y 37 | x := (s.Min.X + s.Max.X) / 2 38 | oldp0 := ^0 39 | first := true 40 | for { 41 | adraw.Display.Flush() 42 | my := Mouse.Point.Y 43 | if my < s.Min.Y { 44 | my = s.Min.Y 45 | } 46 | if my >= s.Max.Y { 47 | my = s.Max.Y 48 | } 49 | if !(Mouse.Point == draw.Pt(x, my)) { 50 | adraw.Display.MoveCursor(draw.Pt(x, my)) 51 | Mousectl.Read() // absorb event generated by moveto() 52 | } 53 | var p0 int 54 | if but == 2 { 55 | y := my 56 | p0 = int(int64(t.Len()) * int64(y-s.Min.Y) / int64(h)) 57 | if p0 >= t.Q1 { 58 | p0 = wind.Textbacknl(t, p0, 2) 59 | } 60 | if oldp0 != p0 { 61 | wind.Textsetorigin(t, p0, false) 62 | } 63 | oldp0 = p0 64 | Mousectl.Read() 65 | goto Continue 66 | } 67 | if but == 1 { 68 | p0 = wind.Textbacknl(t, t.Org, (my-s.Min.Y)/t.Fr.Font.Height) 69 | } else { 70 | p0 = t.Org + t.Fr.CharOf(draw.Pt(s.Max.X, my)) 71 | } 72 | if oldp0 != p0 { 73 | wind.Textsetorigin(t, p0, true) 74 | } 75 | oldp0 = p0 76 | // debounce 77 | if first { 78 | adraw.Display.Flush() 79 | time.Sleep(200 * time.Millisecond) 80 | select { 81 | default: 82 | // non-blocking 83 | case Mousectl.Mouse = <-Mousectl.C: 84 | // ok 85 | } 86 | first = false 87 | } 88 | scrsleep(80 * time.Millisecond) 89 | Continue: 90 | if Mouse.Buttons&(1<<(but-1)) == 0 { 91 | break 92 | } 93 | } 94 | for Mouse.Buttons != 0 { 95 | Mousectl.Read() 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /cmd/acme/internal/ui/wind1.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | // #include 11 | // #include 12 | // #include "dat.h" 13 | // #include "fns.h" 14 | 15 | package ui 16 | 17 | import ( 18 | "9fans.net/go/cmd/acme/internal/adraw" 19 | "9fans.net/go/cmd/acme/internal/file" 20 | "9fans.net/go/cmd/acme/internal/wind" 21 | "9fans.net/go/draw" 22 | ) 23 | 24 | func WinresizeAndMouse(w *wind.Window, r draw.Rectangle, safe, keepextra bool) int { 25 | mouseintag := Mouse.Point.In(w.Tag.All) 26 | mouseinbody := Mouse.Point.In(w.Body.All) 27 | 28 | y := wind.Winresize(w, r, safe, keepextra) 29 | 30 | // If mouse is in tag, pull up as tag closes. 31 | if mouseintag && !Mouse.Point.In(w.Tag.All) { 32 | p := Mouse.Point 33 | p.Y = w.Tag.All.Max.Y - 3 34 | adraw.Display.MoveCursor(p) 35 | } 36 | 37 | // If mouse is in body, push down as tag expands. 38 | if mouseinbody && Mouse.Point.In(w.Tag.All) { 39 | p := Mouse.Point 40 | p.Y = w.Tag.All.Max.Y + 3 41 | adraw.Display.MoveCursor(p) 42 | } 43 | 44 | return y 45 | } 46 | 47 | func Wintype(w *wind.Window, t *wind.Text, r rune) { 48 | Texttype(t, r) 49 | if t.What == wind.Body { 50 | for i := 0; i < len(t.File.Text); i++ { 51 | wind.Textscrdraw(t.File.Text[i]) 52 | } 53 | } 54 | wind.Winsettag(w) 55 | } 56 | 57 | var fff *file.File 58 | -------------------------------------------------------------------------------- /cmd/acme/internal/util/cols1.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | // #include 11 | // #include 12 | // #include "dat.h" 13 | // #include "fns.h" 14 | 15 | package util 16 | 17 | func Abs(x int) int { 18 | if x < 0 { 19 | return -x 20 | } 21 | return x 22 | } 23 | -------------------------------------------------------------------------------- /cmd/acme/internal/util/util.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | // #include 11 | // #include 12 | // #include "dat.h" 13 | // #include "fns.h" 14 | 15 | package util 16 | 17 | import ( 18 | "log" 19 | "sync" 20 | "sync/atomic" 21 | ) 22 | 23 | func Min(a int, b int) int { 24 | if a < b { 25 | return a 26 | } 27 | return b 28 | } 29 | 30 | func Max(a int, b int) int { 31 | if a > b { 32 | return a 33 | } 34 | return b 35 | } 36 | 37 | func Fatal(s string) { 38 | log.Fatalf("acme: %s\n", s) 39 | } 40 | 41 | var locks struct { 42 | mu sync.Mutex 43 | cond sync.Cond 44 | } 45 | 46 | func init() { 47 | locks.cond.L = &locks.mu 48 | } 49 | 50 | type QLock struct { 51 | held uint32 52 | } 53 | 54 | func (l *QLock) TryLock() bool { 55 | return atomic.CompareAndSwapUint32(&l.held, 0, 1) 56 | } 57 | 58 | func (l *QLock) Unlock() { 59 | v := atomic.SwapUint32(&l.held, 0) 60 | if v == 0 { 61 | panic("Unlock of unlocked lock") 62 | } 63 | if v > 1 { 64 | locks.cond.Broadcast() 65 | } 66 | } 67 | 68 | func (l *QLock) Lock() { 69 | if atomic.AddUint32(&l.held, 1) == 1 { 70 | return 71 | } 72 | locks.mu.Lock() 73 | defer locks.mu.Unlock() 74 | 75 | for atomic.AddUint32(&l.held, 1) != 1 { 76 | locks.cond.Wait() 77 | } 78 | } 79 | 80 | func Incref(p *uint32) { atomic.AddUint32(p, 1) } 81 | 82 | func Decref(p *uint32) uint32 { 83 | return atomic.AddUint32(p, ^uint32(0)) 84 | } 85 | -------------------------------------------------------------------------------- /cmd/acme/internal/wind/scrl.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | // #include 11 | // #include 12 | // #include "dat.h" 13 | // #include "fns.h" 14 | 15 | package wind 16 | 17 | import ( 18 | "9fans.net/go/cmd/acme/internal/adraw" 19 | "9fans.net/go/cmd/acme/internal/util" 20 | "9fans.net/go/draw" 21 | "9fans.net/go/draw/frame" 22 | ) 23 | 24 | var scrtmp *draw.Image 25 | 26 | func scrpos(r draw.Rectangle, p0 int, p1 int, tot int) draw.Rectangle { 27 | q := r 28 | h := q.Max.Y - q.Min.Y 29 | if tot == 0 { 30 | return q 31 | } 32 | if tot > 1024*1024 { 33 | tot >>= 10 34 | p0 >>= 10 35 | p1 >>= 10 36 | } 37 | if p0 > 0 { 38 | q.Min.Y += h * p0 / tot 39 | } 40 | if p1 < tot { 41 | q.Max.Y -= h * (tot - p1) / tot 42 | } 43 | if q.Max.Y < q.Min.Y+2 { 44 | if q.Min.Y+2 <= r.Max.Y { 45 | q.Max.Y = q.Min.Y + 2 46 | } else { 47 | q.Min.Y = q.Max.Y - 2 48 | } 49 | } 50 | return q 51 | } 52 | 53 | func Scrlresize() { 54 | scrtmp.Free() 55 | var err error 56 | scrtmp, err = adraw.Display.AllocImage(draw.Rect(0, 0, 32, adraw.Display.ScreenImage.R.Max.Y), adraw.Display.ScreenImage.Pix, false, draw.NoFill) 57 | if err != nil { 58 | util.Fatal("scroll alloc") 59 | } 60 | } 61 | 62 | func Textscrdraw(t *Text) { 63 | if t.W == nil || t != &t.W.Body { 64 | return 65 | } 66 | if scrtmp == nil { 67 | Scrlresize() 68 | } 69 | r := t.ScrollR 70 | b := scrtmp 71 | r1 := r 72 | r1.Min.X = 0 73 | r1.Max.X = r.Dx() 74 | r2 := scrpos(r1, t.Org, t.Org+t.Fr.NumChars, t.Len()) 75 | if !(r2 == t.lastsr) { 76 | t.lastsr = r2 77 | b.Draw(r1, t.Fr.Cols[frame.BORD], nil, draw.ZP) 78 | b.Draw(r2, t.Fr.Cols[frame.BACK], nil, draw.ZP) 79 | r2.Min.X = r2.Max.X - 1 80 | b.Draw(r2, t.Fr.Cols[frame.BORD], nil, draw.ZP) 81 | t.Fr.B.Draw(r, b, nil, draw.Pt(0, r1.Min.Y)) 82 | //flushimage(display, 1); // BUG? 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /cmd/acme/post.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "os/exec" 8 | "strings" 9 | 10 | "9fans.net/go/plan9/client" 11 | ) 12 | 13 | var chattyfuse int 14 | 15 | func post9pservice(rfd, wfd *os.File, name, mtpt string) error { 16 | if name == "" && mtpt == "" { 17 | rfd.Close() 18 | wfd.Close() 19 | return fmt.Errorf("nothing to do") 20 | } 21 | 22 | if name != "" { 23 | var addr string 24 | if strings.Contains(addr, "!") { // assume is already network address 25 | addr = name 26 | } else { 27 | addr = "unix!" + client.Namespace() + "/" + name 28 | } 29 | cmd := exec.Command("9pserve", "-u", addr) 30 | cmd.Stdin = rfd 31 | cmd.Stdout = wfd 32 | cmd.Stderr = os.Stderr 33 | err := cmd.Run() 34 | if err != nil { 35 | return err 36 | } 37 | if mtpt != "" { 38 | // reopen 39 | log.Fatalf("post9pservice mount not implemented") 40 | } 41 | } 42 | if mtpt != "" { 43 | log.Fatalf("post9pservice mount not implemented") 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /cmd/devdraw/drawclient.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | 11 | package main 12 | 13 | type Cmd struct { 14 | cmd *C.char 15 | fn func(int, **C.char) 16 | } 17 | 18 | var b Biobuf 19 | var fd int 20 | var buf [64 * 1024]uint8 21 | 22 | func startsrv() { 23 | var p [2]int 24 | if pipe(p) < 0 { 25 | sysfatal("pipe") 26 | } 27 | pid := fork() 28 | if pid < 0 { 29 | sysfatal("fork") 30 | } 31 | if pid == 0 { 32 | close(p[0]) 33 | dup(p[1], 0) 34 | dup(p[1], 1) 35 | execl("./o.devdraw", "o.devdraw", "-D", nil) 36 | sysfatal("exec: %r") 37 | } 38 | close(p[1]) 39 | fd = p[0] 40 | } 41 | 42 | func domsg(m *Wsysmsg) int { 43 | n := convW2M(m, buf, sizeof(buf)) 44 | fprint(2, "write %d to %d\n", n, fd) 45 | write(fd, buf, n) 46 | n = readwsysmsg(fd, buf, sizeof(buf)) 47 | nn := convM2W(buf, n, m) 48 | assert(nn == n) 49 | if m.type_ == Rerror { 50 | return -1 51 | } 52 | return 0 53 | } 54 | 55 | func cmdinit(argc int, argv **C.char) { 56 | var m Wsysmsg 57 | memset(&m, 0, sizeof(m)) 58 | m.type_ = Tinit 59 | m.winsize = "100x100" 60 | m.label = "label" 61 | if domsg(&m) < 0 { 62 | sysfatal("domsg") 63 | } 64 | } 65 | 66 | func cmdmouse(argc int, argv **C.char) { 67 | var m Wsysmsg 68 | memset(&m, 0, sizeof(m)) 69 | m.type_ = Trdmouse 70 | if domsg(&m) < 0 { 71 | sysfatal("domsg") 72 | } 73 | var tmp1 unknown 74 | if m.resized != 0 { 75 | tmp1 = 'r' 76 | } else { 77 | tmp1 = 'm' 78 | } 79 | print("%c %d %d %d\n", tmp1, m.mouse.xy.x, m.mouse.xy.y, m.mouse.buttons) 80 | } 81 | 82 | func cmdkbd(argc int, argv **C.char) { 83 | var m Wsysmsg 84 | memset(&m, 0, sizeof(m)) 85 | m.type_ = Trdkbd 86 | if domsg(&m) < 0 { 87 | sysfatal("domsg") 88 | } 89 | print("%d\n", m.rune_) 90 | } 91 | 92 | var cmdtab = [3]Cmd{ 93 | Cmd{"init", cmdinit}, 94 | Cmd{"mouse", cmdmouse}, 95 | Cmd{"kbd", cmdkbd}, 96 | } 97 | 98 | func main(argc int, argv **C.char) { 99 | startsrv() 100 | 101 | fprint(2, "started...\n") 102 | Binit(&b, 0, OREAD) 103 | for { 104 | p := Brdstr(&b, '\n', 1) 105 | if p == nil { 106 | break 107 | } 108 | fprint(2, "%s...\n", p) 109 | var f [20]*C.char 110 | nf := tokenize(p, f, len(f)) 111 | var i int 112 | for i = 0; i < len(cmdtab); i++ { 113 | if strcmp(cmdtab[i].cmd, f[0]) == 0 { 114 | cmdtab[i].fn(nf, f) 115 | break 116 | } 117 | } 118 | if i == len(cmdtab) { 119 | print("! unrecognized command %s\n", f[0]) 120 | } 121 | free(p) 122 | } 123 | exits(0) 124 | } 125 | -------------------------------------------------------------------------------- /cmd/devdraw/latin1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "unicode/utf8" 4 | 5 | /* 6 | * The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a 7 | * prefix of latintab[j].ld only when j 0; k = k[1:] { 21 | r := k[0] 22 | c <<= 4 23 | if '0' <= r && r <= '9' { 24 | c += rune(r) - '0' 25 | } else if 'a' <= r && r <= 'f' { 26 | c += 10 + r - 'a' 27 | } else if 'A' <= r && r <= 'F' { 28 | c += 10 + r - 'A' 29 | } else { 30 | return -1 31 | } 32 | if c > utf8.MaxRune { 33 | return -1 34 | } 35 | } 36 | return c 37 | } 38 | 39 | /* 40 | * Given n characters k[0]..k[n-1], find the corresponding rune or return -1 for 41 | * failure, or something < -1 if n is too small. In the latter case, the result 42 | * is minus the required n. 43 | */ 44 | func toLatin1(k []rune) rune { 45 | n := len(k) 46 | if k[0] == 'X' { 47 | if n < 2 { 48 | return -2 49 | } 50 | if k[1] == 'X' { 51 | if n < 3 { 52 | return -3 53 | } 54 | if k[2] == 'X' { 55 | if n < 9 { 56 | if toUnicode(k[3:]) < 0 { 57 | return -1 58 | } 59 | return rune(-(n + 1)) 60 | } 61 | return toUnicode(k[3:9]) 62 | } 63 | if n < 7 { 64 | if toUnicode(k[2:]) < 0 { 65 | return -1 66 | } 67 | return rune(-(n + 1)) 68 | } 69 | return toUnicode(k[2:7]) 70 | } 71 | if n < 5 { 72 | if toUnicode(k[1:]) < 0 { 73 | return -1 74 | } 75 | return rune(-(n + 1)) 76 | } 77 | return toUnicode(k[1:4]) 78 | } 79 | 80 | for i := 0; i < len(latintab); i++ { 81 | l := &latintab[i] 82 | if k[0] == rune(l.ld[0]) { 83 | if n == 1 { 84 | return -2 85 | } 86 | var c rune 87 | if len(l.ld) == 1 { 88 | c = k[1] 89 | } else if rune(l.ld[1]) != k[1] { 90 | continue 91 | } else if n == 2 { 92 | return -3 93 | } else { 94 | c = k[2] 95 | } 96 | for i := range l.si { 97 | if rune(l.si[i]) == c { 98 | return l.so[i] 99 | } 100 | } 101 | return -1 102 | } 103 | } 104 | return -1 105 | } 106 | -------------------------------------------------------------------------------- /cmd/devdraw/mouseswap.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | // #include "devdraw.h" 11 | 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | "os" 17 | "strconv" 18 | ) 19 | 20 | const ( 21 | Nbutton = 10 22 | ) 23 | 24 | var debug int 25 | 26 | var mousemap struct { 27 | b [Nbutton]int 28 | init bool 29 | } 30 | 31 | func initmap() { 32 | p := os.Getenv("mousedebug") 33 | if p != "" { 34 | debug, _ = strconv.Atoi(p) 35 | } 36 | var i int 37 | for i = 0; i < Nbutton; i++ { 38 | mousemap.b[i] = i 39 | } 40 | mousemap.init = true 41 | p = os.Getenv("mousebuttonmap") 42 | if p != "" { 43 | for i := 0; i < Nbutton && i < len(p); i++ { 44 | if '0' <= p[i] && p[i] <= '9' { 45 | mousemap.b[i] = int(p[i]) - '1' 46 | } 47 | } 48 | } 49 | if debug != 0 { 50 | fmt.Fprintf(os.Stderr, "mousemap: ") 51 | for i := 0; i < Nbutton; i++ { 52 | fmt.Fprintf(os.Stderr, " %d", 1+mousemap.b[i]) 53 | } 54 | fmt.Fprintf(os.Stderr, "\n") 55 | } 56 | } 57 | 58 | func mouseswap(but int) int { 59 | if !mousemap.init { 60 | initmap() 61 | } 62 | 63 | nbut := 0 64 | for i := 0; i < Nbutton; i++ { 65 | if but&(1<= 0 { 66 | nbut |= 1 << mousemap.b[i] 67 | } 68 | } 69 | if debug != 0 { 70 | fmt.Fprintf(os.Stderr, "swap %#b -> %#b\n", but, nbut) 71 | } 72 | return nbut 73 | } 74 | -------------------------------------------------------------------------------- /cmd/devdraw/winsize.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "9fans.net/go/draw" 8 | ) 9 | 10 | func parsewinsize(s string, r *draw.Rectangle, havemin *bool) error { 11 | os := s 12 | isdigit := func(c byte) bool { return '0' <= c && c <= '9' } 13 | strtol := func(s string, sp *string, base int) int { 14 | i := 0 15 | for i < len(s) && isdigit(s[i]) { 16 | i++ 17 | } 18 | *sp = s[i:] 19 | n, _ := strconv.ParseInt(s[:i], base, 0) 20 | return int(n) 21 | } 22 | 23 | *havemin = false 24 | *r = draw.Rect(0, 0, 0, 0) 25 | var i, j, k, l int 26 | var c byte 27 | if s == "" || !isdigit(s[0]) { 28 | goto oops 29 | } 30 | i = strtol(s, &s, 0) 31 | if s[0] == 'x' { 32 | s = s[1:] 33 | if s == "" || !isdigit(s[0]) { 34 | goto oops 35 | } 36 | j = strtol(s, &s, 0) 37 | r.Max.X = i 38 | r.Max.Y = j 39 | if len(s) == 0 { 40 | return nil 41 | } 42 | if s[0] != '@' { 43 | goto oops 44 | } 45 | 46 | s = s[1:] 47 | if s == "" || !isdigit(s[0]) { 48 | goto oops 49 | } 50 | i = strtol(s, &s, 0) 51 | if s[0] != ',' && s[0] != ' ' { 52 | goto oops 53 | } 54 | s = s[1:] 55 | if s == "" || !isdigit(s[0]) { 56 | goto oops 57 | } 58 | j = strtol(s, &s, 0) 59 | if s[0] != 0 { 60 | goto oops 61 | } 62 | *r = r.Add(draw.Pt(i, j)) 63 | *havemin = true 64 | return nil 65 | } 66 | 67 | c = s[0] 68 | if c != ' ' && c != ',' { 69 | goto oops 70 | } 71 | s = s[1:] 72 | if len(s) == 0 || !isdigit(s[0]) { 73 | goto oops 74 | } 75 | j = strtol(s, &s, 0) 76 | if s[0] != c { 77 | goto oops 78 | } 79 | s = s[1:] 80 | if len(s) == 0 || !isdigit(s[0]) { 81 | goto oops 82 | } 83 | k = strtol(s, &s, 0) 84 | if s[0] != c { 85 | goto oops 86 | } 87 | s = s[1:] 88 | if len(s) == 0 || !isdigit(s[0]) { 89 | goto oops 90 | } 91 | l = strtol(s, &s, 0) 92 | if s[0] != 0 { 93 | goto oops 94 | } 95 | *r = draw.Rect(i, j, k, l) 96 | *havemin = true 97 | return nil 98 | 99 | oops: 100 | return fmt.Errorf("bad syntax in window size '%s'", os) 101 | } 102 | -------------------------------------------------------------------------------- /cmd/internal/base/dat.h.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | type Mntdir struct { 4 | ID int 5 | Ref int 6 | Dir []rune 7 | Next *Mntdir 8 | // Incl holds include directories as added by the Incl command. 9 | // This is present so that warning messages issued by 10 | // commands can create windows that have the correct 11 | // include directories (see ../../acme/acme.go:/<-exec.Ccommand) 12 | Incl [][]rune 13 | } 14 | -------------------------------------------------------------------------------- /cmd/sam/conv: -------------------------------------------------------------------------------- 1 | #!/usr/local/plan9/bin/rc 2 | 3 | c2gofmt -r rules.txt -w /usr/local/plan9/src/cmd/sam/^(*.c *.h) 4 | rm plan9.go 5 | goimports -w . 6 | go build -gcflags=-e >[2=1] | grep -v 'but does have' | 9 sort +1 7 | 8 | 9 | -------------------------------------------------------------------------------- /cmd/sam/disk.go: -------------------------------------------------------------------------------- 1 | // #include "sam.h" 2 | 3 | package main 4 | 5 | import ( 6 | "io" 7 | "reflect" 8 | "unsafe" 9 | ) 10 | 11 | var blist *Block 12 | 13 | func diskinit() *Disk { 14 | d := new(Disk) 15 | d.fd = tempdisk() 16 | return d 17 | } 18 | 19 | func ntosize(n int, ip *int) int { 20 | if n > Maxblock { 21 | panic_("internal error: ntosize") 22 | } 23 | size := n 24 | if size&(Blockincr-1) != 0 { 25 | size += Blockincr - (size & (Blockincr - 1)) 26 | } 27 | /* last bucket holds blocks of exactly Maxblock */ 28 | if ip != nil { 29 | *ip = size / Blockincr 30 | } 31 | return size * RUNESIZE 32 | } 33 | 34 | func disknewblock(d *Disk, n int) *Block { 35 | var i int 36 | size := ntosize(n, &i) 37 | b := d.free[i] 38 | if b != nil { 39 | d.free[i] = b.u.next 40 | } else { 41 | /* allocate in chunks to reduce malloc overhead */ 42 | if blist == nil { 43 | bb := make([]Block, 100) 44 | for j := 0; j < 100-1; j++ { 45 | bb[j].u.next = &bb[j+1] 46 | } 47 | blist = &bb[0] 48 | } 49 | b = blist 50 | blist = b.u.next 51 | b.addr = d.addr 52 | if d.addr+int64(size) < d.addr { 53 | panic_("temp file overflow") 54 | } 55 | d.addr += int64(size) 56 | } 57 | b.u.n = n 58 | return b 59 | } 60 | 61 | func diskrelease(d *Disk, b *Block) { 62 | var i int 63 | ntosize(b.u.n, &i) 64 | b.u.next = d.free[i] 65 | d.free[i] = b 66 | } 67 | 68 | func runedata(r []rune) []byte { 69 | var b []byte 70 | h := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 71 | h.Data = uintptr(unsafe.Pointer(&r[0])) 72 | h.Len = RUNESIZE * len(r) 73 | h.Cap = RUNESIZE * cap(r) 74 | return b 75 | } 76 | 77 | func diskwrite(d *Disk, bp **Block, r []rune) { 78 | n := len(r) 79 | b := *bp 80 | size := ntosize(b.u.n, nil) 81 | nsize := ntosize(n, nil) 82 | if size != nsize { 83 | diskrelease(d, b) 84 | b = disknewblock(d, n) 85 | *bp = b 86 | } 87 | if nw, err := d.fd.WriteAt(runedata(r), b.addr); nw != n*RUNESIZE || err != nil { 88 | if err == nil { 89 | err = io.ErrShortWrite 90 | } 91 | panic_("writing temp file: %v", err) 92 | } 93 | b.u.n = n 94 | } 95 | 96 | func diskread(d *Disk, b *Block, r []rune) { 97 | n := len(r) 98 | if n > b.u.n { 99 | panic_("internal error: diskread") 100 | } 101 | 102 | ntosize(b.u.n, nil) /* called only for sanity check on Maxblock */ 103 | if nr, err := d.fd.ReadAt(runedata(r), b.addr); nr != n*RUNESIZE || err != nil { 104 | panic_("read error from temp file") 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /cmd/sam/error.go: -------------------------------------------------------------------------------- 1 | // #include "sam.h" 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | var emsg = [47]string{ 11 | /* error_s */ 12 | "can't open", 13 | "can't create", 14 | "not in menu:", 15 | "changes to", 16 | "I/O error:", 17 | "can't write while changing:", 18 | /* error_c */ 19 | "unknown command", 20 | "no operand for", 21 | "bad delimiter", 22 | /* error */ 23 | "can't fork", 24 | "interrupt", 25 | "address", 26 | "search", 27 | "pattern", 28 | "newline expected", 29 | "blank expected", 30 | "pattern expected", 31 | "can't nest X or Y", 32 | "unmatched `}'", 33 | "command takes no address", 34 | "addresses overlap", 35 | "substitution", 36 | "& match too long", 37 | "bad \\ in rhs", 38 | "address range", 39 | "changes not in sequence", 40 | "addresses out of order", 41 | "no file name", 42 | "unmatched `('", 43 | "unmatched `)'", 44 | "malformed `[]'", 45 | "malformed regexp", 46 | "reg. exp. list overflow", 47 | "plan 9 command", 48 | "can't pipe", 49 | "no current file", 50 | "string too long", 51 | "changed files", 52 | "empty string", 53 | "file search", 54 | "non-unique match for \"\"", 55 | "tag match too long", 56 | "too many subexpressions", 57 | "temporary file too large", 58 | "file is append-only", 59 | "no destination for plumb message", 60 | "internal read error in buffer load", 61 | } 62 | var wmsg = [8]string{ 63 | /* warn_s */ 64 | "duplicate file name", 65 | "no such file", 66 | "write might change good version of", 67 | /* warn_S */ 68 | "files might be aliased", 69 | /* warn */ 70 | "null characters elided", 71 | "can't run pwd", 72 | "last char not newline", 73 | "exit status not 0", 74 | } 75 | 76 | func error_(s Err) { 77 | hiccough(fmt.Sprintf("?%s", emsg[s])) 78 | } 79 | 80 | func error_s(s Err, a string) { 81 | hiccough(fmt.Sprintf("?%s \"%s\"", emsg[s], a)) 82 | } 83 | 84 | func error_r(s Err, a string, err error) { 85 | if pe, ok := err.(*os.PathError); ok && pe.Path == a { 86 | err = pe.Err 87 | } 88 | hiccough(fmt.Sprintf("?%s \"%s\": %v", emsg[s], a, err)) 89 | } 90 | 91 | func error_c(s Err, c rune) { 92 | hiccough(fmt.Sprintf("?%s `%c'", emsg[s], c)) 93 | } 94 | 95 | func warn(s Warn) { 96 | dprint("?warning: %s\n", wmsg[s]) 97 | } 98 | 99 | func warn_S(s Warn, a *String) { 100 | print_s(wmsg[s], a) 101 | } 102 | 103 | func warn_SS(s Warn, a *String, b *String) { 104 | print_ss(wmsg[s], a, b) 105 | } 106 | 107 | func warn_s(s Warn, a string) { 108 | dprint("?warning: %s `%s'\n", wmsg[s], a) 109 | } 110 | 111 | func termwrite(s string) { 112 | if downloaded { 113 | p := tmpcstr(s) 114 | if cmd != nil { 115 | loginsert(cmd, cmdpt, p.s) 116 | } else { 117 | Strinsert(&cmdstr, p, len(cmdstr.s)) 118 | } 119 | cmdptadv += len(p.s) 120 | freetmpstr(p) 121 | } else { 122 | os.Stderr.WriteString(s) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /cmd/sam/errors.h.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Err int 4 | 5 | const ( 6 | Eopen Err = iota 7 | Ecreate 8 | Emenu 9 | Emodified 10 | Eio 11 | Ewseq 12 | Eunk 13 | Emissop 14 | Edelim 15 | Efork 16 | Eintr 17 | Eaddress 18 | Esearch 19 | Epattern 20 | Enewline 21 | Eblank 22 | Enopattern 23 | EnestXY 24 | Enolbrace 25 | Enoaddr 26 | Eoverlap 27 | Enosub 28 | Elongrhs 29 | Ebadrhs 30 | Erange 31 | Esequence 32 | Eorder 33 | Enoname 34 | Eleftpar 35 | Erightpar 36 | Ebadclass 37 | Ebadregexp 38 | Eoverflow 39 | Enocmd 40 | Epipe 41 | Enofile 42 | Etoolong 43 | Echanges 44 | Eempty 45 | Efsearch 46 | Emanyfiles 47 | Elongtag 48 | Esubexp 49 | Etmpovfl 50 | Eappend 51 | Ecantplumb 52 | Ebufload 53 | ) 54 | 55 | type Warn int 56 | 57 | const ( 58 | Wdupname Warn = iota 59 | Wfile 60 | Wdate 61 | Wdupfile 62 | Wnulls 63 | Wpwd 64 | Wnotnewline 65 | Wbadstatus 66 | ) 67 | -------------------------------------------------------------------------------- /cmd/sam/list.go: -------------------------------------------------------------------------------- 1 | // #include "sam.h" 2 | 3 | // +build ignore 4 | 5 | package main 6 | 7 | /* 8 | * Check that list has room for one more element. 9 | */ 10 | func growlist(l *List, esize int) { 11 | if l.listptr == nil || l.nalloc == 0 { 12 | l.nalloc = INCR 13 | l.listptr = emalloc(INCR * esize) 14 | l.nused = 0 15 | } else if l.nused == l.nalloc { 16 | p := erealloc(l.listptr, (l.nalloc+INCR)*esize) 17 | l.listptr = p 18 | memset(p+l.nalloc*esize, 0, INCR*esize) 19 | l.nalloc += INCR 20 | } 21 | } 22 | 23 | /* 24 | * Remove the ith element from the list 25 | */ 26 | func dellist(l *List, i int) { 27 | l.nused-- 28 | var pp *Posn 29 | var vpp **[0]byte 30 | 31 | switch l.type_ { 32 | case 'P': 33 | pp = l.posnptr + i 34 | memmove(pp, pp+1, (l.nused-i)*sizeof(*pp)) 35 | case 'p': 36 | vpp = l.voidpptr + i 37 | memmove(vpp, vpp+1, (l.nused-i)*sizeof(*vpp)) 38 | } 39 | } 40 | 41 | /* 42 | * Add a new element, whose position is i, to the list 43 | */ 44 | func inslist(l *List, i int, args ...interface{}) { 45 | var list va_list 46 | 47 | va_start(list, i) 48 | var pp *Posn 49 | var vpp **[0]byte 50 | switch l.type_ { 51 | case 'P': 52 | growlist(l, sizeof(*pp)) 53 | pp = l.posnptr + i 54 | memmove(pp+1, pp, (l.nused-i)*sizeof(*pp)) 55 | *pp = va_arg(list, Posn) 56 | case 'p': 57 | growlist(l, sizeof(*vpp)) 58 | vpp = l.voidpptr + i 59 | memmove(vpp+1, vpp, (l.nused-i)*sizeof(*vpp)) 60 | *vpp = va_arg(list, *[0]byte) 61 | } 62 | va_end(list) 63 | 64 | l.nused++ 65 | } 66 | 67 | func listfree(l *List) { 68 | free(l.listptr) 69 | free(l) 70 | } 71 | 72 | func listalloc(type_ int) *List { 73 | l := emalloc(sizeof(List)) 74 | l.type_ = type_ 75 | l.nalloc = 0 76 | l.nused = 0 77 | 78 | return l 79 | } 80 | -------------------------------------------------------------------------------- /cmd/sam/mesg.h.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "unicode/utf8" 4 | 5 | /* VERSION 1 introduces plumbing 6 | 2 increases SNARFSIZE from 4096 to 32000 7 | */ 8 | const VERSION = 2 9 | 10 | const ( 11 | TBLOCKSIZE = 512 /* largest piece of text sent to terminal */ 12 | DATASIZE = (utf8.UTFMax*TBLOCKSIZE + 30) /* ... including protocol header stuff */ 13 | SNARFSIZE = 32000 /* maximum length of exchanged snarf buffer, must fit in 15 bits */ 14 | ) 15 | 16 | /* 17 | * Messages originating at the terminal 18 | */ 19 | type Tmesg int 20 | 21 | const ( 22 | Tversion Tmesg = iota 23 | Tstartcmdfile 24 | Tcheck 25 | Trequest 26 | Torigin 27 | Tstartfile 28 | Tworkfile 29 | Ttype 30 | Tcut 31 | Tpaste 32 | Tsnarf 33 | Tstartnewfile 34 | Twrite 35 | Tclose 36 | Tlook 37 | Tsearch 38 | Tsend 39 | Tdclick 40 | Tstartsnarf 41 | Tsetsnarf 42 | Tack 43 | Texit 44 | Tplumb 45 | TMAX 46 | ) 47 | 48 | /* 49 | * Messages originating at the host 50 | */ 51 | type Hmesg int 52 | 53 | const ( 54 | Hversion Hmesg = iota 55 | Hbindname 56 | Hcurrent 57 | Hnewname 58 | Hmovname 59 | Hgrow 60 | Hcheck0 61 | Hcheck 62 | Hunlock 63 | Hdata 64 | Horigin 65 | Hunlockfile 66 | Hsetdot 67 | Hgrowdata 68 | Hmoveto 69 | Hclean 70 | Hdirty 71 | Hcut 72 | Hsetpat 73 | Hdelname 74 | Hclose 75 | Hsetsnarf 76 | Hsnarflen 77 | Hack 78 | Hexit 79 | Hplumb 80 | HMAX 81 | ) 82 | 83 | type Header struct { 84 | typ Tmesg 85 | count0 uint8 86 | count1 uint8 87 | data [1]uint8 88 | } 89 | -------------------------------------------------------------------------------- /cmd/sam/multi.go: -------------------------------------------------------------------------------- 1 | // #include "sam.h" 2 | 3 | package main 4 | 5 | import "path/filepath" 6 | 7 | var file []*File 8 | var tag int 9 | 10 | func newfile() *File { 11 | f := fileopen() 12 | file = append(file, f) 13 | f.tag = tag 14 | tag++ 15 | if downloaded { 16 | outTs(Hnewname, f.tag) 17 | } 18 | /* already sorted; file name is "" */ 19 | return f 20 | } 21 | 22 | func whichmenu(f *File) int { 23 | for i := range file { 24 | if file[i] == f { 25 | return i 26 | } 27 | } 28 | return -1 29 | } 30 | 31 | func delfile(f *File) { 32 | w := whichmenu(f) 33 | if w < 0 { /* e.g. x/./D */ 34 | return 35 | } 36 | if downloaded { 37 | outTs(Hdelname, f.tag) 38 | } 39 | delfilelist(w) 40 | fileclose(f) 41 | } 42 | 43 | func delfilelist(w int) { 44 | copy(file[w:], file[w+1:]) 45 | file = file[:len(file)-1] 46 | } 47 | 48 | func fullname(name *String) { 49 | debug("curwd %v", &curwd) 50 | if len(name.s) > 0 && name.s[0] != '/' && name.s[0] != 0 { 51 | Strinsert(name, &curwd, Posn(0)) 52 | debug("post %v", name) 53 | } 54 | } 55 | 56 | func fixname(name *String) { 57 | debug("fixnmae %s\n", name) 58 | fullname(name) 59 | debug("fixfull %s\n", name) 60 | s := Strtoc(name) 61 | if len(s) > 0 { 62 | s = filepath.Clean(s) 63 | } 64 | t := tmpcstr(s) 65 | Strduplstr(name, t) 66 | // free(s) 67 | freetmpstr(t) 68 | 69 | if Strispre(&curwd, name) { 70 | Strdelete(name, 0, len(curwd.s)) 71 | } 72 | } 73 | 74 | func sortname(f *File) { 75 | w := whichmenu(f) 76 | dupwarned := false 77 | delfilelist(w) 78 | var i int 79 | if f == cmd { 80 | i = 0 81 | } else { 82 | for i = 0; i < len(file); i++ { // NOT range - must end with i = len(file) 83 | cmp := Strcmp(&f.name, &file[i].name) 84 | if cmp == 0 && !dupwarned { 85 | dupwarned = true 86 | warn_S(Wdupname, &f.name) 87 | } else if cmp < 0 && (i > 0 || cmd == nil) { 88 | break 89 | } 90 | } 91 | } 92 | insfilelist(i, f) 93 | if downloaded { 94 | outTsS(Hmovname, f.tag, &f.name) 95 | } 96 | } 97 | 98 | func insfilelist(i int, f *File) { 99 | file = append(file, nil) 100 | copy(file[i+1:], file[i:]) 101 | file[i] = f 102 | } 103 | 104 | func state(f *File, cleandirty State) { 105 | if f == cmd { 106 | return 107 | } 108 | f.unread = false 109 | if downloaded && whichmenu(f) >= 0 { /* else flist or menu */ 110 | if f.mod && cleandirty != Dirty { 111 | outTs(Hclean, f.tag) 112 | } else if !f.mod && cleandirty == Dirty { 113 | outTs(Hdirty, f.tag) 114 | } 115 | } 116 | if cleandirty == Clean { 117 | f.mod = false 118 | } else { 119 | f.mod = true 120 | } 121 | } 122 | 123 | func lookfile(s *String) *File { 124 | for _, f := range file { 125 | if Strcmp(&f.name, s) == 0 { 126 | return f 127 | } 128 | } 129 | return nil 130 | } 131 | -------------------------------------------------------------------------------- /cmd/sam/parse.h.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Addr struct { 4 | type_ rune 5 | are *String 6 | left *Addr 7 | num Posn 8 | next *Addr 9 | } 10 | 11 | // #define are g.re 12 | // #define left g.aleft 13 | 14 | type Cmd struct { 15 | addr *Addr 16 | re *String 17 | ccmd *Cmd 18 | ctext *String 19 | caddr *Addr 20 | next *Cmd 21 | num int 22 | flag bool 23 | cmdc rune 24 | } 25 | 26 | // #define ccmd g.cmd 27 | // #define ctext g.text 28 | // #define caddr g.addr 29 | 30 | type Cmdtab struct { 31 | cmdc rune 32 | text bool 33 | regexp bool 34 | addr bool 35 | defcmd rune 36 | defaddr Defaddr 37 | count uint8 38 | token string 39 | fn func(*File, *Cmd) bool 40 | } 41 | 42 | /* extern var cmdtab [unknown]Cmdtab */ /* default addresses */ 43 | 44 | type Defaddr int 45 | 46 | const ( 47 | aNo Defaddr = iota 48 | aDot 49 | aAll 50 | ) 51 | -------------------------------------------------------------------------------- /cmd/sam/plumb.h.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Plumbmsg struct { 4 | src string 5 | dst string 6 | wdir string 7 | type_ string 8 | attr string 9 | data string 10 | ndata int 11 | } 12 | -------------------------------------------------------------------------------- /cmd/sam/rules.txt: -------------------------------------------------------------------------------- 1 | BACK -> frame.BACK 2 | BORD -> frame.BORD 3 | C.char -> int8 4 | C.short -> int 5 | Cursor -> draw.Cursor 6 | DDarkyellow -> draw.Darkyellow 7 | DNofill -> draw.Nofill 8 | DPalebluegreen -> draw.Palebluegreen 9 | DPalegreygreen -> draw.Palegreygreen 10 | DPaleyellow -> draw.Paleyellow 11 | DPurpleblue -> draw.Purpleblue 12 | DWhite -> draw.White 13 | DYellowgreen -> draw.Yellowgreen 14 | Display -> draw.Display 15 | Dx(r) -> r.Dx() 16 | Dy(r) -> r.Dy() 17 | Font -> draw.Font 18 | Frame -> frame.Frame 19 | HIGH -> frame.HIGH 20 | HTEXT -> frame.HTEXT 21 | Image -> draw.Image 22 | Keyboardctl -> draw.Keyboardctl 23 | Menu -> draw.Menu 24 | Mouse -> draw.Mouse 25 | Mousectl -> draw.Mousectl 26 | NCOL -> frame.NCOL 27 | Plumbmsg -> plumb.Message 28 | Point -> image.Point 29 | Pt -> image.Pt 30 | Rect -> image.Rect 31 | Rectangle -> image.Rectangle 32 | Rune -> rune 33 | TEXT -> frame.TEXT 34 | ZP -> draw.ZP 35 | ZR -> draw.ZR 36 | addpt(p, q) -> p.Add(q) 37 | allocimage(d, r, p, 0, c) -> d.AllocImage(r, p, false, c) 38 | allocimage(d, r, p, 1, c) -> d.AllocImage(r, p, true, c) 39 | allocimagemix(d, c, b) -> d.AllocImageMix(c, b) 40 | border(i, r, w, s, p) -> i.Border(r, w, s, p) 41 | draw(i, r, s, m, p) -> i.Draw(r, s, m, p) 42 | eqpt(p, q) -> p.Eq(q) 43 | eqrect(r, s) -> r.Eq(s) 44 | f.nchars -> f.NumChars 45 | flushimage(d, 1) -> d.Flush() 46 | fmt.Fprintf(2, a) -> fmt.Fprintf(os.Stderr, a) 47 | fmt.Fprintf(2, a, b) -> fmt.Fprintf(os.Stderr, a, b) 48 | fmt.Fprintf(2, a, b, c) -> fmt.Fprintf(os.Stderr, a, b, c) 49 | fmt.Fprintf(2, a, b, c, d) -> fmt.Fprintf(os.Stderr, a, b, c, d) 50 | fmt.Fprintf(2, a, b, c, d, e) -> fmt.Fprintf(os.Stderr, a, b, c, d, e) 51 | fmt.Fprintf(2, a, b, c, d, e, f) -> fmt.Fprintf(os.Stderr, a, b, c, d, e, f) 52 | fprint -> fmt.Fprintf 53 | frcharofpt(f, p) -> f.CharOf(p) 54 | frclear(f, 0) -> f.Clear(false) 55 | frclear(f, 1) -> f.Clear(true) 56 | frdelete(f, a, b) -> f.Delete(a, b) 57 | frdrawsel(f, p, q, r, 0) -> f.Drawsel(p, q, r, false) 58 | frdrawsel(f, p, q, r, 1) -> f.Drawsel(p, q, r, true) 59 | freeimage(i) -> i.Free() 60 | frinit(f, a, b, c, d) -> f.Init(a, b, c, d) 61 | frinsert(f, a, b, c) -> f.Insert(a, b, c) 62 | frptofchar(f, i) -> f.PointOf(i) 63 | frselect(f, m) -> f.Select(m) 64 | frsetrects(f, r, b) -> f.SetRects(r, b) 65 | frtick(f, p, t) -> f.Tick(p, t) 66 | getrect -> draw.SweepRect 67 | i.chan_ -> i.Pix 68 | initkeyboard(x) -> display.InitKeyboard() 69 | initmouse(x, i) -> display.InitMouse() 70 | insetrect(r, d) -> r.Inset(d) 71 | l.user1 -> l.text 72 | m.xy -> m.Point 73 | menuhit -> draw.MenuHit 74 | moveto(m, p) -> display.MoveTo(p) 75 | ptinrect(p, r) -> p.In(r) 76 | rectaddpt(r, p) -> r.Add(p) 77 | rectclip -> draw.RectClip 78 | rectsubpt(r, p) -> r.Sub(p) 79 | scalesize(d, i) -> d.ScaleSize(i) 80 | setcursor(m, c) -> display.SetCursor(c) 81 | stringwidth(f, s) -> f.StringWidth(s) 82 | subpt(p, q) -> p.Sub(q) 83 | uchar -> uint8 84 | ulong -> int 85 | ushort -> int 86 | vlong -> int64 87 | getwindow(d, r) -> display.Attach(r) 88 | Refnone -> display.Refnone 89 | dup -> syscall.Dup2 90 | close -> syscall.Close 91 | open -> syscall.Open 92 | OREAD -> syscall.O_RDONLY 93 | OWRITE -> syscall.O_WRONLY 94 | utfrune(x, y) != 0 -> strings.ContainsRune(x, y) 95 | *int8 -> string 96 | -------------------------------------------------------------------------------- /cmd/sam/string.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func Strinit(p *String) { 4 | p.s = nil 5 | } 6 | 7 | func Strinit0(p *String) { 8 | p.s = nil 9 | } 10 | 11 | func Strclose(p *String) { 12 | // free(p.s) 13 | } 14 | 15 | const MAXSIZE = 256 16 | 17 | func Strzero(p *String) { 18 | if cap(p.s) > MAXSIZE { 19 | p.s = nil /* throw away the garbage */ 20 | } 21 | p.s = p.s[:0] 22 | } 23 | 24 | func Strlen(r []rune) int { 25 | return len(r) 26 | } 27 | 28 | func Strdupl(p *String, s []rune) { 29 | Strinsure(p, len(s)) 30 | copy(p.s, s) 31 | } 32 | 33 | func Strduplstr(p *String, q *String) { 34 | Strinsure(p, len(q.s)) 35 | copy(p.s, q.s) 36 | } 37 | 38 | func Straddc(p *String, c rune) { 39 | p.s = append(p.s, c) 40 | } 41 | 42 | func Strinsure(p *String, n int) { 43 | if n > STRSIZE { 44 | error_(Etoolong) 45 | } 46 | for cap(p.s) < n { 47 | p.s = append(p.s[:cap(p.s)], 0) 48 | } 49 | p.s = p.s[:n] 50 | } 51 | 52 | func Strinsert(p *String, q *String, p0 Posn) { 53 | Strinsure(p, len(p.s)+len(q.s)) 54 | copy(p.s[p0+len(q.s):], p.s[p0:]) 55 | copy(p.s[p0:], q.s) 56 | } 57 | 58 | func Strdelete(p *String, p1 Posn, p2 Posn) { 59 | if p1 <= len(p.s) && p2 == len(p.s)+1 { 60 | // "deleting" the NUL at the end is OK 61 | p2-- 62 | } 63 | copy(p.s[p1:], p.s[p2:]) 64 | p.s = p.s[:len(p.s)-(p2-p1)] 65 | } 66 | 67 | func Strcmp(a *String, b *String) int { 68 | var i int 69 | for i = 0; i < len(a.s) && i < len(b.s); i++ { 70 | c := int(a.s[i] - b.s[i]) 71 | if c != 0 { /* assign = */ 72 | return c 73 | } 74 | } 75 | return len(a.s) - len(b.s) 76 | } 77 | 78 | func Strispre(prefix, s *String) bool { 79 | for i := 0; i < len(prefix.s); i++ { 80 | if i >= len(s.s) || s.s[i] != prefix.s[i] { 81 | return false 82 | } 83 | } 84 | return true 85 | } 86 | 87 | func Strtoc(s *String) string { 88 | return string(s.s) 89 | } 90 | 91 | /* 92 | * Build very temporary String from Rune* 93 | */ 94 | var tmprstr_p String 95 | 96 | func tmprstr(r []rune) *String { 97 | return &String{r} 98 | } 99 | 100 | /* 101 | * Convert null-terminated char* into String 102 | */ 103 | func tmpcstr(s string) *String { 104 | if len(s) > 0 && s[len(s)-1] == '\x00' { 105 | s = s[:len(s)-1] 106 | } 107 | r := []rune(s) 108 | return &String{r} 109 | } 110 | 111 | func freetmpstr(s *String) { 112 | // free(s.s) 113 | // free(s) 114 | } 115 | -------------------------------------------------------------------------------- /cmd/sam/sys.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "os/signal" 10 | "os/user" 11 | ) 12 | 13 | var inerror = false 14 | 15 | /* 16 | * A reasonable interface to the system calls 17 | */ 18 | 19 | func resetsys() { 20 | inerror = false 21 | } 22 | 23 | func syserror(a string, err error) { 24 | if !inerror { 25 | inerror = true 26 | dprint("%s: ", a) 27 | error_s(Eio, err.Error()) 28 | } 29 | } 30 | 31 | func Read(f *os.File, a []byte) int { 32 | n, err := io.ReadFull(f, a) 33 | if err != nil { 34 | if lastfile != nil { 35 | lastfile.rescuing = 1 36 | } 37 | if downloaded { 38 | fmt.Fprintf(os.Stderr, "read error: %s\n", err) 39 | } 40 | rescue() 41 | os.Exit(1) 42 | } 43 | return n 44 | } 45 | 46 | func Write(f io.Writer, b []byte) int { 47 | m, err := f.Write(b) 48 | if err != nil || m != len(b) { 49 | if err == nil { 50 | err = io.ErrShortWrite 51 | } 52 | syserror("write", err) 53 | } 54 | return m 55 | } 56 | 57 | func Seek(f *os.File, n int64, w int) { 58 | if _, err := f.Seek(n, w); err != nil { 59 | syserror("seek", err) 60 | } 61 | } 62 | 63 | func Close(f *os.File) { 64 | if err := f.Close(); err != nil { 65 | syserror("close", err) 66 | } 67 | } 68 | 69 | var samname = []rune("~~sam~~") 70 | 71 | var ( 72 | left = [][]rune{[]rune("{[(<«"), []rune("\n"), []rune("'\"`")} 73 | right = [][]rune{[]rune("}])>»"), []rune("\n"), []rune("'\"`")} 74 | ) 75 | 76 | var ( 77 | RSAM = "sam" 78 | SAMTERM = "samterm" 79 | SH = "sh" 80 | SHPATH = "/bin/sh" 81 | RX = "ssh" 82 | RXPATH = "ssh" 83 | ) 84 | 85 | func dprint(z string, args ...interface{}) { 86 | termwrite(fmt.Sprintf(z, args...)) 87 | } 88 | 89 | func print_ss(s string, a *String, b *String) { 90 | dprint("?warning: %s: `%s' and `%s'\n", s, a, b) 91 | } 92 | 93 | func print_s(s string, a *String) { 94 | dprint("?warning: %s `%s'\n", s, a) 95 | } 96 | 97 | var getuser_user string 98 | 99 | func getuser() string { 100 | if getuser_user != "" { 101 | u, err := user.Current() 102 | if err != nil { 103 | getuser_user = "nobody" 104 | } else { 105 | getuser_user = u.Username 106 | } 107 | } 108 | return getuser_user 109 | } 110 | 111 | func hup(c chan os.Signal) { 112 | <-c 113 | panicking = 1 /* ??? */ 114 | rescue() 115 | os.Exit(1) 116 | } 117 | 118 | var SIGHUP os.Signal 119 | 120 | func siginit() { 121 | signal.Notify(make(chan os.Signal), os.Interrupt) 122 | if SIGHUP != nil { 123 | c := make(chan os.Signal, 1) 124 | signal.Notify(c, SIGHUP) 125 | go hup(c) 126 | } 127 | } 128 | 129 | func mktemp() *os.File { 130 | f, err := ioutil.TempFile("", fmt.Sprintf("sam.%d.*", os.Getpid())) 131 | if err != nil { 132 | rescue() 133 | log.Fatalf("creating temp file: %v", err) 134 | } 135 | return f 136 | } 137 | 138 | func samerr() string { 139 | return fmt.Sprintf("%s/sam.%s.err", os.TempDir(), os.Getenv("USER")) 140 | } 141 | 142 | func tempdisk() *os.File { 143 | f := mktemp() 144 | os.Remove(f.Name()) 145 | return f 146 | } 147 | -------------------------------------------------------------------------------- /cmd/sam/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/plan9/bin/rc 2 | 3 | sam=./sam 4 | if(~ $1 -std) { 5 | sam=$PLAN9/bin/sam 6 | shift 7 | } 8 | if(~ $#* 0) 9 | *=(testdata/*.txt) 10 | if(~ $sam(1) ./sam) 11 | go build -race || exit 1 12 | fail=() 13 | for(i) { 14 | rm -f tmp tmp2 15 | echo '#' $i 16 | sed -n '/^-- out --$/,$p' $i | sed 1d >test.want 17 | sed '/^-- out --$/q' $i | sed '/^-- out --$/d' | 18 | $sam -d >[2=1] | sed ' 19 | s/ *$// 20 | s/No such file/no such file/ 21 | ' > test.have 22 | if (! 9 diff -c test.want test.have >test.diff) { 23 | echo FAIL with diff: 24 | cat test.diff 25 | fail=($fail $i) 26 | exit 1 27 | } 28 | } 29 | if(! ~ $#fail 0) { 30 | echo FAILED: $fail 31 | exit 1 32 | } 33 | rm -f test.have test.want test.err test.diff 34 | exit 0 35 | -------------------------------------------------------------------------------- /cmd/sam/testdata/000.in: -------------------------------------------------------------------------------- 1 | B testdata/gettysburg 2 | 1 3 | -------------------------------------------------------------------------------- /cmd/sam/testdata/000.txt: -------------------------------------------------------------------------------- 1 | B testdata/gettysburg 2 | 1 3 | -- out -- 4 | -. 5 | -. testdata/gettysburg 6 | Four score and seven years ago our fathers brought forth on 7 | -------------------------------------------------------------------------------- /cmd/sam/testdata/001.txt: -------------------------------------------------------------------------------- 1 | B testdata/gettysburg 2 | ,s/ago/AGO/g 3 | X ,x/[a-z]+/ g/and/ c/AND/ 4 | 1 5 | -- out -- 6 | -. 7 | -. testdata/gettysburg 8 | Four score AND seven years AGO our fathers brought forth on 9 | ?changed files 10 | -------------------------------------------------------------------------------- /cmd/sam/testdata/002.txt: -------------------------------------------------------------------------------- 1 | B tmp 2 | a 3 | hello 4 | world 5 | . 6 | B tmp2 7 | a 8 | goodbye 9 | world 10 | . 11 | X/'/w 12 | X/'/w 13 | X D 14 | B tmp tmp2 15 | b tmp 16 | ,p 17 | b tmp2 18 | ,p 19 | -- out -- 20 | -. 21 | -. tmp 22 | ?can't open "tmp": no such file or directory 23 | -. tmp2 24 | ?can't open "tmp2": no such file or directory 25 | tmp: (new file) #12 26 | tmp2: (new file) #14 27 | -. tmp2 28 | -. tmp 29 | hello 30 | world 31 | -. tmp2 32 | goodbye 33 | world 34 | -------------------------------------------------------------------------------- /cmd/sam/testdata/003.txt: -------------------------------------------------------------------------------- 1 | B tmp 2 | a 3 | hello 4 | world 5 | . 6 | B tmp2 7 | a 8 | goodbye 9 | world 10 | . 11 | X ,x/./ g/e/ c/E/ 12 | X/'/w 13 | X D 14 | B tmp 15 | B tmp2 16 | X 17 | X ,x g/E/ p 18 | -- out -- 19 | -. 20 | -. tmp 21 | ?can't open "tmp": no such file or directory 22 | -. tmp2 23 | ?can't open "tmp2": no such file or directory 24 | tmp: (new file) #12 25 | tmp2: (new file) #14 26 | -. tmp 27 | -. tmp2 28 | -. tmp 29 | -. tmp2 30 | hEllo 31 | goodbyE 32 | -------------------------------------------------------------------------------- /cmd/sam/testdata/004.txt: -------------------------------------------------------------------------------- 1 | B address.go 2 | B cmd.go 3 | B buff.go 4 | X ,x g/asfasdfs/ 5 | -- out -- 6 | -. 7 | -. address.go 8 | -. cmd.go 9 | -. buff.go 10 | -------------------------------------------------------------------------------- /cmd/sam/testdata/005.txt: -------------------------------------------------------------------------------- 1 | B address.go buff.go cmd.go 2 | X ,x g/asfasdfs/ 3 | -- out -- 4 | -. 5 | -. cmd.go 6 | - address.go 7 | - buff.go 8 | -------------------------------------------------------------------------------- /cmd/sam/testdata/006.txt: -------------------------------------------------------------------------------- 1 | B 0 && (eof || len(b) >= utf8.UTFMax || utf8.FullRune(b)) { 18 | rr, w := utf8.DecodeRune(b) 19 | if rr == 0 { 20 | nulls = true 21 | } else { 22 | r[nr] = rr 23 | nr++ 24 | } 25 | b = b[w:] 26 | } 27 | nb = len(b0) - len(b) 28 | return nb, nr, nulls 29 | } 30 | 31 | func fbufalloc() []rune { 32 | return make([]rune, RBUFSIZE) 33 | } 34 | 35 | func fbuffree(f []rune) { 36 | // free(f) 37 | } 38 | 39 | func min(a int, b int) int { 40 | if a < b { 41 | return a 42 | } 43 | return b 44 | } 45 | -------------------------------------------------------------------------------- /cmd/samterm/conv: -------------------------------------------------------------------------------- 1 | #!/usr/local/plan9/bin/rc 2 | 3 | c2gofmt -r rules.txt -w /usr/local/plan9/src/cmd/samterm/^(*.c *.h) \ 4 | /usr/local/plan9/src/cmd/sam/mesg.h 5 | sed -i '' 's/package pkg/package main/g' *.go 6 | goimports -w . 7 | go build -gcflags=-e >[2=1] | grep -v 'but does have' | 9 sort +1 8 | 9 | 10 | -------------------------------------------------------------------------------- /cmd/samterm/flayer.h.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | 6 | "9fans.net/go/draw/frame" 7 | ) 8 | 9 | type Vis int 10 | 11 | const ( 12 | None Vis = 0 + iota 13 | Some 14 | All 15 | ) 16 | 17 | const ( 18 | Clicktime = 1000 19 | ) /* one second */ 20 | 21 | type Flayer struct { 22 | f frame.Frame 23 | origin int 24 | p0 int 25 | p1 int 26 | click uint32 27 | textfn func(*Flayer, int) []rune 28 | text *Text 29 | entire image.Rectangle 30 | scroll image.Rectangle 31 | lastsr image.Rectangle 32 | visible Vis 33 | } 34 | 35 | func FLMARGIN(l *Flayer) int { return flscale(l, 4) } 36 | func FLSCROLLWID(l *Flayer) int { return flscale(l, 12) } 37 | func FLGAP(l *Flayer) int { return flscale(l, 4) } 38 | -------------------------------------------------------------------------------- /cmd/samterm/icons.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | 6 | "9fans.net/go/draw" 7 | ) 8 | 9 | var bullseye = draw.Cursor{ 10 | Point: image.Point{-7, -7}, 11 | White: [...]uint8{ 12 | 0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, 13 | 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 14 | 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, 15 | 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, 16 | }, 17 | Black: [...]uint8{ 18 | 0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, 19 | 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, 20 | 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 21 | 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, 22 | }, 23 | } 24 | var deadmouse = draw.Cursor{ 25 | Point: image.Point{-7, -7}, 26 | White: [...]uint8{ 27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 28 | 0x00, 0x00, 0x00, 0x0C, 0x00, 0x8E, 0x1D, 0xC7, 29 | 0xFF, 0xE3, 0xFF, 0xF3, 0xFF, 0xFF, 0x7F, 0xFE, 30 | 0x3F, 0xF8, 0x17, 0xF0, 0x03, 0xE0, 0x00, 0x00, 31 | }, 32 | Black: [...]uint8{ 33 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 34 | 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x82, 35 | 0x04, 0x41, 0xFF, 0xE1, 0x5F, 0xF1, 0x3F, 0xFE, 36 | 0x17, 0xF0, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 37 | }, 38 | } 39 | var lockarrow = draw.Cursor{ 40 | Point: image.Point{-7, -7}, 41 | White: [...]uint8{ 42 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 43 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 45 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 46 | }, 47 | Black: [...]uint8{ 48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x0F, 0xC0, 50 | 0x03, 0xC0, 0x07, 0xC0, 0x0E, 0xC0, 0x1C, 0xC0, 51 | 0x38, 0x00, 0x70, 0x00, 0xE0, 0xDB, 0xC0, 0xDB, 52 | }, 53 | } 54 | 55 | var darkgrey *draw.Image 56 | 57 | func iconinit() { 58 | darkgrey, _ = display.AllocImage(image.Rect(0, 0, 1, 1), screen.Pix, true, 0x444444FF) 59 | } 60 | -------------------------------------------------------------------------------- /cmd/samterm/mesg.h.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "unicode/utf8" 4 | 5 | /* VERSION 1 introduces plumbing 6 | 2 increases SNARFSIZE from 4096 to 32000 7 | */ 8 | const VERSION = 2 9 | 10 | const TBLOCKSIZE = 512 /* largest piece of text sent to terminal */ 11 | const DATASIZE = (utf8.UTFMax*TBLOCKSIZE + 30) /* ... including protocol header stuff */ 12 | const SNARFSIZE = 32000 /* maximum length of exchanged snarf buffer, must fit in 15 bits */ 13 | /* 14 | * Messages originating at the terminal 15 | */ 16 | type Tmesg int 17 | 18 | const ( 19 | Tversion Tmesg = iota 20 | Tstartcmdfile 21 | Tcheck 22 | Trequest 23 | Torigin 24 | Tstartfile 25 | Tworkfile 26 | Ttype 27 | Tcut 28 | Tpaste 29 | Tsnarf 30 | Tstartnewfile 31 | Twrite 32 | Tclose 33 | Tlook 34 | Tsearch 35 | Tsend 36 | Tdclick 37 | Tstartsnarf 38 | Tsetsnarf 39 | Tack 40 | Texit 41 | Tplumb 42 | TMAX 43 | ) 44 | 45 | /* 46 | * Messages originating at the host 47 | */ 48 | type Hmesg int 49 | 50 | const ( 51 | Hversion Hmesg = iota 52 | Hbindname 53 | Hcurrent 54 | Hnewname 55 | Hmovname 56 | Hgrow 57 | Hcheck0 58 | Hcheck 59 | Hunlock 60 | Hdata 61 | Horigin 62 | Hunlockfile 63 | Hsetdot 64 | Hgrowdata 65 | Hmoveto 66 | Hclean 67 | Hdirty 68 | Hcut 69 | Hsetpat 70 | Hdelname 71 | Hclose 72 | Hsetsnarf 73 | Hsnarflen 74 | Hack 75 | Hexit 76 | Hplumb 77 | HMAX 78 | ) 79 | 80 | type Header struct { 81 | typ Hmesg 82 | count0 uint8 83 | count1 uint8 84 | } 85 | -------------------------------------------------------------------------------- /cmd/samterm/notunix.go: -------------------------------------------------------------------------------- 1 | // +build !unix 2 | 3 | package main 4 | 5 | func extstart() {} 6 | -------------------------------------------------------------------------------- /cmd/samterm/samterm.h.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const ( 4 | MAXFILES = 256 5 | READBUFSIZE = 8192 6 | NL = 5 7 | ) 8 | 9 | const ( 10 | Up = iota 11 | Down 12 | ) 13 | 14 | type Section struct { 15 | nrunes int 16 | text []rune 17 | next *Section 18 | } 19 | 20 | type Rasp struct { 21 | nrunes int 22 | sect *Section 23 | } 24 | 25 | const Untagged = 0xFFFF 26 | 27 | type Text struct { 28 | rasp Rasp 29 | nwin int 30 | front int 31 | tag int 32 | lock int8 33 | l [NL]Flayer 34 | id int64 35 | } 36 | 37 | type Readbuf struct { 38 | n int 39 | data [READBUFSIZE]uint8 40 | } 41 | 42 | type Resource int 43 | 44 | const ( 45 | RHost Resource = iota 46 | RKeyboard 47 | RMouse 48 | RPlumb 49 | RResize 50 | NRes 51 | ) 52 | -------------------------------------------------------------------------------- /cmd/samterm/unix.go: -------------------------------------------------------------------------------- 1 | // +build unix 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "io/fs" 8 | "os" 9 | "syscall" 10 | 11 | "golang.org/x/sys/unix" 12 | ) 13 | 14 | func extstart() { 15 | user := os.Getenv("USER") 16 | if user == "" { 17 | return 18 | } 19 | disp := os.Getenv("DISPLAY") 20 | if disp != "" { 21 | exname = fmt.Sprintf("/tmp/.sam.%s.%s", user, disp) 22 | } else { 23 | exname = fmt.Sprintf("/tmp/.sam.%s", user) 24 | } 25 | err := syscall.Mkfifo(exname, 0600) 26 | if err != nil { 27 | if !os.IsExist(err) { 28 | return 29 | } 30 | st, err := os.Stat(exname) 31 | if err != nil { 32 | return 33 | } 34 | if st.Mode()&fs.ModeNamedPipe == 0 { 35 | removeextern() 36 | if err := syscall.Mkfifo(exname, 0600); err != nil { 37 | return 38 | } 39 | } 40 | } 41 | 42 | fd, err := syscall.Open(exname, syscall.O_RDONLY|syscall.O_NONBLOCK, 0) 43 | if err != nil { 44 | removeextern() 45 | return 46 | } 47 | 48 | // Turn off no-delay and provide ourselves as a lingering 49 | // writer so as not to get end of file on read. 50 | flags, err := unix.FcntlInt(uintptr(fd), syscall.F_GETFL, 0) 51 | if err != nil { 52 | goto Fail 53 | } 54 | if _, err := unix.FcntlInt(uintptr(fd), syscall.F_SETFL, flags & ^syscall.O_NONBLOCK); err != nil { 55 | goto Fail 56 | } 57 | if _, err := syscall.Open(exname, syscall.O_WRONLY, 0); err != nil { 58 | goto Fail 59 | } 60 | 61 | plumbc = make(chan string) 62 | go extproc(plumbc, os.NewFile(uintptr(fd), exname)) 63 | // atexit(removeextern) 64 | return 65 | 66 | Fail: 67 | syscall.Close(fd) 68 | removeextern() 69 | } 70 | -------------------------------------------------------------------------------- /draw/allocimagemix.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // AllocImageMix is used to allocate background colors. 4 | // It returns a 1×1 replicated image whose pixel is the result of 5 | // mixing the two colors in a one to three ratio. 6 | // On 8-bit color-mapped displays, it returns a 2×2 replicated image 7 | // with one pixel colored the color one and the other three 8 | // with three. (This simulates a wider range of tones than can 9 | // be represented by a single pixel value on a color-mapped 10 | // display.) 11 | func (d *Display) AllocImageMix(color1, color3 Color) *Image { 12 | d.mu.Lock() 13 | defer d.mu.Unlock() 14 | if d.ScreenImage.Depth <= 8 { // create a 2x2 texture 15 | t, _ := d.allocImage(Rect(0, 0, 1, 1), d.ScreenImage.Pix, false, color1) 16 | b, _ := d.allocImage(Rect(0, 0, 2, 2), d.ScreenImage.Pix, true, color3) 17 | b.draw(Rect(0, 0, 1, 1), t, nil, ZP) 18 | t.free() 19 | return b 20 | } 21 | 22 | // use a solid color, blended using alpha 23 | if d.qmask == nil { 24 | d.qmask, _ = d.allocImage(Rect(0, 0, 1, 1), GREY8, true, 0x3F3F3FFF) 25 | } 26 | t, _ := d.allocImage(Rect(0, 0, 1, 1), d.ScreenImage.Pix, true, color1) 27 | b, _ := d.allocImage(Rect(0, 0, 1, 1), d.ScreenImage.Pix, true, color3) 28 | b.draw(b.R, t, d.qmask, ZP) 29 | t.free() 30 | return b 31 | } 32 | -------------------------------------------------------------------------------- /draw/arith.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import "image" 4 | 5 | // A Point is an X, Y coordinate pair, a location in an Image such as the display. 6 | // The coordinate system has X increasing to the right and Y increasing down. 7 | type Point = image.Point 8 | 9 | // A Rectangle is a rectangular area in an image. 10 | // By definition, Min.X ≤ Max.X and Min.Y ≤ Max.Y. 11 | // By convention, the right (Max.X) and bottom (Max.Y) 12 | // edges are excluded from the represented rectangle, 13 | // so abutting rectangles have no points in common. 14 | // Thus, max contains the coordinates of the first point beyond the rectangle. 15 | // If Min.X > Max.X or Min.Y > Max.Y, the rectangle contains no points. 16 | type Rectangle = image.Rectangle 17 | 18 | // Pt is shorthand for Point{X: x, Y: y}. 19 | func Pt(x, y int) Point { 20 | return Point{X: x, Y: y} 21 | } 22 | 23 | // Rect is shorthand for Rectangle{Min: Pt(x0, y0), Max: Pt(x1, y1)}. 24 | // Unlike image.Rect, Rect does not swap x1 ↔ x2 or y1 ↔ y2 25 | // to put them in canonical order. 26 | // In this package, a Rectangle with x1 > x2 or y1 > y2 27 | // is an empty rectangle. 28 | func Rect(x1, y1, x2, y2 int) Rectangle { 29 | return Rectangle{Pt(x1, y1), Pt(x2, y2)} 30 | } 31 | 32 | // Rpt is shorthand for Rectangle{min, max}. 33 | func Rpt(min, max Point) Rectangle { 34 | return Rectangle{Min: min, Max: max} 35 | } 36 | 37 | // ZP is the zero Point. 38 | var ZP Point 39 | 40 | // ZR is the zero Rectangle. 41 | var ZR Rectangle 42 | -------------------------------------------------------------------------------- /draw/bench_test.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // Benchmarks. Some run as regular tests. 4 | 5 | import ( 6 | "fmt" 7 | "sync" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | var ( 13 | testOnce sync.Once 14 | testDisplay *Display 15 | ) 16 | 17 | func testInit() { 18 | var err error 19 | testDisplay, err = Init(nil, "", "drawtest", "") 20 | if err != nil { 21 | panic(err) 22 | } 23 | } 24 | 25 | const aHundredChars = "abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxy" 26 | 27 | func TestBenchmarkString(t *testing.T) { 28 | testOnce.Do(testInit) 29 | im := testDisplay.Image 30 | start := time.Now() 31 | var nchars time.Duration 32 | for i := 0; i < 1e4; i++ { 33 | im.String(im.R.Min, testDisplay.Black, im.R.Min, testDisplay.Font, aHundredChars) 34 | nchars += 100 35 | } 36 | testDisplay.Flush() 37 | end := time.Now() 38 | fmt.Println("time for one char:", end.Sub(start)/nchars) 39 | } 40 | -------------------------------------------------------------------------------- /draw/border.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // BorderOp is like Border but specifies an explicit Porter-Duff operator. 4 | func (dst *Image) BorderOp(r Rectangle, n int, color *Image, sp Point, op Op) { 5 | if n < 0 { 6 | r = r.Inset(n) 7 | sp = sp.Add(Pt(n, n)) 8 | n = -n 9 | } 10 | dst.Display.mu.Lock() 11 | defer dst.Display.mu.Unlock() 12 | draw(dst, Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+n), 13 | color, sp, nil, sp, op) 14 | pt := Pt(sp.X, sp.Y+r.Dy()-n) 15 | draw(dst, Rect(r.Min.X, r.Max.Y-n, r.Max.X, r.Max.Y), 16 | color, pt, nil, pt, op) 17 | pt = Pt(sp.X, sp.Y+n) 18 | draw(dst, Rect(r.Min.X, r.Min.Y+n, r.Min.X+n, r.Max.Y-n), 19 | color, pt, nil, pt, op) 20 | pt = Pt(sp.X+r.Dx()-n, sp.Y+n) 21 | draw(dst, Rect(r.Max.X-n, r.Min.Y+n, r.Max.X, r.Max.Y-n), 22 | color, pt, nil, pt, op) 23 | } 24 | 25 | // Border draws an outline of rectangle r in the specified color. 26 | // The outline has width w; if w is positive, the border goes inside the 27 | // rectangle; if negative, outside. 28 | // The source is aligned so that sp corresponds to r.Min. 29 | func (dst *Image) Border(r Rectangle, w int, color *Image, sp Point) { 30 | dst.BorderOp(r, w, color, sp, SoverD) 31 | } 32 | -------------------------------------------------------------------------------- /draw/bytesperline.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // WordsPerLine returns the number of 32-bit words touched by a scan line of 4 | // the rectangle of specified depth. 5 | func WordsPerLine(r Rectangle, depth int) int { 6 | return unitsPerLine(r, depth, 32) 7 | } 8 | 9 | // BytesPerLine returns the number of 8-bit bytes touched by a scan line of 10 | // the rectangle of specified depth. 11 | func BytesPerLine(r Rectangle, depth int) int { 12 | return unitsPerLine(r, depth, 8) 13 | } 14 | 15 | func unitsPerLine(r Rectangle, depth, bitsperunit int) int { 16 | if depth <= 0 || depth > 32 { 17 | panic("invalid depth") 18 | } 19 | 20 | var l int 21 | if r.Min.X >= 0 { 22 | l = (r.Max.X*depth + bitsperunit - 1) / bitsperunit 23 | l -= (r.Min.X * depth) / bitsperunit 24 | } else { 25 | // make positive before divide 26 | t := (-r.Min.X*depth + bitsperunit - 1) / bitsperunit 27 | l = t + (r.Max.X*depth+bitsperunit-1)/bitsperunit 28 | } 29 | return l 30 | } 31 | -------------------------------------------------------------------------------- /draw/cloadimage.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import "fmt" 4 | 5 | // Cload replaces the specified rectangle in image i with the compressed data, 6 | // returning the number of bytes copied from data. 7 | // It is an error if data does not contain pixels for the entire rectangle. 8 | // 9 | // See the package documentation for details about the compressed data format. 10 | // Each call to Cload must pass data starting at the beginning of a compressed 11 | // data block, specifically the y coordinate and data length for the block. 12 | func (dst *Image) Cload(r Rectangle, data []byte) (int, error) { 13 | dst.Display.mu.Lock() 14 | defer dst.Display.mu.Unlock() 15 | i := dst 16 | if !r.In(i.R) { 17 | return 0, fmt.Errorf("cloadimage: bad rectangle") 18 | } 19 | 20 | miny := r.Min.Y 21 | m := 0 22 | ncblock := compblocksize(r, i.Depth) 23 | for miny != r.Max.Y { 24 | maxy := atoi(data[0*12:]) 25 | nb := atoi(data[1*12:]) 26 | if maxy <= miny || r.Max.Y < maxy { 27 | return 0, fmt.Errorf("creadimage: bad maxy %d", maxy) 28 | } 29 | data = data[2*12:] 30 | m += 2 * 12 31 | if nb <= 0 || ncblock < nb || nb > len(data) { 32 | return 0, fmt.Errorf("creadimage: bad count %d", nb) 33 | } 34 | // TODO: error check? 35 | a := i.Display.bufimage(21 + nb) 36 | a[0] = 'Y' 37 | bplong(a[1:], i.id) 38 | bplong(a[5:], uint32(r.Min.Y)) 39 | bplong(a[9:], uint32(miny)) 40 | bplong(a[13:], uint32(r.Max.Y)) 41 | bplong(a[17:], uint32(maxy)) 42 | copy(a[21:], data) 43 | miny = maxy 44 | data = data[nb:] 45 | m += nb 46 | } 47 | return m, nil 48 | } 49 | -------------------------------------------------------------------------------- /draw/computil.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // Compressed image file parameters. 4 | const ( 5 | _NMATCH = 3 /* shortest match possible */ 6 | _NRUN = (_NMATCH + 31) /* longest match possible */ 7 | _NMEM = 1024 /* window size */ 8 | _NDUMP = 128 /* maximum length of dump */ 9 | _NCBLOCK = 6000 /* size of compressed blocks */ 10 | ) 11 | 12 | /* 13 | * compressed data are sequences of byte codes. 14 | * if the first byte b has the 0x80 bit set, the next (b^0x80)+1 bytes 15 | * are data. otherwise, it's two bytes specifying a previous string to repeat. 16 | */ 17 | 18 | func twiddlecompressed(buf []byte) { 19 | i := 0 20 | for i < len(buf) { 21 | c := buf[i] 22 | i++ 23 | if c >= 0x80 { 24 | k := int(c) - 0x80 + 1 25 | for j := 0; j < k && i < len(buf); j++ { 26 | buf[i] ^= 0xFF 27 | i++ 28 | } 29 | } else { 30 | i++ 31 | } 32 | } 33 | } 34 | 35 | func compblocksize(r Rectangle, depth int) int { 36 | bpl := BytesPerLine(r, depth) 37 | bpl = 2 * bpl /* add plenty extra for blocking, etc. */ 38 | if bpl < _NCBLOCK { 39 | return _NCBLOCK 40 | } 41 | return bpl 42 | } 43 | -------------------------------------------------------------------------------- /draw/creadimage.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | var ldepthToPix = []Pix{ 10 | GREY1, 11 | GREY2, 12 | GREY4, 13 | CMAP8, 14 | } 15 | 16 | func (d *Display) creadimage(rd io.Reader) (*Image, error) { 17 | fd := rd 18 | hdr := make([]byte, 5*12) 19 | 20 | _, err := io.ReadFull(fd, hdr) 21 | if err != nil { 22 | return nil, fmt.Errorf("reading image header: %v", err) 23 | } 24 | 25 | /* 26 | * distinguish new channel descriptor from old ldepth. 27 | * channel descriptors have letters as well as numbers, 28 | * while ldepths are a single digit formatted as %-11d. 29 | */ 30 | new := false 31 | for m := 0; m < 10; m++ { 32 | if hdr[m] != ' ' { 33 | new = true 34 | break 35 | } 36 | } 37 | if hdr[11] != ' ' { 38 | return nil, fmt.Errorf("creadimage: bad format") 39 | } 40 | var pix Pix 41 | if new { 42 | pix, err = ParsePix(strings.TrimSpace(string(hdr[:12]))) 43 | if err != nil { 44 | return nil, fmt.Errorf("creadimage: %v", err) 45 | } 46 | } else { 47 | ldepth := int(hdr[10]) - '0' 48 | if ldepth < 0 || ldepth > 3 { 49 | return nil, fmt.Errorf("creadimage: bad ldepth %d", ldepth) 50 | } 51 | pix = ldepthToPix[ldepth] 52 | } 53 | r := ator(hdr[1*12:]) 54 | if r.Min.X > r.Max.X || r.Min.Y > r.Max.Y { 55 | return nil, fmt.Errorf("creadimage: bad rectangle") 56 | } 57 | 58 | var i *Image 59 | if d != nil { 60 | i, err = d.allocImage(r, pix, false, 0) 61 | if err != nil { 62 | return nil, err 63 | } 64 | } else { 65 | i = &Image{R: r, Pix: pix, Depth: pix.Depth()} 66 | } 67 | 68 | ncblock := compblocksize(r, pix.Depth()) 69 | buf := make([]byte, ncblock) 70 | miny := r.Min.Y 71 | for miny != r.Max.Y { 72 | if _, err = io.ReadFull(fd, hdr[:2*12]); err != nil { 73 | goto Errout 74 | } 75 | maxy := atoi(hdr[0*12:]) 76 | nb := atoi(hdr[1*12:]) 77 | if maxy <= miny || r.Max.Y < maxy { 78 | err = fmt.Errorf("creadimage: bad maxy %d", maxy) 79 | goto Errout 80 | } 81 | if nb <= 0 || ncblock < nb { 82 | err = fmt.Errorf("creadimage: bad count %d", nb) 83 | goto Errout 84 | } 85 | if _, err = io.ReadFull(fd, buf[:nb]); err != nil { 86 | goto Errout 87 | } 88 | if d != nil { 89 | a := d.bufimage(21 + nb) 90 | // XXX err 91 | if err != nil { 92 | goto Errout 93 | } 94 | a[0] = 'Y' 95 | bplong(a[1:], i.id) 96 | bplong(a[5:], uint32(r.Min.X)) 97 | bplong(a[9:], uint32(miny)) 98 | bplong(a[13:], uint32(r.Max.X)) 99 | bplong(a[17:], uint32(maxy)) 100 | if !new { // old image: flip the data bits 101 | twiddlecompressed(buf[:nb]) 102 | } 103 | copy(a[21:], buf) 104 | } 105 | miny = maxy 106 | } 107 | return i, nil 108 | 109 | Errout: 110 | if d != nil { 111 | i.free() 112 | } 113 | return nil, err 114 | } 115 | -------------------------------------------------------------------------------- /draw/cursor.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // A Cursor describes a single cursor. 4 | // 5 | // The arrays White and Black are arranged in rows, two bytes per row, 6 | // left to right in big-endian order, to give 16 rows of 16 bits each. 7 | // A cursor is displayed on the screen by adding Point to the current 8 | // mouse position, then using White as a mask to draw white at 9 | // the pixels where White is 1, and then drawing black at the pixels 10 | // where Black is 1. 11 | type Cursor struct { 12 | Point 13 | White [2 * 16]uint8 14 | Black [2 * 16]uint8 15 | } 16 | 17 | // A Cursor2 describes a single high-DPI cursor, 18 | // with twice the pixels in each direction as a Cursor 19 | // (32 rows of 32 bits each). 20 | type Cursor2 struct { 21 | Point 22 | White [4 * 32]uint8 23 | Black [4 * 32]uint8 24 | } 25 | 26 | var expand = [16]uint8{ 27 | 0x00, 0x03, 0x0c, 0x0f, 28 | 0x30, 0x33, 0x3c, 0x3f, 29 | 0xc0, 0xc3, 0xcc, 0xcf, 30 | 0xf0, 0xf3, 0xfc, 0xff, 31 | } 32 | 33 | // ScaleCursor returns a high-DPI version of c. 34 | func ScaleCursor(c Cursor) Cursor2 { 35 | var c2 Cursor2 36 | c2.X = 2 * c.X 37 | c2.Y = 2 * c.Y 38 | for y := 0; y < 16; y++ { 39 | c2.White[8*y+4] = expand[c.White[2*y]>>4] 40 | c2.White[8*y] = c2.White[8*y+4] 41 | c2.Black[8*y+4] = expand[c.Black[2*y]>>4] 42 | c2.Black[8*y] = c2.Black[8*y+4] 43 | c2.White[8*y+5] = expand[c.White[2*y]&15] 44 | c2.White[8*y+1] = c2.White[8*y+5] 45 | c2.Black[8*y+5] = expand[c.Black[2*y]&15] 46 | c2.Black[8*y+1] = c2.Black[8*y+5] 47 | c2.White[8*y+6] = expand[c.White[2*y+1]>>4] 48 | c2.White[8*y+2] = c2.White[8*y+6] 49 | c2.Black[8*y+6] = expand[c.Black[2*y+1]>>4] 50 | c2.Black[8*y+2] = c2.Black[8*y+6] 51 | c2.White[8*y+7] = expand[c.White[2*y+1]&15] 52 | c2.White[8*y+3] = c2.White[8*y+7] 53 | c2.Black[8*y+7] = expand[c.Black[2*y+1]&15] 54 | c2.Black[8*y+3] = c2.Black[8*y+7] 55 | } 56 | return c2 57 | } 58 | -------------------------------------------------------------------------------- /draw/debug.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // SetDebug enables debugging for the remote devdraw server. 4 | func (d *Display) SetDebug(debug bool) { 5 | d.mu.Lock() 6 | defer d.mu.Unlock() 7 | a := d.bufimage(2) 8 | a[0] = 'D' 9 | a[1] = 0 10 | if debug { 11 | a[1] = 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /draw/drawfcall/bit.go: -------------------------------------------------------------------------------- 1 | package drawfcall // import "9fans.net/go/draw/drawfcall" 2 | 3 | // Note that these are big-endian, unlike Plan 9 fcalls, which are little-endian. 4 | 5 | func gbit8(b []byte) (uint8, []byte) { 6 | return uint8(b[0]), b[1:] 7 | } 8 | 9 | func gbit16(b []byte) (uint16, []byte) { 10 | return uint16(b[1]) | uint16(b[0])<<8, b[2:] 11 | } 12 | 13 | func gbit32(b []byte) (int, []byte) { 14 | return int(uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24), b[4:] 15 | } 16 | 17 | func gbit64(b []byte) (uint64, []byte) { 18 | hi, b := gbit32(b) 19 | lo, b := gbit32(b) 20 | return uint64(hi)<<32 | uint64(lo), b 21 | } 22 | 23 | func gstring(b []byte) (string, []byte) { 24 | n, b := gbit32(b) 25 | return string(b[0:n]), b[n:] 26 | } 27 | 28 | func gbytes(b []byte) ([]byte, []byte) { 29 | n, b := gbit32(b) 30 | return b[0:n], b[n:] 31 | } 32 | 33 | func pbit8(b []byte, x uint8) []byte { 34 | n := len(b) 35 | if n+1 > cap(b) { 36 | nb := make([]byte, n, 100+2*cap(b)) 37 | copy(nb, b) 38 | b = nb 39 | } 40 | b = b[0 : n+1] 41 | b[n] = x 42 | return b 43 | } 44 | 45 | func pbit16(b []byte, x uint16) []byte { 46 | n := len(b) 47 | if n+2 > cap(b) { 48 | nb := make([]byte, n, 100+2*cap(b)) 49 | copy(nb, b) 50 | b = nb 51 | } 52 | b = b[0 : n+2] 53 | b[n] = byte(x >> 8) 54 | b[n+1] = byte(x) 55 | return b 56 | } 57 | 58 | func pbit32(b []byte, i int) []byte { 59 | x := uint32(i) 60 | n := len(b) 61 | if n+4 > cap(b) { 62 | nb := make([]byte, n, 100+2*cap(b)) 63 | copy(nb, b) 64 | b = nb 65 | } 66 | b = b[0 : n+4] 67 | b[n] = byte(x >> 24) 68 | b[n+1] = byte(x >> 16) 69 | b[n+2] = byte(x >> 8) 70 | b[n+3] = byte(x) 71 | return b 72 | } 73 | 74 | func pbit64(b []byte, x uint64) []byte { 75 | b = pbit32(b, int(x>>32)) 76 | b = pbit32(b, int(x)) 77 | return b 78 | } 79 | 80 | func pstring(b []byte, s string) []byte { 81 | b = pbit32(b, len(s)) 82 | b = append(b, []byte(s)...) 83 | return b 84 | } 85 | 86 | func pbytes(b, s []byte) []byte { 87 | b = pbit32(b, len(s)) 88 | b = append(b, s...) 89 | return b 90 | } 91 | -------------------------------------------------------------------------------- /draw/drawrepl.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // ReplXY clips x to be in the half-open interval [min, max) y adding or 4 | // subtracking a multiple of max - min. 5 | // That is, assuming [min, max) specify the base of an infinite tiling 6 | // of the integer line, ReplXY returns the value of the image of x that appears 7 | // in the interval. 8 | func ReplXY(min, max, x int) int { 9 | sx := (x - min) % (max - min) 10 | if sx < 0 { 11 | sx += max - min 12 | } 13 | return sx + min 14 | } 15 | 16 | // Repl clips the point p to be within the rectangle r by translating p 17 | // horizontally by an integer multiple of the rectangle width 18 | // and vertically by an integer multiple of the rectangle height. 19 | // That is, it returns the point corresponding to the image of p that appears inside 20 | // the base rectangle r, which represents a tiling of the plane. 21 | func Repl(r Rectangle, p Point) Point { 22 | return Point{ 23 | ReplXY(r.Min.X, r.Max.X, p.X), 24 | ReplXY(r.Min.Y, r.Max.Y, p.Y), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /draw/egetrect.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // TODO 4 | -------------------------------------------------------------------------------- /draw/emenuhit.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // TODO 4 | -------------------------------------------------------------------------------- /draw/event.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // TODO 4 | -------------------------------------------------------------------------------- /draw/fixbp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 9 sed ' 3 | s/BPLONG/bplong/g 4 | s/->/./g 5 | /bplong/ s/([ab])\+([0-9]+)/\1[\2:]/ 6 | /bplong/ s/, (.*)\)/, uint32(\1))/ 7 | s/\.id/.ID/ 8 | s/\.m(in|ax)/.M\1/ 9 | s/\.x/.X/ 10 | s/\.y/.Y/ 11 | ' 12 | -------------------------------------------------------------------------------- /draw/frame/frptofchar.go: -------------------------------------------------------------------------------- 1 | package frame 2 | 3 | import ( 4 | "unicode/utf8" 5 | 6 | "9fans.net/go/draw" 7 | ) 8 | 9 | func (f *Frame) ptofcharptb(p int, pt draw.Point, bn int) draw.Point { 10 | for ; bn < len(f.box); bn++ { 11 | b := &f.box[bn] 12 | f.cklinewrap(&pt, b) 13 | l := b.NRUNE() 14 | if p < l { 15 | if b.nrune > 0 { 16 | s := b.bytes 17 | for ; p > 0; p-- { 18 | if len(s) == 0 { 19 | drawerror(f.Display, "frptofchar") 20 | } 21 | _, size := utf8.DecodeRune(s) 22 | pt.X += f.Font.BytesWidth(s[:size]) 23 | s = s[size:] 24 | if pt.X > f.R.Max.X { 25 | drawerror(f.Display, "frptofchar") 26 | } 27 | } 28 | } 29 | break 30 | } 31 | p -= l 32 | f.advance(&pt, b) 33 | } 34 | return pt 35 | } 36 | 37 | // PointOf returns 38 | // the location of the upper left corner of the p'th rune, 39 | // starting from 0, in the Frame f. If f holds fewer than p 40 | // runes, frptofchar returns the location of the upper right 41 | // corner of the last character in f. 42 | func (f *Frame) PointOf(p int) draw.Point { 43 | return f.ptofcharptb(p, f.R.Min, 0) 44 | } 45 | 46 | func (f *Frame) ptofcharnb(p, nb int) draw.Point { 47 | // doesn't do final f.advance to next line 48 | box := f.box 49 | f.box = f.box[:nb] 50 | pt := f.ptofcharptb(p, f.R.Min, 0) 51 | f.box = box 52 | return pt 53 | } 54 | 55 | func (f *Frame) grid(p draw.Point) draw.Point { 56 | p.Y -= f.R.Min.Y 57 | p.Y -= p.Y % f.Font.Height 58 | p.Y += f.R.Min.Y 59 | if p.X > f.R.Max.X { 60 | p.X = f.R.Max.X 61 | } 62 | return p 63 | } 64 | 65 | // CharOf is the 66 | // inverse of PointOf: it returns the index of the closest rune whose 67 | // image's upper left corner is up and to the left of pt. 68 | func (f *Frame) CharOf(pt draw.Point) int { 69 | pt = f.grid(pt) 70 | qt := f.R.Min 71 | p := 0 72 | bn := 0 73 | for ; bn < len(f.box) && qt.Y < pt.Y; bn++ { 74 | b := &f.box[bn] 75 | f.cklinewrap(&qt, b) 76 | if qt.Y >= pt.Y { 77 | break 78 | } 79 | f.advance(&qt, b) 80 | p += b.NRUNE() 81 | } 82 | for ; bn < len(f.box) && qt.X <= pt.X; bn++ { 83 | b := &f.box[bn] 84 | f.cklinewrap(&qt, b) 85 | if qt.Y > pt.Y { 86 | break 87 | } 88 | if qt.X+b.wid > pt.X { 89 | if b.nrune < 0 { 90 | f.advance(&qt, b) 91 | } else { 92 | s := b.bytes 93 | for { 94 | if len(s) == 0 { 95 | drawerror(f.Display, "end of string in frcharofpt") 96 | } 97 | _, size := utf8.DecodeRune(s) 98 | qt.X += f.Font.BytesWidth(s[:size]) 99 | s = s[size:] 100 | if qt.X > pt.X { 101 | break 102 | } 103 | p++ 104 | } 105 | } 106 | } else { 107 | p += b.NRUNE() 108 | f.advance(&qt, b) 109 | } 110 | } 111 | return p 112 | } 113 | -------------------------------------------------------------------------------- /draw/frame/frutil.go: -------------------------------------------------------------------------------- 1 | package frame 2 | 3 | import ( 4 | "unicode/utf8" 5 | 6 | "9fans.net/go/draw" 7 | ) 8 | 9 | // canfit returns the number of runes from b that 10 | // will fit into the frame when drawn starting at pt. 11 | func (f *Frame) canfit(pt draw.Point, b *box) int { 12 | left := f.R.Max.X - pt.X 13 | if b.nrune < 0 { 14 | if b.minwid <= left { 15 | return 1 16 | } 17 | return 0 18 | } 19 | if left >= b.wid { 20 | return b.nrune 21 | } 22 | p := b.bytes 23 | for nr := 0; nr < b.nrune; nr++ { 24 | _, size := utf8.DecodeRune(p) 25 | left -= f.Font.BytesWidth(p[:size]) 26 | p = p[size:] 27 | if left < 0 { 28 | return nr 29 | } 30 | } 31 | drawerror(f.Display, "_frcanfit can't") 32 | return 0 33 | } 34 | 35 | // cklinewrap checks whether b when drawn at p will 36 | // fit within f.R. If not, it updates p to the start of the next line. 37 | func (f *Frame) cklinewrap(p *draw.Point, b *box) { 38 | w := b.wid 39 | if b.nrune < 0 { 40 | w = b.minwid 41 | } 42 | if w > f.R.Max.X-p.X { 43 | p.X = f.R.Min.X 44 | p.Y += f.Font.Height 45 | } 46 | } 47 | 48 | // cklinewrap0 is like cklinewrap but with a weaker criterion 49 | // for wrapping: it only wraps if none of the text of b 50 | // will fit. 51 | func (f *Frame) cklinewrap0(p *draw.Point, b *box) { 52 | if f.canfit(*p, b) == 0 { 53 | p.X = f.R.Min.X 54 | p.Y += f.Font.Height 55 | } 56 | } 57 | 58 | func (f *Frame) advance(p *draw.Point, b *box) { 59 | if b.nrune < 0 && b.bc == '\n' { 60 | p.X = f.R.Min.X 61 | p.Y += f.Font.Height 62 | } else { 63 | p.X += b.wid 64 | } 65 | } 66 | 67 | func (f *Frame) newwid(pt draw.Point, b *box) int { 68 | b.wid = f.newwid0(pt, b) 69 | return b.wid 70 | } 71 | 72 | func (f *Frame) newwid0(pt draw.Point, b *box) int { 73 | c := f.R.Max.X 74 | x := pt.X 75 | if b.nrune >= 0 || b.bc != '\t' { 76 | return b.wid 77 | } 78 | if x+b.minwid > c { 79 | pt.X = f.R.Min.X 80 | x = pt.X 81 | } 82 | x += f.MaxTab 83 | x -= (x - f.R.Min.X) % f.MaxTab 84 | if x-pt.X < b.minwid || x > c { 85 | x = pt.X + b.minwid 86 | } 87 | return x - pt.X 88 | } 89 | 90 | func (f *Frame) clean(pt draw.Point, n0, n1 int) { 91 | // look for mergeable boxes 92 | 93 | c := f.R.Max.X 94 | nb := n0 95 | for ; nb < n1-1; nb++ { 96 | b := &f.box[nb] 97 | f.cklinewrap(&pt, b) 98 | for b.nrune >= 0 && nb < n1-1 && f.box[nb+1].nrune >= 0 && pt.X+b.wid+f.box[nb+1].wid < c { 99 | f.mergebox(nb) 100 | n1-- 101 | b = &f.box[nb] 102 | } 103 | f.advance(&pt, &f.box[nb]) 104 | } 105 | for ; nb < len(f.box); nb++ { 106 | b := &f.box[nb] 107 | f.cklinewrap(&pt, b) 108 | f.advance(&pt, &f.box[nb]) 109 | } 110 | f.LastLineFull = pt.Y >= f.R.Max.Y 111 | } 112 | -------------------------------------------------------------------------------- /draw/frame/internal/frametest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "9fans.net/go/draw" 7 | "9fans.net/go/draw/frame" 8 | ) 9 | 10 | var display *draw.Display 11 | var cols [frame.NCOL]*draw.Image 12 | var f frame.Frame 13 | 14 | func main() { 15 | d, err := draw.Init(nil, "", "frametest", "") 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | display = d 20 | 21 | cols = [...]*draw.Image{ 22 | frame.BACK: d.AllocImageMix(draw.PaleBlueGreen, draw.White), 23 | frame.HIGH: d.AllocImageMix(draw.PaleGreyGreen, draw.PaleGreyGreen), 24 | frame.BORD: d.AllocImageMix(draw.PurpleBlue, draw.PurpleBlue), 25 | frame.TEXT: d.Black, 26 | frame.HTEXT: d.Black, 27 | } 28 | mousectl := d.InitMouse() 29 | kbdctl := d.InitKeyboard() 30 | redraw(true) 31 | 32 | Loop: 33 | for { 34 | d.Flush() 35 | select { 36 | case <-mousectl.Resize: 37 | redraw(true) 38 | case <-mousectl.C: 39 | case r := <-kbdctl.C: 40 | if r == 'q' { 41 | break Loop 42 | } 43 | } 44 | } 45 | } 46 | 47 | func redraw(new bool) { 48 | d := display 49 | if new { 50 | if err := d.Attach(draw.RefMesg); err != nil { 51 | log.Fatalf("can't reattach to window: %v", err) 52 | } 53 | } 54 | d.Image.Draw(d.Image.R, cols[frame.BACK], display.Opaque, draw.ZP) 55 | d.Image.Border(d.Image.R, 4, cols[frame.BORD], draw.ZP) 56 | f.Clear(false) 57 | f.Init(d.Image.R.Inset(4), d.Font, d.Image, cols[:]) 58 | f.Insert([]rune("hello, world!\n\tthis is the time!\nhi"), 0) 59 | f.Insert([]rune("curl..."), 7) 60 | f.Insert([]rune("EOF"), f.NumChars) 61 | } 62 | -------------------------------------------------------------------------------- /draw/freesubfont.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // Free frees the server resources for the Subfont. Subfonts have a finalizer that 4 | // calls Free automatically, if necessary, for garbage collected Images, but it 5 | // is more efficient to be explicit. 6 | // TODO: Implement the finalizer! 7 | func (f *subfont) Free() { 8 | if f == nil { 9 | return 10 | } 11 | f.Bits.Display.mu.Lock() 12 | defer f.Bits.Display.mu.Unlock() 13 | f.free() 14 | } 15 | 16 | func (f *subfont) free() { 17 | if f == nil { 18 | return 19 | } 20 | f.ref-- 21 | if f.ref > 0 { 22 | return 23 | } 24 | uninstallsubfont(f) 25 | f.Bits.free() 26 | } 27 | -------------------------------------------------------------------------------- /draw/getdefont.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import "bytes" 4 | 5 | func getdefont(d *Display) (*subfont, error) { 6 | return d.readSubfont("*default*", bytes.NewReader(defontdata), nil) 7 | } 8 | -------------------------------------------------------------------------------- /draw/getsubfont.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | func getsubfont(d *Display, name string) (*subfont, error) { 13 | scale, fname := parsefontscale(name) 14 | data, err := ioutil.ReadFile(fname) 15 | if err != nil && strings.HasPrefix(fname, "/mnt/font/") { 16 | data1, err1 := fontPipe(fname[len("/mnt/font/"):]) 17 | if err1 == nil { 18 | data, err = data1, err1 19 | } 20 | } 21 | if err != nil { 22 | fmt.Fprintf(os.Stderr, "getsubfont: %v\n", err) 23 | return nil, err 24 | } 25 | f, err := d.readSubfont(name, bytes.NewReader(data), nil) 26 | if err != nil { 27 | fmt.Fprintf(os.Stderr, "getsubfont: can't read %s: %v\n", fname, err) 28 | } 29 | if scale > 1 { 30 | scalesubfont(f, scale) 31 | } 32 | return f, err 33 | } 34 | 35 | func scalesubfont(f *subfont, scale int) { 36 | r := f.Bits.R 37 | r2 := r 38 | r2.Min.X *= scale 39 | r2.Min.Y *= scale 40 | r2.Max.X *= scale 41 | r2.Max.Y *= scale 42 | 43 | srcn := BytesPerLine(r, f.Bits.Depth) 44 | src := make([]byte, srcn) 45 | dstn := BytesPerLine(r2, f.Bits.Depth) 46 | dst := make([]byte, dstn) 47 | i, err := allocImage(f.Bits.Display, nil, r2, f.Bits.Pix, false, Black, 0, 0) 48 | if err != nil { 49 | log.Fatalf("allocimage: %v", err) 50 | } 51 | for y := r.Min.Y; y < r.Max.Y; y++ { 52 | _, err := f.Bits.unload(Rect(r.Min.X, y, r.Max.X, y+1), src) 53 | if err != nil { 54 | log.Fatalf("unloadimage: %v", err) 55 | } 56 | for i := range dst { 57 | dst[i] = 0 58 | } 59 | pack := 8 / f.Bits.Depth 60 | mask := byte(1<> uint(8-f.Bits.Depth)) & mask 63 | for j := 0; j < scale; j++ { 64 | x2 := x*scale + j 65 | dst[x2/pack] |= v << uint(8-f.Bits.Depth) >> uint((x2%pack)*f.Bits.Depth) 66 | } 67 | } 68 | for j := 0; j < scale; j++ { 69 | i.load(Rect(r2.Min.X, y*scale+j, r2.Max.X, y*scale+j+1), dst) 70 | } 71 | } 72 | f.Bits.free() 73 | f.Bits = i 74 | f.Height *= scale 75 | f.Ascent *= scale 76 | 77 | for j := 0; j < f.N; j++ { 78 | p := &f.Info[j] 79 | p.X *= scale 80 | p.Top *= uint8(scale) 81 | p.Bottom *= uint8(scale) 82 | p.Left *= int8(scale) 83 | p.Width *= uint8(scale) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /draw/icossin.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | /* 4 | * Integer sine and cosine for integral degree argument. 5 | * Tables computed by (sin,cos)(PI*d/180). 6 | */ 7 | var sinus = [91]int16{ 8 | 0, /* 0 */ 9 | 18, /* 1 */ 10 | 36, /* 2 */ 11 | 54, /* 3 */ 12 | 71, /* 4 */ 13 | 89, /* 5 */ 14 | 107, /* 6 */ 15 | 125, /* 7 */ 16 | 143, /* 8 */ 17 | 160, /* 9 */ 18 | 178, /* 10 */ 19 | 195, /* 11 */ 20 | 213, /* 12 */ 21 | 230, /* 13 */ 22 | 248, /* 14 */ 23 | 265, /* 15 */ 24 | 282, /* 16 */ 25 | 299, /* 17 */ 26 | 316, /* 18 */ 27 | 333, /* 19 */ 28 | 350, /* 20 */ 29 | 367, /* 21 */ 30 | 384, /* 22 */ 31 | 400, /* 23 */ 32 | 416, /* 24 */ 33 | 433, /* 25 */ 34 | 449, /* 26 */ 35 | 465, /* 27 */ 36 | 481, /* 28 */ 37 | 496, /* 29 */ 38 | 512, /* 30 */ 39 | 527, /* 31 */ 40 | 543, /* 32 */ 41 | 558, /* 33 */ 42 | 573, /* 34 */ 43 | 587, /* 35 */ 44 | 602, /* 36 */ 45 | 616, /* 37 */ 46 | 630, /* 38 */ 47 | 644, /* 39 */ 48 | 658, /* 40 */ 49 | 672, /* 41 */ 50 | 685, /* 42 */ 51 | 698, /* 43 */ 52 | 711, /* 44 */ 53 | 724, /* 45 */ 54 | 737, /* 46 */ 55 | 749, /* 47 */ 56 | 761, /* 48 */ 57 | 773, /* 49 */ 58 | 784, /* 50 */ 59 | 796, /* 51 */ 60 | 807, /* 52 */ 61 | 818, /* 53 */ 62 | 828, /* 54 */ 63 | 839, /* 55 */ 64 | 849, /* 56 */ 65 | 859, /* 57 */ 66 | 868, /* 58 */ 67 | 878, /* 59 */ 68 | 887, /* 60 */ 69 | 896, /* 61 */ 70 | 904, /* 62 */ 71 | 912, /* 63 */ 72 | 920, /* 64 */ 73 | 928, /* 65 */ 74 | 935, /* 66 */ 75 | 943, /* 67 */ 76 | 949, /* 68 */ 77 | 956, /* 69 */ 78 | 962, /* 70 */ 79 | 968, /* 71 */ 80 | 974, /* 72 */ 81 | 979, /* 73 */ 82 | 984, /* 74 */ 83 | 989, /* 75 */ 84 | 994, /* 76 */ 85 | 998, /* 77 */ 86 | 1002, /* 78 */ 87 | 1005, /* 79 */ 88 | 1008, /* 80 */ 89 | 1011, /* 81 */ 90 | 1014, /* 82 */ 91 | 1016, /* 83 */ 92 | 1018, /* 84 */ 93 | 1020, /* 85 */ 94 | 1022, /* 86 */ 95 | 1023, /* 87 */ 96 | 1023, /* 88 */ 97 | 1024, /* 89 */ 98 | 1024, /* 90 */ 99 | } 100 | 101 | // IntCosSin returns scaled integers representing the cosine and sine 102 | // of the angle deg, measured in integer degrees. 103 | // The values are scaled by ICOSSCALE (1024), so that cos(0) is 1024. 104 | func IntCosSin(deg int) (cos, sin int) { 105 | deg %= 360 106 | if deg < 0 { 107 | deg += 360 108 | } 109 | sinsign := 1 110 | cossign := 1 111 | var stp, ctp int16 112 | ctp = 0 113 | switch deg / 90 { 114 | case 2: 115 | sinsign = -1 116 | cossign = -1 117 | deg -= 180 118 | fallthrough 119 | case 0: 120 | stp = sinus[deg] 121 | ctp = sinus[90-deg] 122 | case 3: 123 | sinsign = -1 124 | cossign = -1 125 | deg -= 180 126 | fallthrough 127 | case 1: 128 | deg = 180 - deg 129 | cossign = -cossign 130 | stp = sinus[deg] 131 | ctp = sinus[90-deg] 132 | } 133 | return cossign * int(ctp), sinsign * int(stp) 134 | } 135 | -------------------------------------------------------------------------------- /draw/init_test.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // Only called during init, so no need to synchronize. 4 | 5 | var theDisplay *Display 6 | 7 | func display() *Display { 8 | if theDisplay == nil { 9 | var err error 10 | theDisplay, err = Init(nil, "", "test", "") 11 | if err != nil { 12 | panic(err) 13 | } 14 | } 15 | return theDisplay 16 | } 17 | -------------------------------------------------------------------------------- /draw/keyboard.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import "log" 4 | 5 | const ( 6 | KeyFn = '\uF000' 7 | 8 | KeyHome = KeyFn | 0x0D 9 | KeyUp = KeyFn | 0x0E 10 | KeyPageUp = KeyFn | 0xF 11 | KeyPrint = KeyFn | 0x10 12 | KeyLeft = KeyFn | 0x11 13 | KeyRight = KeyFn | 0x12 14 | KeyDown = 0x80 15 | KeyView = 0x80 16 | KeyPageDown = KeyFn | 0x13 17 | KeyInsert = KeyFn | 0x14 18 | KeyEnd = KeyFn | 0x18 19 | KeyAlt = KeyFn | 0x15 20 | KeyShift = KeyFn | 0x16 21 | KeyCtl = KeyFn | 0x17 22 | KeyBackspace = 0x08 23 | KeyDelete = 0x7F 24 | KeyEscape = 0x1b 25 | KeyEOF = 0x04 26 | KeyCmd = 0xF100 27 | ) 28 | 29 | // Keyboardctl is the source of keyboard events. 30 | type Keyboardctl struct { 31 | C <-chan rune // Channel on which keyboard characters are delivered. 32 | } 33 | 34 | // InitKeyboard connects to the keyboard and returns a Keyboardctl to listen to it. 35 | func (d *Display) InitKeyboard() *Keyboardctl { 36 | ch := make(chan rune, 20) 37 | go kbdproc(d, ch) 38 | return &Keyboardctl{ch} 39 | } 40 | 41 | func kbdproc(d *Display, ch chan rune) { 42 | for { 43 | r, err := d.conn.ReadKbd() 44 | if err != nil { 45 | log.Fatal("readkbd: ", err) 46 | } 47 | ch <- r 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /draw/loadimage.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import "fmt" 4 | 5 | // Load replaces the specified rectangle in image dst with the data, 6 | // returning the number of bytes copied from data. 7 | // It is an error if data is too small to supply pixels for the entire rectangle. 8 | // 9 | // In data, the pixels are presented one horizontal line at a time, 10 | // starting with the top-left pixel of r. Each scan line starts with a new byte 11 | // in the array, leaving the last byte of the previous line partially empty 12 | // if necessary when i.Depth < 8. Pixels are packed as tightly as possible 13 | // within a line, regardless of the rectangle being extracted. 14 | // Bytes are filled from most to least significant bit order, 15 | // as the x coordinate increases, aligned so that x = r.Min would appear as 16 | // the leftmost pixel of its byte. 17 | // Thus, for depth 1, the pixel at x offset 165 within the rectangle 18 | // will be in a data byte at bit-position 0x04 regardless of the overall 19 | // rectangle: 165 mod 8 equals 5, and 0x80 >> 5 equals 0x04. 20 | func (dst *Image) Load(r Rectangle, data []byte) (n int, err error) { 21 | dst.Display.mu.Lock() 22 | defer dst.Display.mu.Unlock() 23 | return dst.load(r, data) 24 | } 25 | 26 | func (dst *Image) load(r Rectangle, data []byte) (int, error) { 27 | i := dst 28 | chunk := i.Display.bufsize - 64 29 | if !r.In(i.R) { 30 | return 0, fmt.Errorf("loadimage: bad rectangle") 31 | } 32 | bpl := BytesPerLine(r, i.Depth) 33 | n := bpl * r.Dy() 34 | if n > len(data) { 35 | return 0, fmt.Errorf("loadimage: insufficient data") 36 | } 37 | ndata := 0 38 | for r.Max.Y > r.Min.Y { 39 | dy := r.Max.Y - r.Min.Y 40 | if dy*bpl > chunk { 41 | dy = chunk / bpl 42 | } 43 | if dy <= 0 { 44 | return 0, fmt.Errorf("loadimage: image too wide for buffer") 45 | } 46 | n := dy * bpl 47 | a := i.Display.bufimage(21 + n) 48 | a[0] = 'y' 49 | bplong(a[1:], uint32(i.id)) 50 | bplong(a[5:], uint32(r.Min.X)) 51 | bplong(a[9:], uint32(r.Min.Y)) 52 | bplong(a[13:], uint32(r.Max.X)) 53 | bplong(a[17:], uint32(r.Min.Y+dy)) 54 | copy(a[21:], data) 55 | ndata += n 56 | data = data[n:] 57 | r.Min.Y += dy 58 | } 59 | if err := i.Display.flush(false); err != nil { 60 | return ndata, err 61 | } 62 | return ndata, nil 63 | } 64 | -------------------------------------------------------------------------------- /draw/memdraw/arctest.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | 8 | package memdraw 9 | 10 | import ( 11 | "strconv" 12 | "syscall" 13 | 14 | "9fans.net/go/draw" 15 | ) 16 | 17 | func main(argc int, argv []*int8) { 18 | c := draw.Point{208, 871} 19 | a := 441 20 | b := 441 21 | thick := 0 22 | sp := draw.Point{0, 0} 23 | alpha := 51 24 | phi := 3 25 | memimageinit() 26 | 27 | x := allocmemimage(draw.Rect(0, 0, 1000, 1000), draw.CMAP8) 28 | n := strconv.Atoi(argv[1]) 29 | 30 | t0 := nsec() 31 | t0 = nsec() 32 | t0 = nsec() 33 | t1 := nsec() 34 | del := t1 - t0 35 | t0 = nsec() 36 | for i := 0; i < n; i++ { 37 | memarc(x, c, a, b, thick, memblack, sp, alpha, phi, SoverD) 38 | } 39 | t1 = nsec() 40 | print("%lld %lld\n", t1-t0-del, del) 41 | } 42 | 43 | /* extern var drawdebug int = 0 */ 44 | 45 | func rdb() { 46 | } 47 | 48 | func iprint(fmt_ *int8, args ...interface{}) int { 49 | var va va_list 50 | va_start(va, fmt_) 51 | var buf [1024]int8 52 | n := doprint(buf, buf+sizeof(buf), fmt_, va) - buf 53 | va_end(va) 54 | 55 | syscall.Write(1, buf, n) 56 | return 1 57 | } 58 | -------------------------------------------------------------------------------- /draw/memdraw/cload.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | 6 | package memdraw 7 | 8 | import ( 9 | "fmt" 10 | 11 | "9fans.net/go/draw" 12 | ) 13 | 14 | // Compressed image file parameters. 15 | const ( 16 | _NMATCH = 3 /* shortest match possible */ 17 | _NRUN = (_NMATCH + 31) /* longest match possible */ 18 | _NMEM = 1024 /* window size */ 19 | _NDUMP = 128 /* maximum length of dump */ 20 | _NCBLOCK = 6000 /* size of compressed blocks */ 21 | ) 22 | 23 | func cloadmemimage(i *Image, r draw.Rectangle, data []uint8) (int, error) { 24 | if !draw.RectInRect(r, i.R) { 25 | return 0, fmt.Errorf("invalid rectangle") 26 | } 27 | bpl := draw.BytesPerLine(r, i.Depth) 28 | u := data 29 | var mem [_NMEM]byte 30 | memp := mem[:] 31 | y := r.Min.Y 32 | linep := byteaddr(i, draw.Pt(r.Min.X, y)) 33 | linep = linep[:bpl] 34 | for { 35 | if len(linep) == 0 { 36 | y++ 37 | if y == r.Max.Y { 38 | break 39 | } 40 | linep = byteaddr(i, draw.Pt(r.Min.X, y)) 41 | linep = linep[:bpl] 42 | } 43 | if len(u) == 0 { /* buffer too small */ 44 | return len(data) - len(u), fmt.Errorf("buffer too small") 45 | } 46 | c := u[0] 47 | u = u[1:] 48 | if c >= 128 { 49 | for cnt := c - 128 + 1; cnt != 0; cnt-- { 50 | if len(u) == 0 { /* buffer too small */ 51 | return len(data) - len(u), fmt.Errorf("buffer too small") 52 | } 53 | if len(linep) == 0 { /* phase error */ 54 | return len(data) - len(u), fmt.Errorf("phase error") 55 | } 56 | linep[0] = u[0] 57 | linep = linep[1:] 58 | memp[0] = u[0] 59 | memp = memp[1:] 60 | u = u[1:] 61 | if len(memp) == 0 { 62 | memp = mem[:] 63 | } 64 | } 65 | } else { 66 | if len(u) == 0 { /* short buffer */ 67 | return len(data) - len(u), fmt.Errorf("buffer too small") 68 | } 69 | offs := int(u[0]) + (int(c&3) << 8) + 1 70 | u = u[1:] 71 | var omemp []byte 72 | if δ := len(mem) - len(memp); δ < offs { 73 | omemp = mem[δ+(_NMEM-offs):] 74 | } else { 75 | omemp = mem[δ-offs:] 76 | } 77 | for cnt := (c >> 2) + _NMATCH; cnt != 0; cnt-- { 78 | if len(linep) == 0 { /* phase error */ 79 | return len(data) - len(u), fmt.Errorf("phase error") 80 | } 81 | linep[0] = omemp[0] 82 | linep = linep[1:] 83 | memp[0] = omemp[0] 84 | memp = memp[1:] 85 | omemp = omemp[1:] 86 | if len(omemp) == 0 { 87 | omemp = mem[:] 88 | } 89 | if len(memp) == 0 { 90 | memp = mem[:] 91 | } 92 | } 93 | } 94 | } 95 | return len(data) - len(u), nil 96 | } 97 | -------------------------------------------------------------------------------- /draw/memdraw/conv: -------------------------------------------------------------------------------- 1 | #!/usr/local/plan9/bin/rc 2 | 3 | c2gofmt -r rules.txt -w /usr/local/plan9/src/libmemlayer/*.c /usr/local/plan9/include/memlayer.h 4 | sed -i '' 's/package pkg/package memdraw/g' *.go 5 | goimports -w . 6 | go build -gcflags=-e >[2=1] | grep -v 'but does have' | 9 sort +1 7 | 8 | 9 | -------------------------------------------------------------------------------- /draw/memdraw/draw-stub.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | 6 | package memdraw 7 | 8 | import ( 9 | "9fans.net/go/draw" 10 | ) 11 | 12 | func (i *Image) Draw(r draw.Rectangle, src *Image, sp draw.Point, mask *Image, mp draw.Point, op draw.Op) { 13 | memimagedraw(i, r, src, sp, mask, mp, op) 14 | } 15 | 16 | func memimagedraw(dst *Image, r draw.Rectangle, src *Image, sp draw.Point, mask *Image, mp draw.Point, op draw.Op) { 17 | par := _memimagedrawsetup(dst, r, src, sp, mask, mp, op) 18 | if par == nil { 19 | return 20 | } 21 | _memimagedraw(par) 22 | } 23 | 24 | func FillColor(m *Image, val draw.Color) { 25 | _memfillcolor(m, val) 26 | } 27 | 28 | func pixelbits(m *Image, p draw.Point) uint32 { 29 | return _pixelbits(m, p) 30 | } 31 | -------------------------------------------------------------------------------- /draw/memdraw/hwdraw.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | 6 | package memdraw 7 | 8 | func hwdraw(p *memDrawParam) int { 9 | return 0 /* could not satisfy request */ 10 | } 11 | -------------------------------------------------------------------------------- /draw/memdraw/iprint.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | 6 | package memdraw 7 | 8 | func iprint(format string, args ...interface{}) { 9 | } 10 | -------------------------------------------------------------------------------- /draw/memdraw/lalloc.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | 7 | package memdraw 8 | 9 | import ( 10 | "9fans.net/go/draw" 11 | ) 12 | 13 | var memlalloc_paint *Image 14 | 15 | func LAlloc(s *Screen, screenr draw.Rectangle, refreshfn Refreshfn, refreshptr interface{}, val draw.Color) (*Image, error) { 16 | if memlalloc_paint == nil { 17 | var err error 18 | memlalloc_paint, err = AllocImage(draw.Rect(0, 0, 1, 1), draw.RGBA32) 19 | if err != nil { 20 | return nil, err 21 | } 22 | memlalloc_paint.Flags |= Frepl 23 | memlalloc_paint.Clipr = draw.Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF) 24 | } 25 | 26 | n, err := allocmemimaged(screenr, s.Image.Pix, s.Image.Data, nil) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | l := new(Layer) 32 | l.Screen = s 33 | if refreshfn != nil { 34 | l.save = nil 35 | } else { 36 | var err error 37 | l.save, err = AllocImage(screenr, s.Image.Pix) 38 | if err != nil { 39 | return nil, err 40 | } 41 | /* allocmemimage doesn't initialize memory; this paints save area */ 42 | if val != draw.NoFill { 43 | FillColor(l.save, val) 44 | } 45 | } 46 | l.Refreshfn = refreshfn 47 | l.Refreshptr = nil /* don't set it until we're done */ 48 | l.Screenr = screenr 49 | l.Delta = draw.Pt(0, 0) 50 | 51 | n.Data.ref++ 52 | n.zero = s.Image.zero 53 | n.Width = s.Image.Width 54 | n.Layer = l 55 | 56 | /* start with new window behind all existing ones */ 57 | l.front = s.Rearmost 58 | l.rear = nil 59 | if s.Rearmost != nil { 60 | s.Rearmost.Layer.rear = n 61 | } 62 | s.Rearmost = n 63 | if s.Frontmost == nil { 64 | s.Frontmost = n 65 | } 66 | l.clear = false 67 | 68 | /* now pull new window to front */ 69 | _memltofrontfill(n, val != draw.NoFill) 70 | l.Refreshptr = refreshptr 71 | 72 | /* 73 | * paint with requested color; previously exposed areas are already right 74 | * if this window has backing store, but just painting the whole thing is simplest. 75 | */ 76 | if val != draw.NoFill { 77 | memsetchan(memlalloc_paint, n.Pix) 78 | FillColor(memlalloc_paint, val) 79 | Draw(n, n.R, memlalloc_paint, n.R.Min, nil, n.R.Min, draw.S) 80 | } 81 | return n, nil 82 | } 83 | -------------------------------------------------------------------------------- /draw/memdraw/layerop.go: -------------------------------------------------------------------------------- 1 | package memdraw 2 | 3 | import ( 4 | "9fans.net/go/draw" 5 | ) 6 | 7 | func _layerop(fn func(*Image, draw.Rectangle, draw.Rectangle, interface{}, int), i *Image, r draw.Rectangle, clipr draw.Rectangle, etc interface{}, front *Image) { 8 | RECUR := func(a, b, c, d draw.Point) { 9 | _layerop(fn, i, draw.Rect(a.X, b.Y, c.X, d.Y), clipr, etc, front.Layer.rear) 10 | } 11 | 12 | Top: 13 | if front == i { 14 | /* no one is in front of this part of window; use the screen */ 15 | fn(i.Layer.Screen.Image, r, clipr, etc, 0) 16 | return 17 | } 18 | fr := front.Layer.Screenr 19 | if !draw.RectXRect(r, fr) { 20 | /* r doesn't touch this window; continue on next rearmost */ 21 | /* assert(front && front->layer && front->layer->screen && front->layer->rear); */ 22 | front = front.Layer.rear 23 | goto Top 24 | } 25 | if fr.Max.Y < r.Max.Y { 26 | RECUR(r.Min, fr.Max, r.Max, r.Max) 27 | r.Max.Y = fr.Max.Y 28 | } 29 | if r.Min.Y < fr.Min.Y { 30 | RECUR(r.Min, r.Min, r.Max, fr.Min) 31 | r.Min.Y = fr.Min.Y 32 | } 33 | if fr.Max.X < r.Max.X { 34 | RECUR(fr.Max, r.Min, r.Max, r.Max) 35 | r.Max.X = fr.Max.X 36 | } 37 | if r.Min.X < fr.Min.X { 38 | RECUR(r.Min, r.Min, fr.Min, r.Max) 39 | r.Min.X = fr.Min.X 40 | } 41 | /* r is covered by front, so put in save area */ 42 | fn(i.Layer.save, r, clipr, etc, 1) 43 | } 44 | 45 | /* 46 | * Assumes incoming rectangle has already been clipped to i's logical r and clipr 47 | */ 48 | func _memlayerop(fn func(*Image, draw.Rectangle, draw.Rectangle, interface{}, int), i *Image, screenr draw.Rectangle, clipr draw.Rectangle, etc interface{}) { 49 | l := i.Layer 50 | if !draw.RectClip(&screenr, l.Screenr) { 51 | return 52 | } 53 | if l.clear { 54 | fn(l.Screen.Image, screenr, clipr, etc, 0) 55 | return 56 | } 57 | r := screenr 58 | scr := l.Screen.Image.Clipr 59 | 60 | /* 61 | * Do the piece on the screen 62 | */ 63 | if draw.RectClip(&screenr, scr) { 64 | _layerop(fn, i, screenr, clipr, etc, l.Screen.Frontmost) 65 | } 66 | if draw.RectInRect(r, scr) { 67 | return 68 | } 69 | 70 | /* 71 | * Do the piece off the screen 72 | */ 73 | if !draw.RectXRect(r, scr) { 74 | /* completely offscreen; easy */ 75 | fn(l.save, r, clipr, etc, 1) 76 | return 77 | } 78 | if r.Min.Y < scr.Min.Y { 79 | /* above screen */ 80 | fn(l.save, draw.Rect(r.Min.X, r.Min.Y, r.Max.X, scr.Min.Y), clipr, etc, 1) 81 | r.Min.Y = scr.Min.Y 82 | } 83 | if r.Max.Y > scr.Max.Y { 84 | /* below screen */ 85 | fn(l.save, draw.Rect(r.Min.X, scr.Max.Y, r.Max.X, r.Max.Y), clipr, etc, 1) 86 | r.Max.Y = scr.Max.Y 87 | } 88 | if r.Min.X < scr.Min.X { 89 | /* left of screen */ 90 | fn(l.save, draw.Rect(r.Min.X, r.Min.Y, scr.Min.X, r.Max.Y), clipr, etc, 1) 91 | r.Min.X = scr.Min.X 92 | } 93 | if r.Max.X > scr.Max.X { 94 | /* right of screen */ 95 | fn(l.save, draw.Rect(scr.Max.X, r.Min.Y, r.Max.X, r.Max.Y), clipr, etc, 1) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /draw/memdraw/ldelete.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | 7 | package memdraw 8 | 9 | import "9fans.net/go/draw" 10 | 11 | func LDelete(i *Image) { 12 | l := i.Layer 13 | /* free backing store and disconnect refresh, to make pushback fast */ 14 | Free(l.save) 15 | l.save = nil 16 | l.Refreshptr = nil 17 | memltorear(i) 18 | 19 | /* window is now the rearmost; clean up screen structures and deallocate */ 20 | s := i.Layer.Screen 21 | if s.Fill != nil { 22 | i.Clipr = i.R 23 | Draw(i, i.R, s.Fill, i.R.Min, nil, i.R.Min, draw.S) 24 | } 25 | if l.front != nil { 26 | l.front.Layer.rear = nil 27 | s.Rearmost = l.front 28 | } else { 29 | s.Frontmost = nil 30 | s.Rearmost = nil 31 | } 32 | Free(i) 33 | } 34 | 35 | /* 36 | * Just free the data structures, don't do graphics 37 | */ 38 | func LFree(i *Image) { 39 | l := i.Layer 40 | Free(l.save) 41 | Free(i) 42 | } 43 | 44 | func _memlsetclear(s *Screen) { 45 | for i := s.Rearmost; i != nil; i = i.Layer.front { 46 | l := i.Layer 47 | l.clear = draw.RectInRect(l.Screenr, l.Screen.Image.Clipr) 48 | if l.clear { 49 | for j := l.front; j != nil; j = j.Layer.front { 50 | if draw.RectXRect(l.Screenr, j.Layer.Screenr) { 51 | l.clear = false 52 | break 53 | } 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /draw/memdraw/lhide.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | 7 | /* 8 | * Hide puts that portion of screenr now on the screen into the window's save area. 9 | * Expose puts that portion of screenr now in the save area onto the screen. 10 | * 11 | * Hide and Expose both require that the layer structures in the screen 12 | * match the geometry they are being asked to update, that is, they update the 13 | * save area (hide) or screen (expose) based on what those structures tell them. 14 | * This means they must be called at the correct time during window shuffles. 15 | */ 16 | 17 | package memdraw 18 | 19 | import ( 20 | "9fans.net/go/draw" 21 | ) 22 | 23 | func lhideop(src *Image, screenr draw.Rectangle, clipr draw.Rectangle, etc interface{}, insave int) { 24 | l := etc.(*Layer) 25 | if src != l.save { /* do nothing if src is already in save area */ 26 | r := screenr.Sub(l.Delta) 27 | Draw(l.save, r, src, screenr.Min, nil, screenr.Min, draw.S) 28 | } 29 | } 30 | 31 | func memlhide(i *Image, screenr draw.Rectangle) { 32 | if i.Layer.save == nil { 33 | return 34 | } 35 | if !draw.RectClip(&screenr, i.Layer.Screen.Image.R) { 36 | return 37 | } 38 | _memlayerop(lhideop, i, screenr, screenr, i.Layer) 39 | } 40 | 41 | func lexposeop(dst *Image, screenr draw.Rectangle, clipr draw.Rectangle, etc interface{}, insave int) { 42 | if insave != 0 { /* if dst is save area, don't bother */ 43 | return 44 | } 45 | l := etc.(*Layer) 46 | r := screenr.Sub(l.Delta) 47 | if l.save != nil { 48 | Draw(dst, screenr, l.save, r.Min, nil, r.Min, draw.S) 49 | } else { 50 | l.Refreshfn(dst, r, l.Refreshptr) 51 | } 52 | } 53 | 54 | func memlexpose(i *Image, screenr draw.Rectangle) { 55 | if !draw.RectClip(&screenr, i.Layer.Screen.Image.R) { 56 | return 57 | } 58 | _memlayerop(lexposeop, i, screenr, screenr, i.Layer) 59 | } 60 | -------------------------------------------------------------------------------- /draw/memdraw/lline.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | 7 | package memdraw 8 | 9 | import ( 10 | "9fans.net/go/draw" 11 | ) 12 | 13 | type lline struct { 14 | p0 draw.Point 15 | p1 draw.Point 16 | delta draw.Point 17 | end0 draw.End 18 | end1 draw.End 19 | radius int 20 | sp draw.Point 21 | dstlayer *Layer 22 | src *Image 23 | op draw.Op 24 | } 25 | 26 | func _memline(dst *Image, p0 draw.Point, p1 draw.Point, end0, end1 draw.End, radius int, src *Image, sp draw.Point, clipr draw.Rectangle, op draw.Op) { 27 | if radius < 0 { 28 | return 29 | } 30 | if src.Layer != nil { /* can't draw line with layered source */ 31 | return 32 | } 33 | srcclipped := 0 34 | 35 | Top: 36 | dl := dst.Layer 37 | if dl == nil { 38 | _memimageline(dst, p0, p1, end0, end1, radius, src, sp, clipr, op) 39 | return 40 | } 41 | if srcclipped == 0 { 42 | d := sp.Sub(p0) 43 | if !draw.RectClip(&clipr, src.Clipr.Sub(d)) { 44 | return 45 | } 46 | if src.Flags&Frepl == 0 && !draw.RectClip(&clipr, src.R.Sub(d)) { 47 | return 48 | } 49 | srcclipped = 1 50 | } 51 | 52 | /* dst is known to be a layer */ 53 | p0.X += dl.Delta.X 54 | p0.Y += dl.Delta.Y 55 | p1.X += dl.Delta.X 56 | p1.Y += dl.Delta.Y 57 | clipr.Min.X += dl.Delta.X 58 | clipr.Min.Y += dl.Delta.Y 59 | clipr.Max.X += dl.Delta.X 60 | clipr.Max.Y += dl.Delta.Y 61 | if dl.clear { 62 | dst = dst.Layer.Screen.Image 63 | goto Top 64 | } 65 | 66 | /* XXX */ 67 | /* this is not the correct set of tests */ 68 | /* if(log2[dst->depth] != log2[src->depth] || log2[dst->depth]!=3) */ 69 | /* return; */ 70 | 71 | /* can't use sutherland-cohen clipping because lines are wide */ 72 | r := LineBBox(p0, p1, end0, end1, radius) 73 | /* 74 | * r is now a bounding box for the line; 75 | * use it as a clipping rectangle for subdivision 76 | */ 77 | if !draw.RectClip(&r, clipr) { 78 | return 79 | } 80 | var ll lline 81 | ll.p0 = p0 82 | ll.p1 = p1 83 | ll.end0 = end0 84 | ll.end1 = end1 85 | ll.sp = sp 86 | ll.dstlayer = dst.Layer 87 | ll.src = src 88 | ll.radius = radius 89 | ll.delta = dl.Delta 90 | ll.op = op 91 | _memlayerop(llineop, dst, r, r, &ll) 92 | } 93 | 94 | func llineop(dst *Image, screenr draw.Rectangle, clipr draw.Rectangle, etc interface{}, insave int) { 95 | ll := etc.(*lline) 96 | if insave != 0 && ll.dstlayer.save == nil { 97 | return 98 | } 99 | if !draw.RectClip(&clipr, screenr) { 100 | return 101 | } 102 | var p0 draw.Point 103 | var p1 draw.Point 104 | if insave != 0 { 105 | p0 = ll.p0.Sub(ll.delta) 106 | p1 = ll.p1.Sub(ll.delta) 107 | clipr = clipr.Sub(ll.delta) 108 | } else { 109 | p0 = ll.p0 110 | p1 = ll.p1 111 | } 112 | _memline(dst, p0, p1, ll.end0, ll.end1, ll.radius, ll.src, ll.sp, clipr, ll.op) 113 | } 114 | 115 | func Line(dst *Image, p0 draw.Point, p1 draw.Point, end0, end1 draw.End, radius int, src *Image, sp draw.Point, op draw.Op) { 116 | _memline(dst, p0, p1, end0, end1, radius, src, sp, dst.Clipr, op) 117 | } 118 | -------------------------------------------------------------------------------- /draw/memdraw/lload.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | 7 | package memdraw 8 | 9 | import "9fans.net/go/draw" 10 | 11 | func Load(dst *Image, r draw.Rectangle, data []uint8, iscompressed bool) (int, error) { 12 | loadfn := loadmemimage 13 | if iscompressed { 14 | loadfn = cloadmemimage 15 | } 16 | 17 | Top: 18 | dl := dst.Layer 19 | if dl == nil { 20 | return loadfn(dst, r, data) 21 | } 22 | 23 | /* 24 | * Convert to screen coordinates. 25 | */ 26 | lr := r 27 | r.Min.X += dl.Delta.X 28 | r.Min.Y += dl.Delta.Y 29 | r.Max.X += dl.Delta.X 30 | r.Max.Y += dl.Delta.Y 31 | dx := dl.Delta.X & (7 / dst.Depth) 32 | if dl.clear && dx == 0 { 33 | dst = dl.Screen.Image 34 | goto Top 35 | } 36 | 37 | /* 38 | * dst is an obscured layer or data is unaligned 39 | */ 40 | if dl.save != nil && dx == 0 { 41 | n, err := loadfn(dl.save, lr, data) 42 | if n > 0 { 43 | memlexpose(dst, r) 44 | } 45 | return n, err 46 | } 47 | tmp, err := AllocImage(lr, dst.Pix) 48 | if err != nil { 49 | return 0, err 50 | } 51 | n, err := loadfn(tmp, lr, data) 52 | Draw(dst, lr, tmp, lr.Min, nil, lr.Min, draw.S) 53 | Free(tmp) 54 | return n, err 55 | } 56 | -------------------------------------------------------------------------------- /draw/memdraw/load.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | 6 | package memdraw 7 | 8 | import ( 9 | "fmt" 10 | 11 | "9fans.net/go/draw" 12 | ) 13 | 14 | func loadmemimage(i *Image, r draw.Rectangle, data []uint8) (int, error) { 15 | if !draw.RectInRect(r, i.R) { 16 | return 0, fmt.Errorf("invalid rectangle") 17 | } 18 | l := draw.BytesPerLine(r, i.Depth) 19 | if len(data) < l*r.Dy() { 20 | return 0, fmt.Errorf("buffer too small") 21 | } 22 | ndata := l * r.Dy() 23 | q := byteaddr(i, r.Min) 24 | mx := 7 / i.Depth 25 | lpart := (r.Min.X & mx) * i.Depth 26 | rpart := (r.Max.X & mx) * i.Depth 27 | m := uint8(0xFF) >> lpart 28 | var y int 29 | /* may need to do bit insertion on edges */ 30 | if l == 1 { /* all in one byte */ 31 | if rpart != 0 { 32 | m ^= 0xFF >> rpart 33 | } 34 | for y = r.Min.Y; y < r.Max.Y; y++ { 35 | q[0] ^= (data[0] ^ q[0]) & m 36 | q = q[i.Width*4:] 37 | data = data[1:] 38 | } 39 | return ndata, nil 40 | } 41 | if lpart == 0 && rpart == 0 { /* easy case */ 42 | for y = r.Min.Y; y < r.Max.Y; y++ { 43 | copy(q[:l], data[:l]) 44 | q = q[i.Width*4:] 45 | data = data[l:] 46 | } 47 | return ndata, nil 48 | } 49 | mr := uint8(0xFF) ^ (0xFF >> rpart) 50 | if lpart != 0 && rpart == 0 { 51 | for y = r.Min.Y; y < r.Max.Y; y++ { 52 | q[0] ^= (data[0] ^ q[0]) & m 53 | if l > 1 { 54 | copy(q[1:l], data[1:l]) 55 | } 56 | q = q[i.Width*4:] 57 | data = data[l:] 58 | } 59 | return ndata, nil 60 | } 61 | if lpart == 0 && rpart != 0 { 62 | for y = r.Min.Y; y < r.Max.Y; y++ { 63 | if l > 1 { 64 | copy(q[:l-1], data[:l-1]) 65 | } 66 | q[l-1] ^= (data[l-1] ^ q[l-1]) & mr 67 | q = q[i.Width*4:] 68 | data = data[l:] 69 | } 70 | return ndata, nil 71 | } 72 | for y = r.Min.Y; y < r.Max.Y; y++ { 73 | q[0] ^= (data[0] ^ q[0]) & m 74 | if l > 2 { 75 | copy(q[1:l-1], data[1:l-1]) 76 | } 77 | q[l-1] ^= (data[l-1] ^ q[l-1]) & mr 78 | q = q[i.Width*4:] 79 | data = data[l:] 80 | } 81 | return ndata, nil 82 | } 83 | -------------------------------------------------------------------------------- /draw/memdraw/lorigin.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | 7 | package memdraw 8 | 9 | import ( 10 | "9fans.net/go/draw" 11 | ) 12 | 13 | /* 14 | * Place i so i->r.min = log, i->layer->screenr.min == scr. 15 | */ 16 | func LOrigin(i *Image, log draw.Point, scr draw.Point) (int, error) { 17 | l := i.Layer 18 | s := l.Screen 19 | oldr := l.Screenr 20 | newr := draw.Rect(scr.X, scr.Y, scr.X+oldr.Dx(), scr.Y+oldr.Dy()) 21 | eqscr := scr == oldr.Min 22 | eqlog := log == i.R.Min 23 | if eqscr && eqlog { 24 | return 0, nil 25 | } 26 | var nsave *Image 27 | if !eqlog && l.save != nil { 28 | var err error 29 | nsave, err = AllocImage(draw.Rect(log.X, log.Y, log.X+oldr.Dx(), log.Y+oldr.Dy()), i.Pix) 30 | if err != nil { 31 | return 0, err 32 | } 33 | } 34 | 35 | /* 36 | * Bring it to front and move logical coordinate system. 37 | */ 38 | memltofront(i) 39 | wasclear := l.clear 40 | if nsave != nil { 41 | if !wasclear { 42 | nsave.Draw(nsave.R, l.save, l.save.R.Min, nil, draw.Pt(0, 0), draw.S) 43 | } 44 | Free(l.save) 45 | l.save = nsave 46 | } 47 | delta := log.Sub(i.R.Min) 48 | i.R = i.R.Add(delta) 49 | i.Clipr = i.Clipr.Add(delta) 50 | l.Delta = l.Screenr.Min.Sub(i.R.Min) 51 | if eqscr { 52 | return 0, nil 53 | } 54 | 55 | /* 56 | * To clean up old position, make a shadow window there, don't paint it, 57 | * push it behind this one, and (later) delete it. Because the refresh function 58 | * for this fake window is a no-op, this will cause no graphics action except 59 | * to restore the background and expose the windows previously hidden. 60 | */ 61 | shad, err := LAlloc(s, oldr, LNoRefresh, nil, draw.NoFill) 62 | if err != nil { 63 | return 0, err 64 | } 65 | s.Frontmost = i 66 | if s.Rearmost == i { 67 | s.Rearmost = shad 68 | } else { 69 | l.rear.Layer.front = shad 70 | } 71 | shad.Layer.front = i 72 | shad.Layer.rear = l.rear 73 | l.rear = shad 74 | l.front = nil 75 | shad.Layer.clear = false 76 | 77 | /* 78 | * Shadow is now holding down the fort at the old position. 79 | * Move the window and hide things obscured by new position. 80 | */ 81 | for t := l.rear.Layer.rear; t != nil; t = t.Layer.rear { 82 | x := newr 83 | overlap := draw.RectClip(&x, t.Layer.Screenr) 84 | if overlap { 85 | memlhide(t, x) 86 | t.Layer.clear = false 87 | } 88 | } 89 | l.Screenr = newr 90 | l.Delta = scr.Sub(i.R.Min) 91 | l.clear = draw.RectInRect(newr, l.Screen.Image.Clipr) 92 | 93 | /* 94 | * Everything's covered. Copy to new position and delete shadow window. 95 | */ 96 | if wasclear { 97 | Draw(s.Image, newr, s.Image, oldr.Min, nil, draw.Pt(0, 0), draw.S) 98 | } else { 99 | memlexpose(i, newr) 100 | } 101 | LDelete(shad) 102 | 103 | return 1, nil 104 | } 105 | 106 | func LNoRefresh(l *Image, r draw.Rectangle, v interface{}) { 107 | } 108 | -------------------------------------------------------------------------------- /draw/memdraw/lsetrefresh.go: -------------------------------------------------------------------------------- 1 | package memdraw 2 | 3 | func LSetRefresh(i *Image, fn Refreshfn, ptr interface{}) bool { 4 | l := i.Layer 5 | if l.Refreshfn != nil && fn != nil { /* just change functions */ 6 | l.Refreshfn = fn 7 | l.Refreshptr = ptr 8 | return true 9 | } 10 | 11 | if l.Refreshfn == nil { /* is using backup image; just free it */ 12 | Free(l.save) 13 | l.save = nil 14 | l.Refreshfn = fn 15 | l.Refreshptr = ptr 16 | return true 17 | } 18 | 19 | var err error 20 | l.save, err = AllocImage(i.R, i.Pix) 21 | if err != nil { 22 | return false 23 | } 24 | /* easiest way is just to update the entire save area */ 25 | l.Refreshfn(i, i.R, l.Refreshptr) 26 | l.Refreshfn = nil 27 | l.Refreshptr = nil 28 | return true 29 | } 30 | -------------------------------------------------------------------------------- /draw/memdraw/ltofront.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | 7 | package memdraw 8 | 9 | import "9fans.net/go/draw" 10 | 11 | /* 12 | * Pull i towards top of screen, just behind front 13 | */ 14 | func _memltofront(i *Image, front *Image, fill bool) { 15 | l := i.Layer 16 | s := l.Screen 17 | for l.front != front { 18 | f := l.front 19 | x := l.Screenr 20 | overlap := draw.RectClip(&x, f.Layer.Screenr) 21 | if overlap { 22 | memlhide(f, x) 23 | f.Layer.clear = false 24 | } 25 | /* swap l and f in screen's list */ 26 | ff := f.Layer.front 27 | rr := l.rear 28 | if ff == nil { 29 | s.Frontmost = i 30 | } else { 31 | ff.Layer.rear = i 32 | } 33 | if rr == nil { 34 | s.Rearmost = f 35 | } else { 36 | rr.Layer.front = f 37 | } 38 | l.front = ff 39 | l.rear = f 40 | f.Layer.front = i 41 | f.Layer.rear = rr 42 | if overlap && fill { 43 | memlexpose(i, x) 44 | } 45 | } 46 | } 47 | 48 | func _memltofrontfill(i *Image, fill bool) { 49 | _memltofront(i, nil, fill) 50 | _memlsetclear(i.Layer.Screen) 51 | } 52 | 53 | func memltofront(i *Image) { 54 | _memltofront(i, nil, true) 55 | _memlsetclear(i.Layer.Screen) 56 | } 57 | 58 | func LToFrontN(ip []*Image, n int) { 59 | if n == 0 { 60 | return 61 | } 62 | var front *Image 63 | for { 64 | if n--; n < 0 { 65 | break 66 | } 67 | i := ip[0] 68 | ip = ip[1:] 69 | _memltofront(i, front, true) 70 | front = i 71 | } 72 | s := front.Layer.Screen 73 | _memlsetclear(s) 74 | } 75 | -------------------------------------------------------------------------------- /draw/memdraw/ltorear.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | 7 | package memdraw 8 | 9 | import "9fans.net/go/draw" 10 | 11 | func _memltorear(i *Image, rear *Image) { 12 | l := i.Layer 13 | s := l.Screen 14 | for l.rear != rear { 15 | r := l.rear 16 | x := l.Screenr 17 | overlap := draw.RectClip(&x, r.Layer.Screenr) 18 | if overlap { 19 | memlhide(i, x) 20 | l.clear = false 21 | } 22 | /* swap l and r in screen's list */ 23 | rr := r.Layer.rear 24 | f := l.front 25 | if rr == nil { 26 | s.Rearmost = i 27 | } else { 28 | rr.Layer.front = i 29 | } 30 | if f == nil { 31 | s.Frontmost = r 32 | } else { 33 | f.Layer.rear = r 34 | } 35 | l.rear = rr 36 | l.front = r 37 | r.Layer.rear = i 38 | r.Layer.front = f 39 | if overlap { 40 | memlexpose(r, x) 41 | } 42 | } 43 | } 44 | 45 | func memltorear(i *Image) { 46 | _memltorear(i, nil) 47 | _memlsetclear(i.Layer.Screen) 48 | } 49 | 50 | func LToRearN(ip []*Image, n int) { 51 | if n == 0 { 52 | return 53 | } 54 | var rear *Image 55 | for { 56 | n-- 57 | if n < 0 { 58 | break 59 | } 60 | i := ip[0] 61 | ip = ip[1:] 62 | _memltorear(i, rear) 63 | rear = i 64 | } 65 | s := rear.Layer.Screen 66 | _memlsetclear(s) 67 | } 68 | -------------------------------------------------------------------------------- /draw/memdraw/lunload.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | // #include 6 | 7 | package memdraw 8 | 9 | import ( 10 | "fmt" 11 | 12 | "9fans.net/go/draw" 13 | ) 14 | 15 | func Unload(src *Image, r draw.Rectangle, data []uint8) (int, error) { 16 | 17 | Top: 18 | dl := src.Layer 19 | if dl == nil { 20 | return unloadmemimage(src, r, data) 21 | } 22 | 23 | /* 24 | * Convert to screen coordinates. 25 | */ 26 | lr := r 27 | r.Min.X += dl.Delta.X 28 | r.Min.Y += dl.Delta.Y 29 | r.Max.X += dl.Delta.X 30 | r.Max.Y += dl.Delta.Y 31 | dx := dl.Delta.X & (7 / src.Depth) 32 | if dl.clear && dx == 0 { 33 | src = dl.Screen.Image 34 | goto Top 35 | } 36 | 37 | /* 38 | * src is an obscured layer or data is unaligned 39 | */ 40 | if dl.save != nil && dx == 0 { 41 | if dl.Refreshfn != nil { 42 | return 0, fmt.Errorf("window not Refbackup") /* can't unload window if it's not Refbackup */ 43 | } 44 | if len(data) > 0 { 45 | memlhide(src, r) 46 | } 47 | return unloadmemimage(dl.save, lr, data) 48 | } 49 | tmp, err := AllocImage(lr, src.Pix) 50 | if err != nil { 51 | return 0, err 52 | } 53 | Draw(tmp, lr, src, lr.Min, nil, lr.Min, draw.S) 54 | n, err := unloadmemimage(tmp, lr, data) 55 | Free(tmp) 56 | return n, err 57 | } 58 | -------------------------------------------------------------------------------- /draw/memdraw/memlayer.h.go: -------------------------------------------------------------------------------- 1 | package memdraw 2 | 3 | import "9fans.net/go/draw" 4 | 5 | type Refreshfn func(*Image, draw.Rectangle, interface{}) 6 | 7 | type Screen struct { 8 | Frontmost *Image /* frontmost layer on screen */ 9 | Rearmost *Image /* rearmost layer on screen */ 10 | Image *Image /* upon which all layers are drawn */ 11 | Fill *Image /* if non-zero, picture to use when repainting */ 12 | } 13 | 14 | type Layer struct { 15 | Screenr draw.Rectangle /* true position of layer on screen */ 16 | Delta draw.Point /* add delta to go from image coords to screen */ 17 | Screen *Screen /* screen this layer belongs to */ 18 | front *Image /* window in front of this one */ 19 | rear *Image /* window behind this one*/ 20 | clear bool /* layer is fully visible */ 21 | save *Image /* save area for obscured parts */ 22 | Refreshfn Refreshfn /* function to call to refresh obscured parts if save==nil */ 23 | Refreshptr interface{} /* argument to refreshfn */ 24 | } 25 | -------------------------------------------------------------------------------- /draw/memdraw/mkcmap.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | 6 | // +build ignore 7 | 8 | /* 9 | struct Memcmap 10 | { 11 | uchar cmap2rgb[3*256]; 12 | uchar rgb2cmap[16*16*16]; 13 | }; 14 | */ 15 | 16 | package memdraw 17 | 18 | var mkcmap_def Memcmap 19 | 20 | func mkcmap() *Memcmap { 21 | var r int 22 | var g int 23 | var b int 24 | for i := 0; i < 256; i++ { 25 | rgb := cmap2rgb(i) 26 | r = (rgb >> 16) & 0xff 27 | g = (rgb >> 8) & 0xff 28 | b = rgb & 0xff 29 | mkcmap_def.cmap2rgb[3*i] = r 30 | mkcmap_def.cmap2rgb[3*i+1] = g 31 | mkcmap_def.cmap2rgb[3*i+2] = b 32 | } 33 | 34 | for r = 0; r < 16; r++ { 35 | for g = 0; g < 16; g++ { 36 | for b = 0; b < 16; b++ { 37 | mkcmap_def.rgb2cmap[r*16*16+g*16+b] = rgb2cmap(r*0x11, g*0x11, b*0x11) 38 | } 39 | } 40 | } 41 | return &mkcmap_def 42 | } 43 | 44 | func main(argc int, argv **int8) { 45 | inferno := 0 46 | switch ARGBEGIN { 47 | case 'i': 48 | inferno = 1 49 | } 50 | 51 | memimageinit() 52 | c := mkcmap() 53 | if inferno == 0 { 54 | print("#include \n#include \n") 55 | } else { 56 | print("#include \"lib9.h\"\n") 57 | } 58 | print("#include \n") 59 | print("#include \n\n") 60 | print("static Memcmap def = {\n") 61 | print("/* cmap2rgb */ {\n") 62 | var i int 63 | var j int 64 | for i = 0; i < sizeof(c.cmap2rgb); { 65 | print("\t") 66 | for j = 0; j < 16; func() { j++; i++ }() { 67 | print("0x%2.2x,", c.cmap2rgb[i]) 68 | } 69 | print("\n") 70 | } 71 | print("},\n") 72 | print("/* rgb2cmap */ {\n") 73 | for i = 0; i < sizeof(c.rgb2cmap); { 74 | print("\t") 75 | for j = 0; j < 16; func() { j++; i++ }() { 76 | print("0x%2.2x,", c.rgb2cmap[i]) 77 | } 78 | print("\n") 79 | } 80 | print("}\n") 81 | print("};\n") 82 | print("Memcmap *memdefcmap = &def;\n") 83 | print("void _memmkcmap(void){}\n") 84 | exits(0) 85 | } 86 | -------------------------------------------------------------------------------- /draw/memdraw/openmemsubfont.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | 6 | package memdraw 7 | 8 | import ( 9 | "fmt" 10 | "io" 11 | "os" 12 | 13 | "9fans.net/go/draw" 14 | ) 15 | 16 | func openmemsubfont(name string) (*subfont, error) { 17 | f, err := os.Open(name) 18 | if err != nil { 19 | return nil, err 20 | } 21 | defer f.Close() 22 | 23 | i, err := readmemimage(f) 24 | if err != nil { 25 | return nil, err 26 | } 27 | var hdr [3*12 + 4]byte 28 | if _, err := io.ReadFull(f, hdr[:3*12]); err != nil { 29 | Free(i) 30 | return nil, fmt.Errorf("openmemsubfont: header read error: %v", err) 31 | } 32 | n := atoi(hdr[:1*12]) 33 | p := make([]byte, 6*(n+1)) 34 | if _, err := io.ReadFull(f, p[:6*(n+1)]); err != nil { 35 | Free(i) 36 | return nil, fmt.Errorf("openmemsubfont: fontchar read error: %v", err) 37 | } 38 | 39 | fc := make([]draw.Fontchar, n+1) 40 | unpackinfo(fc, p, n) 41 | sf := allocmemsubfont(name, n, atoi(hdr[1*12:2*12]), atoi(hdr[2*12:3*12]), fc, i) 42 | return sf, nil 43 | } 44 | 45 | func unpackinfo(fc []draw.Fontchar, p []byte, n int) { 46 | for j := 0; j <= n; j++ { 47 | fc[j].X = int(p[0]) | int(p[1])<<8 48 | fc[j].Top = uint8(p[2]) 49 | fc[j].Bottom = uint8(p[3]) 50 | fc[j].Left = int8(p[4]) 51 | fc[j].Width = uint8(p[5]) 52 | p = p[6:] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /draw/memdraw/poly.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | 6 | package memdraw 7 | 8 | import "9fans.net/go/draw" 9 | 10 | func Poly(dst *Image, vert []draw.Point, end0, end1 draw.End, radius int, src *Image, sp draw.Point, op draw.Op) { 11 | nvert := len(vert) 12 | if nvert < 2 { 13 | return 14 | } 15 | d := sp.Sub(vert[0]) 16 | for i := 1; i < nvert; i++ { 17 | e1 := draw.EndDisc 18 | e0 := e1 19 | if i == 1 { 20 | e0 = end0 21 | } 22 | if i == nvert-1 { 23 | e1 = end1 24 | } 25 | Line(dst, vert[i-1], vert[i], e0, e1, radius, src, d.Add(vert[i-1]), op) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /draw/memdraw/read.go: -------------------------------------------------------------------------------- 1 | package memdraw 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | 8 | "9fans.net/go/draw" 9 | ) 10 | 11 | func readmemimage(fd *os.File) (*Image, error) { 12 | var hdr [5*12 + 1]byte 13 | if _, err := io.ReadFull(fd, hdr[:11]); err != nil { 14 | return nil, fmt.Errorf("readimage: %v", err) 15 | } 16 | if string(hdr[:11]) == "compressed\n" { 17 | return creadmemimage(fd) 18 | } 19 | if _, err := io.ReadFull(fd, hdr[11:5*12]); err != nil { 20 | return nil, fmt.Errorf("readimage: %v", err) 21 | } 22 | 23 | /* 24 | * distinguish new channel descriptor from old ldepth. 25 | * channel descriptors have letters as well as numbers, 26 | * while ldepths are a single digit formatted as %-11d. 27 | */ 28 | new := false 29 | var m int 30 | for m = 0; m < 10; m++ { 31 | if hdr[m] != ' ' { 32 | new = true 33 | break 34 | } 35 | } 36 | if hdr[11] != ' ' { 37 | return nil, fmt.Errorf("readimage: bad format") 38 | } 39 | var chan_ draw.Pix 40 | if new { 41 | s := string(hdr[:11]) 42 | var err error 43 | chan_, err = draw.ParsePix(s) 44 | if err != nil { 45 | return nil, fmt.Errorf("readimage: %v", err) 46 | } 47 | } else { 48 | ldepth := (int(hdr[10])) - '0' 49 | if ldepth < 0 || ldepth > 3 { 50 | return nil, fmt.Errorf("readimage: bad ldepth %d", ldepth) 51 | } 52 | chan_ = ldepthToPix[ldepth] 53 | } 54 | var r draw.Rectangle 55 | 56 | r.Min.X = atoi(hdr[1*12 : 2*12]) 57 | r.Min.Y = atoi(hdr[2*12 : 3*12]) 58 | r.Max.X = atoi(hdr[3*12 : 4*12]) 59 | r.Max.Y = atoi(hdr[4*12 : 5*12]) 60 | if r.Min.X > r.Max.X || r.Min.Y > r.Max.Y { 61 | return nil, fmt.Errorf("readimage: bad rectangle") 62 | } 63 | 64 | miny := r.Min.Y 65 | maxy := r.Max.Y 66 | 67 | l := draw.BytesPerLine(r, chan_.Depth()) 68 | i, err := AllocImage(r, chan_) 69 | if err != nil { 70 | return nil, err 71 | } 72 | chunk := 32 * 1024 73 | if chunk < l { 74 | chunk = l 75 | } 76 | tmp := make([]byte, chunk) 77 | for maxy > miny { 78 | dy := maxy - miny 79 | if dy*l > chunk { 80 | dy = chunk / l 81 | } 82 | if dy <= 0 { 83 | Free(i) 84 | return nil, fmt.Errorf("readmemimage: image too wide for buffer") 85 | } 86 | n := dy * l 87 | if _, err = io.ReadFull(fd, tmp[:n]); err != nil { 88 | Free(i) 89 | return nil, fmt.Errorf("readmemimage: %v", err) 90 | } 91 | if !new { 92 | for j := 0; j < chunk; j++ { 93 | tmp[j] ^= 0xFF 94 | } 95 | } 96 | 97 | if _, err := loadmemimage(i, draw.Rect(r.Min.X, miny, r.Max.X, miny+dy), tmp[:n]); err != nil { 98 | Free(i) 99 | return nil, err 100 | } 101 | miny += dy 102 | } 103 | return i, nil 104 | } 105 | -------------------------------------------------------------------------------- /draw/memdraw/string.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | 6 | package memdraw 7 | 8 | import ( 9 | "unicode/utf8" 10 | 11 | "9fans.net/go/draw" 12 | ) 13 | 14 | func memimagestring(b *Image, p draw.Point, color *Image, cp draw.Point, f *subfont, s []byte) draw.Point { 15 | var width int 16 | for ; len(s) > 0; func() { p.X += width; cp.X += width }() { 17 | c := rune(s[0]) 18 | width = 0 19 | if c < utf8.RuneSelf { 20 | s = s[1:] 21 | } else { 22 | var w int 23 | c, w = utf8.DecodeRune(s) 24 | s = s[w:] 25 | } 26 | if int(c) >= f.n { 27 | continue 28 | } 29 | i := &f.info[c] 30 | width = int(i.Width) 31 | Draw(b, draw.Rect(p.X+int(i.Left), p.Y+int(i.Top), p.X+int(i.Left)+(f.info[c+1].X-i.X), p.Y+int(i.Bottom)), color, cp, f.bits, draw.Pt(i.X, int(i.Top)), draw.SoverD) 32 | } 33 | return p 34 | } 35 | 36 | func memsubfontwidth(f *subfont, s []byte) draw.Point { 37 | p := draw.Pt(0, int(f.height)) 38 | var width int 39 | for ; len(s) > 0; p.X += width { 40 | c := rune(s[0]) 41 | width = 0 42 | if c < utf8.RuneSelf { 43 | s = s[1:] 44 | } else { 45 | var w int 46 | c, w = utf8.DecodeRune(s) 47 | s = s[w:] 48 | } 49 | if int(c) >= f.n { 50 | continue 51 | } 52 | i := &f.info[c] 53 | width = int(i.Width) 54 | } 55 | return p 56 | } 57 | -------------------------------------------------------------------------------- /draw/memdraw/subfont.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | 6 | package memdraw 7 | 8 | import "9fans.net/go/draw" 9 | 10 | func allocmemsubfont(name string, n int, height int, ascent int, info []draw.Fontchar, i *Image) *subfont { 11 | f := new(subfont) 12 | f.n = n 13 | f.height = uint8(height) 14 | f.ascent = int8(ascent) 15 | f.info = info 16 | f.bits = i 17 | f.name = name 18 | return f 19 | } 20 | 21 | func freememsubfont(f *subfont) { 22 | if f == nil { 23 | return 24 | } 25 | Free(f.bits) 26 | } 27 | -------------------------------------------------------------------------------- /draw/memdraw/unload.go: -------------------------------------------------------------------------------- 1 | // #include 2 | // #include 3 | // #include 4 | // #include 5 | 6 | package memdraw 7 | 8 | import ( 9 | "fmt" 10 | 11 | "9fans.net/go/draw" 12 | ) 13 | 14 | func unloadmemimage(i *Image, r draw.Rectangle, data []uint8) (int, error) { 15 | if !draw.RectInRect(r, i.R) { 16 | return 0, fmt.Errorf("invalid rectangle") 17 | } 18 | l := draw.BytesPerLine(r, i.Depth) 19 | if len(data) < l*r.Dy() { 20 | return 0, fmt.Errorf("buffer too small") 21 | } 22 | ndata := l * r.Dy() 23 | q := byteaddr(i, r.Min) 24 | for y := r.Min.Y; y < r.Max.Y; y++ { 25 | copy(data[:l], q[:l]) 26 | q = q[i.Width*4:] 27 | data = data[l:] 28 | } 29 | return ndata, nil 30 | } 31 | -------------------------------------------------------------------------------- /draw/mkfont.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | /* 4 | * Cobble fake font using existing subfont 5 | */ 6 | 7 | // MakeFont creates a Font from an existing subfont. The first character of the 8 | // subfont will be rendered with rune value min. 9 | func (subfont *subfont) MakeFont(min rune) *Font { 10 | font := &Font{ 11 | Display: subfont.Bits.Display, 12 | Name: "", 13 | namespec: "", 14 | Height: subfont.Height, 15 | Ascent: subfont.Ascent, 16 | Scale: 1, 17 | cache: make([]cacheinfo, _NFCACHE+_NFLOOK), 18 | subf: make([]cachesubf, _NFSUBF), 19 | age: 1, 20 | sub: []*cachefont{{ 21 | min: min, 22 | max: min + rune(subfont.N) - 1, 23 | }}, 24 | } 25 | font.subf[0].cf = font.sub[0] 26 | font.subf[0].f = subfont 27 | return font 28 | } 29 | -------------------------------------------------------------------------------- /draw/readimage.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | // ReadImage creates an image from data contained in r. 10 | // (See the package documentation for the image file format.) 11 | // The returned image is allocated using AllocImage. 12 | func (d *Display) ReadImage(r io.Reader) (*Image, error) { 13 | d.mu.Lock() 14 | defer d.mu.Unlock() 15 | return d.readImage(r) 16 | } 17 | 18 | func (d *Display) readImage(rd io.Reader) (*Image, error) { 19 | fd := rd 20 | hdr := make([]byte, 5*12) 21 | 22 | _, err := io.ReadFull(fd, hdr[:11]) 23 | if err != nil { 24 | return nil, fmt.Errorf("reading image header: %v", err) 25 | } 26 | if string(hdr[:11]) == "compressed\n" { 27 | return d.creadimage(rd) 28 | } 29 | 30 | _, err = io.ReadFull(fd, hdr[11:]) 31 | if err != nil { 32 | return nil, fmt.Errorf("reading image header: %v", err) 33 | } 34 | 35 | chunk := 8192 36 | if d != nil { 37 | chunk = d.bufsize - 32 // a little room for header 38 | } 39 | 40 | /* 41 | * distinguish new channel descriptor from old ldepth. 42 | * channel descriptors have letters as well as numbers, 43 | * while ldepths are a single digit formatted as %-11d. 44 | */ 45 | new := false 46 | for m := 0; m < 10; m++ { 47 | if hdr[m] != ' ' { 48 | new = true 49 | break 50 | } 51 | } 52 | if hdr[11] != ' ' { 53 | return nil, fmt.Errorf("readimage: bad format") 54 | } 55 | var pix Pix 56 | if new { 57 | pix, err = ParsePix(strings.TrimSpace(string(hdr[:12]))) 58 | if err != nil { 59 | return nil, fmt.Errorf("readimage: %v", err) 60 | } 61 | } else { 62 | ldepth := int(hdr[10]) - '0' 63 | if ldepth < 0 || ldepth > 3 { 64 | return nil, fmt.Errorf("readimage: bad ldepth %d", ldepth) 65 | } 66 | pix = ldepthToPix[ldepth] 67 | } 68 | r := ator(hdr[1*12:]) 69 | if r.Min.X > r.Max.X || r.Min.Y > r.Max.Y { 70 | return nil, fmt.Errorf("readimage: bad rectangle") 71 | } 72 | 73 | miny := r.Min.Y 74 | maxy := r.Max.Y 75 | 76 | l := BytesPerLine(r, pix.Depth()) 77 | var i *Image 78 | if d != nil { 79 | i, err = d.allocImage(r, pix, false, 0) 80 | if err != nil { 81 | return nil, err 82 | } 83 | } else { 84 | i = &Image{R: r, Pix: pix, Depth: pix.Depth()} 85 | } 86 | 87 | tmp := make([]byte, chunk) 88 | if tmp == nil { 89 | goto Err 90 | } 91 | for maxy > miny { 92 | dy := maxy - miny 93 | if dy*l > chunk { 94 | dy = chunk / l 95 | } 96 | if dy <= 0 { 97 | err = fmt.Errorf("readimage: image too wide for buffer") 98 | goto Err 99 | } 100 | n := dy * l 101 | if _, err = io.ReadFull(fd, tmp[:n]); err != nil { 102 | goto Err 103 | } 104 | if !new { /* an old image: must flip all the bits */ 105 | for i, b := range tmp[:n] { 106 | _, _ = i, b // tmp[i] = b ^ 0xFF 107 | } 108 | } 109 | if d != nil { 110 | if _, err = i.load(Rect(r.Min.X, miny, r.Max.X, miny+dy), tmp[:n]); err != nil { 111 | goto Err 112 | } 113 | } 114 | miny += dy 115 | } 116 | return i, nil 117 | 118 | Err: 119 | if d != nil { 120 | i.free() 121 | } 122 | return nil, err 123 | } 124 | -------------------------------------------------------------------------------- /draw/readsubfont.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | func (d *Display) readSubfont(name string, fd io.Reader, ai *Image) (*subfont, error) { 9 | hdr := make([]byte, 3*12+4) 10 | i := ai 11 | if i == nil { 12 | var err error 13 | i, err = d.readImage(fd) 14 | if err != nil { 15 | return nil, err 16 | } 17 | } 18 | var ( 19 | n int 20 | p []byte 21 | fc []Fontchar 22 | f *subfont 23 | err error 24 | ) 25 | // Release lock for the I/O - could take a long time. 26 | if d != nil { 27 | d.mu.Unlock() 28 | } 29 | _, err = io.ReadFull(fd, hdr[:3*12]) 30 | if d != nil { 31 | d.mu.Lock() 32 | } 33 | if err != nil { 34 | err = fmt.Errorf("rdsubfontfile: header read error: %v", err) 35 | goto Err 36 | } 37 | n = atoi(hdr) 38 | p = make([]byte, 6*(n+1)) 39 | if _, err = io.ReadFull(fd, p); err != nil { 40 | err = fmt.Errorf("rdsubfontfile: fontchar read error: %v", err) 41 | goto Err 42 | } 43 | fc = make([]Fontchar, n+1) 44 | unpackinfo(fc, p, n) 45 | f = d.allocSubfont(name, atoi(hdr[12:]), atoi(hdr[24:]), fc, i) 46 | return f, nil 47 | 48 | Err: 49 | if ai == nil { 50 | i.free() 51 | } 52 | return nil, err 53 | } 54 | 55 | // ReadSubfont reads the subfont data from the reader and returns the subfont 56 | // it describes, giving it the specified name. 57 | func (d *Display) _ReadSubfont(name string, r io.Reader) (*subfont, error) { 58 | d.mu.Lock() 59 | defer d.mu.Unlock() 60 | return d.readSubfont(name, r, nil) 61 | } 62 | 63 | func unpackinfo(fc []Fontchar, p []byte, n int) { 64 | for j := 0; j <= n; j++ { 65 | fc[j].X = int(p[0]) | int(p[1])<<8 66 | fc[j].Top = uint8(p[2]) 67 | fc[j].Bottom = uint8(p[3]) 68 | fc[j].Left = int8(p[4]) 69 | fc[j].Width = uint8(p[5]) 70 | p = p[6:] 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /draw/rectclip.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // RectClip attempts to clip *rp to be within b. 4 | // If any of *rp overlaps b, RectClip modifies *rp to denote 5 | // the overlapping portion and returns true. 6 | // Otherwise, when *rp and b do not overlap, 7 | // RectClip leaves *rp unmodified and returns false. 8 | func RectClip(rp *Rectangle, b Rectangle) bool { 9 | if !RectXRect(*rp, b) { 10 | return false 11 | } 12 | 13 | if rp.Min.X < b.Min.X { 14 | rp.Min.X = b.Min.X 15 | } 16 | if rp.Min.Y < b.Min.Y { 17 | rp.Min.Y = b.Min.Y 18 | } 19 | if rp.Max.X > b.Max.X { 20 | rp.Max.X = b.Max.X 21 | } 22 | if rp.Max.Y > b.Max.Y { 23 | rp.Max.Y = b.Max.Y 24 | } 25 | return true 26 | } 27 | 28 | // RectXRect reports whether r and s cross, meaning they share any point 29 | // or r is a zero-width or zero-height rectangle inside s. 30 | // Note that the zero-sized cases make RectXRect(r, s) different from r.Overlaps(s). 31 | func RectXRect(r, s Rectangle) bool { 32 | return r.Min.X < s.Max.X && s.Min.X < r.Max.X && r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y 33 | } 34 | 35 | // RectInRect reports whether r is entirely contained in s. 36 | // RectInRect(r, s) differs from r.In(s) 37 | // in its handling of zero-width or zero-height rectangles. 38 | func RectInRect(r, s Rectangle) bool { 39 | return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X && s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y 40 | } 41 | 42 | // CombineRect overwrites *r1 with the smallest rectangle 43 | // enclosing both *r1 and r2. 44 | // CombineRect(r1, r2) differs from *r1 = r1.Union(r2) 45 | // in its handling of zero-width or zero-height rectangles. 46 | func CombineRect(r1 *Rectangle, r2 Rectangle) { 47 | if r1.Min.X > r2.Min.X { 48 | r1.Min.X = r2.Min.X 49 | } 50 | if r1.Min.Y > r2.Min.Y { 51 | r1.Min.Y = r2.Min.Y 52 | } 53 | if r1.Max.X < r2.Max.X { 54 | r1.Max.X = r2.Max.X 55 | } 56 | if r1.Max.Y < r2.Max.Y { 57 | r1.Max.Y = r2.Max.Y 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /draw/replclipr.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // ReplClipr sets the replication boolean and clip rectangle for the specified image. 4 | func (dst *Image) ReplClipr(repl bool, clipr Rectangle) { 5 | dst.Display.mu.Lock() 6 | defer dst.Display.mu.Unlock() 7 | b := dst.Display.bufimage(22) 8 | b[0] = 'c' 9 | bplong(b[1:], uint32(dst.id)) 10 | byteRepl := byte(0) 11 | if repl { 12 | byteRepl = 1 13 | } 14 | b[5] = byteRepl 15 | bplong(b[6:], uint32(clipr.Min.X)) 16 | bplong(b[10:], uint32(clipr.Min.Y)) 17 | bplong(b[14:], uint32(clipr.Max.X)) 18 | bplong(b[18:], uint32(clipr.Max.Y)) 19 | dst.Repl = repl 20 | dst.Clipr = clipr 21 | } 22 | -------------------------------------------------------------------------------- /draw/rgb.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | /* 4 | * This original version, although fast and a true inverse of 5 | * cmap2rgb, in the sense that rgb2cmap(cmap2rgb(c)) 6 | * returned the original color, does a terrible job for RGB 7 | * triples that do not appear in the color map, so it has been 8 | * replaced by the much slower version below, that loops 9 | * over the color map looking for the nearest point in RGB 10 | * space. There is no visual psychology reason for that 11 | * criterion, but it's easy to implement and the results are 12 | * far more pleasing. 13 | * 14 | int 15 | rgb2cmap(int cr, int cg, int cb) 16 | { 17 | int r, g, b, v, cv; 18 | 19 | if(cr < 0) 20 | cr = 0; 21 | else if(cr > 255) 22 | cr = 255; 23 | if(cg < 0) 24 | cg = 0; 25 | else if(cg > 255) 26 | cg = 255; 27 | if(cb < 0) 28 | cb = 0; 29 | else if(cb > 255) 30 | cb = 255; 31 | r = cr>>6; 32 | g = cg>>6; 33 | b = cb>>6; 34 | cv = cr; 35 | if(cg > cv) 36 | cv = cg; 37 | if(cb > cv) 38 | cv = cb; 39 | v = (cv>>4)&3; 40 | return ((((r<<2)+v)<<4)+(((g<<2)+b+v-r)&15)); 41 | } 42 | */ 43 | 44 | func rgb2cmap(cr, cg, cb int) int { 45 | best := 0 46 | bestsq := 0x7FFFFFFF 47 | for i := 0; i < 256; i++ { 48 | r, g, b := cmap2rgb(i) 49 | sq := (r-cr)*(r-cr) + (g-cg)*(g-cg) + (b-cb)*(b-cb) 50 | if sq < bestsq { 51 | bestsq = sq 52 | best = i 53 | } 54 | } 55 | return best 56 | } 57 | 58 | func cmap2rgb(c int) (r, g, b int) { 59 | r = c >> 6 60 | v := (c >> 4) & 3 61 | j := (c - v + r) & 15 62 | g = j >> 2 63 | b = j & 3 64 | den := r 65 | if g > den { 66 | den = g 67 | } 68 | if b > den { 69 | den = b 70 | } 71 | if den == 0 { 72 | v *= 17 73 | return v, v, v 74 | } 75 | num := 17 * (4*den + v) 76 | r = r * num / den 77 | g = g * num / den 78 | b = b * num / den 79 | return 80 | } 81 | 82 | func cmap2rgba(c int) Color { 83 | r, g, b := cmap2rgb(c) 84 | return Color(r<<24 | g<<16 | b<<8 | 0xFF) 85 | } 86 | -------------------------------------------------------------------------------- /draw/scroll.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | var mss struct { 10 | lines int 11 | percent float64 12 | } 13 | 14 | // Mousescrollsize computes the number of lines of text that 15 | // should be scrolled in response to a mouse scroll wheel 16 | // click. Maxlines is the number of lines visible in the text 17 | // window. 18 | // 19 | // The default scroll increment is one line. This default can 20 | // be overridden by setting the $mousescrollsize environment 21 | // variable to an integer, which specifies a constant number of 22 | // lines, or to a real number followed by a percent character, 23 | // indicating that the scroll increment should be a percentage 24 | // of the total number of lines in the window. For example, 25 | // setting $mousescrollsize to 50% causes a half-window scroll 26 | // increment. 27 | func MouseScrollSize(maxlines int) int { 28 | if mss.lines == 0 && mss.percent == 0 { 29 | if s := strings.TrimSpace(os.Getenv("mousescrollsize")); s != "" { 30 | if strings.HasSuffix(s, "%") { 31 | mss.percent, _ = strconv.ParseFloat(strings.TrimSpace(s[:len(s)-1]), 64) 32 | } else { 33 | mss.lines, _ = strconv.Atoi(s) 34 | } 35 | } 36 | if mss.lines == 0 && mss.percent == 0 { 37 | mss.lines = 1 38 | } 39 | if mss.percent >= 100 { 40 | mss.percent = 100 41 | } 42 | } 43 | 44 | if mss.lines != 0 { 45 | return mss.lines 46 | } 47 | return int(mss.percent * float64(maxlines) / 100.0) 48 | } 49 | -------------------------------------------------------------------------------- /draw/snarf.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // ReadSnarf reads the snarf buffer into buf, returning the number of bytes read, 4 | // the total size of the snarf buffer (useful if buf is too short), and any 5 | // error. No error is returned if there is no problem except for buf being too 6 | // short. 7 | func (d *Display) ReadSnarf(buf []byte) (int, int, error) { 8 | d.mu.Lock() 9 | defer d.mu.Unlock() 10 | n, actual, err := d.conn.ReadSnarf(buf) 11 | if err != nil { 12 | return 0, 0, err 13 | } 14 | return n, actual, nil 15 | } 16 | 17 | // WriteSnarf writes the data to the snarf buffer. 18 | func (d *Display) WriteSnarf(data []byte) error { 19 | d.mu.Lock() 20 | defer d.mu.Unlock() 21 | err := d.conn.WriteSnarf(data) 22 | if err != nil { 23 | return err 24 | } 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /draw/stringsubfont.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // TODO 4 | -------------------------------------------------------------------------------- /draw/stringwidth.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func stringnwidth(f *Font, s string, b []byte, r []rune) int { 9 | const Max = 64 10 | cbuf := make([]uint16, Max) 11 | var in input 12 | in.init(s, b, r) 13 | twid := 0 14 | for !in.done { 15 | max := Max 16 | n := 0 17 | var sf *subfont 18 | var l, wid int 19 | var subfontname string 20 | for { 21 | if l, wid, subfontname = cachechars(f, &in, cbuf, max); l > 0 { 22 | break 23 | } 24 | if n++; n > 10 { 25 | r := in.ch 26 | name := f.Name 27 | if name == "" { 28 | name = "unnamed font" 29 | } 30 | sf.free() 31 | fmt.Fprintf(os.Stderr, "stringwidth: bad character set for rune %U in %s\n", r, name) 32 | return twid 33 | } 34 | if subfontname != "" { 35 | sf.free() 36 | var err error 37 | sf, err = getsubfont(f.Display, subfontname) 38 | if err != nil { 39 | if f.Display != nil && f != f.Display.Font { 40 | f = f.Display.Font 41 | continue 42 | } 43 | break 44 | } 45 | /* 46 | * must not free sf until cachechars has found it in the cache 47 | * and picked up its own reference. 48 | */ 49 | } 50 | } 51 | sf.free() 52 | agefont(f) 53 | twid += wid 54 | } 55 | return twid 56 | } 57 | 58 | // StringWidth returns the number of horizontal pixels that would be occupied 59 | // by the string if it were drawn using the font. 60 | func (f *Font) StringWidth(s string) int { 61 | f.lock() 62 | defer f.unlock() 63 | return stringnwidth(f, s, nil, nil) 64 | } 65 | 66 | // ByteWidth returns the number of horizontal pixels that would be occupied by 67 | // the byte slice if it were drawn using the font. 68 | func (f *Font) BytesWidth(b []byte) int { 69 | f.lock() 70 | defer f.unlock() 71 | return stringnwidth(f, "", b, nil) 72 | } 73 | 74 | // RuneWidth returns the number of horizontal pixels that would be occupied by 75 | // the rune slice if it were drawn using the font. 76 | func (f *Font) RunesWidth(r []rune) int { 77 | f.lock() 78 | defer f.unlock() 79 | return stringnwidth(f, "", nil, r) 80 | } 81 | 82 | // StringSize returns the number of horizontal and vertical pixels that would 83 | // be occupied by the string if it were drawn using the font. 84 | func (f *Font) StringSize(s string) Point { 85 | return Pt(f.StringWidth(s), f.Height) 86 | } 87 | 88 | // ByteSize returns the number of horizontal and vertical pixels that would be 89 | // occupied by the byte slice if it were drawn using the font. 90 | func (f *Font) BytesSize(b []byte) Point { 91 | return Pt(f.BytesWidth(b), f.Height) 92 | } 93 | 94 | // RuneSize returns the number of horizontal and vertical pixels that would be 95 | // occupied by the rune slice if it were drawn using the font. 96 | func (f *Font) RunesSize(r []rune) Point { 97 | return Pt(f.RunesWidth(r), f.Height) 98 | } 99 | -------------------------------------------------------------------------------- /draw/subfont.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // AllocSubfont allocates a subfont on the server. The subfont will have the 4 | // specified name, total height, ascent (height above the baseline), and 5 | // character info. 6 | func (d *Display) _AllocSubfont(name string, height, ascent int, info []Fontchar, i *Image) *subfont { 7 | d.mu.Lock() 8 | defer d.mu.Unlock() 9 | return d.allocSubfont(name, height, ascent, info, i) 10 | } 11 | 12 | func (d *Display) allocSubfont(name string, height, ascent int, info []Fontchar, i *Image) *subfont { 13 | f := &subfont{ 14 | Name: name, 15 | N: len(info) - 1, 16 | Height: height, 17 | Ascent: ascent, 18 | Bits: i, 19 | ref: 1, 20 | Info: info, 21 | } 22 | if name != "" { 23 | /* 24 | * if already caching this subfont, leave older 25 | * (and hopefully more widely used) copy in cache. 26 | * this case should not happen -- we got called 27 | * because cachechars needed this subfont and it 28 | * wasn't in the cache. 29 | */ 30 | cf := lookupsubfont(i.Display, name) 31 | if cf == nil { 32 | installsubfont(name, f) 33 | } else { 34 | cf.free() /* drop ref we just picked up */ 35 | } 36 | } 37 | return f 38 | } 39 | -------------------------------------------------------------------------------- /draw/subfontcache.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import "sync" 4 | 5 | /* 6 | * Easy versions of the cache routines; may be substituted by fancier ones for other purposes 7 | */ 8 | 9 | var lastfont struct { 10 | sync.Mutex 11 | name string 12 | sub *subfont 13 | } 14 | 15 | func lookupsubfont(d *Display, name string) *subfont { 16 | if d != nil && name == "*default*" { 17 | return d.defaultSubfont 18 | } 19 | lastfont.Lock() 20 | defer lastfont.Unlock() 21 | if lastfont.name == name && d == lastfont.sub.Bits.Display { 22 | lastfont.sub.ref++ 23 | return lastfont.sub 24 | } 25 | return nil 26 | } 27 | 28 | func installsubfont(name string, subfont *subfont) { 29 | lastfont.Lock() 30 | defer lastfont.Unlock() 31 | lastfont.name = name 32 | lastfont.sub = subfont /* notice we don't free the old one; that's your business */ 33 | } 34 | 35 | func uninstallsubfont(subfont *subfont) { 36 | lastfont.Lock() 37 | defer lastfont.Unlock() 38 | if subfont == lastfont.sub { 39 | lastfont.name = "" 40 | lastfont.sub = nil 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /draw/subfontname.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | /* 10 | * Default version: convert to file name 11 | */ 12 | 13 | func subfontname(cfname, fname string, maxdepth int) string { 14 | scale, base := parsefontscale(fname) 15 | 16 | t := cfname 17 | if cfname == "*default*" { 18 | return t 19 | } 20 | if !strings.HasPrefix(t, "/") { 21 | dir := base 22 | i := strings.LastIndex(dir, "/") 23 | if i >= 0 { 24 | dir = dir[:i] 25 | } else { 26 | dir = "." 27 | } 28 | t = dir + "/" + t 29 | } 30 | if maxdepth > 8 { 31 | maxdepth = 8 32 | } 33 | for i := 3; i >= 0; i-- { 34 | if 1< maxdepth { 35 | continue 36 | } 37 | // try i-bit grey 38 | tmp2 := fmt.Sprintf("%s.%d", t, i) 39 | if _, err := os.Stat(tmp2); err == nil { 40 | if scale > 1 { 41 | tmp2 = fmt.Sprintf("%d*%s", scale, tmp2) 42 | } 43 | return tmp2 44 | } 45 | } 46 | 47 | // try default 48 | if strings.HasPrefix(t, "/mnt/font/") { 49 | if scale > 1 { 50 | t = fmt.Sprintf("%d*%s", scale, t) 51 | } 52 | return t 53 | } 54 | if _, err := os.Stat(t); err == nil { 55 | if scale > 1 { 56 | t = fmt.Sprintf("%d*%s", scale, t) 57 | } 58 | return t 59 | } 60 | 61 | return "" 62 | } 63 | -------------------------------------------------------------------------------- /draw/unloadimage.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | import "fmt" 4 | 5 | // Unload reads a rectangle of pixels from image i into data, 6 | // returning the number of bytes copied to data. 7 | // It is an error if data does not contain pixels for the entire rectangle. 8 | // 9 | // See the Load method's documentation for details about the data format. 10 | func (i *Image) Unload(r Rectangle, data []byte) (n int, err error) { 11 | i.Display.mu.Lock() 12 | defer i.Display.mu.Unlock() 13 | return i.unload(r, data) 14 | } 15 | 16 | func (src *Image) unload(r Rectangle, data []byte) (n int, err error) { 17 | i := src 18 | if !r.In(i.R) { 19 | return 0, fmt.Errorf("image.Unload: bad rectangle") 20 | } 21 | bpl := BytesPerLine(r, i.Depth) 22 | if len(data) < bpl*r.Dy() { 23 | return 0, fmt.Errorf("image.Unload: buffer too small") 24 | } 25 | 26 | d := i.Display 27 | d.flush(false) // make sure next flush is only us 28 | ntot := 0 29 | for r.Min.Y < r.Max.Y { 30 | a := d.bufimage(1 + 4 + 4*4) 31 | dy := 8000 / bpl 32 | if dy <= 0 { 33 | return 0, fmt.Errorf("unloadimage: image too wide") 34 | } 35 | if dy > r.Dy() { 36 | dy = r.Dy() 37 | } 38 | a[0] = 'r' 39 | bplong(a[1:], uint32(i.id)) 40 | bplong(a[5:], uint32(r.Min.X)) 41 | bplong(a[9:], uint32(r.Min.Y)) 42 | bplong(a[13:], uint32(r.Max.X)) 43 | bplong(a[17:], uint32(r.Min.Y+dy)) 44 | if err := d.flush(false); err != nil { 45 | return ntot, err 46 | } 47 | n, err := d.conn.ReadDraw(data[ntot:]) 48 | ntot += n 49 | if err != nil { 50 | return ntot, err 51 | } 52 | r.Min.Y += dy 53 | } 54 | return ntot, nil 55 | } 56 | -------------------------------------------------------------------------------- /draw/writeimage.go: -------------------------------------------------------------------------------- 1 | package draw 2 | -------------------------------------------------------------------------------- /draw/writesubfont.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // TODO 4 | -------------------------------------------------------------------------------- /draw/wsys.go: -------------------------------------------------------------------------------- 1 | package draw 2 | 3 | // SetLabel sets the host window's title. 4 | func (d *Display) SetLabel(label string) { 5 | d.conn.Label(label) 6 | } 7 | 8 | // Top moves the host window to the top of the host window pile. 9 | func (d *Display) Top() { 10 | d.conn.Top() 11 | } 12 | 13 | // Resize resizes the host window. 14 | func (d *Display) Resize(r Rectangle) { 15 | d.conn.Resize(r) 16 | } 17 | -------------------------------------------------------------------------------- /games/4s/4s.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // 4s is a tetromino stacking game. 6 | // Use 4s -5 for pentominoes. 7 | package main // import "9fans.net/go/games/4s" 8 | 9 | import ( 10 | "log" 11 | "os" 12 | 13 | "9fans.net/go/draw" 14 | ) 15 | 16 | func main() { 17 | args := os.Args 18 | p := pieces4 19 | name := "4s" 20 | if len(args) > 1 && args[1] == "-5" { 21 | p = pieces5 22 | name = "5s" 23 | } 24 | 25 | d, err := draw.Init(nil, "", name, "") 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | Play(p, d) 31 | } 32 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module 9fans.net/go 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | golang.org/x/exp v0.0.0-20210405174845-4513512abef3 7 | golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 8 | golang.org/x/sys v0.0.0-20210415045647-66c3f260301c 9 | ) 10 | 11 | require ( 12 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037 // indirect 13 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 // indirect 14 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 // indirect 15 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /p9trace/deps.go: -------------------------------------------------------------------------------- 1 | // This file is not ignored by 'go mod tidy', 2 | // to keep leveldb as a required module. 3 | 4 | //go:build never 5 | 6 | package p9trace 7 | 8 | import _ "github.com/golang/leveldb/table" 9 | -------------------------------------------------------------------------------- /p9trace/go.mod: -------------------------------------------------------------------------------- 1 | module 9fans.net/go/p9trace 2 | 3 | go 1.23.0 4 | 5 | require github.com/golang/leveldb v0.0.0-20170107010102-259d9253d719 6 | 7 | require github.com/golang/snappy v0.0.4 // indirect 8 | -------------------------------------------------------------------------------- /p9trace/go.sum: -------------------------------------------------------------------------------- 1 | github.com/golang/leveldb v0.0.0-20170107010102-259d9253d719 h1:yahFtfWlyALYDkXw2ETowZqG4vi8hiE0yOEBOkpaXl0= 2 | github.com/golang/leveldb v0.0.0-20170107010102-259d9253d719/go.mod h1:etEpE0xVqxA0N3WNUa5wic5HCNSsQvYm+PFNmOnx2iU= 3 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 4 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 5 | -------------------------------------------------------------------------------- /p9trace/super.go: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | 3 | // Super is a port of the C "super.c" trace reading demo. 4 | // See https://github.com/9fans/p9trace/src/super.c. 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "os" 11 | 12 | "9fans.net/go/p9trace" 13 | ) 14 | 15 | var ( 16 | offset uint32 = ^uint32(0) 17 | last uint32 18 | next uint32 19 | 20 | count [p9trace.TagMax]int 21 | ndir int 22 | nind int 23 | num int 24 | ) 25 | 26 | func main() { 27 | 28 | for b := range p9trace.FileRecords(os.Args[1:]...) { 29 | if ^offset == 0 { 30 | offset = b.Addr 31 | } 32 | if b.Addr != offset { 33 | fmt.Fprintf(os.Stderr, "bad address %v %v\n", offset, b.Addr) 34 | } 35 | offset++ 36 | if b.Tag >= p9trace.TagMax { 37 | log.Fatal("bad tag") 38 | } 39 | count[b.Tag]++ 40 | 41 | switch b.Tag { 42 | case p9trace.TagDir: 43 | ndir += len(b.Dir) 44 | case p9trace.TagInd1, p9trace.TagInd2: 45 | nind += len(b.Ind) 46 | case p9trace.TagSuper: 47 | if next != 0 && next != b.Addr { 48 | fmt.Fprintf(os.Stderr, "missing super block %v\n", next) 49 | } 50 | if last != 0 && last != b.Super.Last { 51 | fmt.Fprintf(os.Stderr, "bad last block %v\n", b.Addr) 52 | } 53 | fmt.Printf("super %v: cwraddr %v roraddr %v last %v next %v\n", 54 | b.Addr, b.Super.CWRAddr, b.Super.RORAddr, 55 | b.Super.Last, b.Super.Next) 56 | next = b.Super.Next 57 | last = b.Addr 58 | } 59 | num++ 60 | } 61 | 62 | for i := range p9trace.TagMax { 63 | fmt.Fprintf(os.Stderr, "%v: %v\n", i, count[i]) 64 | } 65 | fmt.Fprintf(os.Stderr, "num = %v ndir = %v nind = %v\n", num, ndir, nind) 66 | } 67 | -------------------------------------------------------------------------------- /p9trace/super2.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // Super2 is like super but reads from an SSTable instead of raw trace files. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "log" 13 | "os" 14 | 15 | "9fans.net/go/p9trace" 16 | "github.com/golang/leveldb/table" 17 | ) 18 | 19 | var ( 20 | offset uint32 = ^uint32(0) 21 | last uint32 22 | next uint32 23 | 24 | count [p9trace.TagMax]int 25 | ndir int 26 | nind int 27 | num int 28 | ) 29 | 30 | func main() { 31 | f, err := os.Open(os.Args[1]) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | r := table.NewReader(f, nil) 36 | it := r.Find(nil, nil) 37 | for it.Next() { 38 | var b p9trace.Record 39 | if err := b.UnmarshalBinary(it.Value()); err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | if ^offset == 0 { 44 | offset = b.Addr 45 | } 46 | if b.Addr != offset { 47 | fmt.Fprintf(os.Stderr, "bad address %v %v\n", offset, b.Addr) 48 | } 49 | offset++ 50 | if b.Tag >= p9trace.TagMax { 51 | log.Fatal("bad tag") 52 | } 53 | count[b.Tag]++ 54 | 55 | switch b.Tag { 56 | case p9trace.TagDir: 57 | ndir += len(b.Dir) 58 | case p9trace.TagInd1, p9trace.TagInd2: 59 | nind += len(b.Ind) 60 | case p9trace.TagSuper: 61 | if next != 0 && next != b.Addr { 62 | fmt.Fprintf(os.Stderr, "missing super block %v\n", next) 63 | } 64 | if last != 0 && last != b.Super.Last { 65 | fmt.Fprintf(os.Stderr, "bad last block %v\n", b.Addr) 66 | } 67 | fmt.Printf("super %v: cwraddr %v roraddr %v last %v next %v\n", 68 | b.Addr, b.Super.CWRAddr, b.Super.RORAddr, 69 | b.Super.Last, b.Super.Next) 70 | next = b.Super.Next 71 | last = b.Addr 72 | } 73 | num++ 74 | } 75 | if err := it.Close(); err != nil { 76 | log.Fatalf("iter: %v", err) 77 | } 78 | 79 | for i := range p9trace.TagMax { 80 | fmt.Fprintf(os.Stderr, "%v: %v\n", i, count[i]) 81 | } 82 | fmt.Fprintf(os.Stderr, "num = %v ndir = %v nind = %v\n", num, ndir, nind) 83 | } 84 | -------------------------------------------------------------------------------- /plan9/bit.go: -------------------------------------------------------------------------------- 1 | package plan9 // import "9fans.net/go/plan9" 2 | 3 | func gbit8(b []byte) (uint8, []byte) { 4 | return uint8(b[0]), b[1:] 5 | } 6 | 7 | func gbit16(b []byte) (uint16, []byte) { 8 | return uint16(b[0]) | uint16(b[1])<<8, b[2:] 9 | } 10 | 11 | func gbit32(b []byte) (uint32, []byte) { 12 | return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:] 13 | } 14 | 15 | func gbit64(b []byte) (uint64, []byte) { 16 | lo, b := gbit32(b) 17 | hi, b := gbit32(b) 18 | return uint64(hi)<<32 | uint64(lo), b 19 | } 20 | 21 | func gstring(b []byte) (string, []byte) { 22 | n, b := gbit16(b) 23 | return string(b[0:n]), b[n:] 24 | } 25 | 26 | func pbit8(b []byte, x uint8) []byte { 27 | n := len(b) 28 | if n+1 > cap(b) { 29 | nb := make([]byte, n, 100+2*cap(b)) 30 | copy(nb, b) 31 | b = nb 32 | } 33 | b = b[0 : n+1] 34 | b[n] = x 35 | return b 36 | } 37 | 38 | func pbit16(b []byte, x uint16) []byte { 39 | n := len(b) 40 | if n+2 > cap(b) { 41 | nb := make([]byte, n, 100+2*cap(b)) 42 | copy(nb, b) 43 | b = nb 44 | } 45 | b = b[0 : n+2] 46 | b[n] = byte(x) 47 | b[n+1] = byte(x >> 8) 48 | return b 49 | } 50 | 51 | func pbit32(b []byte, x uint32) []byte { 52 | n := len(b) 53 | if n+4 > cap(b) { 54 | nb := make([]byte, n, 100+2*cap(b)) 55 | copy(nb, b) 56 | b = nb 57 | } 58 | b = b[0 : n+4] 59 | b[n] = byte(x) 60 | b[n+1] = byte(x >> 8) 61 | b[n+2] = byte(x >> 16) 62 | b[n+3] = byte(x >> 24) 63 | return b 64 | } 65 | 66 | func pbit64(b []byte, x uint64) []byte { 67 | b = pbit32(b, uint32(x)) 68 | b = pbit32(b, uint32(x>>32)) 69 | return b 70 | } 71 | 72 | func pstring(b []byte, s string) []byte { 73 | if len(s) >= 1<<16 { 74 | panic(ProtocolError("string too long")) 75 | } 76 | b = pbit16(b, uint16(len(s))) 77 | b = append(b, []byte(s)...) 78 | return b 79 | } 80 | -------------------------------------------------------------------------------- /plan9/client/cat/cat.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | "os" 9 | 10 | "9fans.net/go/plan9" 11 | "9fans.net/go/plan9/client" 12 | ) 13 | 14 | func main() { 15 | fsys, err := client.MountService("acme") 16 | if err != nil { 17 | panic(err) 18 | } 19 | 20 | fid, err := fsys.Open("index", plan9.OREAD) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | fid.Write([]byte("hello, world")) 26 | 27 | io.Copy(os.Stdout, fid) 28 | fid.Close() 29 | 30 | d, err := fsys.Stat("/index") 31 | if err != nil { 32 | panic(err) 33 | } 34 | fmt.Printf("%v\n", d) 35 | 36 | fsys.Wstat("/index", d) 37 | } 38 | -------------------------------------------------------------------------------- /plan9/client/conn_plan9.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | type Conn struct { 4 | fd int 5 | name string 6 | } 7 | -------------------------------------------------------------------------------- /plan9/client/dial.go: -------------------------------------------------------------------------------- 1 | // +build !plan9 2 | 3 | package client 4 | 5 | import ( 6 | "fmt" 7 | "net" 8 | "os" 9 | "regexp" 10 | "strings" 11 | ) 12 | 13 | func Dial(network, addr string) (*Conn, error) { 14 | c, err := net.Dial(network, addr) 15 | if err != nil { 16 | return nil, err 17 | } 18 | return NewConn(c) 19 | } 20 | 21 | func DialService(service string) (*Conn, error) { 22 | ns := Namespace() 23 | return Dial("unix", ns+"/"+service) 24 | } 25 | 26 | func Mount(network, addr string) (*Fsys, error) { 27 | c, err := Dial(network, addr) 28 | if err != nil { 29 | return nil, err 30 | } 31 | fsys, err := c.Attach(nil, getuser(), "") 32 | if err != nil { 33 | c.Close() 34 | } 35 | return fsys, err 36 | } 37 | 38 | func MountService(service string) (*Fsys, error) { 39 | c, err := DialService(service) 40 | if err != nil { 41 | return nil, err 42 | } 43 | fsys, err := c.Attach(nil, getuser(), "") 44 | if err != nil { 45 | c.Close() 46 | } 47 | return fsys, err 48 | } 49 | 50 | func MountServiceAname(service, aname string) (*Fsys, error) { 51 | c, err := DialService(service) 52 | if err != nil { 53 | return nil, err 54 | } 55 | fsys, err := c.Attach(nil, getuser(), aname) 56 | if err != nil { 57 | c.Close() 58 | } 59 | return fsys, err 60 | } 61 | 62 | var dotZero = regexp.MustCompile(`\A(.*:\d+)\.0\z`) 63 | 64 | // Namespace returns the path to the name space directory. 65 | func Namespace() string { 66 | ns := os.Getenv("NAMESPACE") 67 | if ns != "" { 68 | return ns 69 | } 70 | 71 | disp := os.Getenv("DISPLAY") 72 | if disp == "" { 73 | // No $DISPLAY? Use :0.0 for non-X11 GUI (OS X). 74 | disp = ":0.0" 75 | } 76 | 77 | // Canonicalize: xxx:0.0 => xxx:0. 78 | if m := dotZero.FindStringSubmatch(disp); m != nil { 79 | disp = m[1] 80 | } 81 | 82 | // Turn /tmp/launch/:0 into _tmp_launch_:0 (OS X 10.5). 83 | disp = strings.Replace(disp, "/", "_", -1) 84 | 85 | // NOTE: plan9port creates this directory on demand. 86 | // Maybe someday we'll need to do that. 87 | 88 | return fmt.Sprintf("/tmp/ns.%s.%s", os.Getenv("USER"), disp) 89 | } 90 | -------------------------------------------------------------------------------- /plan9/client/dial_plan9.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "path/filepath" 5 | "syscall" 6 | ) 7 | 8 | func openSrv(service string) (fd int, err error) { 9 | p := filepath.Join(Namespace(), service) 10 | return syscall.Open(p, syscall.O_RDWR) 11 | } 12 | 13 | func DialService(service string) (*Conn, error) { 14 | fd, err := openSrv(service) 15 | if err != nil { 16 | return nil, err 17 | } 18 | return &Conn{fd: fd, name: service}, nil 19 | } 20 | 21 | func Mount(network, addr string) (*Fsys, error) { 22 | panic("unimplemented") 23 | } 24 | 25 | func MountService(service string) (*Fsys, error) { 26 | panic("unimplemented") 27 | } 28 | 29 | // Namespace returns the path to the name space directory. 30 | func Namespace() string { 31 | return "/srv" 32 | } 33 | -------------------------------------------------------------------------------- /plan9/client/fid.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "io" 5 | 6 | "9fans.net/go/plan9" 7 | ) 8 | 9 | func (fid *Fid) Dirread() ([]*plan9.Dir, error) { 10 | buf := make([]byte, plan9.STATMAX) 11 | n, err := fid.Read(buf) 12 | if err != nil { 13 | return nil, err 14 | } 15 | return dirUnpack(buf[0:n]) 16 | } 17 | 18 | func (fid *Fid) Dirreadall() ([]*plan9.Dir, error) { 19 | // Note: Cannot use ioutil.ReadAll / io.ReadAll here 20 | // because it assumes it can read small amounts. 21 | // Plan 9 requires providing a buffer big enough for 22 | // at least a single directory entry. 23 | var dirs []*plan9.Dir 24 | for { 25 | d, err := fid.Dirread() 26 | dirs = append(dirs, d...) 27 | if err != nil { 28 | if err == io.EOF { 29 | err = nil 30 | } 31 | return dirs, err 32 | } 33 | } 34 | } 35 | 36 | func dirUnpack(b []byte) ([]*plan9.Dir, error) { 37 | var err error 38 | dirs := make([]*plan9.Dir, 0, 10) 39 | for len(b) > 0 { 40 | if len(b) < 2 { 41 | err = io.ErrUnexpectedEOF 42 | break 43 | } 44 | n := int(b[0]) | int(b[1])<<8 45 | if len(b) < n+2 { 46 | err = io.ErrUnexpectedEOF 47 | break 48 | } 49 | var d *plan9.Dir 50 | d, err = plan9.UnmarshalDir(b[0 : n+2]) 51 | if err != nil { 52 | break 53 | } 54 | b = b[n+2:] 55 | if len(dirs) >= cap(dirs) { 56 | ndirs := make([]*plan9.Dir, len(dirs), 2*cap(dirs)) 57 | copy(ndirs, dirs) 58 | dirs = ndirs 59 | } 60 | n = len(dirs) 61 | dirs = dirs[0 : n+1] 62 | dirs[n] = d 63 | } 64 | return dirs, err 65 | } 66 | 67 | func (fid *Fid) ReadFull(b []byte) (n int, err error) { 68 | return io.ReadFull(fid, b) 69 | } 70 | -------------------------------------------------------------------------------- /plan9/client/fid_plan9.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "os" 4 | 5 | type Fid struct { 6 | *os.File 7 | } 8 | -------------------------------------------------------------------------------- /plan9/client/fsys_plan9.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "9fans.net/go/plan9" 8 | ) 9 | 10 | type Fsys struct { 11 | Mtpt string 12 | } 13 | 14 | func (c *Conn) Attach(afid *Fid, user, aname string) (*Fsys, error) { 15 | panic("unimplemented") 16 | } 17 | 18 | func (fs *Fsys) Access(name string, mode int) error { 19 | panic("unimplemented") 20 | } 21 | 22 | func (fs *Fsys) Create(name string, mode uint8, perm plan9.Perm) (*Fid, error) { 23 | panic("unimplemented") 24 | } 25 | 26 | func (fs *Fsys) Open(name string, mode uint8) (*Fid, error) { 27 | f, err := os.OpenFile(filepath.Join(fs.Mtpt, name), int(mode), 0) 28 | return &Fid{File: f}, err 29 | } 30 | 31 | func (fs *Fsys) Remove(name string) error { 32 | panic("unimplemented") 33 | } 34 | 35 | func (fs *Fsys) Stat(name string) (*plan9.Dir, error) { 36 | panic("unimplemented") 37 | } 38 | 39 | func (fs *Fsys) Wstat(name string, d *plan9.Dir) error { 40 | panic("unimplemented") 41 | } 42 | -------------------------------------------------------------------------------- /plan9/const.go: -------------------------------------------------------------------------------- 1 | package plan9 2 | 3 | const ( 4 | VERSION9P = "9P2000" 5 | MAXWELEM = 16 6 | 7 | OREAD = 0 8 | OWRITE = 1 9 | ORDWR = 2 10 | OEXEC = 3 11 | OTRUNC = 16 12 | OCEXEC = 32 13 | ORCLOSE = 64 14 | ODIRECT = 128 15 | ONONBLOCK = 256 16 | OEXCL = 0x1000 17 | OLOCK = 0x2000 18 | OAPPEND = 0x4000 19 | 20 | AEXIST = 0 21 | AEXEC = 1 22 | AWRITE = 2 23 | AREAD = 4 24 | 25 | QTDIR = 0x80 26 | QTAPPEND = 0x40 27 | QTEXCL = 0x20 28 | QTMOUNT = 0x10 29 | QTAUTH = 0x08 30 | QTTMP = 0x04 31 | QTSYMLINK = 0x02 32 | QTFILE = 0x00 33 | 34 | DMDIR = 0x80000000 35 | DMAPPEND = 0x40000000 36 | DMEXCL = 0x20000000 37 | DMMOUNT = 0x10000000 38 | DMAUTH = 0x08000000 39 | DMTMP = 0x04000000 40 | DMSYMLINK = 0x02000000 41 | DMDEVICE = 0x00800000 42 | DMNAMEDPIPE = 0x00200000 43 | DMSOCKET = 0x00100000 44 | DMSETUID = 0x00080000 45 | DMSETGID = 0x00040000 46 | DMREAD = 0x4 47 | DMWRITE = 0x2 48 | DMEXEC = 0x1 49 | 50 | NOTAG = 0xffff 51 | NOFID = 0xffffffff 52 | NOUID = 0xffffffff 53 | IOHDRSZ = 24 54 | 55 | MREPL = 0x0000 56 | MBEFORE = 0x0001 57 | MAFTER = 0x0002 58 | MCREATE = 0x0004 59 | MCACHE = 0x0010 60 | ) 61 | -------------------------------------------------------------------------------- /plan9/srv9p/example/ramfs/main.go: -------------------------------------------------------------------------------- 1 | //go:build plan9 2 | 3 | // Ramfs serves a memory-based file system. 4 | // It is a demo of srv9p with file trees. 5 | package main 6 | 7 | import ( 8 | "context" 9 | "flag" 10 | "fmt" 11 | "log" 12 | "os" 13 | "slices" 14 | "sync" 15 | "syscall" 16 | 17 | "9fans.net/go/plan9" 18 | "9fans.net/go/plan9/srv9p" 19 | ) 20 | 21 | func usage() { 22 | fmt.Fprintf(os.Stderr, "usage: ramfs [-m mtpt] [-s srvname\n") 23 | flag.PrintDefaults() 24 | os.Exit(1) 25 | } 26 | 27 | var ( 28 | mtpt = flag.String("m", "/tmp", "mount at `mtpt`") 29 | srvname = flag.String("s", "", "post service at /srv/`name`") 30 | verbose = flag.Bool("v", false, "print protocol trace on standard error") 31 | ) 32 | 33 | func main() { 34 | log.SetFlags(0) 35 | log.SetPrefix("ramfs: ") 36 | flag.Usage = usage 37 | flag.Parse() 38 | if flag.NArg() != 0 { 39 | usage() 40 | } 41 | 42 | args := []string{} 43 | if *verbose { 44 | args = append(args, "-v") 45 | } 46 | srv9p.PostMountServe(*srvname, *mtpt, syscall.MREPL|syscall.MCREATE, args, ramfsServer) 47 | } 48 | 49 | // A ramFile is the per-File storage, saved in the File's Aux field. 50 | type ramFile struct { 51 | mu sync.Mutex 52 | data []byte 53 | } 54 | 55 | func ramfsServer() *srv9p.Server { 56 | srv := &srv9p.Server{ 57 | Tree: srv9p.NewTree("ram", "ram", plan9.DMDIR|0777, nil), 58 | Open: func(ctx context.Context, fid *srv9p.Fid, mode uint8) error { 59 | if mode&plan9.OTRUNC != 0 { 60 | rf := fid.File().Aux.(*ramFile) 61 | rf.mu.Lock() 62 | defer rf.mu.Unlock() 63 | 64 | rf.data = nil 65 | } 66 | return nil 67 | }, 68 | Create: func(ctx context.Context, fid *srv9p.Fid, name string, perm plan9.Perm, mode uint8) (plan9.Qid, error) { 69 | f, err := fid.File().Create(name, "ram", perm, new(ramFile)) 70 | if err != nil { 71 | return plan9.Qid{}, err 72 | } 73 | fid.SetFile(f) 74 | return f.Stat.Qid, nil 75 | }, 76 | Read: func(ctx context.Context, fid *srv9p.Fid, data []byte, offset int64) (int, error) { 77 | rf := fid.File().Aux.(*ramFile) 78 | rf.mu.Lock() 79 | defer rf.mu.Unlock() 80 | 81 | return fid.ReadBytes(data, offset, rf.data) 82 | }, 83 | Write: func(ctx context.Context, fid *srv9p.Fid, data []byte, offset int64) (int, error) { 84 | rf := fid.File().Aux.(*ramFile) 85 | rf.mu.Lock() 86 | defer rf.mu.Unlock() 87 | 88 | if int64(int(offset)) != offset || int(offset)+len(data) < 0 { 89 | return 0, srv9p.ErrBadOffset 90 | } 91 | end := int(offset) + len(data) 92 | if len(rf.data) < end { 93 | rf.data = slices.Grow(rf.data, end-len(rf.data)) 94 | rf.data = rf.data[:end] 95 | } 96 | copy(rf.data[offset:], data) 97 | return len(data), nil 98 | }, 99 | } 100 | if *verbose { 101 | srv.Trace = os.Stderr 102 | } 103 | 104 | return srv 105 | } 106 | -------------------------------------------------------------------------------- /plan9/srv9p/example/roundtrip/main.go: -------------------------------------------------------------------------------- 1 | //go:build plan9 2 | 3 | // Roundtrip demonstrates a write-read round trip with a synthetic file like /dev/rot13. 4 | package main 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "io" 10 | "log" 11 | "os" 12 | "syscall" 13 | ) 14 | 15 | func usage() { 16 | fmt.Fprintf(os.Stderr, "usage: roundtrip file\n") 17 | os.Exit(1) 18 | } 19 | 20 | func main() { 21 | log.SetFlags(0) 22 | log.SetPrefix("roundtrip: ") 23 | flag.Usage = usage 24 | flag.Parse() 25 | if flag.NArg() != 1 { 26 | usage() 27 | } 28 | 29 | f, err := os.OpenFile(flag.Arg(0), os.O_RDWR, 0) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | check(io.Copy(f, os.Stdin)) 35 | check(syscall.Write(int(f.Fd()), nil)) // syscall.Write because f.Write(nil) doesn't make a system call 36 | check(f.Seek(0, 0)) 37 | check(io.Copy(os.Stdout, f)) 38 | } 39 | 40 | func check[T any](_ T, err error) { 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /plan9/srv9p/fid.go: -------------------------------------------------------------------------------- 1 | package srv9p 2 | 3 | import ( 4 | "errors" 5 | "sync/atomic" 6 | 7 | "9fans.net/go/plan9" 8 | ) 9 | 10 | // A Fid represents a file reference in a 9P connection. 11 | type Fid struct { 12 | // immutable fields 13 | uid string // uid used in auth, attach 14 | aname string // aname used in auth 15 | conn *conn 16 | 17 | // ref counts references to this fid. 18 | // When the last reference to a fid is dropped, 19 | // [Fid.decRef] calls [Srv.Clunk]. 20 | // The fids refMap holds a reference, 21 | // and each active request referring to fid also holds a reference 22 | // (in a local variable in the function running the request). 23 | ref atomic.Int32 // references 24 | 25 | // mutable fields 26 | // These are all atomic to avoid data races. 27 | // In normal protocol usage there should only be one request 28 | // modifying most of these at a time, but a misbehaved client 29 | // might do something like send parallel Topen requests. 30 | // Using atomics avoids memory races in that case. 31 | file atomic.Pointer[File] // file in srv.Tree 32 | iounit atomic.Uint32 // i/o unit 33 | qid atomicValue[plan9.Qid] // current qid 34 | aux atomicValue[any] // for client use 35 | omode atomic.Int32 // open mode (-1 if unopened) 36 | dirReader atomic.Pointer[dirReader] // directory reader (for file trees) 37 | dirOffset atomic.Int64 // required offset of next directory read (0 also okay) 38 | dirIndex atomic.Int64 // directory read index, for use by [Fid.ReadDir] 39 | } 40 | 41 | func (f *Fid) File() *File { return f.file.Load() } 42 | func (f *Fid) SetFile(file *File) { f.file.Store(file) } 43 | 44 | func (f *Fid) Aux() any { return f.aux.Load() } 45 | func (f *Fid) SetAux(aux any) { f.aux.Store(aux) } 46 | 47 | func (f *Fid) Qid() plan9.Qid { return f.qid.Load() } 48 | func (f *Fid) SetQid(qid plan9.Qid) { f.qid.Store(qid) } 49 | 50 | func (f *Fid) Iounit() uint32 { return f.iounit.Load() } 51 | func (f *Fid) SetIounit(iounit uint32) { f.iounit.Store(iounit) } 52 | 53 | // newFid allocates a new fid with the given id and returns a new reference. 54 | func (c *conn) newFid(id uint32) (*Fid, error) { 55 | f := &Fid{conn: c} 56 | f.omode.Store(-1) 57 | f.incRef() // for caller 58 | f.incRef() // for pool 59 | if !c.fids.tryInsert(id, f) { 60 | // no decRef+CleanupFid because no one else knows about f 61 | return nil, errors.New("duplicate fid") 62 | } 63 | return f, nil 64 | } 65 | 66 | func (f *Fid) incRef() { 67 | if f != nil { 68 | f.ref.Add(1) 69 | } 70 | } 71 | 72 | func (f *Fid) decRef() { 73 | if f != nil && f.ref.Add(-1) == 0 { 74 | f.dirReader.Load().Close() 75 | if clunk := f.conn.srv.Clunk; clunk != nil { 76 | clunk(f) 77 | } 78 | } 79 | } 80 | 81 | // An atomicValue can be atomically read and written. 82 | type atomicValue[T any] struct { 83 | ptr atomic.Pointer[T] 84 | } 85 | 86 | func (v *atomicValue[T]) Load() T { 87 | p := v.ptr.Load() 88 | if p == nil { 89 | var zero T 90 | return zero 91 | } 92 | return *p 93 | } 94 | 95 | func (v *atomicValue[T]) Store(t T) { 96 | v.ptr.Store(&t) 97 | } 98 | -------------------------------------------------------------------------------- /plan9/srv9p/help.go: -------------------------------------------------------------------------------- 1 | package srv9p 2 | 3 | import "9fans.net/go/plan9" 4 | 5 | // ReadDir satisfies the read request r by iterating through 6 | // a directory defined by gen. Gen(n, d) must fill in d with 7 | // the n'th directory entry and return true, or else return false. 8 | func (f *Fid) ReadDir(data []byte, offset int64, gen func(int, *plan9.Dir) bool) (int, error) { 9 | start := 0 10 | if offset > 0 { 11 | start = int(f.dirIndex.Load()) 12 | } 13 | 14 | n := 0 15 | for n < len(data) { 16 | var d plan9.Dir 17 | if !gen(start, &d) { 18 | break 19 | } 20 | stat, err := d.Bytes() 21 | if err != nil { 22 | if n == 0 { 23 | return 0, err 24 | } 25 | break 26 | } 27 | if len(data[n:]) < len(stat) { 28 | break 29 | } 30 | copy(data[n:], stat) 31 | n += len(stat) 32 | start++ 33 | } 34 | f.dirIndex.Store(int64(start)) 35 | return n, nil 36 | } 37 | 38 | // ReadBytes satsifies the read request r as if the 39 | // entire file being read were the buffer b, 40 | // handling offset and count appropriately. 41 | func (f *Fid) ReadBytes(dst []byte, offset int64, src []byte) (int, error) { 42 | if offset < 0 || offset >= int64(len(src)) { 43 | return 0, nil 44 | } 45 | n := copy(dst, src[offset:]) 46 | return n, nil 47 | } 48 | 49 | // ReadString satsifies the read request r as if the 50 | // entire file being read were the string s. 51 | // handling offset and count appropriately. 52 | func (f *Fid) ReadString(dst []byte, offset int64, src string) (int, error) { 53 | if offset < 0 || offset >= int64(len(src)) { 54 | return 0, nil 55 | } 56 | n := copy(dst, src[offset:]) 57 | return n, nil 58 | } 59 | -------------------------------------------------------------------------------- /plan9/srv9p/refmap.go: -------------------------------------------------------------------------------- 1 | package srv9p 2 | 3 | import "sync" 4 | 5 | type incDecRef interface { 6 | incRef() 7 | decRef() 8 | } 9 | 10 | // A refMap is a map from K to V, with reference counting on the V's. 11 | type refMap[K comparable, V incDecRef] struct { 12 | rw sync.RWMutex 13 | m map[K]V 14 | } 15 | 16 | func newRefMap[K comparable, V incDecRef]() *refMap[K, V] { 17 | return &refMap[K, V]{ 18 | m: make(map[K]V), 19 | } 20 | } 21 | 22 | // insert inserts the key, val pair into the pool. 23 | // The caller's reference to val is taken over by the pool. 24 | // If there was already a value with the given key in the pool 25 | // replaced by the insert, insert returns that value. 26 | // Otherwise insert returns the zero T. 27 | func (m *refMap[K, V]) insert(key K, val V) (old V) { 28 | m.rw.Lock() 29 | defer m.rw.Unlock() 30 | 31 | old = m.m[key] 32 | m.m[key] = val 33 | return old 34 | } 35 | 36 | // tryInsert tries to insert the key, val pair into the pool. 37 | // The caller's reference to val is taken over by the pool. 38 | // On success, tryInsert returns true. 39 | // If there is already a value with the given id in the pool, 40 | // that value is left in place, and tryInsert returns false. 41 | func (m *refMap[K, V]) tryInsert(key K, val V) bool { 42 | m.rw.Lock() 43 | defer m.rw.Unlock() 44 | 45 | if _, ok := m.m[key]; ok { 46 | return false 47 | } 48 | m.m[key] = val 49 | return true 50 | } 51 | 52 | // lookup returns the value with the given key, or else the zero T. 53 | // If the value is found in the pool, lookup returns a new reference to it 54 | // (calls incRef before returning the value). 55 | func (m *refMap[K, V]) lookup(key K) V { 56 | m.rw.RLock() 57 | defer m.rw.RUnlock() 58 | 59 | v, ok := m.m[key] 60 | if ok { 61 | v.incRef() 62 | } 63 | return v 64 | } 65 | 66 | // delete removes the value with the given id 67 | // from the pool and returns it. 68 | // If there is no value with that id, remove returns nil. 69 | func (m *refMap[K, V]) delete(key K) V { 70 | m.rw.Lock() 71 | defer m.rw.Unlock() 72 | 73 | v, ok := m.m[key] 74 | if !ok { 75 | var zero V 76 | return zero 77 | } 78 | delete(m.m, key) 79 | return v 80 | } 81 | 82 | // drop removes the value with the given id 83 | // from the pool and decRefs it. 84 | // If there is no value with that id, drop is a no-op. 85 | func (m *refMap[K, V]) drop(key K) { 86 | m.rw.Lock() 87 | defer m.rw.Unlock() 88 | 89 | if v, ok := m.m[key]; ok { 90 | delete(m.m, key) 91 | v.decRef() 92 | } 93 | } 94 | 95 | // clear removes all values from the pool, decRef'ing them all. 96 | func (m *refMap[K, V]) clear() { 97 | m.rw.Lock() 98 | all := m.m 99 | m.m = make(map[K]V) 100 | m.rw.Unlock() 101 | 102 | for _, v := range all { 103 | v.decRef() 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /plan9/srv9p/req.go: -------------------------------------------------------------------------------- 1 | package srv9p 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "sync" 7 | "sync/atomic" 8 | 9 | "9fans.net/go/plan9" 10 | ) 11 | 12 | // A request represents a pending request on a 9P connection 13 | type request struct { 14 | // immutable fields 15 | conn *conn 16 | ctx context.Context 17 | cancel func() 18 | ifcall *plan9.Fcall 19 | ofcall *plan9.Fcall 20 | duplicate bool 21 | err error 22 | 23 | // mutable fields 24 | mu sync.Mutex 25 | responded atomic.Bool 26 | flush []*request 27 | } 28 | 29 | // newReq allocates a new request in the conn c for the 9P request f. 30 | // If there is already a request with that tag, the returned request has 31 | // r.duplicate and r.err set, and the caller is expected to call r.respond rather 32 | // than process it. 33 | func (c *conn) newReq(f *plan9.Fcall) *request { 34 | ctx, cancel := context.WithCancel(context.Background()) 35 | r := &request{ 36 | conn: c, 37 | ctx: ctx, 38 | cancel: cancel, 39 | ifcall: f, 40 | ofcall: new(plan9.Fcall), 41 | } 42 | if !c.reqs.tryInsert(f.Tag, r) { 43 | r.duplicate = true 44 | r.err = errors.New("duplicate tag") 45 | return r 46 | } 47 | return r 48 | } 49 | 50 | // incRef and decRef are no-ops. 51 | // They are implemented only so that request can be used with refMap. 52 | func (r *request) incRef() {} 53 | func (r *request) decRef() {} 54 | -------------------------------------------------------------------------------- /plan9/srv9p/testdata/basic.txt: -------------------------------------------------------------------------------- 1 | serve tree 2 | Tversion msize 8192 version 9P2025.u 3 | Rversion msize 8192 version 9P2000 4 | Tauth tag 1 afid 1 uname rsc 5 | Rerror tag 1 ename 'srv9p.test: authentication not required' 6 | # from now on, implicitly using tag 0 7 | Tattach fid 1 afid NOFID uname rsc 8 | Rattach qid 0x0.0.d 9 | Tstat fid 1 10 | Rstat stat (name / uid glenda gid bunny muid glenda qid 0x0.0.d mode d-rwxr-xr-x mtime 123456789 atime 200000000 length 0 type 0 dev 0) 11 | Twalk fid 1 newfid 2 wname hello 12 | Rwalk wqid 0x1.0 13 | Tstat fid 2 14 | Rstat stat (name hello uid gopher gid bunny muid gopher qid 0x1.0 mode --rw-r--r-- mtime 123456789 atime 200000000 length 0 type 0 dev 0) 15 | Twalk fid 2 newfid 3 16 | Rwalk 17 | Topen fid 3 mode OREAD 18 | Ropen qid 0x1.0 19 | Tread fid 3 count 1 20 | Rread data h 21 | Tread fid 3 offset 1 count 100 22 | Rread data 'ello, go nuts' 23 | Tread fid 3 count 100 24 | Rread data 'hello, go nuts' 25 | Tclunk fid 3 26 | Rclunk 27 | Tread fid 3 count 100 28 | Rerror ename 'unknown fid' 29 | Tflush 30 | Rflush 31 | Tflush oldtag 2 32 | Rflush 33 | Twalk fid 1 newfid 3 wname slang 34 | Rwalk wqid 0x2.0.d 35 | Twalk fid 3 newfid 3 wname hi 36 | Rwalk wqid 0x3.0 37 | Tclunk fid 3 38 | Rclunk 39 | Twalk fid 1 newfid 2 40 | Rerror ename 'duplicate fid' 41 | Tclunk fid 2 42 | Rclunk 43 | Twalk fid 1 newfid 2 wname [slang hi] 44 | Rwalk wqid [0x2.0.d 0x3.0] 45 | Tclunk fid 2 46 | Rclunk 47 | Twalk fid 1 newfid 2 48 | Rwalk 49 | Topen fid 2 mode OWRITE 50 | Rerror ename 'is a directory' 51 | Topen fid 2 mode ORDWR 52 | Rerror ename 'is a directory' 53 | Topen fid 2 mode OREAD|OTRUNC 54 | Rerror ename 'is a directory' 55 | Topen fid 2 mode OREAD 56 | Ropen qid 0x0.0.d 57 | Tread fid 2 count 1000 58 | Rread data [(name hello uid gopher gid bunny muid gopher qid 0x1.0 mode --rw-r--r-- atime 200000000 mtime 123456789 length 0 type 0 dev 0) (name slang uid gopher gid bunny muid gopher qid 0x2.0.d mode d-r-xr-xr-x atime 200000000 mtime 123456789 length 0 type 0 dev 0)] 59 | Tread fid 2 offset 142 count 1000 60 | Rread data '' 61 | Tread fid 2 offset 140 count 1000 62 | Rerror ename 'bad offset' 63 | Twrite fid 2 64 | Rerror ename 'not open for write' 65 | Tremove fid 2 66 | Rerror ename 'permission denied' 67 | Tremove fid 2 68 | Rerror ename 'unknown fid' 69 | Twalk fid 1 newfid 2 wname [slang hi] 70 | Rwalk wqid [0x2.0.d 0x3.0] 71 | Twstat fid 2 stat (name yo) 72 | Rerror ename 'wstat prohibited' 73 | Tclunk fid 2 74 | Rclunk 75 | Tclunk fid 1 76 | Rclunk 77 | -------------------------------------------------------------------------------- /plan9/srv9p/testdata/flush.txt: -------------------------------------------------------------------------------- 1 | # TODO test Tflush of actual blocked request 2 | -------------------------------------------------------------------------------- /plan9/srv9p/testdata/pipe.txt: -------------------------------------------------------------------------------- 1 | serve pipe 2 | Tversion msize 8192 version 9P2000 3 | Rversion msize 8192 version 9P2000 4 | Tattach fid 1 afid NOFID 5 | Rattach qid 0x0.0.d 6 | Tstat fid 1 7 | Rstat stat (name / uid pipe gid band muid '' qid 0x0.0.d mode d-r-xr-xr-x mtime 0 atime 0 length 0 type 0 dev 0) 8 | Twalk fid 1 newfid 2 9 | Rwalk 10 | Topen fid 2 mode OREAD 11 | Ropen qid 0x0.0.d 12 | Tread fid 2 count 1000 13 | Rread data [(name read uid pipe gid band muid '' qid 0x1.0 mode --r--r--r-- atime 0 mtime 0 length 0 type 0 dev 0) (name write uid pipe gid band muid '' qid 0x2.0 mode ---w--w--w- atime 0 mtime 0 length 0 type 0 dev 0)] 14 | Tclunk fid 2 15 | Rclunk 16 | Twalk tag 1 fid 1 newfid 2 wname read 17 | Twalk tag 2 fid 1 newfid 3 wname write 18 | Rwalk tag 1 wqid 0x1.0 19 | Rwalk tag 2 wqid 0x2.0 20 | Topen fid 2 mode OREAD 21 | Ropen qid 0x1.0 22 | Topen fid 3 mode OWRITE 23 | Ropen qid 0x2.0 24 | Twrite tag 1 fid 3 data 'hello world' 25 | Tread tag 2 fid 2 count 100 26 | Rread tag 2 data 'hello world' 27 | Rwrite tag 1 count 11 28 | Twrite tag 1 fid 3 data 'hello world' 29 | Tflush tag 2 oldtag 1 30 | Rerror tag 1 ename flushed 31 | Rflush tag 2 32 | -------------------------------------------------------------------------------- /plan9/srv9p/testdata/ramfs.txt: -------------------------------------------------------------------------------- 1 | serve ramfs 2 | Tversion msize 8192 version 9P2000 3 | Rversion msize 8192 version 9P2000 4 | Tattach fid 1 afid NOFID 5 | Rattach qid 0x0.0.d 6 | Twalk fid 1 newfid 2 7 | Rwalk 8 | Tcreate fid 2 name hello perm --rw-rw-rw- mode ORDWR 9 | Rcreate qid 0x1.0 10 | Twrite fid 2 data 'hello world' 11 | Rwrite count 11 12 | Tclunk fid 2 13 | Rclunk 14 | Twalk fid 1 newfid 2 wname hello 15 | Rwalk wqid 0x1.1 16 | Topen fid 2 mode OREAD 17 | Ropen qid 0x1.1 18 | Tread fid 2 count 100 offset 0 19 | Rread data 'hello world' 20 | Tread fid 2 count 100 offset 11 21 | Rread data '' 22 | Tclunk fid 2 23 | Rclunk 24 | 25 | -------------------------------------------------------------------------------- /plan9/srv9p/uid.go: -------------------------------------------------------------------------------- 1 | package srv9p 2 | 3 | // hasPerm does simplistic permission checking. 4 | // It assumes that each user is the leader of her own group. 5 | func hasPerm(f *File, uid string, perm int) bool { 6 | m := int(f.Stat.Mode) // other 7 | if perm&m == perm { 8 | return true 9 | } 10 | 11 | if f.Stat.Uid == uid { 12 | m |= m >> 6 13 | if perm&m == perm { 14 | return true 15 | } 16 | } 17 | 18 | if f.Stat.Gid == uid { 19 | m |= m >> 3 20 | if perm&m == perm { 21 | return true 22 | } 23 | } 24 | 25 | return false 26 | } 27 | -------------------------------------------------------------------------------- /plumb/plumb_p9p.go: -------------------------------------------------------------------------------- 1 | // +build !plan9 2 | 3 | package plumb 4 | 5 | import ( 6 | "9fans.net/go/plan9/client" 7 | ) 8 | 9 | func mountPlumb() { 10 | fsys, fsysErr = client.MountService("plumb") 11 | } 12 | -------------------------------------------------------------------------------- /plumb/plumb_plan9.go: -------------------------------------------------------------------------------- 1 | package plumb 2 | 3 | import ( 4 | "9fans.net/go/plan9/client" 5 | ) 6 | 7 | func mountPlumb() { 8 | fsys = &client.Fsys{Mtpt: "/mnt/plumb"} 9 | fsysErr = nil 10 | } 11 | -------------------------------------------------------------------------------- /plumb/plumb_test.go: -------------------------------------------------------------------------------- 1 | package plumb 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestBasic(t *testing.T) { 11 | attr := &Attribute{ 12 | Name: "addr", 13 | Value: "/root/", 14 | } 15 | message := &Message{ 16 | Src: "plumb", 17 | Dst: "edit", 18 | Dir: "/Users/r", 19 | Type: "text", 20 | Attr: attr, 21 | Data: []byte("/etc/passwd"), 22 | } 23 | var buf bytes.Buffer 24 | buf.Reset() 25 | message.Send(&buf) 26 | m := new(Message) 27 | err := m.Recv(&buf) 28 | if err != nil { 29 | t.Fatalf("recv: %s", err) 30 | } 31 | if !reflect.DeepEqual(message, m) { 32 | t.Fatalf("difference:\n%+v\n%+v", message, m) 33 | } 34 | } 35 | 36 | type quoteTest struct { 37 | unquoted, quoted string 38 | } 39 | 40 | var quoteTests = []quoteTest{ 41 | {"", ""}, 42 | {"abc", "abc"}, 43 | {" ", "' '"}, 44 | {"'", "''''"}, 45 | {"''", "''''''"}, 46 | {"abc def", "'abc def'"}, 47 | {"abc'def", "'abc''def'"}, 48 | {"abc'' ''def", "'abc'''' ''''def'"}, 49 | } 50 | 51 | func TestQuoting(t *testing.T) { 52 | for _, test := range quoteTests { 53 | q := quoteAttribute(test.unquoted) 54 | if q != test.quoted { 55 | t.Errorf("quoting failed: for %q expected %q got %q", test.unquoted, test.quoted, q) 56 | } 57 | u, err := unquoteAttribute(test.quoted) 58 | if err != nil { 59 | t.Errorf("unquoting error for %q: %s", test.quoted, err) 60 | continue 61 | } 62 | if u != test.unquoted { 63 | t.Errorf("unquoting failed: for %q expected %q got %q", test.quoted, test.unquoted, u) 64 | } 65 | } 66 | } 67 | 68 | func TestMultipleAttributes(t *testing.T) { 69 | // Make up a list of attributes from the quoting tests. 70 | var attr *Attribute 71 | for i, test := range quoteTests { 72 | attr = &Attribute{ 73 | Name: fmt.Sprintf("attr%d", i), 74 | Value: test.unquoted, 75 | Next: attr, 76 | } 77 | } 78 | message := &Message{ 79 | Src: "plumb", 80 | Dst: "edit", 81 | Dir: "/Users/r", 82 | Type: "text", 83 | Attr: attr, 84 | Data: []byte("/etc/passwd"), 85 | } 86 | var buf bytes.Buffer 87 | buf.Reset() 88 | message.Send(&buf) 89 | m := new(Message) 90 | err := m.Recv(&buf) 91 | if err != nil { 92 | t.Fatalf("recv: %s", err) 93 | } 94 | if !reflect.DeepEqual(message, m) { 95 | t.Fatalf("difference:\n%+v\n%+v", message, m) 96 | } 97 | } 98 | --------------------------------------------------------------------------------