├── .hgignore ├── fcall.h ├── mkfile ├── devdraw.h ├── devdrawclient.c ├── README ├── devdrawserver.c ├── LICENSE ├── dialdevdraw.c ├── fcall.c └── devdraw.c /.hgignore: -------------------------------------------------------------------------------- 1 | .*\.o$ 2 | ^o\..* 3 | -------------------------------------------------------------------------------- /fcall.h: -------------------------------------------------------------------------------- 1 | Channel* readfcalls(int fd); 2 | void proxyfcalls(int readfd, int writefd); 3 | Channel* procproxyfcalls(int readfd, int writefd); 4 | -------------------------------------------------------------------------------- /mkfile: -------------------------------------------------------------------------------- 1 | <$PLAN9/src/mkhdr 2 | 3 | TARG=devdrawserver devdrawclient dialdevdraw 4 | #OFILES=fcall.$O 5 | 6 | <$PLAN9/src/mkmany 7 | 8 | $O.devdrawserver: fcall.$O 9 | -------------------------------------------------------------------------------- /devdraw.h: -------------------------------------------------------------------------------- 1 | int _drawmsgread(void*, int); 2 | int _drawmsgwrite(void*, int); 3 | void _initdisplaymemimage(Memimage*); 4 | int _latin1(Rune*, int); 5 | int parsewinsize(char*, Rectangle*, int*); 6 | int mouseswap(int); 7 | void abortcompose(void); 8 | 9 | 10 | -------------------------------------------------------------------------------- /devdrawclient.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void 5 | main(int argc, char **argv) 6 | { 7 | int dfd; 8 | char *devdraw; 9 | 10 | if ((dfd = dial("unix!/tmp/devdrawserver", nil, nil, nil)) < 0) 11 | sysfatal("dial: %r"); 12 | 13 | dup(dfd, 0); 14 | dup(dfd, 1); 15 | 16 | putenv("NOLIBTHREADDAEMONIZE", "1"); 17 | devdraw = getenv("DEVDRAW"); 18 | if(devdraw == nil) 19 | devdraw = "devdraw"; 20 | if(execl(devdraw, devdraw, "(devdraw)", nil) < 0) 21 | sysfatal("execl: %r"); 22 | 23 | exits(0); 24 | } 25 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | DEVDRAW 2 | 3 | Devdrawserver is a client-server devdraw[1]. It allows for later 4 | attachment, so it can easily be run on a remote machine, proxying the 5 | draw calls to a local devdraw. 6 | 7 | On a remote machine: 8 | 9 | remote% DEVDRAW=devdrawserver acme 10 | 11 | and locally: 12 | 13 | % dialdevdraw 'ssh remote dial -e unix!/tmp/devdrawserver' 14 | 15 | This is still work in progress, but it's fully functional. 16 | 17 | BUGS 18 | 19 | Wouldn't be needed at all if devdraw was a real file server. 20 | 21 | [1] http://swtch.com/plan9port/man/man1/devdraw.html -------------------------------------------------------------------------------- /devdrawserver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fcall.h" 11 | 12 | void serve(); 13 | void rmsock(); 14 | static char sockpath[200]; 15 | 16 | void 17 | usage() 18 | { 19 | fprint(2, "usage: %s\n", argv0); 20 | threadexitsall("usage"); 21 | } 22 | 23 | void 24 | threadmain(int argc, char **argv) 25 | { 26 | fmtinstall('W', drawfcallfmt); 27 | fmtinstall('H', encodefmt); 28 | 29 | // snprint(sockpath, sizeof sockpath, "%s/devdrawserver", getns()); 30 | strcpy(sockpath, "/tmp/devdrawserver"); 31 | 32 | serve(); 33 | threadexitsall(""); 34 | } 35 | 36 | void 37 | rmsock() 38 | { 39 | if(remove(sockpath) < 0) 40 | fprint(2, "remove: %r\n"); 41 | } 42 | 43 | void 44 | serve() 45 | { 46 | int afd, lfd; 47 | char dir[100], adir[100], buf[200]; 48 | Alt a[3]; 49 | 50 | snprint(buf, sizeof buf, "unix!%s", sockpath); 51 | 52 | atexit(rmsock); 53 | if((afd = announce(buf, adir)) < 0) 54 | sysfatal("announce: %r"); 55 | 56 | if ((lfd = listen(adir, dir)) < 0) 57 | goto Error; 58 | 59 | a[0].c = procproxyfcalls(lfd, 1); 60 | a[0].op = CHANRCV; 61 | a[1].c = procproxyfcalls(0, lfd); 62 | a[1].op = CHANRCV; 63 | a[2].op = CHANEND; 64 | 65 | werrstr(a[alt(a)].v); 66 | close(lfd); 67 | 68 | Error: 69 | close(afd); 70 | threadexitsall("%r"); 71 | } 72 | 73 | void 74 | _flushmemscreen(Rectangle r) 75 | {} 76 | 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Marius Eriksen 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Devdrawserver nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /dialdevdraw.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void 6 | usage() 7 | { 8 | fprint(2, "usage: %s command\n", argv0); 9 | threadexitsall("usage"); 10 | } 11 | 12 | void 13 | threadmain(int argc, char **argv) 14 | { 15 | int p[2], fd[3], pid[2]; 16 | char *a[4], *devdraw; 17 | 18 | ARGBEGIN{}ARGEND 19 | 20 | if(argc != 1) 21 | usage(); 22 | 23 | // Make sure we don't kill our parent whilst killing 24 | // our children. 25 | setsid(); 26 | 27 | if(pipe(p) < 0) 28 | sysfatal("pipe: %r"); 29 | 30 | fd[0] = dup(p[0], -1); 31 | fd[1] = dup(p[0], -1); 32 | fd[2] = dup(2, -1); 33 | 34 | a[0] = "rc"; 35 | a[1] = "-c"; 36 | a[2] = argv[0]; 37 | a[3] = nil; 38 | 39 | if((pid[0] = threadspawn(fd, a[0], a)) < 0) 40 | sysfatal("threadspawn: %r"); 41 | 42 | fd[0] = dup(p[1], -1); 43 | fd[1] = dup(p[1], -1); 44 | fd[2] = dup(2, -1); 45 | 46 | putenv("NOLIBTHREADDAEMONIZE", "1"); 47 | devdraw = getenv("DEVDRAW"); 48 | if(devdraw == nil) 49 | devdraw = "devdraw"; 50 | if((pid[1] = threadspawnl(fd, devdraw, devdraw, "(devdraw)", nil)) < 0) 51 | sysfatal("threadspawnl: %r"); 52 | 53 | close(p[0]); 54 | close(p[1]); 55 | 56 | recvp(threadwaitchan()); 57 | 58 | postnote(PNGROUP, pid[0], "hangup"); 59 | postnote(PNGROUP, pid[1], "hangup"); 60 | //recvp(threadwaitchan()); 61 | 62 | threadexitsall(""); 63 | 64 | /* 65 | if ((pid = fork()) < 0) 66 | sysfatal("fork: %r"); 67 | 68 | switch(pid){ 69 | case 0: 70 | close(p[0]); 71 | dup(p[1], 0); 72 | dup(p[1], 1); 73 | 74 | a[0] = "rc"; 75 | a[1] = "-c"; 76 | a[2] = argv[0]; 77 | a[3] = nil; 78 | 79 | if(exec("rc", a) < 0) 80 | sysfatal("execl: %r"); 81 | 82 | default: 83 | close(p[1]); 84 | dup(p[0], 0); 85 | dup(p[0], 1); 86 | 87 | putenv("NOLIBTHREADDAEMONIZE", "1"); 88 | devdraw = getenv("DEVDRAW"); 89 | if(devdraw == nil) 90 | devdraw = "devdraw"; 91 | 92 | if(execl(devdraw, devdraw, "(devdraw)", nil) < 0) 93 | sysfatal("execl: %r"); 94 | } 95 | 96 | exits(0); 97 | */ 98 | } 99 | -------------------------------------------------------------------------------- /fcall.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fcall.h" 11 | 12 | static void readproc(void *arg); 13 | static uchar *readmsg(int fd); 14 | static int writemsg(int fd, uchar *mbuf); 15 | static void proxyfcallsproc(void *arg); 16 | 17 | Channel * 18 | readfcalls(int fd) 19 | { 20 | Channel *ch; 21 | void **args; 22 | 23 | if ((args = malloc(2*sizeof(void*))) == nil) 24 | sysfatal("malloc: %r"); 25 | 26 | ch = chancreate(sizeof(void*), 0); 27 | args[0] = ch; 28 | args[1] = (void*)(intptr)fd; 29 | proccreate(readproc, args, 1<<15); 30 | 31 | return ch; 32 | } 33 | 34 | 35 | 36 | Channel * 37 | procproxyfcalls(int readfd, int writefd) 38 | { 39 | void **argv; 40 | Channel *c; 41 | 42 | if ((argv = malloc(3*sizeof(void*))) == nil) 43 | sysfatal("malloc: %r"); 44 | 45 | c = chancreate(sizeof(void*), 0); 46 | argv[0] = (void*)(intptr)readfd; 47 | argv[1] = (void*)(intptr)writefd; 48 | argv[2] = c; 49 | proccreate(proxyfcallsproc, argv, 1<<15); 50 | 51 | return c; 52 | } 53 | 54 | void 55 | proxyfcalls(int readfd, int writefd) 56 | { 57 | int r; 58 | uchar *mbuf = nil; 59 | 60 | for(;;){ 61 | if((mbuf = readmsg(readfd)) == nil) 62 | break; 63 | r = writemsg(writefd, mbuf); 64 | free(mbuf); 65 | if(r < 0) 66 | break; 67 | } 68 | 69 | } 70 | 71 | static void 72 | proxyfcallsproc(void *arg) 73 | { 74 | int writefd, readfd; 75 | Channel *c; 76 | void **argv = arg; 77 | 78 | readfd = (intptr)argv[0]; 79 | writefd = (intptr)argv[1]; 80 | c = argv[2]; 81 | free(argv); 82 | 83 | proxyfcalls(readfd, writefd); 84 | sendp(c, smprint("%r")); 85 | } 86 | 87 | static void 88 | readproc(void *arg) 89 | { 90 | uchar *mbuf; 91 | int fd; 92 | Channel *ch; 93 | void **argv = (void **)arg; 94 | 95 | ch = (Channel *)argv[0]; 96 | fd = (uint)(intptr)argv[1]; 97 | 98 | while((mbuf = readmsg(fd)) != nil) 99 | sendp(ch, mbuf); 100 | 101 | sendp(ch, nil); 102 | } 103 | 104 | static uchar * 105 | readmsg(int fd) 106 | { 107 | int n; 108 | uchar buf[4], *mbuf; 109 | 110 | if(readn(fd, buf, 4) != 4){ 111 | werrstr("eof on length"); 112 | return nil; 113 | } 114 | 115 | GET(buf, n); 116 | if((mbuf = malloc(n+4)) == nil) 117 | return nil; 118 | 119 | PUT(mbuf, n); 120 | if(readn(fd, mbuf+4, n-4) != n-4){ 121 | free(mbuf); 122 | werrstr("eof on input"); 123 | return nil; 124 | } 125 | 126 | return mbuf; 127 | } 128 | 129 | static int 130 | writemsg(int fd, uchar *mbuf) 131 | { 132 | int n; 133 | 134 | GET(mbuf, n); 135 | if(write(fd, mbuf, n) != n){ 136 | werrstr("write: %r"); 137 | return -1; 138 | } 139 | 140 | return 0; 141 | } -------------------------------------------------------------------------------- /devdraw.c: -------------------------------------------------------------------------------- 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 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "devdraw.h" 12 | 13 | extern void _flushmemscreen(Rectangle); 14 | 15 | #define NHASH (1<<5) 16 | #define HASHMASK (NHASH-1) 17 | 18 | typedef struct Client Client; 19 | typedef struct Draw Draw; 20 | typedef struct DImage DImage; 21 | typedef struct DScreen DScreen; 22 | typedef struct CScreen CScreen; 23 | typedef struct FChar FChar; 24 | typedef struct Refresh Refresh; 25 | typedef struct Refx Refx; 26 | typedef struct DName DName; 27 | 28 | struct Draw 29 | { 30 | QLock lk; 31 | int clientid; 32 | int nclient; 33 | Client* client[1]; 34 | int nname; 35 | DName* name; 36 | int vers; 37 | int softscreen; 38 | }; 39 | 40 | struct Client 41 | { 42 | /*Ref r;*/ 43 | DImage* dimage[NHASH]; 44 | CScreen* cscreen; 45 | Refresh* refresh; 46 | Rendez refrend; 47 | uchar* readdata; 48 | int nreaddata; 49 | int busy; 50 | int clientid; 51 | int slot; 52 | int refreshme; 53 | int infoid; 54 | int op; 55 | }; 56 | 57 | struct Refresh 58 | { 59 | DImage* dimage; 60 | Rectangle r; 61 | Refresh* next; 62 | }; 63 | 64 | struct Refx 65 | { 66 | Client* client; 67 | DImage* dimage; 68 | }; 69 | 70 | struct DName 71 | { 72 | char *name; 73 | Client *client; 74 | DImage* dimage; 75 | int vers; 76 | }; 77 | 78 | struct FChar 79 | { 80 | int minx; /* left edge of bits */ 81 | int maxx; /* right edge of bits */ 82 | uchar miny; /* first non-zero scan-line */ 83 | uchar maxy; /* last non-zero scan-line + 1 */ 84 | schar left; /* offset of baseline */ 85 | uchar width; /* width of baseline */ 86 | }; 87 | 88 | /* 89 | * Reference counts in DImages: 90 | * one per open by original client 91 | * one per screen image or fill 92 | * one per image derived from this one by name 93 | */ 94 | struct DImage 95 | { 96 | int id; 97 | int ref; 98 | char *name; 99 | int vers; 100 | Memimage* image; 101 | int ascent; 102 | int nfchar; 103 | FChar* fchar; 104 | DScreen* dscreen; /* 0 if not a window */ 105 | DImage* fromname; /* image this one is derived from, by name */ 106 | DImage* next; 107 | }; 108 | 109 | struct CScreen 110 | { 111 | DScreen* dscreen; 112 | CScreen* next; 113 | }; 114 | 115 | struct DScreen 116 | { 117 | int id; 118 | int public; 119 | int ref; 120 | DImage *dimage; 121 | DImage *dfill; 122 | Memscreen* screen; 123 | Client* owner; 124 | DScreen* next; 125 | }; 126 | 127 | static Draw sdraw; 128 | static Client *client0; 129 | static Memimage *screenimage; 130 | static Rectangle flushrect; 131 | static int waste; 132 | static DScreen* dscreen; 133 | static int drawuninstall(Client*, int); 134 | static Memimage* drawinstall(Client*, int, Memimage*, DScreen*); 135 | static void drawfreedimage(DImage*); 136 | 137 | void 138 | _initdisplaymemimage(Memimage *m) 139 | { 140 | screenimage = m; 141 | m->screenref = 1; 142 | client0 = mallocz(sizeof(Client), 1); 143 | if(client0 == nil){ 144 | fprint(2, "initdraw: allocating client0: out of memory"); 145 | abort(); 146 | } 147 | client0->slot = 0; 148 | client0->clientid = ++sdraw.clientid; 149 | client0->op = SoverD; 150 | sdraw.client[0] = client0; 151 | sdraw.nclient = 1; 152 | sdraw.softscreen = 1; 153 | } 154 | 155 | void 156 | _drawreplacescreenimage(Memimage *m) 157 | { 158 | /* 159 | * Replace the screen image because the screen 160 | * was resized. 161 | * 162 | * In theory there should only be one reference 163 | * to the current screen image, and that's through 164 | * client0's image 0, installed a few lines above. 165 | * Once the client drops the image, the underlying backing 166 | * store freed properly. The client is being notified 167 | * about the resize through external means, so all we 168 | * need to do is this assignment. 169 | */ 170 | Memimage *om; 171 | 172 | qlock(&sdraw.lk); 173 | om = screenimage; 174 | screenimage = m; 175 | m->screenref = 1; 176 | if(om && --om->screenref == 0){ 177 | _freememimage(om); 178 | } 179 | qunlock(&sdraw.lk); 180 | } 181 | 182 | static 183 | void 184 | drawrefreshscreen(DImage *l, Client *client) 185 | { 186 | while(l != nil && l->dscreen == nil) 187 | l = l->fromname; 188 | if(l != nil && l->dscreen->owner != client) 189 | l->dscreen->owner->refreshme = 1; 190 | } 191 | 192 | static 193 | void 194 | drawrefresh(Memimage *m, Rectangle r, void *v) 195 | { 196 | Refx *x; 197 | DImage *d; 198 | Client *c; 199 | Refresh *ref; 200 | 201 | USED(m); 202 | 203 | if(v == 0) 204 | return; 205 | x = v; 206 | c = x->client; 207 | d = x->dimage; 208 | for(ref=c->refresh; ref; ref=ref->next) 209 | if(ref->dimage == d){ 210 | combinerect(&ref->r, r); 211 | return; 212 | } 213 | ref = mallocz(sizeof(Refresh), 1); 214 | if(ref){ 215 | ref->dimage = d; 216 | ref->r = r; 217 | ref->next = c->refresh; 218 | c->refresh = ref; 219 | } 220 | } 221 | 222 | static void 223 | addflush(Rectangle r) 224 | { 225 | int abb, ar, anbb; 226 | Rectangle nbb; 227 | 228 | if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r)) 229 | return; 230 | 231 | if(flushrect.min.x >= flushrect.max.x){ 232 | flushrect = r; 233 | waste = 0; 234 | return; 235 | } 236 | nbb = flushrect; 237 | combinerect(&nbb, r); 238 | ar = Dx(r)*Dy(r); 239 | abb = Dx(flushrect)*Dy(flushrect); 240 | anbb = Dx(nbb)*Dy(nbb); 241 | /* 242 | * Area of new waste is area of new bb minus area of old bb, 243 | * less the area of the new segment, which we assume is not waste. 244 | * This could be negative, but that's OK. 245 | */ 246 | waste += anbb-abb - ar; 247 | if(waste < 0) 248 | waste = 0; 249 | /* 250 | * absorb if: 251 | * total area is small 252 | * waste is less than half total area 253 | * rectangles touch 254 | */ 255 | if(anbb<=1024 || waste*2layer; 282 | if(l == nil) 283 | return; 284 | do{ 285 | if(l->screen->image->data != screenimage->data) 286 | return; 287 | r = rectaddpt(r, l->delta); 288 | l = l->screen->image->layer; 289 | }while(l); 290 | addflush(r); 291 | } 292 | 293 | static 294 | void 295 | drawflush(void) 296 | { 297 | if(flushrect.min.x < flushrect.max.x) 298 | _flushmemscreen(flushrect); 299 | flushrect = Rect(10000, 10000, -10000, -10000); 300 | } 301 | 302 | static 303 | int 304 | drawcmp(char *a, char *b, int n) 305 | { 306 | if(strlen(a) != n) 307 | return 1; 308 | return memcmp(a, b, n); 309 | } 310 | 311 | static 312 | DName* 313 | drawlookupname(int n, char *str) 314 | { 315 | DName *name, *ename; 316 | 317 | name = sdraw.name; 318 | ename = &name[sdraw.nname]; 319 | for(; namename, str, n) == 0) 321 | return name; 322 | return 0; 323 | } 324 | 325 | static 326 | int 327 | drawgoodname(DImage *d) 328 | { 329 | DName *n; 330 | 331 | /* if window, validate the screen's own images */ 332 | if(d->dscreen) 333 | if(drawgoodname(d->dscreen->dimage) == 0 334 | || drawgoodname(d->dscreen->dfill) == 0) 335 | return 0; 336 | if(d->name == nil) 337 | return 1; 338 | n = drawlookupname(strlen(d->name), d->name); 339 | if(n==nil || n->vers!=d->vers) 340 | return 0; 341 | return 1; 342 | } 343 | 344 | static 345 | DImage* 346 | drawlookup(Client *client, int id, int checkname) 347 | { 348 | DImage *d; 349 | 350 | d = client->dimage[id&HASHMASK]; 351 | while(d){ 352 | if(d->id == id){ 353 | /* 354 | * BUG: should error out but too hard. 355 | * Return 0 instead. 356 | */ 357 | if(checkname && !drawgoodname(d)) 358 | return 0; 359 | return d; 360 | } 361 | d = d->next; 362 | } 363 | return 0; 364 | } 365 | 366 | static 367 | DScreen* 368 | drawlookupdscreen(int id) 369 | { 370 | DScreen *s; 371 | 372 | s = dscreen; 373 | while(s){ 374 | if(s->id == id) 375 | return s; 376 | s = s->next; 377 | } 378 | return 0; 379 | } 380 | 381 | static 382 | DScreen* 383 | drawlookupscreen(Client *client, int id, CScreen **cs) 384 | { 385 | CScreen *s; 386 | 387 | s = client->cscreen; 388 | while(s){ 389 | if(s->dscreen->id == id){ 390 | *cs = s; 391 | return s->dscreen; 392 | } 393 | s = s->next; 394 | } 395 | /* caller must check! */ 396 | return 0; 397 | } 398 | 399 | static 400 | Memimage* 401 | drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) 402 | { 403 | DImage *d; 404 | 405 | d = mallocz(sizeof(DImage), 1); 406 | if(d == 0) 407 | return 0; 408 | d->id = id; 409 | d->ref = 1; 410 | d->name = 0; 411 | d->vers = 0; 412 | d->image = i; 413 | if(i->screenref) 414 | ++i->screenref; 415 | d->nfchar = 0; 416 | d->fchar = 0; 417 | d->fromname = 0; 418 | d->dscreen = dscreen; 419 | d->next = client->dimage[id&HASHMASK]; 420 | client->dimage[id&HASHMASK] = d; 421 | return i; 422 | } 423 | 424 | static 425 | Memscreen* 426 | drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) 427 | { 428 | Memscreen *s; 429 | CScreen *c; 430 | 431 | c = mallocz(sizeof(CScreen), 1); 432 | if(dimage && dimage->image && dimage->image->chan == 0){ 433 | fprint(2, "bad image %p in drawinstallscreen", dimage->image); 434 | abort(); 435 | } 436 | 437 | if(c == 0) 438 | return 0; 439 | if(d == 0){ 440 | d = mallocz(sizeof(DScreen), 1); 441 | if(d == 0){ 442 | free(c); 443 | return 0; 444 | } 445 | s = mallocz(sizeof(Memscreen), 1); 446 | if(s == 0){ 447 | free(c); 448 | free(d); 449 | return 0; 450 | } 451 | s->frontmost = 0; 452 | s->rearmost = 0; 453 | d->dimage = dimage; 454 | if(dimage){ 455 | s->image = dimage->image; 456 | dimage->ref++; 457 | } 458 | d->dfill = dfill; 459 | if(dfill){ 460 | s->fill = dfill->image; 461 | dfill->ref++; 462 | } 463 | d->ref = 0; 464 | d->id = id; 465 | d->screen = s; 466 | d->public = public; 467 | d->next = dscreen; 468 | d->owner = client; 469 | dscreen = d; 470 | } 471 | c->dscreen = d; 472 | d->ref++; 473 | c->next = client->cscreen; 474 | client->cscreen = c; 475 | return d->screen; 476 | } 477 | 478 | static 479 | void 480 | drawdelname(DName *name) 481 | { 482 | int i; 483 | 484 | i = name-sdraw.name; 485 | memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); 486 | sdraw.nname--; 487 | } 488 | 489 | static 490 | void 491 | drawfreedscreen(DScreen *this) 492 | { 493 | DScreen *ds, *next; 494 | 495 | this->ref--; 496 | if(this->ref < 0) 497 | fprint(2, "negative ref in drawfreedscreen\n"); 498 | if(this->ref > 0) 499 | return; 500 | ds = dscreen; 501 | if(ds == this){ 502 | dscreen = this->next; 503 | goto Found; 504 | } 505 | while(next = ds->next){ /* assign = */ 506 | if(next == this){ 507 | ds->next = this->next; 508 | goto Found; 509 | } 510 | ds = next; 511 | } 512 | /* 513 | * Should signal Enodrawimage, but too hard. 514 | */ 515 | return; 516 | 517 | Found: 518 | if(this->dimage) 519 | drawfreedimage(this->dimage); 520 | if(this->dfill) 521 | drawfreedimage(this->dfill); 522 | free(this->screen); 523 | free(this); 524 | } 525 | 526 | static 527 | void 528 | drawfreedimage(DImage *dimage) 529 | { 530 | int i; 531 | Memimage *l; 532 | DScreen *ds; 533 | 534 | dimage->ref--; 535 | if(dimage->ref < 0) 536 | fprint(2, "negative ref in drawfreedimage\n"); 537 | if(dimage->ref > 0) 538 | return; 539 | 540 | /* any names? */ 541 | for(i=0; ifromname){ /* acquired by name; owned by someone else*/ 547 | drawfreedimage(dimage->fromname); 548 | goto Return; 549 | } 550 | ds = dimage->dscreen; 551 | l = dimage->image; 552 | dimage->dscreen = nil; /* paranoia */ 553 | dimage->image = nil; 554 | if(ds){ 555 | if(l->data == screenimage->data) 556 | addflush(l->layer->screenr); 557 | if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ 558 | free(l->layer->refreshptr); 559 | l->layer->refreshptr = nil; 560 | if(drawgoodname(dimage)) 561 | memldelete(l); 562 | else 563 | memlfree(l); 564 | drawfreedscreen(ds); 565 | }else{ 566 | if(l->screenref==0) 567 | freememimage(l); 568 | else if(--l->screenref==0) 569 | _freememimage(l); 570 | } 571 | Return: 572 | free(dimage->fchar); 573 | free(dimage); 574 | } 575 | 576 | static 577 | void 578 | drawuninstallscreen(Client *client, CScreen *this) 579 | { 580 | CScreen *cs, *next; 581 | 582 | cs = client->cscreen; 583 | if(cs == this){ 584 | client->cscreen = this->next; 585 | drawfreedscreen(this->dscreen); 586 | free(this); 587 | return; 588 | } 589 | while(next = cs->next){ /* assign = */ 590 | if(next == this){ 591 | cs->next = this->next; 592 | drawfreedscreen(this->dscreen); 593 | free(this); 594 | return; 595 | } 596 | cs = next; 597 | } 598 | } 599 | 600 | static 601 | int 602 | drawuninstall(Client *client, int id) 603 | { 604 | DImage *d, **l; 605 | 606 | for(l=&client->dimage[id&HASHMASK]; (d=*l) != nil; l=&d->next){ 607 | if(d->id == id){ 608 | *l = d->next; 609 | drawfreedimage(d); 610 | return 0; 611 | } 612 | } 613 | return -1; 614 | } 615 | 616 | static 617 | int 618 | drawaddname(Client *client, DImage *di, int n, char *str, char **err) 619 | { 620 | DName *name, *ename, *new, *t; 621 | char *ns; 622 | 623 | name = sdraw.name; 624 | ename = &name[sdraw.nname]; 625 | for(; namename, str, n) == 0){ 627 | *err = "image name in use"; 628 | return -1; 629 | } 630 | t = mallocz((sdraw.nname+1)*sizeof(DName), 1); 631 | ns = malloc(n+1); 632 | if(t == nil || ns == nil){ 633 | free(t); 634 | free(ns); 635 | *err = "out of memory"; 636 | return -1; 637 | } 638 | memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); 639 | free(sdraw.name); 640 | sdraw.name = t; 641 | new = &sdraw.name[sdraw.nname++]; 642 | new->name = ns; 643 | memmove(new->name, str, n); 644 | new->name[n] = 0; 645 | new->dimage = di; 646 | new->client = client; 647 | new->vers = ++sdraw.vers; 648 | return 0; 649 | } 650 | 651 | static int 652 | drawclientop(Client *cl) 653 | { 654 | int op; 655 | 656 | op = cl->op; 657 | cl->op = SoverD; 658 | return op; 659 | } 660 | 661 | static 662 | Memimage* 663 | drawimage(Client *client, uchar *a) 664 | { 665 | DImage *d; 666 | 667 | d = drawlookup(client, BGLONG(a), 1); 668 | if(d == nil) 669 | return nil; /* caller must check! */ 670 | return d->image; 671 | } 672 | 673 | static 674 | void 675 | drawrectangle(Rectangle *r, uchar *a) 676 | { 677 | r->min.x = BGLONG(a+0*4); 678 | r->min.y = BGLONG(a+1*4); 679 | r->max.x = BGLONG(a+2*4); 680 | r->max.y = BGLONG(a+3*4); 681 | } 682 | 683 | static 684 | void 685 | drawpoint(Point *p, uchar *a) 686 | { 687 | p->x = BGLONG(a+0*4); 688 | p->y = BGLONG(a+1*4); 689 | } 690 | 691 | static 692 | Point 693 | drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) 694 | { 695 | FChar *fc; 696 | Rectangle r; 697 | Point sp1; 698 | 699 | fc = &font->fchar[index]; 700 | r.min.x = p.x+fc->left; 701 | r.min.y = p.y-(font->ascent-fc->miny); 702 | r.max.x = r.min.x+(fc->maxx-fc->minx); 703 | r.max.y = r.min.y+(fc->maxy-fc->miny); 704 | sp1.x = sp->x+fc->left; 705 | sp1.y = sp->y+fc->miny; 706 | memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); 707 | p.x += fc->width; 708 | sp->x += fc->width; 709 | return p; 710 | } 711 | 712 | static 713 | uchar* 714 | drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) 715 | { 716 | int b, x; 717 | 718 | if(p >= maxp) 719 | return nil; 720 | b = *p++; 721 | x = b & 0x7F; 722 | if(b & 0x80){ 723 | if(p+1 >= maxp) 724 | return nil; 725 | x |= *p++ << 7; 726 | x |= *p++ << 15; 727 | if(x & (1<<22)) 728 | x |= ~0<<23; 729 | }else{ 730 | if(b & 0x40) 731 | x |= ~0<<7; 732 | x += oldx; 733 | } 734 | *newx = x; 735 | return p; 736 | } 737 | 738 | int 739 | _drawmsgread(void *a, int n) 740 | { 741 | Client *cl; 742 | 743 | qlock(&sdraw.lk); 744 | cl = client0; 745 | if(cl->readdata == nil){ 746 | werrstr("no draw data"); 747 | goto err; 748 | } 749 | if(n < cl->nreaddata){ 750 | werrstr("short read"); 751 | goto err; 752 | } 753 | n = cl->nreaddata; 754 | memmove(a, cl->readdata, cl->nreaddata); 755 | free(cl->readdata); 756 | cl->readdata = nil; 757 | qunlock(&sdraw.lk); 758 | return n; 759 | 760 | err: 761 | qunlock(&sdraw.lk); 762 | return -1; 763 | } 764 | 765 | int 766 | _drawmsgwrite(void *v, int n) 767 | { 768 | char cbuf[40], *err, ibuf[12*12+1], *s; 769 | int c, ci, doflush, dstid, e0, e1, esize, j, m; 770 | int ni, nw, oesize, oldn, op, ox, oy, repl, scrnid, y; 771 | uchar *a, refresh, *u; 772 | u32int chan, value; 773 | Client *client; 774 | CScreen *cs; 775 | DImage *di, *ddst, *dsrc, *font, *ll; 776 | DName *dn; 777 | DScreen *dscrn; 778 | FChar *fc; 779 | Memimage *dst, *i, *l, **lp, *mask, *src; 780 | Memscreen *scrn; 781 | Point p, *pp, q, sp; 782 | Rectangle clipr, r; 783 | Refreshfn reffn; 784 | Refx *refx; 785 | 786 | qlock(&sdraw.lk); 787 | a = v; 788 | m = 0; 789 | oldn = n; 790 | client = client0; 791 | 792 | while((n-=m) > 0){ 793 | a += m; 794 | /*fprint(2, "msgwrite %d(%d)...", n, *a); */ 795 | switch(*a){ 796 | default: 797 | /*fprint(2, "bad command %d\n", *a); */ 798 | err = "bad draw command"; 799 | goto error; 800 | 801 | /* allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] 802 | R[4*4] clipR[4*4] rrggbbaa[4] 803 | */ 804 | case 'b': 805 | m = 1+4+4+1+4+1+4*4+4*4+4; 806 | if(n < m) 807 | goto Eshortdraw; 808 | dstid = BGLONG(a+1); 809 | scrnid = BGSHORT(a+5); 810 | refresh = a[9]; 811 | chan = BGLONG(a+10); 812 | repl = a[14]; 813 | drawrectangle(&r, a+15); 814 | drawrectangle(&clipr, a+31); 815 | value = BGLONG(a+47); 816 | if(drawlookup(client, dstid, 0)) 817 | goto Eimageexists; 818 | if(scrnid){ 819 | dscrn = drawlookupscreen(client, scrnid, &cs); 820 | if(!dscrn) 821 | goto Enodrawscreen; 822 | scrn = dscrn->screen; 823 | if(repl || chan!=scrn->image->chan){ 824 | err = "image parameters incompatibile with screen"; 825 | goto error; 826 | } 827 | reffn = 0; 828 | switch(refresh){ 829 | case Refbackup: 830 | break; 831 | case Refnone: 832 | reffn = memlnorefresh; 833 | break; 834 | case Refmesg: 835 | reffn = drawrefresh; 836 | break; 837 | default: 838 | err = "unknown refresh method"; 839 | goto error; 840 | } 841 | l = memlalloc(scrn, r, reffn, 0, value); 842 | if(l == 0) 843 | goto Edrawmem; 844 | addflush(l->layer->screenr); 845 | l->clipr = clipr; 846 | rectclip(&l->clipr, r); 847 | if(drawinstall(client, dstid, l, dscrn) == 0){ 848 | memldelete(l); 849 | goto Edrawmem; 850 | } 851 | dscrn->ref++; 852 | if(reffn){ 853 | refx = nil; 854 | if(reffn == drawrefresh){ 855 | refx = mallocz(sizeof(Refx), 1); 856 | if(refx == 0){ 857 | if(drawuninstall(client, dstid) < 0) 858 | goto Enodrawimage; 859 | goto Edrawmem; 860 | } 861 | refx->client = client; 862 | refx->dimage = drawlookup(client, dstid, 1); 863 | } 864 | memlsetrefresh(l, reffn, refx); 865 | } 866 | continue; 867 | } 868 | i = allocmemimage(r, chan); 869 | if(i == 0) 870 | goto Edrawmem; 871 | if(repl) 872 | i->flags |= Frepl; 873 | i->clipr = clipr; 874 | if(!repl) 875 | rectclip(&i->clipr, r); 876 | if(drawinstall(client, dstid, i, 0) == 0){ 877 | freememimage(i); 878 | goto Edrawmem; 879 | } 880 | memfillcolor(i, value); 881 | continue; 882 | 883 | /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ 884 | case 'A': 885 | m = 1+4+4+4+1; 886 | if(n < m) 887 | goto Eshortdraw; 888 | dstid = BGLONG(a+1); 889 | if(dstid == 0) 890 | goto Ebadarg; 891 | if(drawlookupdscreen(dstid)) 892 | goto Escreenexists; 893 | ddst = drawlookup(client, BGLONG(a+5), 1); 894 | dsrc = drawlookup(client, BGLONG(a+9), 1); 895 | if(ddst==0 || dsrc==0) 896 | goto Enodrawimage; 897 | if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0) 898 | goto Edrawmem; 899 | continue; 900 | 901 | /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ 902 | case 'c': 903 | m = 1+4+1+4*4; 904 | if(n < m) 905 | goto Eshortdraw; 906 | ddst = drawlookup(client, BGLONG(a+1), 1); 907 | if(ddst == nil) 908 | goto Enodrawimage; 909 | if(ddst->name){ 910 | err = "can't change repl/clipr of shared image"; 911 | goto error; 912 | } 913 | dst = ddst->image; 914 | if(a[5]) 915 | dst->flags |= Frepl; 916 | drawrectangle(&dst->clipr, a+6); 917 | continue; 918 | 919 | /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */ 920 | case 'd': 921 | m = 1+4+4+4+4*4+2*4+2*4; 922 | if(n < m) 923 | goto Eshortdraw; 924 | dst = drawimage(client, a+1); 925 | dstid = BGLONG(a+1); 926 | src = drawimage(client, a+5); 927 | mask = drawimage(client, a+9); 928 | if(!dst || !src || !mask) 929 | goto Enodrawimage; 930 | drawrectangle(&r, a+13); 931 | drawpoint(&p, a+29); 932 | drawpoint(&q, a+37); 933 | op = drawclientop(client); 934 | memdraw(dst, r, src, p, mask, q, op); 935 | dstflush(dstid, dst, r); 936 | continue; 937 | 938 | /* toggle debugging: 'D' val[1] */ 939 | case 'D': 940 | m = 1+1; 941 | if(n < m) 942 | goto Eshortdraw; 943 | drawdebug = a[1]; 944 | continue; 945 | 946 | /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/ 947 | case 'e': 948 | case 'E': 949 | m = 1+4+4+2*4+4+4+4+2*4+2*4; 950 | if(n < m) 951 | goto Eshortdraw; 952 | dst = drawimage(client, a+1); 953 | dstid = BGLONG(a+1); 954 | src = drawimage(client, a+5); 955 | if(!dst || !src) 956 | goto Enodrawimage; 957 | drawpoint(&p, a+9); 958 | e0 = BGLONG(a+17); 959 | e1 = BGLONG(a+21); 960 | if(e0<0 || e1<0){ 961 | err = "invalid ellipse semidiameter"; 962 | goto error; 963 | } 964 | j = BGLONG(a+25); 965 | if(j < 0){ 966 | err = "negative ellipse thickness"; 967 | goto error; 968 | } 969 | 970 | drawpoint(&sp, a+29); 971 | c = j; 972 | if(*a == 'E') 973 | c = -1; 974 | ox = BGLONG(a+37); 975 | oy = BGLONG(a+41); 976 | op = drawclientop(client); 977 | /* high bit indicates arc angles are present */ 978 | if(ox & ((ulong)1<<31)){ 979 | if((ox & ((ulong)1<<30)) == 0) 980 | ox &= ~((ulong)1<<31); 981 | memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); 982 | }else 983 | memellipse(dst, p, e0, e1, c, src, sp, op); 984 | dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); 985 | continue; 986 | 987 | /* free: 'f' id[4] */ 988 | case 'f': 989 | m = 1+4; 990 | if(n < m) 991 | goto Eshortdraw; 992 | ll = drawlookup(client, BGLONG(a+1), 0); 993 | if(ll && ll->dscreen && ll->dscreen->owner != client) 994 | ll->dscreen->owner->refreshme = 1; 995 | if(drawuninstall(client, BGLONG(a+1)) < 0) 996 | goto Enodrawimage; 997 | continue; 998 | 999 | /* free screen: 'F' id[4] */ 1000 | case 'F': 1001 | m = 1+4; 1002 | if(n < m) 1003 | goto Eshortdraw; 1004 | if(!drawlookupscreen(client, BGLONG(a+1), &cs)) 1005 | goto Enodrawscreen; 1006 | drawuninstallscreen(client, cs); 1007 | continue; 1008 | 1009 | /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ 1010 | case 'i': 1011 | m = 1+4+4+1; 1012 | if(n < m) 1013 | goto Eshortdraw; 1014 | dstid = BGLONG(a+1); 1015 | if(dstid == 0){ 1016 | err = "can't use display as font"; 1017 | goto error; 1018 | } 1019 | font = drawlookup(client, dstid, 1); 1020 | if(font == 0) 1021 | goto Enodrawimage; 1022 | if(font->image->layer){ 1023 | err = "can't use window as font"; 1024 | goto error; 1025 | } 1026 | ni = BGLONG(a+5); 1027 | if(ni<=0 || ni>4096){ 1028 | err = "bad font size (4096 chars max)"; 1029 | goto error; 1030 | } 1031 | free(font->fchar); /* should we complain if non-zero? */ 1032 | font->fchar = mallocz(ni*sizeof(FChar), 1); 1033 | if(font->fchar == 0){ 1034 | err = "no memory for font"; 1035 | goto error; 1036 | } 1037 | memset(font->fchar, 0, ni*sizeof(FChar)); 1038 | font->nfchar = ni; 1039 | font->ascent = a[9]; 1040 | continue; 1041 | 1042 | /* set image 0 to screen image */ 1043 | case 'J': 1044 | m = 1; 1045 | if(n < m) 1046 | goto Eshortdraw; 1047 | if(drawlookup(client, 0, 0)) 1048 | goto Eimageexists; 1049 | drawinstall(client, 0, screenimage, 0); 1050 | client->infoid = 0; 1051 | continue; 1052 | 1053 | /* get image info: 'I' */ 1054 | case 'I': 1055 | m = 1; 1056 | if(n < m) 1057 | goto Eshortdraw; 1058 | if(client->infoid < 0) 1059 | goto Enodrawimage; 1060 | if(client->infoid == 0){ 1061 | i = screenimage; 1062 | if(i == nil) 1063 | goto Enodrawimage; 1064 | }else{ 1065 | di = drawlookup(client, client->infoid, 1); 1066 | if(di == nil) 1067 | goto Enodrawimage; 1068 | i = di->image; 1069 | } 1070 | ni = sprint(ibuf, "%11d %11d %11s %11d %11d %11d %11d %11d" 1071 | " %11d %11d %11d %11d ", 1072 | client->clientid, 1073 | client->infoid, 1074 | chantostr(cbuf, i->chan), 1075 | (i->flags&Frepl)==Frepl, 1076 | i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, 1077 | i->clipr.min.x, i->clipr.min.y, 1078 | i->clipr.max.x, i->clipr.max.y); 1079 | free(client->readdata); 1080 | client->readdata = malloc(ni); 1081 | if(client->readdata == nil) 1082 | goto Enomem; 1083 | memmove(client->readdata, ibuf, ni); 1084 | client->nreaddata = ni; 1085 | client->infoid = -1; 1086 | continue; 1087 | 1088 | /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */ 1089 | case 'l': 1090 | m = 1+4+4+2+4*4+2*4+1+1; 1091 | if(n < m) 1092 | goto Eshortdraw; 1093 | font = drawlookup(client, BGLONG(a+1), 1); 1094 | if(font == 0) 1095 | goto Enodrawimage; 1096 | if(font->nfchar == 0) 1097 | goto Enotfont; 1098 | src = drawimage(client, a+5); 1099 | if(!src) 1100 | goto Enodrawimage; 1101 | ci = BGSHORT(a+9); 1102 | if(ci >= font->nfchar) 1103 | goto Eindex; 1104 | drawrectangle(&r, a+11); 1105 | drawpoint(&p, a+27); 1106 | memdraw(font->image, r, src, p, memopaque, p, S); 1107 | fc = &font->fchar[ci]; 1108 | fc->minx = r.min.x; 1109 | fc->maxx = r.max.x; 1110 | fc->miny = r.min.y; 1111 | fc->maxy = r.max.y; 1112 | fc->left = a[35]; 1113 | fc->width = a[36]; 1114 | continue; 1115 | 1116 | /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */ 1117 | case 'L': 1118 | m = 1+4+2*4+2*4+4+4+4+4+2*4; 1119 | if(n < m) 1120 | goto Eshortdraw; 1121 | dst = drawimage(client, a+1); 1122 | dstid = BGLONG(a+1); 1123 | drawpoint(&p, a+5); 1124 | drawpoint(&q, a+13); 1125 | e0 = BGLONG(a+21); 1126 | e1 = BGLONG(a+25); 1127 | j = BGLONG(a+29); 1128 | if(j < 0){ 1129 | err = "negative line width"; 1130 | goto error; 1131 | } 1132 | src = drawimage(client, a+33); 1133 | if(!dst || !src) 1134 | goto Enodrawimage; 1135 | drawpoint(&sp, a+37); 1136 | op = drawclientop(client); 1137 | memline(dst, p, q, e0, e1, j, src, sp, op); 1138 | /* avoid memlinebbox if possible */ 1139 | if(dstid==0 || dst->layer!=nil){ 1140 | /* BUG: this is terribly inefficient: update maximal containing rect*/ 1141 | r = memlinebbox(p, q, e0, e1, j); 1142 | dstflush(dstid, dst, insetrect(r, -(1+1+j))); 1143 | } 1144 | continue; 1145 | 1146 | /* create image mask: 'm' newid[4] id[4] */ 1147 | /* 1148 | * 1149 | case 'm': 1150 | m = 4+4; 1151 | if(n < m) 1152 | goto Eshortdraw; 1153 | break; 1154 | * 1155 | */ 1156 | 1157 | /* attach to a named image: 'n' dstid[4] j[1] name[j] */ 1158 | case 'n': 1159 | m = 1+4+1; 1160 | if(n < m) 1161 | goto Eshortdraw; 1162 | j = a[5]; 1163 | if(j == 0) /* give me a non-empty name please */ 1164 | goto Eshortdraw; 1165 | m += j; 1166 | if(n < m) 1167 | goto Eshortdraw; 1168 | dstid = BGLONG(a+1); 1169 | if(drawlookup(client, dstid, 0)) 1170 | goto Eimageexists; 1171 | dn = drawlookupname(j, (char*)a+6); 1172 | if(dn == nil) 1173 | goto Enoname; 1174 | s = malloc(j+1); 1175 | if(s == nil) 1176 | goto Enomem; 1177 | if(drawinstall(client, dstid, dn->dimage->image, 0) == 0) 1178 | goto Edrawmem; 1179 | di = drawlookup(client, dstid, 0); 1180 | if(di == 0) 1181 | goto Eoldname; 1182 | di->vers = dn->vers; 1183 | di->name = s; 1184 | di->fromname = dn->dimage; 1185 | di->fromname->ref++; 1186 | memmove(di->name, a+6, j); 1187 | di->name[j] = 0; 1188 | client->infoid = dstid; 1189 | continue; 1190 | 1191 | /* name an image: 'N' dstid[4] in[1] j[1] name[j] */ 1192 | case 'N': 1193 | m = 1+4+1+1; 1194 | if(n < m) 1195 | goto Eshortdraw; 1196 | c = a[5]; 1197 | j = a[6]; 1198 | if(j == 0) /* give me a non-empty name please */ 1199 | goto Eshortdraw; 1200 | m += j; 1201 | if(n < m) 1202 | goto Eshortdraw; 1203 | di = drawlookup(client, BGLONG(a+1), 0); 1204 | if(di == 0) 1205 | goto Enodrawimage; 1206 | if(di->name) 1207 | goto Enamed; 1208 | if(c) 1209 | if(drawaddname(client, di, j, (char*)a+7, &err) < 0) 1210 | goto error; 1211 | else{ 1212 | dn = drawlookupname(j, (char*)a+7); 1213 | if(dn == nil) 1214 | goto Enoname; 1215 | if(dn->dimage != di) 1216 | goto Ewrongname; 1217 | drawdelname(dn); 1218 | } 1219 | continue; 1220 | 1221 | /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ 1222 | case 'o': 1223 | m = 1+4+2*4+2*4; 1224 | if(n < m) 1225 | goto Eshortdraw; 1226 | dst = drawimage(client, a+1); 1227 | if(!dst) 1228 | goto Enodrawimage; 1229 | if(dst->layer){ 1230 | drawpoint(&p, a+5); 1231 | drawpoint(&q, a+13); 1232 | r = dst->layer->screenr; 1233 | ni = memlorigin(dst, p, q); 1234 | if(ni < 0){ 1235 | err = "image origin failed"; 1236 | goto error; 1237 | } 1238 | if(ni > 0){ 1239 | addflush(r); 1240 | addflush(dst->layer->screenr); 1241 | ll = drawlookup(client, BGLONG(a+1), 1); 1242 | drawrefreshscreen(ll, client); 1243 | } 1244 | } 1245 | continue; 1246 | 1247 | /* set compositing operator for next draw operation: 'O' op */ 1248 | case 'O': 1249 | m = 1+1; 1250 | if(n < m) 1251 | goto Eshortdraw; 1252 | client->op = a[1]; 1253 | continue; 1254 | 1255 | /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ 1256 | /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ 1257 | case 'p': 1258 | case 'P': 1259 | m = 1+4+2+4+4+4+4+2*4; 1260 | if(n < m) 1261 | goto Eshortdraw; 1262 | dstid = BGLONG(a+1); 1263 | dst = drawimage(client, a+1); 1264 | ni = BGSHORT(a+5); 1265 | if(ni < 0){ 1266 | err = "negative cout in polygon"; 1267 | goto error; 1268 | } 1269 | e0 = BGLONG(a+7); 1270 | e1 = BGLONG(a+11); 1271 | j = 0; 1272 | if(*a == 'p'){ 1273 | j = BGLONG(a+15); 1274 | if(j < 0){ 1275 | err = "negative polygon line width"; 1276 | goto error; 1277 | } 1278 | } 1279 | src = drawimage(client, a+19); 1280 | if(!dst || !src) 1281 | goto Enodrawimage; 1282 | drawpoint(&sp, a+23); 1283 | drawpoint(&p, a+31); 1284 | ni++; 1285 | pp = mallocz(ni*sizeof(Point), 1); 1286 | if(pp == nil) 1287 | goto Enomem; 1288 | doflush = 0; 1289 | if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data)) 1290 | doflush = 1; /* simplify test in loop */ 1291 | ox = oy = 0; 1292 | esize = 0; 1293 | u = a+m; 1294 | for(y=0; y esize) 1311 | esize = c; 1312 | } 1313 | if(y == ni-1){ 1314 | c = memlineendsize(e1); 1315 | if(c > esize) 1316 | esize = c; 1317 | } 1318 | } 1319 | if(*a=='P' && e0!=1 && e0 !=~0) 1320 | r = dst->clipr; 1321 | else if(y > 0){ 1322 | r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); 1323 | combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); 1324 | } 1325 | if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ 1326 | dstflush(dstid, dst, r); 1327 | } 1328 | pp[y] = p; 1329 | } 1330 | if(y == 1) 1331 | dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); 1332 | op = drawclientop(client); 1333 | if(*a == 'p') 1334 | mempoly(dst, pp, ni, e0, e1, j, src, sp, op); 1335 | else 1336 | memfillpoly(dst, pp, ni, e0, src, sp, op); 1337 | free(pp); 1338 | m = u-a; 1339 | continue; 1340 | 1341 | /* read: 'r' id[4] R[4*4] */ 1342 | case 'r': 1343 | m = 1+4+4*4; 1344 | if(n < m) 1345 | goto Eshortdraw; 1346 | i = drawimage(client, a+1); 1347 | if(!i) 1348 | goto Enodrawimage; 1349 | drawrectangle(&r, a+5); 1350 | if(!rectinrect(r, i->r)) 1351 | goto Ereadoutside; 1352 | c = bytesperline(r, i->depth); 1353 | c *= Dy(r); 1354 | free(client->readdata); 1355 | client->readdata = mallocz(c, 0); 1356 | if(client->readdata == nil){ 1357 | err = "readimage malloc failed"; 1358 | goto error; 1359 | } 1360 | client->nreaddata = memunload(i, r, client->readdata, c); 1361 | if(client->nreaddata < 0){ 1362 | free(client->readdata); 1363 | client->readdata = nil; 1364 | err = "bad readimage call"; 1365 | goto error; 1366 | } 1367 | continue; 1368 | 1369 | /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */ 1370 | /* 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]) */ 1371 | case 's': 1372 | case 'x': 1373 | m = 1+4+4+4+2*4+4*4+2*4+2; 1374 | if(*a == 'x') 1375 | m += 4+2*4; 1376 | if(n < m) 1377 | goto Eshortdraw; 1378 | 1379 | dst = drawimage(client, a+1); 1380 | dstid = BGLONG(a+1); 1381 | src = drawimage(client, a+5); 1382 | if(!dst || !src) 1383 | goto Enodrawimage; 1384 | font = drawlookup(client, BGLONG(a+9), 1); 1385 | if(font == 0) 1386 | goto Enodrawimage; 1387 | if(font->nfchar == 0) 1388 | goto Enotfont; 1389 | drawpoint(&p, a+13); 1390 | drawrectangle(&r, a+21); 1391 | drawpoint(&sp, a+37); 1392 | ni = BGSHORT(a+45); 1393 | u = a+m; 1394 | m += ni*2; 1395 | if(n < m) 1396 | goto Eshortdraw; 1397 | clipr = dst->clipr; 1398 | dst->clipr = r; 1399 | op = drawclientop(client); 1400 | if(*a == 'x'){ 1401 | /* paint background */ 1402 | l = drawimage(client, a+47); 1403 | if(!l) 1404 | goto Enodrawimage; 1405 | drawpoint(&q, a+51); 1406 | r.min.x = p.x; 1407 | r.min.y = p.y-font->ascent; 1408 | r.max.x = p.x; 1409 | r.max.y = r.min.y+Dy(font->image->r); 1410 | j = ni; 1411 | while(--j >= 0){ 1412 | ci = BGSHORT(u); 1413 | if(ci<0 || ci>=font->nfchar){ 1414 | dst->clipr = clipr; 1415 | goto Eindex; 1416 | } 1417 | r.max.x += font->fchar[ci].width; 1418 | u += 2; 1419 | } 1420 | memdraw(dst, r, l, q, memopaque, ZP, op); 1421 | u -= 2*ni; 1422 | } 1423 | q = p; 1424 | while(--ni >= 0){ 1425 | ci = BGSHORT(u); 1426 | if(ci<0 || ci>=font->nfchar){ 1427 | dst->clipr = clipr; 1428 | goto Eindex; 1429 | } 1430 | q = drawchar(dst, q, src, &sp, font, ci, op); 1431 | u += 2; 1432 | } 1433 | dst->clipr = clipr; 1434 | p.y -= font->ascent; 1435 | dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); 1436 | continue; 1437 | 1438 | /* use public screen: 'S' id[4] chan[4] */ 1439 | case 'S': 1440 | m = 1+4+4; 1441 | if(n < m) 1442 | goto Eshortdraw; 1443 | dstid = BGLONG(a+1); 1444 | if(dstid == 0) 1445 | goto Ebadarg; 1446 | dscrn = drawlookupdscreen(dstid); 1447 | if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) 1448 | goto Enodrawscreen; 1449 | if(dscrn->screen->image->chan != BGLONG(a+5)){ 1450 | err = "inconsistent chan"; 1451 | goto error; 1452 | } 1453 | if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) 1454 | goto Edrawmem; 1455 | continue; 1456 | 1457 | /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ 1458 | case 't': 1459 | m = 1+1+2; 1460 | if(n < m) 1461 | goto Eshortdraw; 1462 | nw = BGSHORT(a+2); 1463 | if(nw < 0) 1464 | goto Ebadarg; 1465 | if(nw == 0) 1466 | continue; 1467 | m += nw*4; 1468 | if(n < m) 1469 | goto Eshortdraw; 1470 | lp = mallocz(nw*sizeof(Memimage*), 1); 1471 | if(lp == 0) 1472 | goto Enomem; 1473 | for(j=0; jlayer == 0){ 1481 | err = "images are not windows"; 1482 | free(lp); 1483 | goto error; 1484 | } 1485 | for(j=1; jlayer->screen != lp[0]->layer->screen){ 1487 | err = "images not on same screen"; 1488 | free(lp); 1489 | goto error; 1490 | } 1491 | if(a[1]) 1492 | memltofrontn(lp, nw); 1493 | else 1494 | memltorearn(lp, nw); 1495 | if(lp[0]->layer->screen->image->data == screenimage->data) 1496 | for(j=0; jlayer->screenr); 1498 | free(lp); 1499 | ll = drawlookup(client, BGLONG(a+1+1+2), 1); 1500 | drawrefreshscreen(ll, client); 1501 | continue; 1502 | 1503 | /* visible: 'v' */ 1504 | case 'v': 1505 | m = 1; 1506 | drawflush(); 1507 | continue; 1508 | 1509 | /* write: 'y' id[4] R[4*4] data[x*1] */ 1510 | /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */ 1511 | case 'y': 1512 | case 'Y': 1513 | m = 1+4+4*4; 1514 | if(n < m) 1515 | goto Eshortdraw; 1516 | dstid = BGLONG(a+1); 1517 | dst = drawimage(client, a+1); 1518 | if(!dst) 1519 | goto Enodrawimage; 1520 | drawrectangle(&r, a+5); 1521 | if(!rectinrect(r, dst->r)) 1522 | goto Ewriteoutside; 1523 | y = memload(dst, r, a+m, n-m, *a=='Y'); 1524 | if(y < 0){ 1525 | err = "bad writeimage call"; 1526 | goto error; 1527 | } 1528 | dstflush(dstid, dst, r); 1529 | m += y; 1530 | continue; 1531 | } 1532 | } 1533 | qunlock(&sdraw.lk); 1534 | return oldn - n; 1535 | 1536 | Enodrawimage: 1537 | err = "unknown id for draw image"; 1538 | goto error; 1539 | Enodrawscreen: 1540 | err = "unknown id for draw screen"; 1541 | goto error; 1542 | Eshortdraw: 1543 | err = "short draw message"; 1544 | goto error; 1545 | /* 1546 | Eshortread: 1547 | err = "draw read too short"; 1548 | goto error; 1549 | */ 1550 | Eimageexists: 1551 | err = "image id in use"; 1552 | goto error; 1553 | Escreenexists: 1554 | err = "screen id in use"; 1555 | goto error; 1556 | Edrawmem: 1557 | err = "image memory allocation failed"; 1558 | goto error; 1559 | Ereadoutside: 1560 | err = "readimage outside image"; 1561 | goto error; 1562 | Ewriteoutside: 1563 | err = "writeimage outside image"; 1564 | goto error; 1565 | Enotfont: 1566 | err = "image not a font"; 1567 | goto error; 1568 | Eindex: 1569 | err = "character index out of range"; 1570 | goto error; 1571 | /* 1572 | Enoclient: 1573 | err = "no such draw client"; 1574 | goto error; 1575 | Edepth: 1576 | err = "image has bad depth"; 1577 | goto error; 1578 | Enameused: 1579 | err = "image name in use"; 1580 | goto error; 1581 | */ 1582 | Enoname: 1583 | err = "no image with that name"; 1584 | goto error; 1585 | Eoldname: 1586 | err = "named image no longer valid"; 1587 | goto error; 1588 | Enamed: 1589 | err = "image already has name"; 1590 | goto error; 1591 | Ewrongname: 1592 | err = "wrong name for image"; 1593 | goto error; 1594 | Enomem: 1595 | err = "out of memory"; 1596 | goto error; 1597 | Ebadarg: 1598 | err = "bad argument in draw message"; 1599 | goto error; 1600 | 1601 | error: 1602 | werrstr("%s", err); 1603 | qunlock(&sdraw.lk); 1604 | return -1; 1605 | } 1606 | 1607 | 1608 | --------------------------------------------------------------------------------