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