ref: d180b70c6c69ffe8168cc8571772ad6e15ac6aba
author: Ori Bernstein <[email protected]>
date: Sun Nov 29 13:39:41 EST 2020
import from hg
--- /dev/null
+++ b/bld.proj
@@ -1,0 +1,17 @@
+lib draw =
+ chan.myr
+ draw.myr
+ event.myr
+ font.myr
+ image.myr
+ init.myr
+ load.myr
+ pack.myr
+ string.myr
+ types.myr
+;;
+
+bin testdraw =
+ testdraw.myr
+ lib draw
+;;
--- /dev/null
+++ b/chan.myr
@@ -1,0 +1,83 @@
+use std
+use "types"
+
+pkg draw =
+ pkglocal const chanparse : (str : byte[:] -> std.option(chan))
+ pkglocal const chanfmt : (buf : byte[:], chan : chan -> byte[:])
+ pkglocal const chandepth : (chan : chan -> uint32)
+;;
+
+var channames = "rgbkamx"
+
+const chanparse = {str
+ var c, n, chan, depth
+
+ str = std.strstrip(str)
+ depth = 0
+ chan = 0
+ for var i = 0; i < str.len - 1; i += 2
+ if std.isspace((str[i] : char))
+ break
+ ;;
+ match std.strfind(channames, str[i:i+1])
+ | `std.None: -> `std.None
+ | `std.Some ty:
+ c = (str[i + 1] : char)
+ n = std.charval(c, 10)
+ if n < 0
+ -> `std.None
+ ;;
+ depth += n
+ chan <<= 8
+ chan |= dc((ty : uint32), n)
+ ;;
+ ;;
+
+ if depth==0 || (depth>8 && depth%8 != 0) || (depth<8 && 8%depth != 0)
+ -> `std.None
+ ;;
+ -> `std.Some chan
+}
+
+const chanfmt = {buf, chan
+ var c, name, rc, i
+
+ rc = 0
+ for c = chan; c != 0; c >>= 8
+ rc <<= 8
+ rc |= c & 0xff
+ ;;
+
+ i = 0
+ for c = rc; c != 0; c >>= 8
+ name = std.decode(channames[ctype(c & 0xff):])
+ std.bfmt(buf[i:i+2], "{}{}", name, cdepth(c & 0xff))
+ i += 2
+ ;;
+ -> buf[:i]
+}
+
+const chandepth = {chan
+ var depth
+
+ depth = 0
+ for var c = chan; c != 0; c >>= 8
+ depth += cdepth(c)
+ ;;
+ if depth==0 || (depth>8 && depth%8 != 0) || (depth<8 && 8%depth != 0)
+ -> 0
+ ;;
+ -> depth
+}
+
+const dc = {ty, nbit
+ -> (((ty & 15) << 4) | (nbit & 15) : chan)
+}
+
+const ctype = {c
+ -> ((c & 0xf0) >> 4 : uint32)
+}
+
+const cdepth = {c
+ -> (c & 0xf : uint32)
+}
\ No newline at end of file
--- /dev/null
+++ b/draw.myr
@@ -1,0 +1,51 @@
+use std
+
+use "types"
+use "chan"
+use "pack"
+
+pkg draw =
+ const fill : (dst : image#, src : image#, r : rect -> void)
+ const draw : (dst : image#, r : rect, src : image#, p0 : point -> void)
+ const drawop : (dst : image#, r : rect, src : image#, p0 : point, op : drawop -> void)
+ const mdrawop : (dst : image#, r : rect, s : image#, p0 : point, mask : image#, p1 : point, op : drawop -> void)
+ const inset : (r : rect, off : int -> rect)
+;;
+
+const fill = {dst, src, r
+ // d dstid[4] srcid[4] maskid[4] dstr[4*4] srcp[2*4] maskp[2*4]
+ mdrawop(dst, r, src, Zp, dst.dpy.opaque, Zp, SoverD)
+}
+
+const draw = {dst, r, src, p0
+ mdrawop(dst, r, src, p0, dst.dpy.opaque, Zp, SoverD)
+}
+
+const drawop = {dst, r, src, p0, op
+ mdrawop(dst, r, src, p0, dst.dpy.opaque, Zp, SoverD)
+}
+
+const mdrawop = {dst, r, src, p0, mask, p1, op
+ if op != SoverD
+ pack(dst.dpy, "bb", 0, ('O' : byte), (op : byte))
+ ;;
+ pack(dst.dpy, "biiiriiii", 0, \
+ ('d' : byte), /* draw */ \
+ dst.id, \
+ src.id, \
+ mask.id, \
+ r, \
+ p0.x, \
+ p0.y, \
+ p1.x, \
+ p1.y)
+
+}
+
+const inset = {r, off
+ r.x0 += off
+ r.y0 += off
+ r.x1 -= off
+ r.y1 -= off
+ -> r
+}
--- /dev/null
+++ b/event.myr
@@ -1,0 +1,167 @@
+use std
+use sys
+use thread
+
+use "types"
+
+pkg draw =
+ const event : (dpy : display# -> event#)
+
+ pkglocal const mouseproc : (dpy : display# -> void)
+ pkglocal const kbdproc : (dpy : display# -> void)
+ pkglocal const refrproc : (dpy : display# -> void)
+;;
+
+const mouseproc = {dpy
+ var buf : byte[49]
+ var sp : byte[:][5]
+ var ev : event#
+ var x, y, b, o
+
+ o = -1
+ while true
+ match std.read(dpy.mousefd, buf[:])
+ | `std.Ok 49: /* got the message */
+ | `std.Ok _: break
+ | `std.Err _: break
+ ;;
+
+ std.bstrtok(sp[:], buf[:])
+ x = std.getv(std.intparse(sp[1]), 0)
+ y = std.getv(std.intparse(sp[2]), 0)
+ b = std.getv(std.intparse(sp[3]), 0)
+ if o < 0
+ o = b
+ ;;
+ match sp[0]
+ | "r": ev = std.mk(`Resize)
+ | "m": ev = std.mk(`Mmove (x, y))
+ | chr: std.fatal("garbled mouse message")
+ ;;
+ send(dpy, &dpy.mousep, ev)
+ if b > o
+ ev = std.mk(`Mdown (x, y, b))
+ send(dpy, &dpy.mousep, ev)
+ elif b < o
+ ev = std.mk(`Mup (x, y, b))
+ send(dpy, &dpy.mousep, ev)
+ ;;
+ o = b
+ ;;
+}
+
+const kbdproc = {dpy
+ var buf : byte[128]
+ var ev : event#
+ var k, c, s
+
+ while true
+ match std.read(dpy.kbdfd, buf[:])
+ | `std.Err e: break
+ | `std.Ok 0: break
+ | `std.Ok n:
+ c = -1
+ s = std.cstrconv(buf[:n])
+ (k, s) = std.charstep(s)
+ match k
+ | 'k': /* drop */
+ while s.len > 0 && c != 0
+ (c, s) = std.charstep(s)
+ if dpy.rawevt
+ send(dpy, &dpy.kbdp, std.mk(`Kp c))
+ ;;
+ ;;
+ | 'K':
+ while s.len > 0 && c != 0
+ (c, s) = std.charstep(s)
+ if dpy.rawevt
+ send(dpy, &dpy.kbdp, std.mk(`Kr c))
+ ;;
+ ;;
+ | 'c':
+ while s.len > 0 && c != 0
+ (c, s) = std.charstep(s)
+ send(dpy, &dpy.kbdp, std.mk(`Kp c))
+ ;;
+ | _:
+ std.die("fail")
+ ;;
+
+ ;;
+ ;;
+:fail
+}
+
+const refrproc = {dpy
+ var buf : byte[128]
+ var ev : event#
+
+ while true
+ std.read(dpy.refrfd, buf[:])
+ ev = std.mk(`Refresh)
+ send(dpy, &dpy.refrp, ev)
+
+ ;;
+}
+
+const event = {dpy
+ var p, r
+
+ thread.mtxlock(&dpy.qmtx)
+ /* find tag for rendezvous */
+ for evp : [dpy.refrp, dpy.mousep, dpy.kbdp][:]
+ if evp != (0 : void#)
+ p = evp
+ goto found
+ ;;
+ ;;
+
+ /* if we couldn't find a tag, enqueue ourselves */
+ p = (dpy : void#)
+ for qp : [&dpy.refrp, &dpy.mousep, &dpy.kbdp][:]
+ qp# = p
+ ;;
+:found
+ thread.mtxunlock(&dpy.qmtx)
+
+ r = (-1 : void#)
+ while r == (-1 : void#)
+ r = sys.rendezvous(p, (0 : void#))
+ ;;
+
+ /* now dequeue */
+ thread.mtxlock(&dpy.qmtx)
+ for qp : [&dpy.refrp, &dpy.mousep, &dpy.kbdp][:]
+ if qp# == (dpy : void#)
+ qp# = (0 : void#)
+ ;;
+ ;;
+ thread.mtxunlock(&dpy.qmtx)
+ -> (r : event#)
+}
+
+const send = {dpy, qp, evp
+ var p, r
+
+ thread.mtxlock(&dpy.qmtx)
+ if qp# == (0 : void#)
+ /* nobody is queued; we enquue ourselves */
+ p = (qp : void#)
+ qp# = p
+ else
+ /* somebody is waiting, use their tag */
+ p = qp#
+ qp# = (0 : void#)
+ ;;
+ thread.mtxunlock(&dpy.qmtx)
+ r = (-1 : void#)
+ while r == (-1 : void#)
+ r = sys.rendezvous(p, (evp : void#))
+ ;;
+ /* now dequeue */
+ thread.mtxlock(&dpy.qmtx)
+ if qp# == (qp : void#)
+ qp# = (0 : void#)
+ ;;
+ thread.mtxunlock(&dpy.qmtx)
+}
--- /dev/null
+++ b/font.myr
@@ -1,0 +1,171 @@
+use std
+use bio
+use iter
+
+use "types"
+use "image"
+use "load"
+use "pack"
+
+pkg draw =
+ const openfont : (dpy : display#, name : byte[:] -> std.result(font#, err))
+
+ pkglocal const loadsubfont : (dpy : display#, sub : subfont# -> void)
+ pkglocal const cachechar : (font : font#, c : char -> int16)
+;;
+
+const openfont = {dpy, name
+ var dir
+
+ dir = std.dirname(name)
+ match bio.open(name, bio.Rd)
+ | `std.Ok f: -> loadfont(dpy, dir, f)
+ | `std.Err e: -> `std.Err `Efont
+ ;;
+}
+
+const cachechar = {ft, c
+ match std.htget(ft.cache, c)
+ | `std.Some inf:
+ inf.hits++
+ -> inf.idx
+ | `std.None:
+ -> loadglyph(ft, c)
+ ;;
+}
+
+const loadglyph = {ft, c
+ var r : rect
+ var p : point
+ var g, info
+
+ for sub : iter.byref(ft.subf)
+ if c < sub.lo || c >= sub.hi
+ continue
+ ;;
+ if sub.glyph.len == 0
+ loadsubfont(ft.cimage.dpy, sub)
+ ;;
+ /*
+ l cacheid[4] srcid[4] index[2] r[4*4] sp[2*4] left[1]
+ width[1]
+ */
+ g = &sub.glyph[c - sub.lo]
+ p = [.x=g.x, .y=0]
+ r = [.x0=ft.x, .y0=0, .x1=ft.x + g.width, .y1=ft.ascent]
+ pack(ft.cimage.dpy, "biisrpbb", 0, \
+ ('l' : byte), \
+ ft.cimage.id, \
+ sub.image.id, \
+ ft.nextidx, \
+ r, \
+ p, \
+ g.left, \
+ g.width)
+ flush(ft.cimage.dpy)
+ info.hits = 0
+ info.idx = ft.nextidx
+ std.htput(ft.cache, c, info)
+ ft.x += g.width
+ ft.nextidx++
+ -> info.idx
+ ;;
+ -> -1
+}
+
+const loadfont = {dpy, dir, f
+ var sp : byte[:][3]
+ var lo, hi, start, n, r
+ var font
+
+ font = std.zalloc()
+ match bio.readln(f)
+ | `std.Ok ln:
+ std.bstrtok(sp[:2], ln)
+ font.height = std.getv(std.intparse(sp[0]), -1)
+ font.ascent = std.getv(std.intparse(sp[0]), -1)
+ | `std.Err `bio.Eof:
+ | `std.Err e:
+ ;;
+
+ for ln : bio.byline(f)
+ n = 0
+ start = 0
+ std.bstrtok(sp[:3], ln)
+ lo = std.getv(std.intparse(sp[n++]), -1)
+ hi = std.getv(std.intparse(sp[n++]), -1)
+ if sp.len == 4
+ start = std.getv(std.intparse(sp[n++]), -1)
+ ;;
+ std.slpush(&font.subf, [
+ .lo=lo,
+ .hi=hi,
+ .start=start,
+ .path=std.pathcat(dir, sp[n]),
+ ])
+ ;;
+
+ /* heuristic: probably big enough... */
+ font.cache = std.mkht()
+ r = [.x0 = 0, .y0 = 0, .x1 = 1000, .y1 = font.ascent]
+ font.cimage = genallocimage(dpy, 0, Refnone, Cgrey8, DBlack, false, r, r)
+ pack(dpy, "biib", 0, \
+ ('i' : byte), \
+ font.cimage.id, \
+ 128, \
+ font.ascent)
+ -> `std.Ok font
+}
+
+const loadsubfont = {dpy, subf
+ var buf
+
+ match std.slurp(subf.path)
+ | `std.Err e: std.fatal("could not open subf {}: {}\n", subf.path, e)
+ | `std.Ok b: buf = b
+ ;;
+
+ match xload(dpy, buf)
+ | `std.Err e: std.fatal("could not load image {}: {}\n")
+ | `std.Ok (img, off):
+ match readglyphs(buf[off:])
+ | `std.Ok gd:
+ subf.image = img
+ subf.glyph = gd
+ | `std.Err e:
+ std.fatal("invalid glyph data in subf {}\n", subf.path)
+ ;;
+ ;;
+}
+
+const readglyphs = {glyphdata
+ var sp : byte[:][3]
+ var n, height, ascent, err
+ var gd, g
+
+ if glyphdata.len < 3*12 || std.bstrtok(sp[:], glyphdata[:3*12]).len != 3
+ -> `std.Err `Efmt
+ ;;
+
+ err = false
+ n = getint(sp[0], &err)
+ height = getint(sp[1], &err)
+ ascent = getint(sp[2], &err)
+ glyphdata = glyphdata[3*12:]
+ if err || glyphdata.len < n
+ -> `std.Err `Efmt
+ ;;
+
+ gd = [][:]
+ for var i = 0; i <= n; i++
+ g = [
+ .x=std.getle16(glyphdata[6*i+0 : 6*i+2]),
+ .top=(glyphdata[6*i + 2] : int),
+ .bottom=(glyphdata[6*i + 3] : int),
+ .left=(glyphdata[6*i + 4] : int),
+ .width=(glyphdata[6*i + 5] : int),
+ ]
+ std.slpush(&gd, g)
+ ;;
+ -> `std.Ok gd
+}
--- /dev/null
+++ b/image.myr
@@ -1,0 +1,182 @@
+use std
+use sys
+
+use "types"
+use "chan"
+use "pack"
+
+pkg draw =
+ const Ninfo
+ const Infosz
+
+ const namedimage : (dpy : display#, name : byte[:] -> std.result(image#, err))
+ const parseimgfmt : (img : image#, buf : byte[:][:] -> std.result(image#, err))
+ const allocimage : (dpy : display#, r : rect, bg : color, repl : bool -> image#)
+ const allocscreen : (dpy : display#, img : image#, fill : image#, public : bool -> screen#)
+ const allocwindow : (dpy : display#, r : rect -> std.result(image#, err))
+ const freeimage : (img : image# -> void)
+
+ /* general versions */
+ const genallocimage : (dpy : display#, \
+ screenid : int32, \
+ refr : refresh, \
+ chan : chan, \
+ color : color, \
+ repl : bool, \
+ r : rect, \
+ clipr : rect \
+ -> image#)
+
+ pkglocal generic getint : (str : byte[:], errp : bool# -> @a) :: integral,numeric @a
+;;
+
+const Ninfo = 12
+const Infosz = 12
+
+const Ctldir = 0
+const Imgid = 1
+const Chan = 2
+const Repl = 3
+const Scrx0 = 4
+const Scry0 = 5
+const Scrx1 = 6
+const Scry1 = 7
+const Clipx0 = 8
+const Clipy0 = 9
+const Clipx1 = 10
+const Clipy1 = 11
+
+const namedimage = {dpy, name
+ var info : byte[:][Ninfo]
+ var buf : byte[12*Ninfo]
+ var img, id //, n, chan
+
+ if name.len > 256
+ -> `std.Err `Ename
+ ;;
+ flush(dpy)
+ id = dpy.imageid++
+ pack(dpy, "biS", name.len + 1, \
+ ('n' : byte), \
+ id, \
+ name)
+ flush(dpy)
+
+ if sys.pread((dpy.ctlfd : sys.fd), buf[:], 0) == -1
+ -> `std.Err `Eimg
+ ;;
+ if std.bstrtok(info[:], buf[:]).len != Ninfo
+ -> `std.Err `Edpy
+ ;;
+ img = std.zalloc()
+ img.dpy = dpy
+ img.id = id
+ parseimgfmt(img, info[:])
+ -> `std.Ok img
+}
+
+const genallocimage = {dpy, screenid, refr, chan, color, repl, r, clipr
+ var img
+
+ img = std.zalloc()
+ img.dpy = dpy
+ img.chan = chan
+ img.repl = repl
+ img.refresh = refr
+ img.clipr = clipr
+ img.color = color
+ img.r = r
+ img.id = dpy.imageid++
+
+ pack(dpy, "biibibrri", 0, \
+ ('b' : byte), \
+ img.id, \
+ screenid, \
+ (refr : byte), \
+ chan, \
+ (repl : byte), \
+ r, \
+ clipr, \
+ color)
+
+ -> img
+}
+
+const allocimage = {dpy, r, color, repl
+ var clipr
+
+ if repl
+ clipr = [.x0=-0x3FFFFFFF, .y0=-0x3FFFFFFF, .x1=0x3FFFFFFF, .y1=0x3FFFFFFF]
+ else
+ clipr = r
+ ;;
+ -> genallocimage(dpy, dpy.screen.id, Refnone, dpy.root.chan, color, repl, r, clipr)
+}
+
+const freeimage = {img
+ pack(img.dpy, "bi", 0, ('f' : byte), img.id)
+ std.free(img)
+}
+
+const allocscreen = {dpy, img, fill, public
+ var scr
+
+ scr = std.zalloc()
+ scr.store = img
+ scr.fill = fill
+
+ for var i = 0; i < 30; i++
+ scr.id = dpy.scrid++
+ pack(dpy, "biiib", 0, \
+ ('A' : byte), \
+ scr.id, \
+ scr.store.id, \
+ scr.fill.id, \
+ (public : byte))
+ if flushbuf(dpy)
+ break
+ ;;
+ ;;
+
+ -> scr
+}
+
+const allocwindow = {scr, r
+ -> `std.Err `Edpy
+}
+
+
+const parseimgfmt = {img, info
+ var err
+
+ err = false
+ match chanparse(info[Chan])
+ | `std.Some c:
+ img.chan = c
+ img.depth = chandepth(img.chan)
+ | `std.None: -> `std.Err `Edpy
+ ;;
+
+ img.repl = std.sleq("1", std.strstrip(info[Repl]))
+ img.r.x0 = getint(info[Scrx0], &err)
+ img.r.y0 = getint(info[Scry0], &err)
+ img.r.x1 = getint(info[Scrx1], &err)
+ img.r.y1 = getint(info[Scry1], &err)
+ img.clipr.x0 = getint(info[Clipx0], &err)
+ img.clipr.y0 = getint(info[Clipy0], &err)
+ img.clipr.x1 = getint(info[Clipx1], &err)
+ img.clipr.y1 = getint(info[Clipy1], &err)
+ if err
+ -> `std.Err `Edpy
+ ;;
+ -> `std.Ok img
+}
+
+generic getint = {str, errp -> @a :: integral,numeric @a
+ match std.intparse(std.strstrip(str))
+ | `std.Some n: -> n
+ | `std.None: errp# = true
+ ;;
+ -> 0
+}
+
--- /dev/null
+++ b/init.myr
@@ -1,0 +1,186 @@
+use std
+use thread
+use sys
+
+use "types"
+use "chan"
+use "font"
+use "image"
+use "pack"
+use "draw"
+use "event"
+
+pkg draw =
+ const open : (-> std.result(display#, err))
+ const close : (dpy : display# -> void)
+ const getwindow : (dpy : display#, ref : refresh -> std.result(image#, err))
+;;
+
+const open = {
+ var infobuf : byte[12*12] /* 12 infos, 12 bytes each */
+ var r, clipr, font
+ var dpy
+
+ /* connect */
+ dpy = std.zalloc()
+ dpy.imageid = 1
+ dpy.scrid = (std.getpid() : int32)
+ dpy.ctlfd = std.try(std.open("/dev/draw/new", std.Ordwr | std.Ocexec))
+ match std.read(dpy.ctlfd, infobuf[:])
+ | `std.Err e: -> `std.Err `Edpy
+ | `std.Ok 0: -> `std.Err `Edpy
+ | `std.Ok n:
+ match setup(dpy, infobuf[:n])
+ | `std.Ok void:
+ | `std.Err e: -> `std.Err e
+ ;;
+ ;;
+
+ /* set up default images */
+ r = [.x0=0, .y0=0,.x1=1,.y1=1]
+ clipr = [.x0=-0x3FFFFFFF, .y0=-0x3FFFFFFF, .x1=0x3FFFFFFF, .y1=0x3FFFFFFF]
+ dpy.black = genallocimage(dpy, 0, Refnone, dpy.root.chan, DBlack, true, r, clipr)
+ dpy.white = genallocimage(dpy, 0, Refnone, dpy.root.chan, DWhite, true, r, clipr)
+ dpy.opaque = genallocimage(dpy, 0, Refnone, dpy.root.chan, DOpaque, true, r, clipr)
+
+ /* load up default font */
+ font = std.getenvv("font", Dfont)
+ match openfont(dpy, font)
+ | `std.Ok ft: dpy.dfont = ft
+ | `std.Err e: -> `std.Err e
+ ;;
+ -> `std.Ok dpy
+}
+
+const setup = {dpy, infobuf
+ var info : byte[:][Ninfo]
+ var buf : byte[12*Ninfo]
+ var mp, kp, rp
+ var p
+
+ if std.bstrtok(info[:], infobuf).len != Ninfo
+ -> `std.Err `Edpy
+ ;;
+ p = std.bfmt(buf[:], "/dev/draw/{}/data", info[0])
+ dpy.datafd = std.try(std.open(p, std.Ordwr | std.Ocexec))
+
+ /* open device fds */
+ p = std.bfmt(buf[:], "/dev/draw/{}/refresh", info[0])
+ dpy.qmtx = thread.mkmtx()
+ dpy.refrfd = std.try(std.open(p, std.Ordwr | std.Ocexec))
+ dpy.mousefd = std.try(std.open("/dev/mouse", std.Ordwr | std.Ocexec))
+ dpy.kbdfd = std.try(std.open("/dev/kbd", std.Ordwr | std.Ocexec))
+ mp = std.try(thread.spawn({; mouseproc(dpy)}))
+ kp = std.try(thread.spawn({; kbdproc(dpy)}))
+ rp = std.try(thread.spawn({; refrproc(dpy)}))
+
+ sys.atexit(std.fndup({; kill(mp)}))
+ sys.atexit(std.fndup({; kill(kp)}))
+ sys.atexit(std.fndup({; kill(rp)}))
+
+
+ /* parse out root image properties */
+ dpy.ctldir = std.get(std.intparse(info[0]))
+ dpy.buf = std.slalloc(iounit(dpy.datafd) + 5) /* slop for flush */
+ dpy.root = std.zalloc()
+ parseimgfmt(dpy.root, info[:])
+
+ -> `std.Ok void
+}
+
+const initfont = {dpy
+ var font
+
+ font = std.getenvv("font", "/lib/font/bit/vga/unicode.font")
+ match openfont(dpy, font)
+ | `std.Err e: -> `std.Err `Efont
+ | `std.Ok f:
+ dpy.dfont = f
+ -> `std.Ok void
+ ;;
+}
+
+const getwindow = {dpy, ref
+ var obuf : byte[128], buf : byte[128]
+ var on, fd, img, win
+ var r
+
+ on = 0
+:retry
+ match std.open("/dev/winname", std.Oread)
+ | `std.Ok f: fd = f
+ | `std.Err e: -> `std.Err `Ewin
+ ;;
+
+ match std.read(fd, buf[:])
+ | `std.Err e:
+ -> `std.Err `Edpy
+ | `std.Ok n:
+ match namedimage(dpy, buf[:n])
+ | `std.Ok i:
+ img = i
+ | `std.Err e:
+ /*
+ * theres a race where the winname can change after
+ * we read it, so keep trying as long as the name
+ * keeps changing.
+ */
+ if !std.sleq(buf[:n], obuf[:on])
+ std.close(fd)
+ std.slcp(obuf[:n], buf[:n])
+ on = n
+ goto retry
+ ;;
+ -> `std.Err `Edpy
+ ;;
+ ;;
+ r = inset(img.r, Borderwidth)
+ dpy.screen = allocscreen(dpy, img, dpy.white, false)
+ win = allocimage(dpy, r, DWhite, false)
+ if flush(dpy)
+ -> `std.Ok win
+ else
+ -> `std.Err `Edpy
+ ;;
+}
+
+const close = {dpy
+// freescreen(dpy.screen)
+// freeimage(dpy.root)
+ std.slfree(dpy.buf)
+ std.close(dpy.ctlfd)
+ std.close(dpy.datafd)
+ std.close(dpy.refrfd)
+ std.close(dpy.mousefd)
+ std.close(dpy.kbdfd)
+ std.free(dpy)
+}
+
+/*
+Format: 3 r M 4 (0000000000457def 11 00) 8192 512 /rc/lib/rcmain
+ */
+const iounit = {fd
+ var args : byte[:][10]
+ var buf : byte[128]
+ var cfd, ret
+ var path
+
+ path = std.bfmt(buf[:], "#d/{}ctl", fd)
+ cfd = std.try(std.open(path, std.Oread))
+ match std.read(cfd,buf[:])
+ | `std.Ok n:
+ std.bstrtok(args[:], buf[:n])
+ ret = std.get(std.intparse(args[7]))
+ | `std.Err e:
+ ret = -1
+ ;;
+ std.close(cfd);
+ -> ret
+}
+
+const kill = {pid
+ match std.open(std.fmt("/proc/{}/note", pid), std.Owrite)
+ | `std.Ok fd: std.write(fd, "kill")
+ | `std.Err e: /* already gone */
+ ;;
+}
--- /dev/null
+++ b/load.myr
@@ -1,0 +1,151 @@
+use std
+use sys
+
+use "types"
+use "chan"
+use "image"
+use "pack"
+
+pkg draw =
+ const load : (dpy : display#, buf : byte[:] -> std.result(image#, err))
+ const xload : (dpy : display#, buf : byte[:] -> std.result((image#, std.size), err))
+;;
+
+const load = {dpy, buf
+ match xload(dpy, buf)
+ | `std.Ok (img, n):
+ if n != buf.len
+ // freeimg(img)
+ -> `std.Err `Efmt
+ ;;
+ -> `std.Ok img
+ | `std.Err e:
+ -> `std.Err e
+ ;;
+}
+
+const xload = {dpy, buf
+ var chan, rect, compressed
+ var n, img
+
+ if !parsehdr(buf, &chan, &rect, &compressed, &buf)
+ -> `std.Err `Efmt
+ ;;
+
+ img = genallocimage(dpy, 0, Refnone, chan, DWhite, false, rect, rect)
+ if compressed
+ n = 5*12 + 11
+ n += loadcomp(dpy, img, buf)
+ else
+ n = 5*12
+ n += loadraw(dpy, img, buf)
+ ;;
+ if n > 0
+ -> `std.Ok (img, n)
+ else
+ // freeimg(img)
+ -> `std.Err `Efmt
+ ;;
+}
+
+const loadcomp = {dpy, img, buf
+ var l, n, ymin, ymax, err
+
+ l = 0
+ ymin = img.r.y0
+ while ymin != img.r.y1
+ if buf.len < 24
+ -> -1
+ ;;
+ err = false
+ ymax = getint(buf[:12], &err)
+ n = getint(buf[12:24], &err)
+ if err
+ -> -1
+ ;;
+ l += 24
+ buf = buf[24:]
+ pack(dpy, "biiiiid", (n : std.size), \
+ ('Y' : byte), \
+ img.id, \
+ img.r.x0, \
+ ymin, \
+ img.r.x1, \
+ ymax, \
+ buf[:n])
+ buf = buf[n:]
+ ymin = ymax
+ l += n
+ ;;
+ -> (l : std.size)
+}
+
+const loadraw = {dpy, img, buf
+ var chunk, btp, btl
+ var l, n, r, dx, dy
+
+ l = 0
+ r = img.r
+ dx = r.x1 - r.x0
+ btp = (chandepth(img.chan) / 8 : int)
+ btl = btp * dx
+ chunk = dpy.buf.len - 64
+ while r.y0 != r.y1
+ dy = r.y1 - r.y0
+ /* FIXME: deal with images where btl > chunk */
+ if dy * btl > chunk
+ dy = chunk / btl
+ ;;
+ n = dy * btl
+ pack(dpy, "biiiiid", (n : std.size), \
+ ('y' : byte), \
+ img.id, \
+ r.x0, \
+ r.y0, \
+ r.x1, \
+ r.y0 + dy, \
+ buf[:n])
+ l += n
+ buf = buf[n:]
+ r.y0 += dy
+ ;;
+ -> (l : std.size)
+}
+
+const parsehdr = {buf, chanp, rectp, compressedp, bufp
+ if buf.len < 5*12
+ -> false
+ ;;
+ if std.sleq(buf[:11], "compressed\n") && buf.len >= 5*12 + 11
+ compressedp# = true
+ bufp# = buf[5*12 + 11:]
+ -> parsefmt(buf[11:11+5*12], chanp, rectp)
+ else
+ compressedp# = false
+ bufp# = buf[5*12:]
+ -> parsefmt(buf[:5*12], chanp, rectp)
+ ;;
+}
+
+const parsefmt = {buf, chanp, rectp
+ var spbuf : byte[:][5]
+ var sp, err
+
+ sp = std.bstrtok(spbuf[:], buf)
+ if sp.len == 0
+ -> false
+ ;;
+
+ match chanparse(sp[0])
+ | `std.Some c: chanp# = c
+ | `std.None: std.fatal("bad chan\n"); -> false
+ ;;
+
+ err = false
+ rectp.x0 = getint(sp[1], &err)
+ rectp.y0 = getint(sp[2], &err)
+ rectp.x1 = getint(sp[3], &err)
+ rectp.y1 = getint(sp[4], &err)
+ -> !err
+}
+
--- /dev/null
+++ b/pack.myr
@@ -1,0 +1,121 @@
+use std
+use sys
+
+use "types"
+
+pkg draw =
+ const pack : (dpy : display#, fmt : byte[:], vsz : std.size, args : ... -> void)
+ const flush : (dpy : display# -> bool)
+ const flushbuf : (dpy : display# -> bool)
+;;
+
+const pack = {dpy, fmt, vsz, args
+ var ap
+ var sz, b, o
+
+ ap = std.vastart(&args)
+ sz = fixsize(fmt) + vsz
+ b = ensure(dpy, sz)
+ o = 0
+ for c : fmt
+ match (c : char)
+ | 'b':
+ b[o] = std.vanext(&ap)
+ o += 1
+ | 's':
+ var v : int16 = std.vanext(&ap)
+ std.putle16(b[o:o+2], v)
+ o += 2
+ | 'i':
+ var v : int32 = std.vanext(&ap)
+ std.putle32(b[o:o+4], v)
+ o += 4
+ | 'p':
+ var p : point = std.vanext(&ap)
+ std.putle32(b[o + 0:o + 4], p.x)
+ std.putle32(b[o + 4:o + 8], p.y)
+ o += 2*4
+ | 'r':
+ var r : rect = std.vanext(&ap)
+ std.putle32(b[o+ 0:o+ 4], r.x0)
+ std.putle32(b[o+ 4:o+ 8], r.y0)
+ std.putle32(b[o+ 8:o+12], r.x1)
+ std.putle32(b[o+12:o+16], r.y1)
+ o += 4*4
+ | 'S':
+ var s : byte[:] = std.vanext(&ap)
+ b[o++] = (s.len : byte)
+ std.slcp(b[o:o+s.len], s)
+ o += s.len
+ | 'I':
+ var s : int16[:] = std.vanext(&ap)
+ for var i = 0; i < s.len; i++
+ std.putle16(b[o:o+2], s[i])
+ o += 2
+ ;;
+ | 'd':
+ var d : byte[:] = std.vanext(&ap)
+ std.slcp(b[o:o+d.len], d)
+ o += d.len
+ | ' ':
+ /* for readability of format strings */
+ | chr:
+ std.fatal("unknown specifier {} in pack\n", chr)
+ ;;
+ ;;
+}
+
+const fixsize = {fmt
+ var sz
+
+ sz = 0
+ for c : fmt
+ match (c : char)
+ | 'b': sz += 1
+ | 's': sz += 2
+ | 'i': sz += 4
+ | 'r': sz += 4*4
+ | 'p': sz += 2*4
+ | _: /* variable sizes */
+ ;;
+ ;;
+ -> sz
+}
+
+const ensure = {dpy, sz
+ var ret
+
+ if dpy.bufoff + sz > dpy.buf.len
+ flush(dpy)
+ ;;
+ ret = dpy.buf[dpy.bufoff:dpy.bufoff + sz]
+ dpy.bufoff += sz
+ -> ret
+}
+
+const flush = {dpy
+
+ /* five bytes always reserved here */
+ dpy.buf[dpy.bufoff++] = ('v' : byte)
+ -> flushbuf(dpy)
+}
+
+const flushbuf = {dpy
+ var off, ret
+ var errbuf : byte[128]
+
+ off = 0
+ ret = true
+ while off != dpy.bufoff
+ match std.write(dpy.datafd, dpy.buf[off:dpy.bufoff])
+ | `std.Ok n: off += n
+ | `std.Err e:
+ sys.errstr(errbuf[:])
+ std.fatal("**error: {}\n", std.cstrconv(errbuf[:]))
+ ret = false
+ break
+ ;;
+ ;;
+ dpy.bufoff = 0
+ -> true
+}
--- /dev/null
+++ b/shape.myr
@@ -1,0 +1,51 @@
+use std
+
+use "types"
+use "chan"
+use "pack"
+
+pkg draw =
+ const fill : (dst : image#, src : image#, r : rect -> void)
+ const draw : (dst : image#, r : rect, src : image#, p0 : point -> void)
+ const drawop : (dst : image#, r : rect, src : image#, p0 : point, op : drawop -> void)
+ const mdrawop : (dst : image#, r : rect, s : image#, p0 : point, mask : image#, p1 : point, op : drawop -> void)
+ const inset : (r : rect, off : int -> rect)
+;;
+
+const fill = {dst, src, r
+ // d dstid[4] srcid[4] maskid[4] dstr[4*4] srcp[2*4] maskp[2*4]
+ mdrawop(dst, r, src, Zp, dst.dpy.opaque, Zp, SoverD)
+}
+
+const draw = {dst, r, src, p0
+ mdrawop(dst, r, src, p0, dst.dpy.opaque, Zp, SoverD)
+}
+
+const drawop = {dst, r, src, p0, op
+ mdrawop(dst, r, src, p0, dst.dpy.opaque, Zp, SoverD)
+}
+
+const mdrawop = {dst, r, src, p0, mask, p1, op
+ if op != SoverD
+ pack(dst.dpy, "bb", 0, ('O' : byte), (op : byte))
+ ;;
+ pack(dst.dpy, "biiiriiii", 0, \
+ ('d' : byte), /* draw */ \
+ dst.id, \
+ src.id, \
+ mask.id, \
+ r, \
+ p0.x, \
+ p0.y, \
+ p1.x, \
+ p1.y)
+
+}
+
+const inset = {r, off
+ r.x0 += off
+ r.y0 += off
+ r.x1 -= off
+ r.y1 -= off
+ -> r
+}
--- /dev/null
+++ b/string.myr
@@ -1,0 +1,39 @@
+use std
+use "types"
+use "font"
+use "pack"
+
+pkg draw =
+ const string : (dst : image#, dp : point, str : byte[:] -> void)
+ const gstring : (dst : image#, dp : point, clipr : rect, src : image#, sp : point, ft : font#, str : byte[:] -> void)
+;;
+
+const string = {dst, dp, str
+ gstring(dst, dp, dst.r, dst.dpy.black, [.x=0, .y=0], dst.dpy.dfont, str)
+}
+
+const gstring = {dst, dp, clipr, src, sp, ft, str
+ var cids, id
+
+ cids = [][:]
+ for c : std.bychar(str)
+ id = cachechar(ft, c)
+ if id < 0
+ break
+ ;;
+ std.slpush(&cids, id)
+ ;;
+
+ /* s dstid[4] srcid[4] fontid[4] p[2*4] clipr[4*4] sp[2*4] n[2] n*(index[2]) */
+ pack(dst.dpy, "biiiprpsI", 2*cids.len, \
+ ('s' : byte), \
+ dst.id, \
+ src.id, \
+ ft.cimage.id, \
+ dp, \
+ clipr, \
+ sp, \
+ cids.len, \
+ cids)
+ std.slfree(cids)
+}
--- /dev/null
+++ b/testdraw.myr
@@ -1,0 +1,41 @@
+use std
+use draw
+
+const main = {
+ var ev
+
+ match draw.open()
+ | `std.Err e: std.fatal("error: {}\n", e)
+ | `std.Ok dpy:
+ testpattern(dpy)
+ draw.flush(dpy)
+ while true
+ ev = draw.event(dpy)
+ match ev#
+ | `draw.Mmove _:
+ | `draw.Mdown (x, y, mask): std.put("click {} at {}\n", mask, (x, y))
+ | `draw.Mup (x, y, mask): std.put("release {} at {}\n", mask, (x, y))
+ | `draw.Kp 'q':
+ break
+ | _:
+ std.put("ev: {}\n", ev)
+ ;;
+ std.free(ev)
+ ;;
+ draw.close(dpy)
+ ;;
+}
+
+const testpattern = {dpy
+ var w, r, p : draw.point
+
+
+ match draw.getwindow(dpy, draw.Refnone)
+ | `std.Ok win: w = win
+ | `std.Err e: -> std.fatal("could not get window: {}\n", e);
+ ;;
+ r = [.x0=w.r.x0+100, .y0=w.r.y0+100, .x1=w.r.x0+200, .y1=w.r.y0+200]
+ draw.fill(w, dpy.black, r)
+ p = [.x=w.r.x0+100, .y=w.r.y0+210]
+ draw.string(w, p, "hello world")
+}
--- /dev/null
+++ b/testload.myr
@@ -1,0 +1,27 @@
+use std
+use draw
+
+const main = {args : byte[:][:]
+ var img, dpy, w
+ var b : byte[1]
+
+ dpy = std.try(draw.open())
+ for a : args[1:]
+ match std.slurp(a)
+ | `std.Err e: std.fatal("could not open {}: {}\n", a, e)
+ | `std.Ok buf:
+ match draw.xload(dpy, buf)
+ | `std.Ok (i, _): img = i
+ | `std.Err e: std.fatal("could not load image {}: {}\n", a, e)
+ ;;
+ match draw.getwindow(dpy, draw.Refnone)
+ | `std.Ok win: w = win
+ | `std.Err e: -> std.fatal("could not get window: {}\n", e);
+ ;;
+ draw.fill(w, img, w.r)
+ draw.flush(dpy)
+ std.read(std.In, b[:])
+ ;;
+ ;;
+ draw.close(dpy)
+}
--- /dev/null
+++ b/types.myr
@@ -1,0 +1,252 @@
+use std
+use thread
+
+pkg draw =
+ type chan = uint32
+ type color = uint32
+ type refresh = uint32
+ type drawop = uint32
+
+ /* KF|1, KF|2, ..., KF|0xC is F1, F2, ..., F12 */
+ const KF = 0xF000 /* Rune: beginning of private Unicode space */
+ const Spec = 0xF800
+ const PF = Spec|0x20 /* num pad function key */
+ const Kview = Spec|0x00 /* view (shift window up) */
+ const Khome = KF|0x0D
+ const Kup = KF|0x0E
+ const Kdown = Kview
+ const Kpgup = KF|0x0F
+ const Kprint = KF|0x10
+ const Kleft = KF|0x11
+ const Kright = KF|0x12
+ const Kpgdown = KF|0x13
+ const Kins = KF|0x14
+
+ const Kalt = KF|0x15
+ const Kshift = KF|0x16
+ const Kctl = KF|0x17
+
+ const Kend = KF|0x18
+ const Kscroll = KF|0x19
+ const Kscrolloneup = KF|0x20
+ const Kscrollonedown = KF|0x21
+
+ const Ksoh = 0x01
+ const Kstx = 0x02
+ const Ketx = 0x03
+ const Keof = 0x04
+ const Kenq = 0x05
+ const Kack = 0x06
+ const Kbs = 0x08
+ const Knack = 0x15
+ const Ketb = 0x17
+ const Kdel = 0x7f
+ const Kesc = 0x1b
+
+ const Kbreak = Spec|0x61
+ const Kcaps = Spec|0x64
+ const Knum = Spec|0x65
+ const Kmiddle = Spec|0x66
+ const Kaltgr = Spec|0x67
+ const Kmouse = Spec|0x100
+
+ type display = struct
+ datafd : std.fd
+ ctlfd : std.fd
+
+ /* inputs */
+ qmtx : thread.mutex
+ rawevt : bool
+ refrfd : std.fd
+ mousefd : std.fd
+ kbdfd : std.fd
+ refrp : void#
+ mousep : void#
+ kbdp : void#
+
+ ctldir : int
+ windir : byte[:]
+
+ imageid : int32
+ scrid : int32
+
+ root : image#
+ screen : screen#
+ dfont : font#
+ buf : byte[:]
+ bufoff : std.size
+
+ white : image#
+ black : image#
+ opaque : image#
+ trans : image#
+ ;;
+
+ type event = union
+ `Kp char
+ `Kr char
+ `Mmove (int, int)
+ `Mdown (int, int, int)
+ `Mup (int, int, int)
+ `Resize
+ `Refresh
+ ;;
+
+ type err = union
+ `Edpy
+ `Efont
+ `Ewin
+ `Ename
+ `Enamelen
+ `Eimg
+ `Eio
+ `Efmt
+ ;;
+
+ type font = struct
+ name : byte[:]
+ height : int
+ ascent : int
+ subf : subfont[:]
+ cache : std.htab(char, fcinfo)#
+ cimage : image#
+ x : int
+ nextidx : int16
+ ;;
+
+ type fcinfo = struct
+ hits : int
+ idx : int16
+ ;;
+
+ type subfont = struct
+ lo : char
+ hi : char
+ start : std.size
+ path : byte[:]
+ image : image#
+ glyph : glyph[:]
+ ;;
+
+ type glyph = struct
+ x : int
+ top : int
+ bottom : int
+ left : int
+ width : int
+ ;;
+
+ type screen = struct
+ dpy : display#
+ id : int32
+ store : image#
+ fill : image#
+ ;;
+
+ type image = struct
+ dpy : display#
+ chan : chan
+ color : color
+ refresh : refresh
+ depth : uint32
+ id : int32
+ r : rect
+ clipr : rect
+ repl : bool
+ ;;
+
+ type rect = struct
+ x0 : int
+ x1 : int
+ y0 : int
+ y1 : int
+ ;;
+
+ type point = struct
+ x : int
+ y : int
+ ;;
+
+ const Zp : point = [.x=0, .y=0]
+ const Dfont : byte[:] = "/lib/font/bit/vga/unicode.font"
+
+ const CidxRed : int = 0
+ const CidxGreen : int = 1
+ const CidxBlue : int = 2
+ const CidxGrey : int = 3
+ const CidxAlpha : int = 4
+ const CidxMap : int = 5
+ const CidxIgnore : int = 6
+
+ const Cgrey1 : chan = 0x31
+ const Cgrey2 : chan = 0x32
+ const Cgrey4 : chan = 0x34
+ const Cgrey8 : chan = 0x38
+ const Ccmap8 : chan = 0x58
+ const Crgb15 : chan = 0x61051525
+ const Crgb16 : chan = 0x51625
+ const Crgb24 : chan = 0x81828
+ const Crgba32 : chan = 0x8182848
+ const Cargb32 : chan = 0x48081828
+ const Cxrgb32 : chan = 0x68081828
+ const Cbgr24 : chan = 0x281808
+ const Cabgr32 : chan = 0x48281808
+ const Cxbgr32 : chan = 0x68281808
+
+ /* Porter-Duff compositing operators */
+ const Clear : drawop = 0
+ const SinD : drawop = 8
+ const DinS : drawop = 4
+ const SoutD : drawop = 2
+ const DoutS : drawop = 1
+
+ const S : drawop = SinD|SoutD
+ const SoverD : drawop = SinD|SoutD|DoutS
+ const SatopD : drawop = SinD|DoutS
+ const SxorD : drawop = SoutD|DoutS
+
+ const D : drawop = DinS|DoutS
+ const DoverS : drawop = DinS|DoutS|SoutD
+ const DatopS : drawop = DinS|SoutD
+ const DxorS : drawop = DoutS|SoutD /* == SxorD */
+
+ /* refresh methods */
+ const Refbackup : refresh = 0
+ const Refnone : refresh = 1
+ const Refmesg : refresh = 2
+
+ /* colors */
+ const DOpaque : color = 0xFFFFFFFF
+ const DTransparent : color = 0x00000000 /* only useful for allocimage, memfillcolor */
+ const DBlack : color = 0x000000FF
+ const DWhite : color = 0xFFFFFFFF
+ const DRed : color = 0xFF0000FF
+ const DGreen : color = 0x00FF00FF
+ const DBlue : color = 0x0000FFFF
+ const DCyan : color = 0x00FFFFFF
+ const DMagenta : color = 0xFF00FFFF
+ const DYellow : color = 0xFFFF00FF
+ const DPaleyellow : color = 0xFFFFAAFF
+ const DDarkyellow : color = 0xEEEE9EFF
+ const DDarkgreen : color = 0x448844FF
+ const DPalegreen : color = 0xAAFFAAFF
+ const DMedgreen : color = 0x88CC88FF
+ const DDarkblue : color = 0x000055FF
+ const DPalebluegreen : color = 0xAAFFFFFF
+ const DPaleblue : color = 0x0000BBFF
+ const DBluegreen : color = 0x008888FF
+ const DGreygreen : color = 0x55AAAAFF
+ const DPalegreygreen : color = 0x9EEEEEFF
+ const DYellowgreen : color = 0x99994CFF
+ const DMedblue : color = 0x000099FF
+ const DGreyblue : color = 0x005DBBFF
+ const DPalegreyblue : color = 0x4993DDFF
+ const DPurpleblue : color = 0x8888CCFF
+
+ const DNotacolor : color = 0xFFFFFF00
+ const DNofill : color = DNotacolor
+
+ /* rio expects us to reserve space for the border */
+ const Borderwidth : int = 4
+
+;;