aboutsummaryrefslogtreecommitdiff
path: root/src/libmemlayer
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2006-06-25 18:58:06 +0000
committerrsc <devnull@localhost>2006-06-25 18:58:06 +0000
commit324891a5579d6f504201a6107369c64dab245a98 (patch)
tree3069429c14484b100d86dd5a3b1013565bf5db6c /src/libmemlayer
parent9361131304f39db29b1bec59d881e585035ec93c (diff)
downloadplan9port-324891a5579d6f504201a6107369c64dab245a98.tar.gz
plan9port-324891a5579d6f504201a6107369c64dab245a98.tar.bz2
plan9port-324891a5579d6f504201a6107369c64dab245a98.zip
separate out
Diffstat (limited to 'src/libmemlayer')
-rw-r--r--src/libmemlayer/draw.c192
-rw-r--r--src/libmemlayer/lalloc.c79
-rw-r--r--src/libmemlayer/layerop.c112
-rw-r--r--src/libmemlayer/ldelete.c67
-rw-r--r--src/libmemlayer/lhide.c67
-rw-r--r--src/libmemlayer/line.c122
-rw-r--r--src/libmemlayer/load.c55
-rw-r--r--src/libmemlayer/lorigin.c107
-rw-r--r--src/libmemlayer/lsetrefresh.c35
-rw-r--r--src/libmemlayer/ltofront.c80
-rw-r--r--src/libmemlayer/ltorear.c69
-rw-r--r--src/libmemlayer/mkfile25
-rw-r--r--src/libmemlayer/unload.c52
13 files changed, 1062 insertions, 0 deletions
diff --git a/src/libmemlayer/draw.c b/src/libmemlayer/draw.c
new file mode 100644
index 00000000..c352a0b2
--- /dev/null
+++ b/src/libmemlayer/draw.c
@@ -0,0 +1,192 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+struct Draw
+{
+ Point deltas;
+ Point deltam;
+ Memlayer *dstlayer;
+ Memimage *src;
+ Memimage *mask;
+ int op;
+};
+
+static
+void
+ldrawop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave)
+{
+ struct Draw *d;
+ Point p0, p1;
+ Rectangle oclipr, srcr, r, mr;
+ int ok;
+
+ d = etc;
+ if(insave && d->dstlayer->save==nil)
+ return;
+
+ p0 = addpt(screenr.min, d->deltas);
+ p1 = addpt(screenr.min, d->deltam);
+
+ if(insave){
+ r = rectsubpt(screenr, d->dstlayer->delta);
+ clipr = rectsubpt(clipr, d->dstlayer->delta);
+ }else
+ r = screenr;
+
+ /* now in logical coordinates */
+
+ /* clipr may have narrowed what we should draw on, so clip if necessary */
+ if(!rectinrect(r, clipr)){
+ oclipr = dst->clipr;
+ dst->clipr = clipr;
+ ok = drawclip(dst, &r, d->src, &p0, d->mask, &p1, &srcr, &mr);
+ dst->clipr = oclipr;
+ if(!ok)
+ return;
+ }
+ memdraw(dst, r, d->src, p0, d->mask, p1, d->op);
+}
+
+void
+memdraw(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memimage *mask, Point p1, int op)
+{
+ struct Draw d;
+ Rectangle srcr, tr, mr;
+ Memlayer *dl, *sl;
+
+ if(drawdebug)
+ iprint("memdraw %p %R %p %P %p %P\n", dst, r, src, p0, mask, p1);
+
+ if(mask == nil)
+ mask = memopaque;
+
+ if(mask->layer){
+if(drawdebug) iprint("mask->layer != nil\n");
+ return; /* too hard, at least for now */
+ }
+
+ Top:
+ if(dst->layer==nil && src->layer==nil){
+ memimagedraw(dst, r, src, p0, mask, p1, op);
+ return;
+ }
+
+ if(drawclip(dst, &r, src, &p0, mask, &p1, &srcr, &mr) == 0){
+if(drawdebug) iprint("drawclip dstcr %R srccr %R maskcr %R\n", dst->clipr, src->clipr, mask->clipr);
+ return;
+ }
+
+ /*
+ * Convert to screen coordinates.
+ */
+ dl = dst->layer;
+ if(dl != nil){
+ r.min.x += dl->delta.x;
+ r.min.y += dl->delta.y;
+ r.max.x += dl->delta.x;
+ r.max.y += dl->delta.y;
+ }
+ Clearlayer:
+ if(dl!=nil && dl->clear){
+ if(src == dst){
+ p0.x += dl->delta.x;
+ p0.y += dl->delta.y;
+ src = dl->screen->image;
+ }
+ dst = dl->screen->image;
+ goto Top;
+ }
+
+ sl = src->layer;
+ if(sl != nil){
+ p0.x += sl->delta.x;
+ p0.y += sl->delta.y;
+ srcr.min.x += sl->delta.x;
+ srcr.min.y += sl->delta.y;
+ srcr.max.x += sl->delta.x;
+ srcr.max.y += sl->delta.y;
+ }
+
+ /*
+ * Now everything is in screen coordinates.
+ * mask is an image. dst and src are images or obscured layers.
+ */
+
+ /*
+ * if dst and src are the same layer, just draw in save area and expose.
+ */
+ if(dl!=nil && dst==src){
+ if(dl->save == nil)
+ return; /* refresh function makes this case unworkable */
+ if(rectXrect(r, srcr)){
+ tr = r;
+ if(srcr.min.x < tr.min.x){
+ p1.x += tr.min.x - srcr.min.x;
+ tr.min.x = srcr.min.x;
+ }
+ if(srcr.min.y < tr.min.y){
+ p1.y += tr.min.x - srcr.min.x;
+ tr.min.y = srcr.min.y;
+ }
+ if(srcr.max.x > tr.max.x)
+ tr.max.x = srcr.max.x;
+ if(srcr.max.y > tr.max.y)
+ tr.max.y = srcr.max.y;
+ memlhide(dst, tr);
+ }else{
+ memlhide(dst, r);
+ memlhide(dst, srcr);
+ }
+ memdraw(dl->save, rectsubpt(r, dl->delta), dl->save,
+ subpt(srcr.min, src->layer->delta), mask, p1, op);
+ memlexpose(dst, r);
+ return;
+ }
+
+ if(sl){
+ if(sl->clear){
+ src = sl->screen->image;
+ if(dl != nil){
+ r.min.x -= dl->delta.x;
+ r.min.y -= dl->delta.y;
+ r.max.x -= dl->delta.x;
+ r.max.y -= dl->delta.y;
+ }
+ goto Top;
+ }
+ /* relatively rare case; use save area */
+ if(sl->save == nil)
+ return; /* refresh function makes this case unworkable */
+ memlhide(src, srcr);
+ /* convert back to logical coordinates */
+ p0.x -= sl->delta.x;
+ p0.y -= sl->delta.y;
+ srcr.min.x -= sl->delta.x;
+ srcr.min.y -= sl->delta.y;
+ srcr.max.x -= sl->delta.x;
+ srcr.max.y -= sl->delta.y;
+ src = src->layer->save;
+ }
+
+ /*
+ * src is now an image. dst may be an image or a clear layer
+ */
+ if(dst->layer==nil)
+ goto Top;
+ if(dst->layer->clear)
+ goto Clearlayer;
+
+ /*
+ * dst is an obscured layer
+ */
+ d.deltas = subpt(p0, r.min);
+ d.deltam = subpt(p1, r.min);
+ d.dstlayer = dl;
+ d.src = src;
+ d.op = op;
+ d.mask = mask;
+ _memlayerop(ldrawop, dst, r, r, &d);
+}
diff --git a/src/libmemlayer/lalloc.c b/src/libmemlayer/lalloc.c
new file mode 100644
index 00000000..17d934d3
--- /dev/null
+++ b/src/libmemlayer/lalloc.c
@@ -0,0 +1,79 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+Memimage*
+memlalloc(Memscreen *s, Rectangle screenr, Refreshfn refreshfn, void *refreshptr, u32int val)
+{
+ Memlayer *l;
+ Memimage *n;
+ static Memimage *paint;
+
+ if(paint == nil){
+ paint = allocmemimage(Rect(0,0,1,1), RGBA32);
+ if(paint == nil)
+ return nil;
+ paint->flags |= Frepl;
+ paint->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
+ }
+
+ n = allocmemimaged(screenr, s->image->chan, s->image->data, nil);
+ if(n == nil)
+ return nil;
+ l = malloc(sizeof(Memlayer));
+ if(l == nil){
+ free(n);
+ return nil;
+ }
+
+ l->screen = s;
+ if(refreshfn)
+ l->save = nil;
+ else{
+ l->save = allocmemimage(screenr, s->image->chan);
+ if(l->save == nil){
+ free(l);
+ free(n);
+ return nil;
+ }
+ /* allocmemimage doesn't initialize memory; this paints save area */
+ if(val != DNofill)
+ memfillcolor(l->save, val);
+ }
+ l->refreshfn = refreshfn;
+ l->refreshptr = nil; /* don't set it until we're done */
+ l->screenr = screenr;
+ l->delta = Pt(0,0);
+
+ n->data->ref++;
+ n->zero = s->image->zero;
+ n->width = s->image->width;
+ n->layer = l;
+
+ /* start with new window behind all existing ones */
+ l->front = s->rearmost;
+ l->rear = nil;
+ if(s->rearmost)
+ s->rearmost->layer->rear = n;
+ s->rearmost = n;
+ if(s->frontmost == nil)
+ s->frontmost = n;
+ l->clear = 0;
+
+ /* now pull new window to front */
+ _memltofrontfill(n, val != DNofill);
+ l->refreshptr = refreshptr;
+
+ /*
+ * paint with requested color; previously exposed areas are already right
+ * if this window has backing store, but just painting the whole thing is simplest.
+ */
+ if(val != DNofill){
+ memsetchan(paint, n->chan);
+ memfillcolor(paint, val);
+ memdraw(n, n->r, paint, n->r.min, nil, n->r.min, S);
+ }
+ return n;
+}
diff --git a/src/libmemlayer/layerop.c b/src/libmemlayer/layerop.c
new file mode 100644
index 00000000..800ffc85
--- /dev/null
+++ b/src/libmemlayer/layerop.c
@@ -0,0 +1,112 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+#define RECUR(a,b,c,d) _layerop(fn, i, Rect(a.x, b.y, c.x, d.y), clipr, etc, front->layer->rear);
+
+static void
+_layerop(
+ void (*fn)(Memimage*, Rectangle, Rectangle, void*, int),
+ Memimage *i,
+ Rectangle r,
+ Rectangle clipr,
+ void *etc,
+ Memimage *front)
+{
+ Rectangle fr;
+
+ Top:
+ if(front == i){
+ /* no one is in front of this part of window; use the screen */
+ fn(i->layer->screen->image, r, clipr, etc, 0);
+ return;
+ }
+ fr = front->layer->screenr;
+ if(rectXrect(r, fr) == 0){
+ /* r doesn't touch this window; continue on next rearmost */
+ /* assert(front && front->layer && front->layer->screen && front->layer->rear); */
+ front = front->layer->rear;
+ goto Top;
+ }
+ if(fr.max.y < r.max.y){
+ RECUR(r.min, fr.max, r.max, r.max);
+ r.max.y = fr.max.y;
+ }
+ if(r.min.y < fr.min.y){
+ RECUR(r.min, r.min, r.max, fr.min);
+ r.min.y = fr.min.y;
+ }
+ if(fr.max.x < r.max.x){
+ RECUR(fr.max, r.min, r.max, r.max);
+ r.max.x = fr.max.x;
+ }
+ if(r.min.x < fr.min.x){
+ RECUR(r.min, r.min, fr.min, r.max);
+ r.min.x = fr.min.x;
+ }
+ /* r is covered by front, so put in save area */
+ (*fn)(i->layer->save, r, clipr, etc, 1);
+}
+
+/*
+ * Assumes incoming rectangle has already been clipped to i's logical r and clipr
+ */
+void
+_memlayerop(
+ void (*fn)(Memimage*, Rectangle, Rectangle, void*, int),
+ Memimage *i,
+ Rectangle screenr, /* clipped to window boundaries */
+ Rectangle clipr, /* clipped also to clipping rectangles of hierarchy */
+ void *etc)
+{
+ Memlayer *l;
+ Rectangle r, scr;
+
+ l = i->layer;
+ if(!rectclip(&screenr, l->screenr))
+ return;
+ if(l->clear){
+ fn(l->screen->image, screenr, clipr, etc, 0);
+ return;
+ }
+ r = screenr;
+ scr = l->screen->image->clipr;
+
+ /*
+ * Do the piece on the screen
+ */
+ if(rectclip(&screenr, scr))
+ _layerop(fn, i, screenr, clipr, etc, l->screen->frontmost);
+ if(rectinrect(r, scr))
+ return;
+
+ /*
+ * Do the piece off the screen
+ */
+ if(!rectXrect(r, scr)){
+ /* completely offscreen; easy */
+ fn(l->save, r, clipr, etc, 1);
+ return;
+ }
+ if(r.min.y < scr.min.y){
+ /* above screen */
+ fn(l->save, Rect(r.min.x, r.min.y, r.max.x, scr.min.y), clipr, etc, 1);
+ r.min.y = scr.min.y;
+ }
+ if(r.max.y > scr.max.y){
+ /* below screen */
+ fn(l->save, Rect(r.min.x, scr.max.y, r.max.x, r.max.y), clipr, etc, 1);
+ r.max.y = scr.max.y;
+ }
+ if(r.min.x < scr.min.x){
+ /* left of screen */
+ fn(l->save, Rect(r.min.x, r.min.y, scr.min.x, r.max.y), clipr, etc, 1);
+ r.min.x = scr.min.x;
+ }
+ if(r.max.x > scr.max.x){
+ /* right of screen */
+ fn(l->save, Rect(scr.max.x, r.min.y, r.max.x, r.max.y), clipr, etc, 1);
+ }
+}
diff --git a/src/libmemlayer/ldelete.c b/src/libmemlayer/ldelete.c
new file mode 100644
index 00000000..34cd6ead
--- /dev/null
+++ b/src/libmemlayer/ldelete.c
@@ -0,0 +1,67 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+void
+memldelete(Memimage *i)
+{
+ Memscreen *s;
+ Memlayer *l;
+
+ l = i->layer;
+ /* free backing store and disconnect refresh, to make pushback fast */
+ freememimage(l->save);
+ l->save = nil;
+ l->refreshptr = nil;
+ memltorear(i);
+
+ /* window is now the rearmost; clean up screen structures and deallocate */
+ s = i->layer->screen;
+ if(s->fill){
+ i->clipr = i->r;
+ memdraw(i, i->r, s->fill, i->r.min, nil, i->r.min, S);
+ }
+ if(l->front){
+ l->front->layer->rear = nil;
+ s->rearmost = l->front;
+ }else{
+ s->frontmost = nil;
+ s->rearmost = nil;
+ }
+ free(l);
+ freememimage(i);
+}
+
+/*
+ * Just free the data structures, don't do graphics
+ */
+void
+memlfree(Memimage *i)
+{
+ Memlayer *l;
+
+ l = i->layer;
+ freememimage(l->save);
+ free(l);
+ freememimage(i);
+}
+
+void
+_memlsetclear(Memscreen *s)
+{
+ Memimage *i, *j;
+ Memlayer *l;
+
+ for(i=s->rearmost; i; i=i->layer->front){
+ l = i->layer;
+ l->clear = rectinrect(l->screenr, l->screen->image->clipr);
+ if(l->clear)
+ for(j=l->front; j; j=j->layer->front)
+ if(rectXrect(l->screenr, j->layer->screenr)){
+ l->clear = 0;
+ break;
+ }
+ }
+}
diff --git a/src/libmemlayer/lhide.c b/src/libmemlayer/lhide.c
new file mode 100644
index 00000000..d6aaa55f
--- /dev/null
+++ b/src/libmemlayer/lhide.c
@@ -0,0 +1,67 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+/*
+ * Hide puts that portion of screenr now on the screen into the window's save area.
+ * Expose puts that portion of screenr now in the save area onto the screen.
+ *
+ * Hide and Expose both require that the layer structures in the screen
+ * match the geometry they are being asked to update, that is, they update the
+ * save area (hide) or screen (expose) based on what those structures tell them.
+ * This means they must be called at the correct time during window shuffles.
+ */
+
+static
+void
+lhideop(Memimage *src, Rectangle screenr, Rectangle clipr, void *etc, int insave)
+{
+ Rectangle r;
+ Memlayer *l;
+
+ USED(clipr.min.x);
+ USED(insave);
+ l = etc;
+ if(src != l->save){ /* do nothing if src is already in save area */
+ r = rectsubpt(screenr, l->delta);
+ memdraw(l->save, r, src, screenr.min, nil, screenr.min, S);
+ }
+}
+
+void
+memlhide(Memimage *i, Rectangle screenr)
+{
+ if(i->layer->save == nil)
+ return;
+ if(rectclip(&screenr, i->layer->screen->image->r) == 0)
+ return;
+ _memlayerop(lhideop, i, screenr, screenr, i->layer);
+}
+
+static
+void
+lexposeop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave)
+{
+ Memlayer *l;
+ Rectangle r;
+
+ USED(clipr.min.x);
+ if(insave) /* if dst is save area, don't bother */
+ return;
+ l = etc;
+ r = rectsubpt(screenr, l->delta);
+ if(l->save)
+ memdraw(dst, screenr, l->save, r.min, nil, r.min, S);
+ else
+ l->refreshfn(dst, r, l->refreshptr);
+}
+
+void
+memlexpose(Memimage *i, Rectangle screenr)
+{
+ if(rectclip(&screenr, i->layer->screen->image->r) == 0)
+ return;
+ _memlayerop(lexposeop, i, screenr, screenr, i->layer);
+}
diff --git a/src/libmemlayer/line.c b/src/libmemlayer/line.c
new file mode 100644
index 00000000..f74930ef
--- /dev/null
+++ b/src/libmemlayer/line.c
@@ -0,0 +1,122 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+struct Lline
+{
+ Point p0;
+ Point p1;
+ Point delta;
+ int end0;
+ int end1;
+ int radius;
+ Point sp;
+ Memlayer *dstlayer;
+ Memimage *src;
+ int op;
+};
+
+static void llineop(Memimage*, Rectangle, Rectangle, void*, int);
+
+static
+void
+_memline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, Rectangle clipr, int op)
+{
+ Rectangle r;
+ struct Lline ll;
+ Point d;
+ int srcclipped;
+ Memlayer *dl;
+
+ if(radius < 0)
+ return;
+ if(src->layer) /* can't draw line with layered source */
+ return;
+ srcclipped = 0;
+
+ Top:
+ dl = dst->layer;
+ if(dl == nil){
+ _memimageline(dst, p0, p1, end0, end1, radius, src, sp, clipr, op);
+ return;
+ }
+ if(!srcclipped){
+ d = subpt(sp, p0);
+ if(rectclip(&clipr, rectsubpt(src->clipr, d)) == 0)
+ return;
+ if((src->flags&Frepl)==0 && rectclip(&clipr, rectsubpt(src->r, d))==0)
+ return;
+ srcclipped = 1;
+ }
+
+ /* dst is known to be a layer */
+ p0.x += dl->delta.x;
+ p0.y += dl->delta.y;
+ p1.x += dl->delta.x;
+ p1.y += dl->delta.y;
+ clipr.min.x += dl->delta.x;
+ clipr.min.y += dl->delta.y;
+ clipr.max.x += dl->delta.x;
+ clipr.max.y += dl->delta.y;
+ if(dl->clear){
+ dst = dst->layer->screen->image;
+ goto Top;
+ }
+
+ /* XXX */
+ /* this is not the correct set of tests */
+/* if(log2[dst->depth] != log2[src->depth] || log2[dst->depth]!=3) */
+/* return; */
+
+ /* can't use sutherland-cohen clipping because lines are wide */
+ r = memlinebbox(p0, p1, end0, end1, radius);
+ /*
+ * r is now a bounding box for the line;
+ * use it as a clipping rectangle for subdivision
+ */
+ if(rectclip(&r, clipr) == 0)
+ return;
+ ll.p0 = p0;
+ ll.p1 = p1;
+ ll.end0 = end0;
+ ll.end1 = end1;
+ ll.sp = sp;
+ ll.dstlayer = dst->layer;
+ ll.src = src;
+ ll.radius = radius;
+ ll.delta = dl->delta;
+ ll.op = op;
+ _memlayerop(llineop, dst, r, r, &ll);
+}
+
+static
+void
+llineop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave)
+{
+ struct Lline *ll;
+ Point p0, p1;
+
+ USED(screenr.min.x);
+ ll = etc;
+ if(insave && ll->dstlayer->save==nil)
+ return;
+ if(!rectclip(&clipr, screenr))
+ return;
+ if(insave){
+ p0 = subpt(ll->p0, ll->delta);
+ p1 = subpt(ll->p1, ll->delta);
+ clipr = rectsubpt(clipr, ll->delta);
+ }else{
+ p0 = ll->p0;
+ p1 = ll->p1;
+ }
+ _memline(dst, p0, p1, ll->end0, ll->end1, ll->radius, ll->src, ll->sp, clipr, ll->op);
+}
+
+void
+memline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, int op)
+{
+ _memline(dst, p0, p1, end0, end1, radius, src, sp, dst->clipr, op);
+}
diff --git a/src/libmemlayer/load.c b/src/libmemlayer/load.c
new file mode 100644
index 00000000..d211564b
--- /dev/null
+++ b/src/libmemlayer/load.c
@@ -0,0 +1,55 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+int
+memload(Memimage *dst, Rectangle r, uchar *data, int n, int iscompressed)
+{
+ int (*loadfn)(Memimage*, Rectangle, uchar*, int);
+ Memimage *tmp;
+ Memlayer *dl;
+ Rectangle lr;
+ int dx;
+
+ loadfn = loadmemimage;
+ if(iscompressed)
+ loadfn = cloadmemimage;
+
+ Top:
+ dl = dst->layer;
+ if(dl == nil)
+ return loadfn(dst, r, data, n);
+
+ /*
+ * Convert to screen coordinates.
+ */
+ lr = r;
+ r.min.x += dl->delta.x;
+ r.min.y += dl->delta.y;
+ r.max.x += dl->delta.x;
+ r.max.y += dl->delta.y;
+ dx = dl->delta.x&(7/dst->depth);
+ if(dl->clear && dx==0){
+ dst = dl->screen->image;
+ goto Top;
+ }
+
+ /*
+ * dst is an obscured layer or data is unaligned
+ */
+ if(dl->save && dx==0){
+ n = loadfn(dl->save, lr, data, n);
+ if(n > 0)
+ memlexpose(dst, r);
+ return n;
+ }
+ tmp = allocmemimage(lr, dst->chan);
+ if(tmp == nil)
+ return -1;
+ n = loadfn(tmp, lr, data, n);
+ memdraw(dst, lr, tmp, lr.min, nil, lr.min, S);
+ freememimage(tmp);
+ return n;
+}
diff --git a/src/libmemlayer/lorigin.c b/src/libmemlayer/lorigin.c
new file mode 100644
index 00000000..0926ee8d
--- /dev/null
+++ b/src/libmemlayer/lorigin.c
@@ -0,0 +1,107 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+/*
+ * Place i so i->r.min = log, i->layer->screenr.min == scr.
+*/
+int
+memlorigin(Memimage *i, Point log, Point scr)
+{
+ Memlayer *l;
+ Memscreen *s;
+ Memimage *t, *shad, *nsave;
+ Rectangle x, newr, oldr;
+ Point delta;
+ int overlap, eqlog, eqscr, wasclear;
+
+ l = i->layer;
+ s = l->screen;
+ oldr = l->screenr;
+ newr = Rect(scr.x, scr.y, scr.x+Dx(oldr), scr.y+Dy(oldr));
+ eqscr = eqpt(scr, oldr.min);
+ eqlog = eqpt(log, i->r.min);
+ if(eqscr && eqlog)
+ return 0;
+ nsave = nil;
+ if(eqlog==0 && l->save!=nil){
+ nsave = allocmemimage(Rect(log.x, log.y, log.x+Dx(oldr), log.y+Dy(oldr)), i->chan);
+ if(nsave == nil)
+ return -1;
+ }
+
+ /*
+ * Bring it to front and move logical coordinate system.
+ */
+ memltofront(i);
+ wasclear = l->clear;
+ if(nsave){
+ if(!wasclear)
+ memimagedraw(nsave, nsave->r, l->save, l->save->r.min, nil, Pt(0,0), S);
+ freememimage(l->save);
+ l->save = nsave;
+ }
+ delta = subpt(log, i->r.min);
+ i->r = rectaddpt(i->r, delta);
+ i->clipr = rectaddpt(i->clipr, delta);
+ l->delta = subpt(l->screenr.min, i->r.min);
+ if(eqscr)
+ return 0;
+
+ /*
+ * To clean up old position, make a shadow window there, don't paint it,
+ * push it behind this one, and (later) delete it. Because the refresh function
+ * for this fake window is a no-op, this will cause no graphics action except
+ * to restore the background and expose the windows previously hidden.
+ */
+ shad = memlalloc(s, oldr, memlnorefresh, nil, DNofill);
+ if(shad == nil)
+ return -1;
+ s->frontmost = i;
+ if(s->rearmost == i)
+ s->rearmost = shad;
+ else
+ l->rear->layer->front = shad;
+ shad->layer->front = i;
+ shad->layer->rear = l->rear;
+ l->rear = shad;
+ l->front = nil;
+ shad->layer->clear = 0;
+
+ /*
+ * Shadow is now holding down the fort at the old position.
+ * Move the window and hide things obscured by new position.
+ */
+ for(t=l->rear->layer->rear; t!=nil; t=t->layer->rear){
+ x = newr;
+ overlap = rectclip(&x, t->layer->screenr);
+ if(overlap){
+ memlhide(t, x);
+ t->layer->clear = 0;
+ }
+ }
+ l->screenr = newr;
+ l->delta = subpt(scr, i->r.min);
+ l->clear = rectinrect(newr, l->screen->image->clipr);
+
+ /*
+ * Everything's covered. Copy to new position and delete shadow window.
+ */
+ if(wasclear)
+ memdraw(s->image, newr, s->image, oldr.min, nil, Pt(0,0), S);
+ else
+ memlexpose(i, newr);
+ memldelete(shad);
+
+ return 1;
+}
+
+void
+memlnorefresh(Memimage *l, Rectangle r, void *v)
+{
+ USED(l);
+ USED(r.min.x);
+ USED(v);
+}
diff --git a/src/libmemlayer/lsetrefresh.c b/src/libmemlayer/lsetrefresh.c
new file mode 100644
index 00000000..526bd668
--- /dev/null
+++ b/src/libmemlayer/lsetrefresh.c
@@ -0,0 +1,35 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+int
+memlsetrefresh(Memimage *i, Refreshfn fn, void *ptr)
+{
+ Memlayer *l;
+
+ l = i->layer;
+ if(l->refreshfn!=0 && fn!=0){ /* just change functions */
+ l->refreshfn = fn;
+ l->refreshptr = ptr;
+ return 1;
+ }
+
+ if(l->refreshfn == 0){ /* is using backup image; just free it */
+ freememimage(l->save);
+ l->save = nil;
+ l->refreshfn = fn;
+ l->refreshptr = ptr;
+ return 1;
+ }
+
+ l->save = allocmemimage(i->r, i->chan);
+ if(l->save == nil)
+ return 0;
+ /* easiest way is just to update the entire save area */
+ l->refreshfn(i, i->r, l->refreshptr);
+ l->refreshfn = 0;
+ l->refreshptr = nil;
+ return 1;
+}
diff --git a/src/libmemlayer/ltofront.c b/src/libmemlayer/ltofront.c
new file mode 100644
index 00000000..447b40bd
--- /dev/null
+++ b/src/libmemlayer/ltofront.c
@@ -0,0 +1,80 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+/*
+ * Pull i towards top of screen, just behind front
+*/
+static
+void
+_memltofront(Memimage *i, Memimage *front, int fill)
+{
+ Memlayer *l;
+ Memscreen *s;
+ Memimage *f, *ff, *rr;
+ Rectangle x;
+ int overlap;
+
+ l = i->layer;
+ s = l->screen;
+ while(l->front != front){
+ f = l->front;
+ x = l->screenr;
+ overlap = rectclip(&x, f->layer->screenr);
+ if(overlap){
+ memlhide(f, x);
+ f->layer->clear = 0;
+ }
+ /* swap l and f in screen's list */
+ ff = f->layer->front;
+ rr = l->rear;
+ if(ff == nil)
+ s->frontmost = i;
+ else
+ ff->layer->rear = i;
+ if(rr == nil)
+ s->rearmost = f;
+ else
+ rr->layer->front = f;
+ l->front = ff;
+ l->rear = f;
+ f->layer->front = i;
+ f->layer->rear = rr;
+ if(overlap && fill)
+ memlexpose(i, x);
+ }
+}
+
+void
+_memltofrontfill(Memimage *i, int fill)
+{
+ _memltofront(i, nil, fill);
+ _memlsetclear(i->layer->screen);
+}
+
+void
+memltofront(Memimage *i)
+{
+ _memltofront(i, nil, 1);
+ _memlsetclear(i->layer->screen);
+}
+
+void
+memltofrontn(Memimage **ip, int n)
+{
+ Memimage *i, *front;
+ Memscreen *s;
+
+ if(n == 0)
+ return;
+ front = nil;
+ while(--n >= 0){
+ i = *ip++;
+ _memltofront(i, front, 1);
+ front = i;
+ }
+ s = front->layer->screen;
+ _memlsetclear(s);
+}
diff --git a/src/libmemlayer/ltorear.c b/src/libmemlayer/ltorear.c
new file mode 100644
index 00000000..d53e8cc9
--- /dev/null
+++ b/src/libmemlayer/ltorear.c
@@ -0,0 +1,69 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+void
+_memltorear(Memimage *i, Memimage *rear)
+{
+ Memlayer *l;
+ Memscreen *s;
+ Memimage *f, *r, *rr;
+ Rectangle x;
+ int overlap;
+
+ l = i->layer;
+ s = l->screen;
+ while(l->rear != rear){
+ r = l->rear;
+ x = l->screenr;
+ overlap = rectclip(&x, r->layer->screenr);
+ if(overlap){
+ memlhide(i, x);
+ l->clear = 0;
+ }
+ /* swap l and r in screen's list */
+ rr = r->layer->rear;
+ f = l->front;
+ if(rr == nil)
+ s->rearmost = i;
+ else
+ rr->layer->front = i;
+ if(f == nil)
+ s->frontmost = r;
+ else
+ f->layer->rear = r;
+ l->rear = rr;
+ l->front = r;
+ r->layer->rear = i;
+ r->layer->front = f;
+ if(overlap)
+ memlexpose(r, x);
+ }
+}
+
+void
+memltorear(Memimage *i)
+{
+ _memltorear(i, nil);
+ _memlsetclear(i->layer->screen);
+}
+
+void
+memltorearn(Memimage **ip, int n)
+{
+ Memimage *i, *rear;
+ Memscreen *s;
+
+ if(n == 0)
+ return;
+ rear = nil;
+ while(--n >= 0){
+ i = *ip++;
+ _memltorear(i, rear);
+ rear = i;
+ }
+ s = rear->layer->screen;
+ _memlsetclear(s);
+}
diff --git a/src/libmemlayer/mkfile b/src/libmemlayer/mkfile
new file mode 100644
index 00000000..c1d05419
--- /dev/null
+++ b/src/libmemlayer/mkfile
@@ -0,0 +1,25 @@
+<$PLAN9/src/mkhdr
+
+LIB=libmemlayer.a
+
+OFILES=\
+ draw.$O\
+ lalloc.$O\
+ layerop.$O\
+ ldelete.$O\
+ lhide.$O\
+ line.$O\
+ load.$O\
+ lorigin.$O\
+ lsetrefresh.$O\
+ ltofront.$O\
+ ltorear.$O\
+ unload.$O\
+
+HFILES=\
+ $PLAN9/include/draw.h\
+ $PLAN9/include/memdraw.h\
+ $PLAN9/include/memlayer.h\
+
+<$PLAN9/src/mksyslib
+
diff --git a/src/libmemlayer/unload.c b/src/libmemlayer/unload.c
new file mode 100644
index 00000000..b9534117
--- /dev/null
+++ b/src/libmemlayer/unload.c
@@ -0,0 +1,52 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+int
+memunload(Memimage *src, Rectangle r, uchar *data, int n)
+{
+ Memimage *tmp;
+ Memlayer *dl;
+ Rectangle lr;
+ int dx;
+
+ Top:
+ dl = src->layer;
+ if(dl == nil)
+ return unloadmemimage(src, r, data, n);
+
+ /*
+ * Convert to screen coordinates.
+ */
+ lr = r;
+ r.min.x += dl->delta.x;
+ r.min.y += dl->delta.y;
+ r.max.x += dl->delta.x;
+ r.max.y += dl->delta.y;
+ dx = dl->delta.x&(7/src->depth);
+ if(dl->clear && dx==0){
+ src = dl->screen->image;
+ goto Top;
+ }
+
+ /*
+ * src is an obscured layer or data is unaligned
+ */
+ if(dl->save && dx==0){
+ if(dl->refreshfn != 0)
+ return -1; /* can't unload window if it's not Refbackup */
+ if(n > 0)
+ memlhide(src, r);
+ n = unloadmemimage(dl->save, lr, data, n);
+ return n;
+ }
+ tmp = allocmemimage(lr, src->chan);
+ if(tmp == nil)
+ return -1;
+ memdraw(tmp, lr, src, lr.min, nil, lr.min, S);
+ n = unloadmemimage(tmp, lr, data, n);
+ freememimage(tmp);
+ return n;
+}