Blob


1 /*
2 * /dev/draw simulator -- handles the messages prepared by the draw library.
3 * Doesn't simulate the file system part, just the messages.
4 */
6 #include <u.h>
7 #include <libc.h>
8 #include <draw.h>
9 #include <memdraw.h>
10 #include <memlayer.h>
11 #include <mouse.h>
12 #include <cursor.h>
13 #include <keyboard.h>
14 #include <drawfcall.h>
15 #include "devdraw.h"
17 static int drawuninstall(Client*, int);
18 static Memimage* drawinstall(Client*, int, Memimage*, DScreen*);
19 static void drawfreedimage(Client*, DImage*);
21 void
22 draw_initdisplaymemimage(Client *c, Memimage *m)
23 {
24 c->screenimage = m;
25 m->screenref = 1;
26 c->slot = 0;
27 c->clientid = 1;
28 c->op = SoverD;
29 }
31 // gfx_replacescreenimage replaces c's screen image with m.
32 // It is called by the host driver on the main host thread.
33 void
34 gfx_replacescreenimage(Client *c, Memimage *m)
35 {
36 /*
37 * Replace the screen image because the screen
38 * was resized.
39 *
40 * In theory there should only be one reference
41 * to the current screen image, and that's through
42 * client0's image 0, installed a few lines above.
43 * Once the client drops the image, the underlying backing
44 * store freed properly. The client is being notified
45 * about the resize through external means, so all we
46 * need to do is this assignment.
47 */
48 Memimage *om;
50 qlock(&c->drawlk);
51 om = c->screenimage;
52 c->screenimage = m;
53 m->screenref = 1;
54 if(om && --om->screenref == 0){
55 _freememimage(om);
56 }
57 qunlock(&c->drawlk);
58 gfx_mouseresized(c);
59 }
61 static void
62 drawrefreshscreen(DImage *l, Client *client)
63 {
64 while(l != nil && l->dscreen == nil)
65 l = l->fromname;
66 if(l != nil && l->dscreen->owner != client)
67 l->dscreen->owner->refreshme = 1;
68 }
70 static void
71 drawrefresh(Memimage *m, Rectangle r, void *v)
72 {
73 Refx *x;
74 DImage *d;
75 Client *c;
76 Refresh *ref;
78 USED(m);
80 if(v == 0)
81 return;
82 x = v;
83 c = x->client;
84 d = x->dimage;
85 for(ref=c->refresh; ref; ref=ref->next)
86 if(ref->dimage == d){
87 combinerect(&ref->r, r);
88 return;
89 }
90 ref = mallocz(sizeof(Refresh), 1);
91 if(ref){
92 ref->dimage = d;
93 ref->r = r;
94 ref->next = c->refresh;
95 c->refresh = ref;
96 }
97 }
99 static void
100 addflush(Client *c, Rectangle r)
102 int abb, ar, anbb;
103 Rectangle nbb, fr;
105 if(/*sdraw.softscreen==0 ||*/ !rectclip(&r, c->screenimage->r))
106 return;
108 if(c->flushrect.min.x >= c->flushrect.max.x){
109 c->flushrect = r;
110 c->waste = 0;
111 return;
113 nbb = c->flushrect;
114 combinerect(&nbb, r);
115 ar = Dx(r)*Dy(r);
116 abb = Dx(c->flushrect)*Dy(c->flushrect);
117 anbb = Dx(nbb)*Dy(nbb);
118 /*
119 * Area of new waste is area of new bb minus area of old bb,
120 * less the area of the new segment, which we assume is not waste.
121 * This could be negative, but that's OK.
122 */
123 c->waste += anbb-abb - ar;
124 if(c->waste < 0)
125 c->waste = 0;
126 /*
127 * absorb if:
128 * total area is small
129 * waste is less than half total area
130 * rectangles touch
131 */
132 if(anbb<=1024 || c->waste*2<anbb || rectXrect(c->flushrect, r)){
133 c->flushrect = nbb;
134 return;
136 /* emit current state */
137 fr = c->flushrect;
138 c->flushrect = r;
139 c->waste = 0;
140 if(fr.min.x < fr.max.x) {
141 // Unlock drawlk because rpc_flush may want to run on gfx thread,
142 // and gfx thread might be blocked on drawlk trying to install a new screen
143 // during a resize.
144 rpc_gfxdrawunlock();
145 qunlock(&c->drawlk);
146 rpc_flush(c, fr);
147 qlock(&c->drawlk);
148 rpc_gfxdrawlock();
152 static void
153 dstflush(Client *c, int dstid, Memimage *dst, Rectangle r)
155 Memlayer *l;
157 if(dstid == 0){
158 combinerect(&c->flushrect, r);
159 return;
161 /* how can this happen? -rsc, dec 12 2002 */
162 if(dst == 0){
163 fprint(2, "nil dstflush\n");
164 return;
166 l = dst->layer;
167 if(l == nil)
168 return;
169 do{
170 if(l->screen->image->data != c->screenimage->data)
171 return;
172 r = rectaddpt(r, l->delta);
173 l = l->screen->image->layer;
174 }while(l);
175 addflush(c, r);
178 static void
179 drawflush(Client *c)
181 Rectangle r;
183 r = c->flushrect;
184 c->flushrect = Rect(10000, 10000, -10000, -10000);
185 if(r.min.x < r.max.x) {
186 // Unlock drawlk because rpc_flush may want to run on gfx thread,
187 // and gfx thread might be blocked on drawlk trying to install a new screen
188 // during a resize.
189 rpc_gfxdrawunlock();
190 qunlock(&c->drawlk);
191 rpc_flush(c, r);
192 qlock(&c->drawlk);
193 rpc_gfxdrawlock();
197 static int
198 drawcmp(char *a, char *b, int n)
200 if(strlen(a) != n)
201 return 1;
202 return memcmp(a, b, n);
205 static DName*
206 drawlookupname(Client *client, int n, char *str)
208 DName *name, *ename;
210 name = client->name;
211 ename = &name[client->nname];
212 for(; name<ename; name++)
213 if(drawcmp(name->name, str, n) == 0)
214 return name;
215 return 0;
218 static int
219 drawgoodname(Client *client, DImage *d)
221 DName *n;
223 /* if window, validate the screen's own images */
224 if(d->dscreen)
225 if(drawgoodname(client, d->dscreen->dimage) == 0
226 || drawgoodname(client, d->dscreen->dfill) == 0)
227 return 0;
228 if(d->name == nil)
229 return 1;
230 n = drawlookupname(client, strlen(d->name), d->name);
231 if(n==nil || n->vers!=d->vers)
232 return 0;
233 return 1;
236 static DImage*
237 drawlookup(Client *client, int id, int checkname)
239 DImage *d;
241 d = client->dimage[id&HASHMASK];
242 while(d){
243 if(d->id == id){
244 /*
245 * BUG: should error out but too hard.
246 * Return 0 instead.
247 */
248 if(checkname && !drawgoodname(client, d))
249 return 0;
250 return d;
252 d = d->next;
254 return 0;
257 static DScreen*
258 drawlookupdscreen(Client *c, int id)
260 DScreen *s;
262 s = c->dscreen;
263 while(s){
264 if(s->id == id)
265 return s;
266 s = s->next;
268 return 0;
271 static DScreen*
272 drawlookupscreen(Client *client, int id, CScreen **cs)
274 CScreen *s;
276 s = client->cscreen;
277 while(s){
278 if(s->dscreen->id == id){
279 *cs = s;
280 return s->dscreen;
282 s = s->next;
284 /* caller must check! */
285 return 0;
288 static Memimage*
289 drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
291 DImage *d;
293 d = mallocz(sizeof(DImage), 1);
294 if(d == 0)
295 return 0;
296 d->id = id;
297 d->ref = 1;
298 d->name = 0;
299 d->vers = 0;
300 d->image = i;
301 if(i->screenref)
302 ++i->screenref;
303 d->nfchar = 0;
304 d->fchar = 0;
305 d->fromname = 0;
306 d->dscreen = dscreen;
307 d->next = client->dimage[id&HASHMASK];
308 client->dimage[id&HASHMASK] = d;
309 return i;
312 static Memscreen*
313 drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public)
315 Memscreen *s;
316 CScreen *c;
318 c = mallocz(sizeof(CScreen), 1);
319 if(dimage && dimage->image && dimage->image->chan == 0){
320 fprint(2, "bad image %p in drawinstallscreen", dimage->image);
321 abort();
324 if(c == 0)
325 return 0;
326 if(d == 0){
327 d = mallocz(sizeof(DScreen), 1);
328 if(d == 0){
329 free(c);
330 return 0;
332 s = mallocz(sizeof(Memscreen), 1);
333 if(s == 0){
334 free(c);
335 free(d);
336 return 0;
338 s->frontmost = 0;
339 s->rearmost = 0;
340 d->dimage = dimage;
341 if(dimage){
342 s->image = dimage->image;
343 dimage->ref++;
345 d->dfill = dfill;
346 if(dfill){
347 s->fill = dfill->image;
348 dfill->ref++;
350 d->ref = 0;
351 d->id = id;
352 d->screen = s;
353 d->public = public;
354 d->next = client->dscreen;
355 d->owner = client;
356 client->dscreen = d;
358 c->dscreen = d;
359 d->ref++;
360 c->next = client->cscreen;
361 client->cscreen = c;
362 return d->screen;
365 static void
366 drawdelname(Client *client, DName *name)
368 int i;
370 i = name-client->name;
371 memmove(name, name+1, (client->nname-(i+1))*sizeof(DName));
372 client->nname--;
375 static void
376 drawfreedscreen(Client *client, DScreen *this)
378 DScreen *ds, *next;
380 this->ref--;
381 if(this->ref < 0)
382 fprint(2, "negative ref in drawfreedscreen\n");
383 if(this->ref > 0)
384 return;
385 ds = client->dscreen;
386 if(ds == this){
387 client->dscreen = this->next;
388 goto Found;
390 while(next = ds->next){ /* assign = */
391 if(next == this){
392 ds->next = this->next;
393 goto Found;
395 ds = next;
397 /*
398 * Should signal Enodrawimage, but too hard.
399 */
400 return;
402 Found:
403 if(this->dimage)
404 drawfreedimage(client, this->dimage);
405 if(this->dfill)
406 drawfreedimage(client, this->dfill);
407 free(this->screen);
408 free(this);
411 static void
412 drawfreedimage(Client *client, DImage *dimage)
414 int i;
415 Memimage *l;
416 DScreen *ds;
418 dimage->ref--;
419 if(dimage->ref < 0)
420 fprint(2, "negative ref in drawfreedimage\n");
421 if(dimage->ref > 0)
422 return;
424 /* any names? */
425 for(i=0; i<client->nname; )
426 if(client->name[i].dimage == dimage)
427 drawdelname(client, client->name+i);
428 else
429 i++;
430 if(dimage->fromname){ /* acquired by name; owned by someone else*/
431 drawfreedimage(client, dimage->fromname);
432 goto Return;
434 ds = dimage->dscreen;
435 l = dimage->image;
436 dimage->dscreen = nil; /* paranoia */
437 dimage->image = nil;
438 if(ds){
439 if(l->data == client->screenimage->data)
440 addflush(client, l->layer->screenr);
441 if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */
442 free(l->layer->refreshptr);
443 l->layer->refreshptr = nil;
444 if(drawgoodname(client, dimage))
445 memldelete(l);
446 else
447 memlfree(l);
448 drawfreedscreen(client, ds);
449 }else{
450 if(l->screenref==0)
451 freememimage(l);
452 else if(--l->screenref==0)
453 _freememimage(l);
455 Return:
456 free(dimage->fchar);
457 free(dimage);
460 static void
461 drawuninstallscreen(Client *client, CScreen *this)
463 CScreen *cs, *next;
465 cs = client->cscreen;
466 if(cs == this){
467 client->cscreen = this->next;
468 drawfreedscreen(client, this->dscreen);
469 free(this);
470 return;
472 while(next = cs->next){ /* assign = */
473 if(next == this){
474 cs->next = this->next;
475 drawfreedscreen(client, this->dscreen);
476 free(this);
477 return;
479 cs = next;
483 static int
484 drawuninstall(Client *client, int id)
486 DImage *d, **l;
488 for(l=&client->dimage[id&HASHMASK]; (d=*l) != nil; l=&d->next){
489 if(d->id == id){
490 *l = d->next;
491 drawfreedimage(client, d);
492 return 0;
495 return -1;
498 static int
499 drawaddname(Client *client, DImage *di, int n, char *str, char **err)
501 DName *name, *ename, *new, *t;
502 char *ns;
504 name = client->name;
505 ename = &name[client->nname];
506 for(; name<ename; name++)
507 if(drawcmp(name->name, str, n) == 0){
508 *err = "image name in use";
509 return -1;
511 t = mallocz((client->nname+1)*sizeof(DName), 1);
512 ns = malloc(n+1);
513 if(t == nil || ns == nil){
514 free(t);
515 free(ns);
516 *err = "out of memory";
517 return -1;
519 memmove(t, client->name, client->nname*sizeof(DName));
520 free(client->name);
521 client->name = t;
522 new = &client->name[client->nname++];
523 new->name = ns;
524 memmove(new->name, str, n);
525 new->name[n] = 0;
526 new->dimage = di;
527 new->client = client;
528 new->vers = ++client->namevers;
529 return 0;
532 static int
533 drawclientop(Client *cl)
535 int op;
537 op = cl->op;
538 cl->op = SoverD;
539 return op;
542 static Memimage*
543 drawimage(Client *client, uchar *a)
545 DImage *d;
547 d = drawlookup(client, BGLONG(a), 1);
548 if(d == nil)
549 return nil; /* caller must check! */
550 return d->image;
553 static void
554 drawrectangle(Rectangle *r, uchar *a)
556 r->min.x = BGLONG(a+0*4);
557 r->min.y = BGLONG(a+1*4);
558 r->max.x = BGLONG(a+2*4);
559 r->max.y = BGLONG(a+3*4);
562 static void
563 drawpoint(Point *p, uchar *a)
565 p->x = BGLONG(a+0*4);
566 p->y = BGLONG(a+1*4);
569 static Point
570 drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
572 FChar *fc;
573 Rectangle r;
574 Point sp1;
576 fc = &font->fchar[index];
577 r.min.x = p.x+fc->left;
578 r.min.y = p.y-(font->ascent-fc->miny);
579 r.max.x = r.min.x+(fc->maxx-fc->minx);
580 r.max.y = r.min.y+(fc->maxy-fc->miny);
581 sp1.x = sp->x+fc->left;
582 sp1.y = sp->y+fc->miny;
583 memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
584 p.x += fc->width;
585 sp->x += fc->width;
586 return p;
589 static uchar*
590 drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
592 int b, x;
594 if(p >= maxp)
595 return nil;
596 b = *p++;
597 x = b & 0x7F;
598 if(b & 0x80){
599 if(p+1 >= maxp)
600 return nil;
601 x |= *p++ << 7;
602 x |= *p++ << 15;
603 if(x & (1<<22))
604 x |= ~0U<<23;
605 }else{
606 if(b & 0x40)
607 x |= ~0U<<7;
608 x += oldx;
610 *newx = x;
611 return p;
614 int
615 draw_dataread(Client *cl, void *a, int n)
617 qlock(&cl->drawlk);
618 if(cl->readdata == nil){
619 werrstr("no draw data");
620 goto err;
622 if(n < cl->nreaddata){
623 werrstr("short read");
624 goto err;
626 n = cl->nreaddata;
627 memmove(a, cl->readdata, cl->nreaddata);
628 free(cl->readdata);
629 cl->readdata = nil;
630 qunlock(&cl->drawlk);
631 return n;
633 err:
634 qunlock(&cl->drawlk);
635 return -1;
638 int
639 draw_datawrite(Client *client, void *v, int n)
641 char cbuf[40], *err, ibuf[12*12+1], *s;
642 int c, ci, doflush, dstid, e0, e1, esize, j, m;
643 int ni, nw, oesize, oldn, op, ox, oy, repl, scrnid, y;
644 uchar *a, refresh, *u;
645 u32int chan, value;
646 CScreen *cs;
647 DImage *di, *ddst, *dsrc, *font, *ll;
648 DName *dn;
649 DScreen *dscrn;
650 FChar *fc;
651 Fmt fmt;
652 Memimage *dst, *i, *l, **lp, *mask, *src;
653 Memscreen *scrn;
654 Point p, *pp, q, sp;
655 Rectangle clipr, r;
656 Refreshfn reffn;
657 Refx *refx;
659 qlock(&client->drawlk);
660 rpc_gfxdrawlock();
661 a = v;
662 m = 0;
663 oldn = n;
665 while((n-=m) > 0){
666 a += m;
667 /*fprint(2, "msgwrite %d(%d)...", n, *a); */
668 switch(*a){
669 default:
670 /*fprint(2, "bad command %d\n", *a); */
671 err = "bad draw command";
672 goto error;
674 /* allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1]
675 R[4*4] clipR[4*4] rrggbbaa[4]
676 */
677 case 'b':
678 m = 1+4+4+1+4+1+4*4+4*4+4;
679 if(n < m)
680 goto Eshortdraw;
681 dstid = BGLONG(a+1);
682 scrnid = BGSHORT(a+5);
683 refresh = a[9];
684 chan = BGLONG(a+10);
685 repl = a[14];
686 drawrectangle(&r, a+15);
687 drawrectangle(&clipr, a+31);
688 value = BGLONG(a+47);
689 if(drawlookup(client, dstid, 0))
690 goto Eimageexists;
691 if(scrnid){
692 dscrn = drawlookupscreen(client, scrnid, &cs);
693 if(!dscrn)
694 goto Enodrawscreen;
695 scrn = dscrn->screen;
696 if(repl || chan!=scrn->image->chan){
697 err = "image parameters incompatibile with screen";
698 goto error;
700 reffn = 0;
701 switch(refresh){
702 case Refbackup:
703 break;
704 case Refnone:
705 reffn = memlnorefresh;
706 break;
707 case Refmesg:
708 reffn = drawrefresh;
709 break;
710 default:
711 err = "unknown refresh method";
712 goto error;
714 l = memlalloc(scrn, r, reffn, 0, value);
715 if(l == 0)
716 goto Edrawmem;
717 addflush(client, l->layer->screenr);
718 l->clipr = clipr;
719 rectclip(&l->clipr, r);
720 if(drawinstall(client, dstid, l, dscrn) == 0){
721 memldelete(l);
722 goto Edrawmem;
724 dscrn->ref++;
725 if(reffn){
726 refx = nil;
727 if(reffn == drawrefresh){
728 refx = mallocz(sizeof(Refx), 1);
729 if(refx == 0){
730 if(drawuninstall(client, dstid) < 0)
731 goto Enodrawimage;
732 goto Edrawmem;
734 refx->client = client;
735 refx->dimage = drawlookup(client, dstid, 1);
737 memlsetrefresh(l, reffn, refx);
739 continue;
741 i = allocmemimage(r, chan);
742 if(i == 0)
743 goto Edrawmem;
744 if(repl)
745 i->flags |= Frepl;
746 i->clipr = clipr;
747 if(!repl)
748 rectclip(&i->clipr, r);
749 if(drawinstall(client, dstid, i, 0) == 0){
750 freememimage(i);
751 goto Edrawmem;
753 memfillcolor(i, value);
754 continue;
756 /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
757 case 'A':
758 m = 1+4+4+4+1;
759 if(n < m)
760 goto Eshortdraw;
761 dstid = BGLONG(a+1);
762 if(dstid == 0)
763 goto Ebadarg;
764 if(drawlookupdscreen(client, dstid))
765 goto Escreenexists;
766 ddst = drawlookup(client, BGLONG(a+5), 1);
767 dsrc = drawlookup(client, BGLONG(a+9), 1);
768 if(ddst==0 || dsrc==0)
769 goto Enodrawimage;
770 if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0)
771 goto Edrawmem;
772 continue;
774 /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
775 case 'c':
776 m = 1+4+1+4*4;
777 if(n < m)
778 goto Eshortdraw;
779 ddst = drawlookup(client, BGLONG(a+1), 1);
780 if(ddst == nil)
781 goto Enodrawimage;
782 if(ddst->name){
783 err = "can't change repl/clipr of shared image";
784 goto error;
786 dst = ddst->image;
787 if(a[5])
788 dst->flags |= Frepl;
789 drawrectangle(&dst->clipr, a+6);
790 continue;
792 /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
793 case 'd':
794 m = 1+4+4+4+4*4+2*4+2*4;
795 if(n < m)
796 goto Eshortdraw;
797 dst = drawimage(client, a+1);
798 dstid = BGLONG(a+1);
799 src = drawimage(client, a+5);
800 mask = drawimage(client, a+9);
801 if(!dst || !src || !mask)
802 goto Enodrawimage;
803 drawrectangle(&r, a+13);
804 drawpoint(&p, a+29);
805 drawpoint(&q, a+37);
806 op = drawclientop(client);
807 memdraw(dst, r, src, p, mask, q, op);
808 dstflush(client, dstid, dst, r);
809 continue;
811 /* toggle debugging: 'D' val[1] */
812 case 'D':
813 m = 1+1;
814 if(n < m)
815 goto Eshortdraw;
816 drawdebug = a[1];
817 continue;
819 /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
820 case 'e':
821 case 'E':
822 m = 1+4+4+2*4+4+4+4+2*4+2*4;
823 if(n < m)
824 goto Eshortdraw;
825 dst = drawimage(client, a+1);
826 dstid = BGLONG(a+1);
827 src = drawimage(client, a+5);
828 if(!dst || !src)
829 goto Enodrawimage;
830 drawpoint(&p, a+9);
831 e0 = BGLONG(a+17);
832 e1 = BGLONG(a+21);
833 if(e0<0 || e1<0){
834 err = "invalid ellipse semidiameter";
835 goto error;
837 j = BGLONG(a+25);
838 if(j < 0){
839 err = "negative ellipse thickness";
840 goto error;
843 drawpoint(&sp, a+29);
844 c = j;
845 if(*a == 'E')
846 c = -1;
847 ox = BGLONG(a+37);
848 oy = BGLONG(a+41);
849 op = drawclientop(client);
850 /* high bit indicates arc angles are present */
851 if(ox & ((ulong)1<<31)){
852 if((ox & ((ulong)1<<30)) == 0)
853 ox &= ~((ulong)1<<31);
854 memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
855 }else
856 memellipse(dst, p, e0, e1, c, src, sp, op);
857 dstflush(client, dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
858 continue;
860 /* free: 'f' id[4] */
861 case 'f':
862 m = 1+4;
863 if(n < m)
864 goto Eshortdraw;
865 ll = drawlookup(client, BGLONG(a+1), 0);
866 if(ll && ll->dscreen && ll->dscreen->owner != client)
867 ll->dscreen->owner->refreshme = 1;
868 if(drawuninstall(client, BGLONG(a+1)) < 0)
869 goto Enodrawimage;
870 continue;
872 /* free screen: 'F' id[4] */
873 case 'F':
874 m = 1+4;
875 if(n < m)
876 goto Eshortdraw;
877 if(!drawlookupscreen(client, BGLONG(a+1), &cs))
878 goto Enodrawscreen;
879 drawuninstallscreen(client, cs);
880 continue;
882 /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
883 case 'i':
884 m = 1+4+4+1;
885 if(n < m)
886 goto Eshortdraw;
887 dstid = BGLONG(a+1);
888 if(dstid == 0){
889 err = "can't use display as font";
890 goto error;
892 font = drawlookup(client, dstid, 1);
893 if(font == 0)
894 goto Enodrawimage;
895 if(font->image->layer){
896 err = "can't use window as font";
897 goto error;
899 ni = BGLONG(a+5);
900 if(ni<=0 || ni>4096){
901 err = "bad font size (4096 chars max)";
902 goto error;
904 free(font->fchar); /* should we complain if non-zero? */
905 font->fchar = mallocz(ni*sizeof(FChar), 1);
906 if(font->fchar == 0){
907 err = "no memory for font";
908 goto error;
910 memset(font->fchar, 0, ni*sizeof(FChar));
911 font->nfchar = ni;
912 font->ascent = a[9];
913 continue;
915 /* set image 0 to screen image */
916 case 'J':
917 m = 1;
918 if(n < m)
919 goto Eshortdraw;
920 if(drawlookup(client, 0, 0))
921 goto Eimageexists;
922 drawinstall(client, 0, client->screenimage, 0);
923 client->infoid = 0;
924 continue;
926 /* get image info: 'I' */
927 case 'I':
928 m = 1;
929 if(n < m)
930 goto Eshortdraw;
931 if(client->infoid < 0)
932 goto Enodrawimage;
933 if(client->infoid == 0){
934 i = client->screenimage;
935 if(i == nil)
936 goto Enodrawimage;
937 }else{
938 di = drawlookup(client, client->infoid, 1);
939 if(di == nil)
940 goto Enodrawimage;
941 i = di->image;
943 ni = sprint(ibuf, "%11d %11d %11s %11d %11d %11d %11d %11d"
944 " %11d %11d %11d %11d ",
945 client->clientid,
946 client->infoid,
947 chantostr(cbuf, i->chan),
948 (i->flags&Frepl)==Frepl,
949 i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
950 i->clipr.min.x, i->clipr.min.y,
951 i->clipr.max.x, i->clipr.max.y);
952 free(client->readdata);
953 client->readdata = malloc(ni);
954 if(client->readdata == nil)
955 goto Enomem;
956 memmove(client->readdata, ibuf, ni);
957 client->nreaddata = ni;
958 client->infoid = -1;
959 continue;
961 /* query: 'Q' n[1] queryspec[n] */
962 case 'q':
963 if(n < 2)
964 goto Eshortdraw;
965 m = 1+1+a[1];
966 if(n < m)
967 goto Eshortdraw;
968 fmtstrinit(&fmt);
969 for(c=0; c<a[1]; c++) {
970 switch(a[2+c]) {
971 default:
972 err = "unknown query";
973 goto error;
974 case 'd': /* dpi */
975 if(client->forcedpi)
976 fmtprint(&fmt, "%11d ", client->forcedpi);
977 else
978 fmtprint(&fmt, "%11d ", client->displaydpi);
979 break;
982 client->readdata = (uchar*)fmtstrflush(&fmt);
983 if(client->readdata == nil)
984 goto Enomem;
985 client->nreaddata = strlen((char*)client->readdata);
986 continue;
988 /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
989 case 'l':
990 m = 1+4+4+2+4*4+2*4+1+1;
991 if(n < m)
992 goto Eshortdraw;
993 font = drawlookup(client, BGLONG(a+1), 1);
994 if(font == 0)
995 goto Enodrawimage;
996 if(font->nfchar == 0)
997 goto Enotfont;
998 src = drawimage(client, a+5);
999 if(!src)
1000 goto Enodrawimage;
1001 ci = BGSHORT(a+9);
1002 if(ci >= font->nfchar)
1003 goto Eindex;
1004 drawrectangle(&r, a+11);
1005 drawpoint(&p, a+27);
1006 memdraw(font->image, r, src, p, memopaque, p, S);
1007 fc = &font->fchar[ci];
1008 fc->minx = r.min.x;
1009 fc->maxx = r.max.x;
1010 fc->miny = r.min.y;
1011 fc->maxy = r.max.y;
1012 fc->left = a[35];
1013 fc->width = a[36];
1014 continue;
1016 /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
1017 case 'L':
1018 m = 1+4+2*4+2*4+4+4+4+4+2*4;
1019 if(n < m)
1020 goto Eshortdraw;
1021 dst = drawimage(client, a+1);
1022 dstid = BGLONG(a+1);
1023 drawpoint(&p, a+5);
1024 drawpoint(&q, a+13);
1025 e0 = BGLONG(a+21);
1026 e1 = BGLONG(a+25);
1027 j = BGLONG(a+29);
1028 if(j < 0){
1029 err = "negative line width";
1030 goto error;
1032 src = drawimage(client, a+33);
1033 if(!dst || !src)
1034 goto Enodrawimage;
1035 drawpoint(&sp, a+37);
1036 op = drawclientop(client);
1037 memline(dst, p, q, e0, e1, j, src, sp, op);
1038 /* avoid memlinebbox if possible */
1039 if(dstid==0 || dst->layer!=nil){
1040 /* BUG: this is terribly inefficient: update maximal containing rect*/
1041 r = memlinebbox(p, q, e0, e1, j);
1042 dstflush(client, dstid, dst, insetrect(r, -(1+1+j)));
1044 continue;
1046 /* create image mask: 'm' newid[4] id[4] */
1049 case 'm':
1050 m = 4+4;
1051 if(n < m)
1052 goto Eshortdraw;
1053 break;
1057 /* attach to a named image: 'n' dstid[4] j[1] name[j] */
1058 case 'n':
1059 m = 1+4+1;
1060 if(n < m)
1061 goto Eshortdraw;
1062 j = a[5];
1063 if(j == 0) /* give me a non-empty name please */
1064 goto Eshortdraw;
1065 m += j;
1066 if(n < m)
1067 goto Eshortdraw;
1068 dstid = BGLONG(a+1);
1069 if(drawlookup(client, dstid, 0))
1070 goto Eimageexists;
1071 dn = drawlookupname(client, j, (char*)a+6);
1072 if(dn == nil)
1073 goto Enoname;
1074 s = malloc(j+1);
1075 if(s == nil)
1076 goto Enomem;
1077 if(drawinstall(client, dstid, dn->dimage->image, 0) == 0)
1078 goto Edrawmem;
1079 di = drawlookup(client, dstid, 0);
1080 if(di == 0)
1081 goto Eoldname;
1082 di->vers = dn->vers;
1083 di->name = s;
1084 di->fromname = dn->dimage;
1085 di->fromname->ref++;
1086 memmove(di->name, a+6, j);
1087 di->name[j] = 0;
1088 client->infoid = dstid;
1089 continue;
1091 /* name an image: 'N' dstid[4] in[1] j[1] name[j] */
1092 case 'N':
1093 m = 1+4+1+1;
1094 if(n < m)
1095 goto Eshortdraw;
1096 c = a[5];
1097 j = a[6];
1098 if(j == 0) /* give me a non-empty name please */
1099 goto Eshortdraw;
1100 m += j;
1101 if(n < m)
1102 goto Eshortdraw;
1103 di = drawlookup(client, BGLONG(a+1), 0);
1104 if(di == 0)
1105 goto Enodrawimage;
1106 if(di->name)
1107 goto Enamed;
1108 if(c)
1109 if(drawaddname(client, di, j, (char*)a+7, &err) < 0)
1110 goto error;
1111 else{
1112 dn = drawlookupname(client, j, (char*)a+7);
1113 if(dn == nil)
1114 goto Enoname;
1115 if(dn->dimage != di)
1116 goto Ewrongname;
1117 drawdelname(client, dn);
1119 continue;
1121 /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
1122 case 'o':
1123 m = 1+4+2*4+2*4;
1124 if(n < m)
1125 goto Eshortdraw;
1126 dst = drawimage(client, a+1);
1127 if(!dst)
1128 goto Enodrawimage;
1129 if(dst->layer){
1130 drawpoint(&p, a+5);
1131 drawpoint(&q, a+13);
1132 r = dst->layer->screenr;
1133 ni = memlorigin(dst, p, q);
1134 if(ni < 0){
1135 err = "image origin failed";
1136 goto error;
1138 if(ni > 0){
1139 addflush(client, r);
1140 addflush(client, dst->layer->screenr);
1141 ll = drawlookup(client, BGLONG(a+1), 1);
1142 drawrefreshscreen(ll, client);
1145 continue;
1147 /* set compositing operator for next draw operation: 'O' op */
1148 case 'O':
1149 m = 1+1;
1150 if(n < m)
1151 goto Eshortdraw;
1152 client->op = a[1];
1153 continue;
1155 /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1156 /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1157 case 'p':
1158 case 'P':
1159 m = 1+4+2+4+4+4+4+2*4;
1160 if(n < m)
1161 goto Eshortdraw;
1162 dstid = BGLONG(a+1);
1163 dst = drawimage(client, a+1);
1164 ni = BGSHORT(a+5);
1165 if(ni < 0){
1166 err = "negative cout in polygon";
1167 goto error;
1169 e0 = BGLONG(a+7);
1170 e1 = BGLONG(a+11);
1171 j = 0;
1172 if(*a == 'p'){
1173 j = BGLONG(a+15);
1174 if(j < 0){
1175 err = "negative polygon line width";
1176 goto error;
1179 src = drawimage(client, a+19);
1180 if(!dst || !src)
1181 goto Enodrawimage;
1182 drawpoint(&sp, a+23);
1183 drawpoint(&p, a+31);
1184 ni++;
1185 pp = mallocz(ni*sizeof(Point), 1);
1186 if(pp == nil)
1187 goto Enomem;
1188 doflush = 0;
1189 if(dstid==0 || (dst->layer && dst->layer->screen->image->data == client->screenimage->data))
1190 doflush = 1; /* simplify test in loop */
1191 ox = oy = 0;
1192 esize = 0;
1193 u = a+m;
1194 for(y=0; y<ni; y++){
1195 q = p;
1196 oesize = esize;
1197 u = drawcoord(u, a+n, ox, &p.x);
1198 if(!u)
1199 goto Eshortdraw;
1200 u = drawcoord(u, a+n, oy, &p.y);
1201 if(!u)
1202 goto Eshortdraw;
1203 ox = p.x;
1204 oy = p.y;
1205 if(doflush){
1206 esize = j;
1207 if(*a == 'p'){
1208 if(y == 0){
1209 c = memlineendsize(e0);
1210 if(c > esize)
1211 esize = c;
1213 if(y == ni-1){
1214 c = memlineendsize(e1);
1215 if(c > esize)
1216 esize = c;
1219 if(*a=='P' && e0!=1 && e0 !=~0)
1220 r = dst->clipr;
1221 else if(y > 0){
1222 r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1);
1223 combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1225 if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */
1226 dstflush(client, dstid, dst, r);
1228 pp[y] = p;
1230 if(y == 1)
1231 dstflush(client, dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1232 op = drawclientop(client);
1233 if(*a == 'p')
1234 mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
1235 else
1236 memfillpoly(dst, pp, ni, e0, src, sp, op);
1237 free(pp);
1238 m = u-a;
1239 continue;
1241 /* read: 'r' id[4] R[4*4] */
1242 case 'r':
1243 m = 1+4+4*4;
1244 if(n < m)
1245 goto Eshortdraw;
1246 i = drawimage(client, a+1);
1247 if(!i)
1248 goto Enodrawimage;
1249 drawrectangle(&r, a+5);
1250 if(!rectinrect(r, i->r))
1251 goto Ereadoutside;
1252 c = bytesperline(r, i->depth);
1253 c *= Dy(r);
1254 free(client->readdata);
1255 client->readdata = mallocz(c, 0);
1256 if(client->readdata == nil){
1257 err = "readimage malloc failed";
1258 goto error;
1260 client->nreaddata = memunload(i, r, client->readdata, c);
1261 if(client->nreaddata < 0){
1262 free(client->readdata);
1263 client->readdata = nil;
1264 err = "bad readimage call";
1265 goto error;
1267 continue;
1269 /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
1270 /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */
1271 case 's':
1272 case 'x':
1273 m = 1+4+4+4+2*4+4*4+2*4+2;
1274 if(*a == 'x')
1275 m += 4+2*4;
1276 if(n < m)
1277 goto Eshortdraw;
1279 dst = drawimage(client, a+1);
1280 dstid = BGLONG(a+1);
1281 src = drawimage(client, a+5);
1282 if(!dst || !src)
1283 goto Enodrawimage;
1284 font = drawlookup(client, BGLONG(a+9), 1);
1285 if(font == 0)
1286 goto Enodrawimage;
1287 if(font->nfchar == 0)
1288 goto Enotfont;
1289 drawpoint(&p, a+13);
1290 drawrectangle(&r, a+21);
1291 drawpoint(&sp, a+37);
1292 ni = BGSHORT(a+45);
1293 u = a+m;
1294 m += ni*2;
1295 if(n < m)
1296 goto Eshortdraw;
1297 clipr = dst->clipr;
1298 dst->clipr = r;
1299 op = drawclientop(client);
1300 if(*a == 'x'){
1301 /* paint background */
1302 l = drawimage(client, a+47);
1303 if(!l)
1304 goto Enodrawimage;
1305 drawpoint(&q, a+51);
1306 r.min.x = p.x;
1307 r.min.y = p.y-font->ascent;
1308 r.max.x = p.x;
1309 r.max.y = r.min.y+Dy(font->image->r);
1310 j = ni;
1311 while(--j >= 0){
1312 ci = BGSHORT(u);
1313 if(ci<0 || ci>=font->nfchar){
1314 dst->clipr = clipr;
1315 goto Eindex;
1317 r.max.x += font->fchar[ci].width;
1318 u += 2;
1320 memdraw(dst, r, l, q, memopaque, ZP, op);
1321 u -= 2*ni;
1323 q = p;
1324 while(--ni >= 0){
1325 ci = BGSHORT(u);
1326 if(ci<0 || ci>=font->nfchar){
1327 dst->clipr = clipr;
1328 goto Eindex;
1330 q = drawchar(dst, q, src, &sp, font, ci, op);
1331 u += 2;
1333 dst->clipr = clipr;
1334 p.y -= font->ascent;
1335 dstflush(client, dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
1336 continue;
1338 /* use public screen: 'S' id[4] chan[4] */
1339 case 'S':
1340 m = 1+4+4;
1341 if(n < m)
1342 goto Eshortdraw;
1343 dstid = BGLONG(a+1);
1344 if(dstid == 0)
1345 goto Ebadarg;
1346 dscrn = drawlookupdscreen(client, dstid);
1347 if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
1348 goto Enodrawscreen;
1349 if(dscrn->screen->image->chan != BGLONG(a+5)){
1350 err = "inconsistent chan";
1351 goto error;
1353 if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
1354 goto Edrawmem;
1355 continue;
1357 /* top or bottom windows: 't' top[1] nw[2] n*id[4] */
1358 case 't':
1359 m = 1+1+2;
1360 if(n < m)
1361 goto Eshortdraw;
1362 nw = BGSHORT(a+2);
1363 if(nw < 0)
1364 goto Ebadarg;
1365 if(nw == 0)
1366 continue;
1367 m += nw*4;
1368 if(n < m)
1369 goto Eshortdraw;
1370 lp = mallocz(nw*sizeof(Memimage*), 1);
1371 if(lp == 0)
1372 goto Enomem;
1373 for(j=0; j<nw; j++){
1374 lp[j] = drawimage(client, a+1+1+2+j*4);
1375 if(lp[j] == nil){
1376 free(lp);
1377 goto Enodrawimage;
1380 if(lp[0]->layer == 0){
1381 err = "images are not windows";
1382 free(lp);
1383 goto error;
1385 for(j=1; j<nw; j++)
1386 if(lp[j]->layer->screen != lp[0]->layer->screen){
1387 err = "images not on same screen";
1388 free(lp);
1389 goto error;
1391 if(a[1])
1392 memltofrontn(lp, nw);
1393 else
1394 memltorearn(lp, nw);
1395 if(lp[0]->layer->screen->image->data == client->screenimage->data)
1396 for(j=0; j<nw; j++)
1397 addflush(client, lp[j]->layer->screenr);
1398 free(lp);
1399 ll = drawlookup(client, BGLONG(a+1+1+2), 1);
1400 drawrefreshscreen(ll, client);
1401 continue;
1403 /* visible: 'v' */
1404 case 'v':
1405 m = 1;
1406 drawflush(client);
1407 continue;
1409 /* write: 'y' id[4] R[4*4] data[x*1] */
1410 /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
1411 case 'y':
1412 case 'Y':
1413 m = 1+4+4*4;
1414 if(n < m)
1415 goto Eshortdraw;
1416 dstid = BGLONG(a+1);
1417 dst = drawimage(client, a+1);
1418 if(!dst)
1419 goto Enodrawimage;
1420 drawrectangle(&r, a+5);
1421 if(!rectinrect(r, dst->r))
1422 goto Ewriteoutside;
1423 y = memload(dst, r, a+m, n-m, *a=='Y');
1424 if(y < 0){
1425 err = "bad writeimage call";
1426 goto error;
1428 dstflush(client, dstid, dst, r);
1429 m += y;
1430 continue;
1433 rpc_gfxdrawunlock();
1434 qunlock(&client->drawlk);
1435 return oldn - n;
1437 Enodrawimage:
1438 err = "unknown id for draw image";
1439 goto error;
1440 Enodrawscreen:
1441 err = "unknown id for draw screen";
1442 goto error;
1443 Eshortdraw:
1444 err = "short draw message";
1445 goto error;
1447 Eshortread:
1448 err = "draw read too short";
1449 goto error;
1451 Eimageexists:
1452 err = "image id in use";
1453 goto error;
1454 Escreenexists:
1455 err = "screen id in use";
1456 goto error;
1457 Edrawmem:
1458 err = "image memory allocation failed";
1459 goto error;
1460 Ereadoutside:
1461 err = "readimage outside image";
1462 goto error;
1463 Ewriteoutside:
1464 err = "writeimage outside image";
1465 goto error;
1466 Enotfont:
1467 err = "image not a font";
1468 goto error;
1469 Eindex:
1470 err = "character index out of range";
1471 goto error;
1473 Enoclient:
1474 err = "no such draw client";
1475 goto error;
1476 Edepth:
1477 err = "image has bad depth";
1478 goto error;
1479 Enameused:
1480 err = "image name in use";
1481 goto error;
1483 Enoname:
1484 err = "no image with that name";
1485 goto error;
1486 Eoldname:
1487 err = "named image no longer valid";
1488 goto error;
1489 Enamed:
1490 err = "image already has name";
1491 goto error;
1492 Ewrongname:
1493 err = "wrong name for image";
1494 goto error;
1495 Enomem:
1496 err = "out of memory";
1497 goto error;
1498 Ebadarg:
1499 err = "bad argument in draw message";
1500 goto error;
1502 error:
1503 werrstr("%s", err);
1504 rpc_gfxdrawunlock();
1505 qunlock(&client->drawlk);
1506 return -1;