FreeBSD/Linux Kernel Cross Reference
sys/port/devdraw.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 #define Image IMAGE
9 #include <draw.h>
10 #include <memdraw.h>
11 #include <memlayer.h>
12 #include <cursor.h>
13 #include "screen.h"
14
15 enum
16 {
17 Qtopdir = 0,
18 Qnew,
19 Qwinname,
20 Q3rd,
21 Q2nd,
22 Qcolormap,
23 Qctl,
24 Qdata,
25 Qrefresh,
26 };
27
28 /*
29 * Qid path is:
30 * 4 bits of file type (qids above)
31 * 24 bits of mux slot number +1; 0 means not attached to client
32 */
33 #define QSHIFT 4 /* location in qid of client # */
34
35 #define QID(q) ((((ulong)(q).path)&0x0000000F)>>0)
36 #define CLIENTPATH(q) ((((ulong)q)&0x7FFFFFF0)>>QSHIFT)
37 #define CLIENT(q) CLIENTPATH((q).path)
38
39 #define NHASH (1<<5)
40 #define HASHMASK (NHASH-1)
41 #define IOUNIT (64*1024)
42
43 typedef struct Client Client;
44 typedef struct Draw Draw;
45 typedef struct DImage DImage;
46 typedef struct DScreen DScreen;
47 typedef struct CScreen CScreen;
48 typedef struct FChar FChar;
49 typedef struct Refresh Refresh;
50 typedef struct Refx Refx;
51 typedef struct DName DName;
52
53 ulong blanktime = 30; /* in minutes; a half hour */
54
55 struct Draw
56 {
57 int clientid;
58 int nclient;
59 Client** client;
60 int nname;
61 DName* name;
62 int vers;
63 int softscreen;
64 int blanked; /* screen turned off */
65 ulong blanktime; /* time of last operation */
66 ulong savemap[3*256];
67 };
68
69 struct Client
70 {
71 Ref r;
72 DImage* dimage[NHASH];
73 CScreen* cscreen;
74 Refresh* refresh;
75 Rendez refrend;
76 uchar* readdata;
77 int nreaddata;
78 int busy;
79 int clientid;
80 int slot;
81 int refreshme;
82 int infoid;
83 int op;
84 };
85
86 struct Refresh
87 {
88 DImage* dimage;
89 Rectangle r;
90 Refresh* next;
91 };
92
93 struct Refx
94 {
95 Client* client;
96 DImage* dimage;
97 };
98
99 struct DName
100 {
101 char *name;
102 Client *client;
103 DImage* dimage;
104 int vers;
105 };
106
107 struct FChar
108 {
109 int minx; /* left edge of bits */
110 int maxx; /* right edge of bits */
111 uchar miny; /* first non-zero scan-line */
112 uchar maxy; /* last non-zero scan-line + 1 */
113 schar left; /* offset of baseline */
114 uchar width; /* width of baseline */
115 };
116
117 /*
118 * Reference counts in DImages:
119 * one per open by original client
120 * one per screen image or fill
121 * one per image derived from this one by name
122 */
123 struct DImage
124 {
125 int id;
126 int ref;
127 char *name;
128 int vers;
129 Memimage* image;
130 int ascent;
131 int nfchar;
132 FChar* fchar;
133 DScreen* dscreen; /* 0 if not a window */
134 DImage* fromname; /* image this one is derived from, by name */
135 DImage* next;
136 };
137
138 struct CScreen
139 {
140 DScreen* dscreen;
141 CScreen* next;
142 };
143
144 struct DScreen
145 {
146 int id;
147 int public;
148 int ref;
149 DImage *dimage;
150 DImage *dfill;
151 Memscreen* screen;
152 Client* owner;
153 DScreen* next;
154 };
155
156 static Draw sdraw;
157 QLock drawlock;
158
159 static Memimage *screenimage;
160 static DImage* screendimage;
161 static char screenname[40];
162 static int screennameid;
163
164 static Rectangle flushrect;
165 static int waste;
166 static DScreen* dscreen;
167 extern void flushmemscreen(Rectangle);
168 void drawmesg(Client*, void*, int);
169 void drawuninstall(Client*, int);
170 void drawfreedimage(DImage*);
171 Client* drawclientofpath(ulong);
172 DImage* allocdimage(Memimage*);
173
174 static char Enodrawimage[] = "unknown id for draw image";
175 static char Enodrawscreen[] = "unknown id for draw screen";
176 static char Eshortdraw[] = "short draw message";
177 static char Eshortread[] = "draw read too short";
178 static char Eimageexists[] = "image id in use";
179 static char Escreenexists[] = "screen id in use";
180 static char Edrawmem[] = "image memory allocation failed";
181 static char Ereadoutside[] = "readimage outside image";
182 static char Ewriteoutside[] = "writeimage outside image";
183 static char Enotfont[] = "image not a font";
184 static char Eindex[] = "character index out of range";
185 static char Enoclient[] = "no such draw client";
186 static char Edepth[] = "image has bad depth";
187 static char Enameused[] = "image name in use";
188 static char Enoname[] = "no image with that name";
189 static char Eoldname[] = "named image no longer valid";
190 static char Enamed[] = "image already has name";
191 static char Ewrongname[] = "wrong name for image";
192
193 static void
194 dlock(void)
195 {
196 qlock(&drawlock);
197 }
198
199 static int
200 candlock(void)
201 {
202 return canqlock(&drawlock);
203 }
204
205 static void
206 dunlock(void)
207 {
208 qunlock(&drawlock);
209 }
210
211 static int
212 drawgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
213 {
214 int t;
215 Qid q;
216 ulong path;
217 Client *cl;
218
219 q.vers = 0;
220
221 if(s == DEVDOTDOT){
222 switch(QID(c->qid)){
223 case Qtopdir:
224 case Q2nd:
225 mkqid(&q, Qtopdir, 0, QTDIR);
226 devdir(c, q, "#i", 0, eve, 0500, dp);
227 break;
228 case Q3rd:
229 cl = drawclientofpath(c->qid.path);
230 if(cl == nil)
231 strcpy(up->genbuf, "??");
232 else
233 sprint(up->genbuf, "%d", cl->clientid);
234 mkqid(&q, Q2nd, 0, QTDIR);
235 devdir(c, q, up->genbuf, 0, eve, 0500, dp);
236 break;
237 default:
238 panic("drawwalk %llux", c->qid.path);
239 }
240 return 1;
241 }
242
243 /*
244 * Top level directory contains the name of the device.
245 */
246 t = QID(c->qid);
247 if(t == Qtopdir){
248 switch(s){
249 case 0:
250 mkqid(&q, Q2nd, 0, QTDIR);
251 devdir(c, q, "draw", 0, eve, 0555, dp);
252 break;
253 case 1:
254 mkqid(&q, Qwinname, 0, 0);
255 devdir(c, q, "winname", 0, eve, 0444, dp);
256 break;
257 default:
258 return -1;
259 }
260 return 1;
261 }
262
263 /*
264 * Second level contains "new" plus all the clients.
265 */
266 if(t == Q2nd || t == Qnew){
267 if(s == 0){
268 mkqid(&q, Qnew, 0, QTFILE);
269 devdir(c, q, "new", 0, eve, 0666, dp);
270 }
271 else if(s <= sdraw.nclient){
272 cl = sdraw.client[s-1];
273 if(cl == 0)
274 return 0;
275 sprint(up->genbuf, "%d", cl->clientid);
276 mkqid(&q, (s<<QSHIFT)|Q3rd, 0, QTDIR);
277 devdir(c, q, up->genbuf, 0, eve, 0555, dp);
278 return 1;
279 }
280 else
281 return -1;
282 return 1;
283 }
284
285 /*
286 * Third level.
287 */
288 path = c->qid.path&~((1<<QSHIFT)-1); /* slot component */
289 q.vers = c->qid.vers;
290 q.type = QTFILE;
291 switch(s){
292 case 0:
293 q.path = path|Qcolormap;
294 devdir(c, q, "colormap", 0, eve, 0600, dp);
295 break;
296 case 1:
297 q.path = path|Qctl;
298 devdir(c, q, "ctl", 0, eve, 0600, dp);
299 break;
300 case 2:
301 q.path = path|Qdata;
302 devdir(c, q, "data", 0, eve, 0600, dp);
303 break;
304 case 3:
305 q.path = path|Qrefresh;
306 devdir(c, q, "refresh", 0, eve, 0400, dp);
307 break;
308 default:
309 return -1;
310 }
311 return 1;
312 }
313
314 static
315 int
316 drawrefactive(void *a)
317 {
318 Client *c;
319
320 c = a;
321 return c->refreshme || c->refresh!=0;
322 }
323
324 static
325 void
326 drawrefreshscreen(DImage *l, Client *client)
327 {
328 while(l != nil && l->dscreen == nil)
329 l = l->fromname;
330 if(l != nil && l->dscreen->owner != client)
331 l->dscreen->owner->refreshme = 1;
332 }
333
334 static
335 void
336 drawrefresh(Memimage*, Rectangle r, void *v)
337 {
338 Refx *x;
339 DImage *d;
340 Client *c;
341 Refresh *ref;
342
343 if(v == 0)
344 return;
345 x = v;
346 c = x->client;
347 d = x->dimage;
348 for(ref=c->refresh; ref; ref=ref->next)
349 if(ref->dimage == d){
350 combinerect(&ref->r, r);
351 return;
352 }
353 ref = malloc(sizeof(Refresh));
354 if(ref){
355 ref->dimage = d;
356 ref->r = r;
357 ref->next = c->refresh;
358 c->refresh = ref;
359 }
360 }
361
362 static void
363 addflush(Rectangle r)
364 {
365 int abb, ar, anbb;
366 Rectangle nbb;
367
368 if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r))
369 return;
370
371 if(flushrect.min.x >= flushrect.max.x){
372 flushrect = r;
373 waste = 0;
374 return;
375 }
376 nbb = flushrect;
377 combinerect(&nbb, r);
378 ar = Dx(r)*Dy(r);
379 abb = Dx(flushrect)*Dy(flushrect);
380 anbb = Dx(nbb)*Dy(nbb);
381 /*
382 * Area of new waste is area of new bb minus area of old bb,
383 * less the area of the new segment, which we assume is not waste.
384 * This could be negative, but that's OK.
385 */
386 waste += anbb-abb - ar;
387 if(waste < 0)
388 waste = 0;
389 /*
390 * absorb if:
391 * total area is small
392 * waste is less than half total area
393 * rectangles touch
394 */
395 if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){
396 flushrect = nbb;
397 return;
398 }
399 /* emit current state */
400 if(flushrect.min.x < flushrect.max.x)
401 flushmemscreen(flushrect);
402 flushrect = r;
403 waste = 0;
404 }
405
406 static
407 void
408 dstflush(int dstid, Memimage *dst, Rectangle r)
409 {
410 Memlayer *l;
411
412 if(dstid == 0){
413 combinerect(&flushrect, r);
414 return;
415 }
416 /* how can this happen? -rsc, dec 12 2002 */
417 if(dst == 0){
418 print("nil dstflush\n");
419 return;
420 }
421 l = dst->layer;
422 if(l == nil)
423 return;
424 do{
425 if(l->screen->image->data != screenimage->data)
426 return;
427 r = rectaddpt(r, l->delta);
428 l = l->screen->image->layer;
429 }while(l);
430 addflush(r);
431 }
432
433 void
434 drawflush(void)
435 {
436 if(flushrect.min.x < flushrect.max.x)
437 flushmemscreen(flushrect);
438 flushrect = Rect(10000, 10000, -10000, -10000);
439 }
440
441 static
442 int
443 drawcmp(char *a, char *b, int n)
444 {
445 if(strlen(a) != n)
446 return 1;
447 return memcmp(a, b, n);
448 }
449
450 DName*
451 drawlookupname(int n, char *str)
452 {
453 DName *name, *ename;
454
455 name = sdraw.name;
456 ename = &name[sdraw.nname];
457 for(; name<ename; name++)
458 if(drawcmp(name->name, str, n) == 0)
459 return name;
460 return 0;
461 }
462
463 int
464 drawgoodname(DImage *d)
465 {
466 DName *n;
467
468 /* if window, validate the screen's own images */
469 if(d->dscreen)
470 if(drawgoodname(d->dscreen->dimage) == 0
471 || drawgoodname(d->dscreen->dfill) == 0)
472 return 0;
473 if(d->name == nil)
474 return 1;
475 n = drawlookupname(strlen(d->name), d->name);
476 if(n==nil || n->vers!=d->vers)
477 return 0;
478 return 1;
479 }
480
481 DImage*
482 drawlookup(Client *client, int id, int checkname)
483 {
484 DImage *d;
485
486 d = client->dimage[id&HASHMASK];
487 while(d){
488 if(d->id == id){
489 if(checkname && !drawgoodname(d))
490 error(Eoldname);
491 return d;
492 }
493 d = d->next;
494 }
495 return 0;
496 }
497
498 DScreen*
499 drawlookupdscreen(int id)
500 {
501 DScreen *s;
502
503 s = dscreen;
504 while(s){
505 if(s->id == id)
506 return s;
507 s = s->next;
508 }
509 return 0;
510 }
511
512 DScreen*
513 drawlookupscreen(Client *client, int id, CScreen **cs)
514 {
515 CScreen *s;
516
517 s = client->cscreen;
518 while(s){
519 if(s->dscreen->id == id){
520 *cs = s;
521 return s->dscreen;
522 }
523 s = s->next;
524 }
525 error(Enodrawscreen);
526 return 0;
527 }
528
529 DImage*
530 allocdimage(Memimage *i)
531 {
532 DImage *d;
533
534 d = malloc(sizeof(DImage));
535 if(d == 0)
536 return 0;
537 d->ref = 1;
538 d->name = 0;
539 d->vers = 0;
540 d->image = i;
541 d->nfchar = 0;
542 d->fchar = 0;
543 d->fromname = 0;
544 return d;
545 }
546
547 Memimage*
548 drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
549 {
550 DImage *d;
551
552 d = allocdimage(i);
553 if(d == 0)
554 return 0;
555 d->id = id;
556 d->dscreen = dscreen;
557 d->next = client->dimage[id&HASHMASK];
558 client->dimage[id&HASHMASK] = d;
559 return i;
560 }
561
562 Memscreen*
563 drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public)
564 {
565 Memscreen *s;
566 CScreen *c;
567
568 c = malloc(sizeof(CScreen));
569 if(dimage && dimage->image && dimage->image->chan == 0)
570 panic("bad image %p in drawinstallscreen", dimage->image);
571
572 if(c == 0)
573 return 0;
574 if(d == 0){
575 d = malloc(sizeof(DScreen));
576 if(d == 0){
577 free(c);
578 return 0;
579 }
580 s = malloc(sizeof(Memscreen));
581 if(s == 0){
582 free(c);
583 free(d);
584 return 0;
585 }
586 s->frontmost = 0;
587 s->rearmost = 0;
588 d->dimage = dimage;
589 if(dimage){
590 s->image = dimage->image;
591 dimage->ref++;
592 }
593 d->dfill = dfill;
594 if(dfill){
595 s->fill = dfill->image;
596 dfill->ref++;
597 }
598 d->ref = 0;
599 d->id = id;
600 d->screen = s;
601 d->public = public;
602 d->next = dscreen;
603 d->owner = client;
604 dscreen = d;
605 }
606 c->dscreen = d;
607 d->ref++;
608 c->next = client->cscreen;
609 client->cscreen = c;
610 return d->screen;
611 }
612
613 void
614 drawdelname(DName *name)
615 {
616 int i;
617
618 i = name-sdraw.name;
619 memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName));
620 sdraw.nname--;
621 }
622
623 void
624 drawfreedscreen(DScreen *this)
625 {
626 DScreen *ds, *next;
627
628 this->ref--;
629 if(this->ref < 0)
630 print("negative ref in drawfreedscreen\n");
631 if(this->ref > 0)
632 return;
633 ds = dscreen;
634 if(ds == this){
635 dscreen = this->next;
636 goto Found;
637 }
638 while(next = ds->next){ /* assign = */
639 if(next == this){
640 ds->next = this->next;
641 goto Found;
642 }
643 ds = next;
644 }
645 error(Enodrawimage);
646
647 Found:
648 if(this->dimage)
649 drawfreedimage(this->dimage);
650 if(this->dfill)
651 drawfreedimage(this->dfill);
652 free(this->screen);
653 free(this);
654 }
655
656 void
657 drawfreedimage(DImage *dimage)
658 {
659 int i;
660 Memimage *l;
661 DScreen *ds;
662
663 dimage->ref--;
664 if(dimage->ref < 0)
665 print("negative ref in drawfreedimage\n");
666 if(dimage->ref > 0)
667 return;
668
669 /* any names? */
670 for(i=0; i<sdraw.nname; )
671 if(sdraw.name[i].dimage == dimage)
672 drawdelname(sdraw.name+i);
673 else
674 i++;
675 if(dimage->fromname){ /* acquired by name; owned by someone else*/
676 drawfreedimage(dimage->fromname);
677 goto Return;
678 }
679 // if(dimage->image == screenimage) /* don't free the display */
680 // goto Return;
681 ds = dimage->dscreen;
682 if(ds){
683 l = dimage->image;
684 if(l->data == screenimage->data)
685 addflush(l->layer->screenr);
686 if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */
687 free(l->layer->refreshptr);
688 l->layer->refreshptr = nil;
689 if(drawgoodname(dimage))
690 memldelete(l);
691 else
692 memlfree(l);
693 drawfreedscreen(ds);
694 }else
695 freememimage(dimage->image);
696 Return:
697 free(dimage->fchar);
698 free(dimage);
699 }
700
701 void
702 drawuninstallscreen(Client *client, CScreen *this)
703 {
704 CScreen *cs, *next;
705
706 cs = client->cscreen;
707 if(cs == this){
708 client->cscreen = this->next;
709 drawfreedscreen(this->dscreen);
710 free(this);
711 return;
712 }
713 while(next = cs->next){ /* assign = */
714 if(next == this){
715 cs->next = this->next;
716 drawfreedscreen(this->dscreen);
717 free(this);
718 return;
719 }
720 cs = next;
721 }
722 }
723
724 void
725 drawuninstall(Client *client, int id)
726 {
727 DImage *d, *next;
728
729 d = client->dimage[id&HASHMASK];
730 if(d == 0)
731 error(Enodrawimage);
732 if(d->id == id){
733 client->dimage[id&HASHMASK] = d->next;
734 drawfreedimage(d);
735 return;
736 }
737 while(next = d->next){ /* assign = */
738 if(next->id == id){
739 d->next = next->next;
740 drawfreedimage(next);
741 return;
742 }
743 d = next;
744 }
745 error(Enodrawimage);
746 }
747
748 void
749 drawaddname(Client *client, DImage *di, int n, char *str)
750 {
751 DName *name, *ename, *new, *t;
752
753 name = sdraw.name;
754 ename = &name[sdraw.nname];
755 for(; name<ename; name++)
756 if(drawcmp(name->name, str, n) == 0)
757 error(Enameused);
758 t = smalloc((sdraw.nname+1)*sizeof(DName));
759 memmove(t, sdraw.name, sdraw.nname*sizeof(DName));
760 free(sdraw.name);
761 sdraw.name = t;
762 new = &sdraw.name[sdraw.nname++];
763 new->name = smalloc(n+1);
764 memmove(new->name, str, n);
765 new->name[n] = 0;
766 new->dimage = di;
767 new->client = client;
768 new->vers = ++sdraw.vers;
769 }
770
771 Client*
772 drawnewclient(void)
773 {
774 Client *cl, **cp;
775 int i;
776
777 for(i=0; i<sdraw.nclient; i++){
778 cl = sdraw.client[i];
779 if(cl == 0)
780 break;
781 }
782 if(i == sdraw.nclient){
783 cp = malloc((sdraw.nclient+1)*sizeof(Client*));
784 if(cp == 0)
785 return 0;
786 memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*));
787 free(sdraw.client);
788 sdraw.client = cp;
789 sdraw.nclient++;
790 cp[i] = 0;
791 }
792 cl = malloc(sizeof(Client));
793 if(cl == 0)
794 return 0;
795 memset(cl, 0, sizeof(Client));
796 cl->slot = i;
797 cl->clientid = ++sdraw.clientid;
798 cl->op = SoverD;
799 sdraw.client[i] = cl;
800 return cl;
801 }
802
803 static int
804 drawclientop(Client *cl)
805 {
806 int op;
807
808 op = cl->op;
809 cl->op = SoverD;
810 return op;
811 }
812
813 int
814 drawhasclients(void)
815 {
816 /*
817 * if draw has ever been used, we can't resize the frame buffer,
818 * even if all clients have exited (nclients is cumulative); it's too
819 * hard to make work.
820 */
821 return sdraw.nclient != 0;
822 }
823
824 Client*
825 drawclientofpath(ulong path)
826 {
827 Client *cl;
828 int slot;
829
830 slot = CLIENTPATH(path);
831 if(slot == 0)
832 return nil;
833 cl = sdraw.client[slot-1];
834 if(cl==0 || cl->clientid==0)
835 return nil;
836 return cl;
837 }
838
839
840 Client*
841 drawclient(Chan *c)
842 {
843 Client *client;
844
845 client = drawclientofpath(c->qid.path);
846 if(client == nil)
847 error(Enoclient);
848 return client;
849 }
850
851 Memimage*
852 drawimage(Client *client, uchar *a)
853 {
854 DImage *d;
855
856 d = drawlookup(client, BGLONG(a), 1);
857 if(d == nil)
858 error(Enodrawimage);
859 return d->image;
860 }
861
862 void
863 drawrectangle(Rectangle *r, uchar *a)
864 {
865 r->min.x = BGLONG(a+0*4);
866 r->min.y = BGLONG(a+1*4);
867 r->max.x = BGLONG(a+2*4);
868 r->max.y = BGLONG(a+3*4);
869 }
870
871 void
872 drawpoint(Point *p, uchar *a)
873 {
874 p->x = BGLONG(a+0*4);
875 p->y = BGLONG(a+1*4);
876 }
877
878 Point
879 drawchar(Memimage *dst, Memimage *rdst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
880 {
881 FChar *fc;
882 Rectangle r;
883 Point sp1;
884 static Memimage *tmp;
885
886 fc = &font->fchar[index];
887 r.min.x = p.x+fc->left;
888 r.min.y = p.y-(font->ascent-fc->miny);
889 r.max.x = r.min.x+(fc->maxx-fc->minx);
890 r.max.y = r.min.y+(fc->maxy-fc->miny);
891 sp1.x = sp->x+fc->left;
892 sp1.y = sp->y+fc->miny;
893
894 /*
895 * If we're drawing greyscale fonts onto a VGA screen,
896 * it's very costly to read the screen memory to do the
897 * alpha blending inside memdraw. If this is really a stringbg,
898 * then rdst is the bg image (in main memory) which we can
899 * refer to for the underlying dst pixels instead of reading dst
900 * directly.
901 */
902 if(ishwimage(dst) && !ishwimage(rdst) && font->image->depth > 1){
903 if(tmp == nil || tmp->chan != dst->chan || Dx(tmp->r) < Dx(r) || Dy(tmp->r) < Dy(r)){
904 if(tmp)
905 freememimage(tmp);
906 tmp = allocmemimage(Rect(0,0,Dx(r),Dy(r)), dst->chan);
907 if(tmp == nil)
908 goto fallback;
909 }
910 memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), rdst, r.min, memopaque, ZP, S);
911 memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), src, sp1, font->image, Pt(fc->minx, fc->miny), op);
912 memdraw(dst, r, tmp, ZP, memopaque, ZP, S);
913 }else{
914 fallback:
915 memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
916 }
917
918 p.x += fc->width;
919 sp->x += fc->width;
920 return p;
921 }
922
923 static DImage*
924 makescreenimage(void)
925 {
926 int width, depth;
927 ulong chan;
928 DImage *di;
929 Memdata *md;
930 Memimage *i;
931 Rectangle r;
932
933 md = malloc(sizeof *md);
934 if(md == nil)
935 return nil;
936 md->allocd = 1;
937 md->base = nil;
938 md->bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen);
939 if(md->bdata == nil){
940 free(md);
941 return nil;
942 }
943 md->ref = 1;
944 i = allocmemimaged(r, chan, md);
945 if(i == nil){
946 free(md);
947 return nil;
948 }
949 i->width = width;
950 i->clipr = r;
951
952 di = allocdimage(i);
953 if(di == nil){
954 freememimage(i); /* frees md */
955 return nil;
956 }
957 if(!waserror()){
958 snprint(screenname, sizeof screenname, "noborder.screen.%d", ++screennameid);
959 drawaddname(nil, di, strlen(screenname), screenname);
960 poperror();
961 }
962 return di;
963 }
964
965 static int
966 initscreenimage(void)
967 {
968 if(screenimage != nil)
969 return 1;
970
971 screendimage = makescreenimage();
972 if(screendimage == nil)
973 return 0;
974 screenimage = screendimage->image;
975 // iprint("initscreenimage %p %p\n", screendimage, screenimage);
976 mouseresize();
977 return 1;
978 }
979
980 void
981 deletescreenimage(void)
982 {
983 dlock();
984 if(screenimage){
985 /* will be freed via screendimage; disable */
986 screenimage->clipr = ZR;
987 screenimage = nil;
988 }
989 if(screendimage){
990 drawfreedimage(screendimage);
991 screendimage = nil;
992 }
993 dunlock();
994 }
995
996 void
997 resetscreenimage(void)
998 {
999 dlock();
1000 initscreenimage();
1001 dunlock();
1002 }
1003
1004 static Chan*
1005 drawattach(char *spec)
1006 {
1007 dlock();
1008 if(!initscreenimage()){
1009 dunlock();
1010 error("no frame buffer");
1011 }
1012 dunlock();
1013 return devattach('i', spec);
1014 }
1015
1016 static Walkqid*
1017 drawwalk(Chan *c, Chan *nc, char **name, int nname)
1018 {
1019 if(screenimage == nil)
1020 error("no frame buffer");
1021 return devwalk(c, nc, name, nname, 0, 0, drawgen);
1022 }
1023
1024 static int
1025 drawstat(Chan *c, uchar *db, int n)
1026 {
1027 return devstat(c, db, n, 0, 0, drawgen);
1028 }
1029
1030 static Chan*
1031 drawopen(Chan *c, int omode)
1032 {
1033 Client *cl;
1034 DName *dn;
1035 DImage *di;
1036
1037 if(c->qid.type & QTDIR){
1038 c = devopen(c, omode, 0, 0, drawgen);
1039 c->iounit = IOUNIT;
1040 }
1041
1042 dlock();
1043 if(waserror()){
1044 dunlock();
1045 nexterror();
1046 }
1047
1048 if(QID(c->qid) == Qnew){
1049 cl = drawnewclient();
1050 if(cl == 0)
1051 error(Enodev);
1052 c->qid.path = Qctl|((cl->slot+1)<<QSHIFT);
1053 }
1054
1055 switch(QID(c->qid)){
1056 case Qwinname:
1057 break;
1058
1059 case Qnew:
1060 break;
1061
1062 case Qctl:
1063 cl = drawclient(c);
1064 if(cl->busy)
1065 error(Einuse);
1066 cl->busy = 1;
1067 flushrect = Rect(10000, 10000, -10000, -10000);
1068 dn = drawlookupname(strlen(screenname), screenname);
1069 if(dn == 0)
1070 error("draw: cannot happen 2");
1071 if(drawinstall(cl, 0, dn->dimage->image, 0) == 0)
1072 error(Edrawmem);
1073 di = drawlookup(cl, 0, 0);
1074 if(di == 0)
1075 error("draw: cannot happen 1");
1076 di->vers = dn->vers;
1077 di->name = smalloc(strlen(screenname)+1);
1078 strcpy(di->name, screenname);
1079 di->fromname = dn->dimage;
1080 di->fromname->ref++;
1081 incref(&cl->r);
1082 break;
1083
1084 case Qcolormap:
1085 case Qdata:
1086 case Qrefresh:
1087 cl = drawclient(c);
1088 incref(&cl->r);
1089 break;
1090 }
1091 dunlock();
1092 poperror();
1093 c->mode = openmode(omode);
1094 c->flag |= COPEN;
1095 c->offset = 0;
1096 c->iounit = IOUNIT;
1097 return c;
1098 }
1099
1100 static void
1101 drawclose(Chan *c)
1102 {
1103 int i;
1104 DImage *d, **dp;
1105 Client *cl;
1106 Refresh *r;
1107
1108 if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */
1109 return;
1110 dlock();
1111 if(waserror()){
1112 dunlock();
1113 nexterror();
1114 }
1115
1116 cl = drawclient(c);
1117 if(QID(c->qid) == Qctl)
1118 cl->busy = 0;
1119 if((c->flag&COPEN) && (decref(&cl->r)==0)){
1120 while(r = cl->refresh){ /* assign = */
1121 cl->refresh = r->next;
1122 free(r);
1123 }
1124 /* free names */
1125 for(i=0; i<sdraw.nname; )
1126 if(sdraw.name[i].client == cl)
1127 drawdelname(sdraw.name+i);
1128 else
1129 i++;
1130 while(cl->cscreen)
1131 drawuninstallscreen(cl, cl->cscreen);
1132 /* all screens are freed, so now we can free images */
1133 dp = cl->dimage;
1134 for(i=0; i<NHASH; i++){
1135 while((d = *dp) != nil){
1136 *dp = d->next;
1137 drawfreedimage(d);
1138 }
1139 dp++;
1140 }
1141 sdraw.client[cl->slot] = 0;
1142 drawflush(); /* to erase visible, now dead windows */
1143 free(cl);
1144 }
1145 dunlock();
1146 poperror();
1147 }
1148
1149 long
1150 drawread(Chan *c, void *a, long n, vlong off)
1151 {
1152 int index, m;
1153 ulong red, green, blue;
1154 Client *cl;
1155 uchar *p;
1156 Refresh *r;
1157 DImage *di;
1158 Memimage *i;
1159 ulong offset = off;
1160 char buf[16];
1161
1162 if(c->qid.type & QTDIR)
1163 return devdirread(c, a, n, 0, 0, drawgen);
1164 if(QID(c->qid) == Qwinname)
1165 return readstr(off, a, n, screenname);
1166
1167 cl = drawclient(c);
1168 dlock();
1169 if(waserror()){
1170 dunlock();
1171 nexterror();
1172 }
1173 switch(QID(c->qid)){
1174 case Qctl:
1175 if(n < 12*12)
1176 error(Eshortread);
1177 if(cl->infoid < 0)
1178 error(Enodrawimage);
1179 if(cl->infoid == 0){
1180 i = screenimage;
1181 if(i == nil)
1182 error(Enodrawimage);
1183 }else{
1184 di = drawlookup(cl, cl->infoid, 1);
1185 if(di == nil)
1186 error(Enodrawimage);
1187 i = di->image;
1188 }
1189 n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
1190 cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl,
1191 i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
1192 i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
1193 cl->infoid = -1;
1194 break;
1195
1196 case Qcolormap:
1197 drawactive(1); /* to restore map from backup */
1198 p = malloc(4*12*256+1);
1199 if(p == 0)
1200 error(Enomem);
1201 m = 0;
1202 for(index = 0; index < 256; index++){
1203 getcolor(index, &red, &green, &blue);
1204 m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24);
1205 }
1206 n = readstr(offset, a, n, (char*)p);
1207 free(p);
1208 break;
1209
1210 case Qdata:
1211 if(cl->readdata == nil)
1212 error("no draw data");
1213 if(n < cl->nreaddata)
1214 error(Eshortread);
1215 n = cl->nreaddata;
1216 memmove(a, cl->readdata, cl->nreaddata);
1217 free(cl->readdata);
1218 cl->readdata = nil;
1219 break;
1220
1221 case Qrefresh:
1222 if(n < 5*4)
1223 error(Ebadarg);
1224 for(;;){
1225 if(cl->refreshme || cl->refresh)
1226 break;
1227 dunlock();
1228 if(waserror()){
1229 dlock(); /* restore lock for waserror() above */
1230 nexterror();
1231 }
1232 sleep(&cl->refrend, drawrefactive, cl);
1233 poperror();
1234 dlock();
1235 }
1236 p = a;
1237 while(cl->refresh && n>=5*4){
1238 r = cl->refresh;
1239 BPLONG(p+0*4, r->dimage->id);
1240 BPLONG(p+1*4, r->r.min.x);
1241 BPLONG(p+2*4, r->r.min.y);
1242 BPLONG(p+3*4, r->r.max.x);
1243 BPLONG(p+4*4, r->r.max.y);
1244 cl->refresh = r->next;
1245 free(r);
1246 p += 5*4;
1247 n -= 5*4;
1248 }
1249 cl->refreshme = 0;
1250 n = p-(uchar*)a;
1251 break;
1252 }
1253 dunlock();
1254 poperror();
1255 return n;
1256 }
1257
1258 void
1259 drawwakeall(void)
1260 {
1261 Client *cl;
1262 int i;
1263
1264 for(i=0; i<sdraw.nclient; i++){
1265 cl = sdraw.client[i];
1266 if(cl && (cl->refreshme || cl->refresh))
1267 wakeup(&cl->refrend);
1268 }
1269 }
1270
1271 static long
1272 drawwrite(Chan *c, void *a, long n, vlong)
1273 {
1274 char buf[128], *fields[4], *q;
1275 Client *cl;
1276 int i, m, red, green, blue, x;
1277
1278 if(c->qid.type & QTDIR)
1279 error(Eisdir);
1280 cl = drawclient(c);
1281 dlock();
1282 if(waserror()){
1283 drawwakeall();
1284 dunlock();
1285 nexterror();
1286 }
1287 switch(QID(c->qid)){
1288 case Qctl:
1289 if(n != 4)
1290 error("unknown draw control request");
1291 cl->infoid = BGLONG((uchar*)a);
1292 break;
1293
1294 case Qcolormap:
1295 drawactive(1); /* to restore map from backup */
1296 m = n;
1297 n = 0;
1298 while(m > 0){
1299 x = m;
1300 if(x > sizeof(buf)-1)
1301 x = sizeof(buf)-1;
1302 q = memccpy(buf, a, '\n', x);
1303 if(q == 0)
1304 break;
1305 i = q-buf;
1306 n += i;
1307 a = (char*)a + i;
1308 m -= i;
1309 *q = 0;
1310 if(tokenize(buf, fields, nelem(fields)) != 4)
1311 error(Ebadarg);
1312 i = strtoul(fields[0], 0, 0);
1313 red = strtoul(fields[1], 0, 0);
1314 green = strtoul(fields[2], 0, 0);
1315 blue = strtoul(fields[3], &q, 0);
1316 if(fields[3] == q)
1317 error(Ebadarg);
1318 if(red>255 || green>255 || blue>255 || i<0 || i>255)
1319 error(Ebadarg);
1320 red |= red<<8;
1321 red |= red<<16;
1322 green |= green<<8;
1323 green |= green<<16;
1324 blue |= blue<<8;
1325 blue |= blue<<16;
1326 setcolor(i, red, green, blue);
1327 }
1328 break;
1329
1330 case Qdata:
1331 drawmesg(cl, a, n);
1332 drawwakeall();
1333 break;
1334
1335 default:
1336 error(Ebadusefd);
1337 }
1338 dunlock();
1339 poperror();
1340 return n;
1341 }
1342
1343 uchar*
1344 drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
1345 {
1346 int b, x;
1347
1348 if(p >= maxp)
1349 error(Eshortdraw);
1350 b = *p++;
1351 x = b & 0x7F;
1352 if(b & 0x80){
1353 if(p+1 >= maxp)
1354 error(Eshortdraw);
1355 x |= *p++ << 7;
1356 x |= *p++ << 15;
1357 if(x & (1<<22))
1358 x |= ~0<<23;
1359 }else{
1360 if(b & 0x40)
1361 x |= ~0<<7;
1362 x += oldx;
1363 }
1364 *newx = x;
1365 return p;
1366 }
1367
1368 static void
1369 printmesg(char *fmt, uchar *a, int plsprnt)
1370 {
1371 char buf[256];
1372 char *p, *q;
1373 int s;
1374
1375 if(1|| plsprnt==0){
1376 SET(s,q,p);
1377 USED(fmt, a, buf, p, q, s);
1378 return;
1379 }
1380 q = buf;
1381 *q++ = *a++;
1382 for(p=fmt; *p; p++){
1383 switch(*p){
1384 case 'l':
1385 q += sprint(q, " %ld", (long)BGLONG(a));
1386 a += 4;
1387 break;
1388 case 'L':
1389 q += sprint(q, " %.8lux", (ulong)BGLONG(a));
1390 a += 4;
1391 break;
1392 case 'R':
1393 q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12));
1394 a += 16;
1395 break;
1396 case 'P':
1397 q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4));
1398 a += 8;
1399 break;
1400 case 'b':
1401 q += sprint(q, " %d", *a++);
1402 break;
1403 case 's':
1404 q += sprint(q, " %d", BGSHORT(a));
1405 a += 2;
1406 break;
1407 case 'S':
1408 q += sprint(q, " %.4ux", BGSHORT(a));
1409 a += 2;
1410 break;
1411 }
1412 }
1413 *q++ = '\n';
1414 *q = 0;
1415 iprint("%.*s", (int)(q-buf), buf);
1416 }
1417
1418 void
1419 drawmesg(Client *client, void *av, int n)
1420 {
1421 int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, op, ox, oy, oesize, esize, doflush;
1422 uchar *u, *a, refresh;
1423 char *fmt;
1424 ulong value, chan;
1425 Rectangle r, clipr;
1426 Point p, q, *pp, sp;
1427 Memimage *i, *bg, *dst, *src, *mask;
1428 Memimage *l, **lp;
1429 Memscreen *scrn;
1430 DImage *font, *ll, *di, *ddst, *dsrc;
1431 DName *dn;
1432 DScreen *dscrn;
1433 FChar *fc;
1434 Refx *refx;
1435 CScreen *cs;
1436 Refreshfn reffn;
1437
1438 a = av;
1439 m = 0;
1440 fmt = nil;
1441 if(waserror()){
1442 if(fmt) printmesg(fmt, a, 1);
1443 /* iprint("error: %s\n", up->errstr); */
1444 nexterror();
1445 }
1446 while((n-=m) > 0){
1447 USED(fmt);
1448 a += m;
1449 switch(*a){
1450 default:
1451 error("bad draw command");
1452 /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */
1453 case 'b':
1454 printmesg(fmt="LLbLbRRL", a, 0);
1455 m = 1+4+4+1+4+1+4*4+4*4+4;
1456 if(n < m)
1457 error(Eshortdraw);
1458 dstid = BGLONG(a+1);
1459 scrnid = BGSHORT(a+5);
1460 refresh = a[9];
1461 chan = BGLONG(a+10);
1462 repl = a[14];
1463 drawrectangle(&r, a+15);
1464 drawrectangle(&clipr, a+31);
1465 value = BGLONG(a+47);
1466 if(drawlookup(client, dstid, 0))
1467 error(Eimageexists);
1468 if(scrnid){
1469 dscrn = drawlookupscreen(client, scrnid, &cs);
1470 scrn = dscrn->screen;
1471 if(repl || chan!=scrn->image->chan)
1472 error("image parameters incompatible with screen");
1473 reffn = nil;
1474 switch(refresh){
1475 case Refbackup:
1476 break;
1477 case Refnone:
1478 reffn = memlnorefresh;
1479 break;
1480 case Refmesg:
1481 reffn = drawrefresh;
1482 break;
1483 default:
1484 error("unknown refresh method");
1485 }
1486 l = memlalloc(scrn, r, reffn, 0, value);
1487 if(l == 0)
1488 error(Edrawmem);
1489 addflush(l->layer->screenr);
1490 l->clipr = clipr;
1491 rectclip(&l->clipr, r);
1492 if(drawinstall(client, dstid, l, dscrn) == 0){
1493 memldelete(l);
1494 error(Edrawmem);
1495 }
1496 dscrn->ref++;
1497 if(reffn){
1498 refx = nil;
1499 if(reffn == drawrefresh){
1500 refx = malloc(sizeof(Refx));
1501 if(refx == 0){
1502 drawuninstall(client, dstid);
1503 error(Edrawmem);
1504 }
1505 refx->client = client;
1506 refx->dimage = drawlookup(client, dstid, 1);
1507 }
1508 memlsetrefresh(l, reffn, refx);
1509 }
1510 continue;
1511 }
1512 i = allocmemimage(r, chan);
1513 if(i == 0)
1514 error(Edrawmem);
1515 if(repl)
1516 i->flags |= Frepl;
1517 i->clipr = clipr;
1518 if(!repl)
1519 rectclip(&i->clipr, r);
1520 if(drawinstall(client, dstid, i, 0) == 0){
1521 freememimage(i);
1522 error(Edrawmem);
1523 }
1524 memfillcolor(i, value);
1525 continue;
1526
1527 /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
1528 case 'A':
1529 printmesg(fmt="LLLb", a, 1);
1530 m = 1+4+4+4+1;
1531 if(n < m)
1532 error(Eshortdraw);
1533 dstid = BGLONG(a+1);
1534 if(dstid == 0)
1535 error(Ebadarg);
1536 if(drawlookupdscreen(dstid))
1537 error(Escreenexists);
1538 ddst = drawlookup(client, BGLONG(a+5), 1);
1539 dsrc = drawlookup(client, BGLONG(a+9), 1);
1540 if(ddst==0 || dsrc==0)
1541 error(Enodrawimage);
1542 if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0)
1543 error(Edrawmem);
1544 continue;
1545
1546 /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
1547 case 'c':
1548 printmesg(fmt="LbR", a, 0);
1549 m = 1+4+1+4*4;
1550 if(n < m)
1551 error(Eshortdraw);
1552 ddst = drawlookup(client, BGLONG(a+1), 1);
1553 if(ddst == nil)
1554 error(Enodrawimage);
1555 if(ddst->name)
1556 error("cannot change repl/clipr of shared image");
1557 dst = ddst->image;
1558 if(a[5])
1559 dst->flags |= Frepl;
1560 drawrectangle(&dst->clipr, a+6);
1561 continue;
1562
1563 /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
1564 case 'd':
1565 printmesg(fmt="LLLRPP", a, 0);
1566 m = 1+4+4+4+4*4+2*4+2*4;
1567 if(n < m)
1568 error(Eshortdraw);
1569 dst = drawimage(client, a+1);
1570 dstid = BGLONG(a+1);
1571 src = drawimage(client, a+5);
1572 mask = drawimage(client, a+9);
1573 drawrectangle(&r, a+13);
1574 drawpoint(&p, a+29);
1575 drawpoint(&q, a+37);
1576 op = drawclientop(client);
1577 memdraw(dst, r, src, p, mask, q, op);
1578 dstflush(dstid, dst, r);
1579 continue;
1580
1581 /* toggle debugging: 'D' val[1] */
1582 case 'D':
1583 printmesg(fmt="b", a, 0);
1584 m = 1+1;
1585 if(n < m)
1586 error(Eshortdraw);
1587 drawdebug = a[1];
1588 continue;
1589
1590 /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
1591 case 'e':
1592 case 'E':
1593 printmesg(fmt="LLPlllPll", a, 0);
1594 m = 1+4+4+2*4+4+4+4+2*4+2*4;
1595 if(n < m)
1596 error(Eshortdraw);
1597 dst = drawimage(client, a+1);
1598 dstid = BGLONG(a+1);
1599 src = drawimage(client, a+5);
1600 drawpoint(&p, a+9);
1601 e0 = BGLONG(a+17);
1602 e1 = BGLONG(a+21);
1603 if(e0<0 || e1<0)
1604 error("invalid ellipse semidiameter");
1605 j = BGLONG(a+25);
1606 if(j < 0)
1607 error("negative ellipse thickness");
1608 drawpoint(&sp, a+29);
1609 c = j;
1610 if(*a == 'E')
1611 c = -1;
1612 ox = BGLONG(a+37);
1613 oy = BGLONG(a+41);
1614 op = drawclientop(client);
1615 /* high bit indicates arc angles are present */
1616 if(ox & (1<<31)){
1617 if((ox & (1<<30)) == 0)
1618 ox &= ~(1<<31);
1619 memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
1620 }else
1621 memellipse(dst, p, e0, e1, c, src, sp, op);
1622 dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
1623 continue;
1624
1625 /* free: 'f' id[4] */
1626 case 'f':
1627 printmesg(fmt="L", a, 1);
1628 m = 1+4;
1629 if(n < m)
1630 error(Eshortdraw);
1631 ll = drawlookup(client, BGLONG(a+1), 0);
1632 if(ll && ll->dscreen && ll->dscreen->owner != client)
1633 ll->dscreen->owner->refreshme = 1;
1634 drawuninstall(client, BGLONG(a+1));
1635 continue;
1636
1637 /* free screen: 'F' id[4] */
1638 case 'F':
1639 printmesg(fmt="L", a, 1);
1640 m = 1+4;
1641 if(n < m)
1642 error(Eshortdraw);
1643 drawlookupscreen(client, BGLONG(a+1), &cs);
1644 drawuninstallscreen(client, cs);
1645 continue;
1646
1647 /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
1648 case 'i':
1649 printmesg(fmt="Llb", a, 1);
1650 m = 1+4+4+1;
1651 if(n < m)
1652 error(Eshortdraw);
1653 dstid = BGLONG(a+1);
1654 if(dstid == 0)
1655 error("cannot use display as font");
1656 font = drawlookup(client, dstid, 1);
1657 if(font == 0)
1658 error(Enodrawimage);
1659 if(font->image->layer)
1660 error("cannot use window as font");
1661 ni = BGLONG(a+5);
1662 if(ni<=0 || ni>4096)
1663 error("bad font size (4096 chars max)");
1664 free(font->fchar); /* should we complain if non-zero? */
1665 font->fchar = malloc(ni*sizeof(FChar));
1666 if(font->fchar == 0)
1667 error("no memory for font");
1668 memset(font->fchar, 0, ni*sizeof(FChar));
1669 font->nfchar = ni;
1670 font->ascent = a[9];
1671 continue;
1672
1673 /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
1674 case 'l':
1675 printmesg(fmt="LLSRPbb", a, 0);
1676 m = 1+4+4+2+4*4+2*4+1+1;
1677 if(n < m)
1678 error(Eshortdraw);
1679 font = drawlookup(client, BGLONG(a+1), 1);
1680 if(font == 0)
1681 error(Enodrawimage);
1682 if(font->nfchar == 0)
1683 error(Enotfont);
1684 src = drawimage(client, a+5);
1685 ci = BGSHORT(a+9);
1686 if(ci >= font->nfchar)
1687 error(Eindex);
1688 drawrectangle(&r, a+11);
1689 drawpoint(&p, a+27);
1690 memdraw(font->image, r, src, p, memopaque, p, S);
1691 fc = &font->fchar[ci];
1692 fc->minx = r.min.x;
1693 fc->maxx = r.max.x;
1694 fc->miny = r.min.y;
1695 fc->maxy = r.max.y;
1696 fc->left = a[35];
1697 fc->width = a[36];
1698 continue;
1699
1700 /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
1701 case 'L':
1702 printmesg(fmt="LPPlllLP", a, 0);
1703 m = 1+4+2*4+2*4+4+4+4+4+2*4;
1704 if(n < m)
1705 error(Eshortdraw);
1706 dst = drawimage(client, a+1);
1707 dstid = BGLONG(a+1);
1708 drawpoint(&p, a+5);
1709 drawpoint(&q, a+13);
1710 e0 = BGLONG(a+21);
1711 e1 = BGLONG(a+25);
1712 j = BGLONG(a+29);
1713 if(j < 0)
1714 error("negative line width");
1715 src = drawimage(client, a+33);
1716 drawpoint(&sp, a+37);
1717 op = drawclientop(client);
1718 memline(dst, p, q, e0, e1, j, src, sp, op);
1719 /* avoid memlinebbox if possible */
1720 if(dstid==0 || dst->layer!=nil){
1721 /* BUG: this is terribly inefficient: update maximal containing rect*/
1722 r = memlinebbox(p, q, e0, e1, j);
1723 dstflush(dstid, dst, insetrect(r, -(1+1+j)));
1724 }
1725 continue;
1726
1727 /* create image mask: 'm' newid[4] id[4] */
1728 /*
1729 *
1730 case 'm':
1731 printmesg("LL", a, 0);
1732 m = 4+4;
1733 if(n < m)
1734 error(Eshortdraw);
1735 break;
1736 *
1737 */
1738
1739 /* attach to a named image: 'n' dstid[4] j[1] name[j] */
1740 case 'n':
1741 printmesg(fmt="Lz", a, 0);
1742 m = 1+4+1;
1743 if(n < m)
1744 error(Eshortdraw);
1745 j = a[5];
1746 if(j == 0) /* give me a non-empty name please */
1747 error(Eshortdraw);
1748 m += j;
1749 if(n < m)
1750 error(Eshortdraw);
1751 dstid = BGLONG(a+1);
1752 if(drawlookup(client, dstid, 0))
1753 error(Eimageexists);
1754 dn = drawlookupname(j, (char*)a+6);
1755 if(dn == nil)
1756 error(Enoname);
1757 if(drawinstall(client, dstid, dn->dimage->image, 0) == 0)
1758 error(Edrawmem);
1759 di = drawlookup(client, dstid, 0);
1760 if(di == 0)
1761 error("draw: cannot happen");
1762 di->vers = dn->vers;
1763 di->name = smalloc(j+1);
1764 di->fromname = dn->dimage;
1765 di->fromname->ref++;
1766 memmove(di->name, a+6, j);
1767 di->name[j] = 0;
1768 client->infoid = dstid;
1769 continue;
1770
1771 /* name an image: 'N' dstid[4] in[1] j[1] name[j] */
1772 case 'N':
1773 printmesg(fmt="Lbz", a, 0);
1774 m = 1+4+1+1;
1775 if(n < m)
1776 error(Eshortdraw);
1777 c = a[5];
1778 j = a[6];
1779 if(j == 0) /* give me a non-empty name please */
1780 error(Eshortdraw);
1781 m += j;
1782 if(n < m)
1783 error(Eshortdraw);
1784 di = drawlookup(client, BGLONG(a+1), 0);
1785 if(di == 0)
1786 error(Enodrawimage);
1787 if(di->name)
1788 error(Enamed);
1789 if(c)
1790 drawaddname(client, di, j, (char*)a+7);
1791 else{
1792 dn = drawlookupname(j, (char*)a+7);
1793 if(dn == nil)
1794 error(Enoname);
1795 if(dn->dimage != di)
1796 error(Ewrongname);
1797 drawdelname(dn);
1798 }
1799 continue;
1800
1801 /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
1802 case 'o':
1803 printmesg(fmt="LPP", a, 0);
1804 m = 1+4+2*4+2*4;
1805 if(n < m)
1806 error(Eshortdraw);
1807 dst = drawimage(client, a+1);
1808 if(dst->layer){
1809 drawpoint(&p, a+5);
1810 drawpoint(&q, a+13);
1811 r = dst->layer->screenr;
1812 ni = memlorigin(dst, p, q);
1813 if(ni < 0)
1814 error("image origin failed");
1815 if(ni > 0){
1816 addflush(r);
1817 addflush(dst->layer->screenr);
1818 ll = drawlookup(client, BGLONG(a+1), 1);
1819 drawrefreshscreen(ll, client);
1820 }
1821 }
1822 continue;
1823
1824 /* set compositing operator for next draw operation: 'O' op */
1825 case 'O':
1826 printmesg(fmt="b", a, 0);
1827 m = 1+1;
1828 if(n < m)
1829 error(Eshortdraw);
1830 client->op = a[1];
1831 continue;
1832
1833 /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1834 /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1835 case 'p':
1836 case 'P':
1837 printmesg(fmt="LslllLPP", a, 0);
1838 m = 1+4+2+4+4+4+4+2*4;
1839 if(n < m)
1840 error(Eshortdraw);
1841 dstid = BGLONG(a+1);
1842 dst = drawimage(client, a+1);
1843 ni = BGSHORT(a+5);
1844 if(ni < 0)
1845 error("negative count in polygon");
1846 e0 = BGLONG(a+7);
1847 e1 = BGLONG(a+11);
1848 j = 0;
1849 if(*a == 'p'){
1850 j = BGLONG(a+15);
1851 if(j < 0)
1852 error("negative polygon line width");
1853 }
1854 src = drawimage(client, a+19);
1855 drawpoint(&sp, a+23);
1856 drawpoint(&p, a+31);
1857 ni++;
1858 pp = malloc(ni*sizeof(Point));
1859 if(pp == nil)
1860 error(Enomem);
1861 doflush = 0;
1862 if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data))
1863 doflush = 1; /* simplify test in loop */
1864 ox = oy = 0;
1865 esize = 0;
1866 u = a+m;
1867 for(y=0; y<ni; y++){
1868 q = p;
1869 oesize = esize;
1870 u = drawcoord(u, a+n, ox, &p.x);
1871 u = drawcoord(u, a+n, oy, &p.y);
1872 ox = p.x;
1873 oy = p.y;
1874 if(doflush){
1875 esize = j;
1876 if(*a == 'p'){
1877 if(y == 0){
1878 c = memlineendsize(e0);
1879 if(c > esize)
1880 esize = c;
1881 }
1882 if(y == ni-1){
1883 c = memlineendsize(e1);
1884 if(c > esize)
1885 esize = c;
1886 }
1887 }
1888 if(*a=='P' && e0!=1 && e0 !=~0)
1889 r = dst->clipr;
1890 else if(y > 0){
1891 r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1);
1892 combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1893 }
1894 if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */
1895 dstflush(dstid, dst, r);
1896 }
1897 pp[y] = p;
1898 }
1899 if(y == 1)
1900 dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1901 op = drawclientop(client);
1902 if(*a == 'p')
1903 mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
1904 else
1905 memfillpoly(dst, pp, ni, e0, src, sp, op);
1906 free(pp);
1907 m = u-a;
1908 continue;
1909
1910 /* read: 'r' id[4] R[4*4] */
1911 case 'r':
1912 printmesg(fmt="LR", a, 0);
1913 m = 1+4+4*4;
1914 if(n < m)
1915 error(Eshortdraw);
1916 i = drawimage(client, a+1);
1917 drawrectangle(&r, a+5);
1918 if(!rectinrect(r, i->r))
1919 error(Ereadoutside);
1920 c = bytesperline(r, i->depth);
1921 c *= Dy(r);
1922 free(client->readdata);
1923 client->readdata = mallocz(c, 0);
1924 if(client->readdata == nil)
1925 error("readimage malloc failed");
1926 client->nreaddata = memunload(i, r, client->readdata, c);
1927 if(client->nreaddata < 0){
1928 free(client->readdata);
1929 client->readdata = nil;
1930 error("bad readimage call");
1931 }
1932 continue;
1933
1934 /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
1935 /* 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]) */
1936 case 's':
1937 case 'x':
1938 printmesg(fmt="LLLPRPs", a, 0);
1939 m = 1+4+4+4+2*4+4*4+2*4+2;
1940 if(*a == 'x')
1941 m += 4+2*4;
1942 if(n < m)
1943 error(Eshortdraw);
1944
1945 dst = drawimage(client, a+1);
1946 dstid = BGLONG(a+1);
1947 src = drawimage(client, a+5);
1948 font = drawlookup(client, BGLONG(a+9), 1);
1949 if(font == 0)
1950 error(Enodrawimage);
1951 if(font->nfchar == 0)
1952 error(Enotfont);
1953 drawpoint(&p, a+13);
1954 drawrectangle(&r, a+21);
1955 drawpoint(&sp, a+37);
1956 ni = BGSHORT(a+45);
1957 u = a+m;
1958 m += ni*2;
1959 if(n < m)
1960 error(Eshortdraw);
1961 clipr = dst->clipr;
1962 dst->clipr = r;
1963 op = drawclientop(client);
1964 bg = dst;
1965 if(*a == 'x'){
1966 /* paint background */
1967 bg = drawimage(client, a+47);
1968 drawpoint(&q, a+51);
1969 r.min.x = p.x;
1970 r.min.y = p.y-font->ascent;
1971 r.max.x = p.x;
1972 r.max.y = r.min.y+Dy(font->image->r);
1973 j = ni;
1974 while(--j >= 0){
1975 ci = BGSHORT(u);
1976 if(ci<0 || ci>=font->nfchar){
1977 dst->clipr = clipr;
1978 error(Eindex);
1979 }
1980 r.max.x += font->fchar[ci].width;
1981 u += 2;
1982 }
1983 memdraw(dst, r, bg, q, memopaque, ZP, op);
1984 u -= 2*ni;
1985 }
1986 q = p;
1987 while(--ni >= 0){
1988 ci = BGSHORT(u);
1989 if(ci<0 || ci>=font->nfchar){
1990 dst->clipr = clipr;
1991 error(Eindex);
1992 }
1993 q = drawchar(dst, bg, q, src, &sp, font, ci, op);
1994 u += 2;
1995 }
1996 dst->clipr = clipr;
1997 p.y -= font->ascent;
1998 dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
1999 continue;
2000
2001 /* use public screen: 'S' id[4] chan[4] */
2002 case 'S':
2003 printmesg(fmt="Ll", a, 0);
2004 m = 1+4+4;
2005 if(n < m)
2006 error(Eshortdraw);
2007 dstid = BGLONG(a+1);
2008 if(dstid == 0)
2009 error(Ebadarg);
2010 dscrn = drawlookupdscreen(dstid);
2011 if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
2012 error(Enodrawscreen);
2013 if(dscrn->screen->image->chan != BGLONG(a+5))
2014 error("inconsistent chan");
2015 if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
2016 error(Edrawmem);
2017 continue;
2018
2019 /* top or bottom windows: 't' top[1] nw[2] n*id[4] */
2020 case 't':
2021 printmesg(fmt="bsL", a, 0);
2022 m = 1+1+2;
2023 if(n < m)
2024 error(Eshortdraw);
2025 nw = BGSHORT(a+2);
2026 if(nw < 0)
2027 error(Ebadarg);
2028 if(nw == 0)
2029 continue;
2030 m += nw*4;
2031 if(n < m)
2032 error(Eshortdraw);
2033 lp = malloc(nw*sizeof(Memimage*));
2034 if(lp == 0)
2035 error(Enomem);
2036 if(waserror()){
2037 free(lp);
2038 nexterror();
2039 }
2040 for(j=0; j<nw; j++)
2041 lp[j] = drawimage(client, a+1+1+2+j*4);
2042 if(lp[0]->layer == 0)
2043 error("images are not windows");
2044 for(j=1; j<nw; j++)
2045 if(lp[j]->layer->screen != lp[0]->layer->screen)
2046 error("images not on same screen");
2047 if(a[1])
2048 memltofrontn(lp, nw);
2049 else
2050 memltorearn(lp, nw);
2051 if(lp[0]->layer->screen->image->data == screenimage->data)
2052 for(j=0; j<nw; j++)
2053 addflush(lp[j]->layer->screenr);
2054 ll = drawlookup(client, BGLONG(a+1+1+2), 1);
2055 drawrefreshscreen(ll, client);
2056 poperror();
2057 free(lp);
2058 continue;
2059
2060 /* visible: 'v' */
2061 case 'v':
2062 printmesg(fmt="", a, 0);
2063 m = 1;
2064 drawflush();
2065 continue;
2066
2067 /* write: 'y' id[4] R[4*4] data[x*1] */
2068 /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
2069 case 'y':
2070 case 'Y':
2071 printmesg(fmt="LR", a, 0);
2072 // iprint("load %c\n", *a);
2073 m = 1+4+4*4;
2074 if(n < m)
2075 error(Eshortdraw);
2076 dstid = BGLONG(a+1);
2077 dst = drawimage(client, a+1);
2078 drawrectangle(&r, a+5);
2079 if(!rectinrect(r, dst->r))
2080 error(Ewriteoutside);
2081 y = memload(dst, r, a+m, n-m, *a=='Y');
2082 if(y < 0)
2083 error("bad writeimage call");
2084 dstflush(dstid, dst, r);
2085 m += y;
2086 continue;
2087 }
2088 }
2089 poperror();
2090 }
2091
2092 Dev drawdevtab = {
2093 'i',
2094 "draw",
2095
2096 devreset,
2097 devinit,
2098 devshutdown,
2099 drawattach,
2100 drawwalk,
2101 drawstat,
2102 drawopen,
2103 devcreate,
2104 drawclose,
2105 drawread,
2106 devbread,
2107 drawwrite,
2108 devbwrite,
2109 devremove,
2110 devwstat,
2111 };
2112
2113 /*
2114 * On 8 bit displays, load the default color map
2115 */
2116 void
2117 drawcmap(void)
2118 {
2119 int r, g, b, cr, cg, cb, v;
2120 int num, den;
2121 int i, j;
2122
2123 drawactive(1); /* to restore map from backup */
2124 for(r=0,i=0; r!=4; r++)
2125 for(v=0; v!=4; v++,i+=16){
2126 for(g=0,j=v-r; g!=4; g++)
2127 for(b=0;b!=4;b++,j++){
2128 den = r;
2129 if(g > den)
2130 den = g;
2131 if(b > den)
2132 den = b;
2133 if(den == 0) /* divide check -- pick grey shades */
2134 cr = cg = cb = v*17;
2135 else{
2136 num = 17*(4*den+v);
2137 cr = r*num/den;
2138 cg = g*num/den;
2139 cb = b*num/den;
2140 }
2141 setcolor(i+(j&15),
2142 cr*0x01010101, cg*0x01010101, cb*0x01010101);
2143 }
2144 }
2145 }
2146
2147 void
2148 drawblankscreen(int blank)
2149 {
2150 int i, nc;
2151 ulong *p;
2152
2153 if(blank == sdraw.blanked)
2154 return;
2155 if(!candlock())
2156 return;
2157 if(!initscreenimage()){
2158 dunlock();
2159 return;
2160 }
2161 p = sdraw.savemap;
2162 nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth;
2163
2164 /*
2165 * blankscreen uses the hardware to blank the screen
2166 * when possible. to help in cases when it is not possible,
2167 * we set the color map to be all black.
2168 */
2169 if(blank == 0){ /* turn screen on */
2170 for(i=0; i<nc; i++, p+=3)
2171 setcolor(i, p[0], p[1], p[2]);
2172 blankscreen(0);
2173 }else{ /* turn screen off */
2174 blankscreen(1);
2175 for(i=0; i<nc; i++, p+=3){
2176 getcolor(i, &p[0], &p[1], &p[2]);
2177 setcolor(i, 0, 0, 0);
2178 }
2179 }
2180 sdraw.blanked = blank;
2181 dunlock();
2182 }
2183
2184 /*
2185 * record activity on screen, changing blanking as appropriate
2186 */
2187 void
2188 drawactive(int active)
2189 {
2190 if(active){
2191 drawblankscreen(0);
2192 sdraw.blanktime = MACHP(0)->ticks;
2193 }else{
2194 if(blanktime && sdraw.blanktime && TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60 >= blanktime)
2195 drawblankscreen(1);
2196 }
2197 }
2198
2199 int
2200 drawidletime(void)
2201 {
2202 return TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60;
2203 }
Cache object: 4658a9d76d5af085f8b609b9037381af
|